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:
authorPeter Kim <pk15950@gmail.com>2021-10-03 06:22:05 +0300
committerPeter Kim <pk15950@gmail.com>2021-10-03 06:22:05 +0300
commit6fc81d6bca6424a1e44305df7cdc3598e03b00ba (patch)
treea66f17c5378f2a68f4c5d8b09f56687c3d9bf888
parent85e1f28fcaafd137a546bf192777b00f96851e80 (diff)
parentd3afe0c1265c9ebb53053de68f176b30f0132281 (diff)
Merge branch 'master' into xr-controller-supportxr-controller-support
-rw-r--r--CMakeLists.txt10
-rw-r--r--doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py40
-rw-r--r--extern/CMakeLists.txt5
-rw-r--r--extern/audaspace/bindings/C/AUD_Sound.cpp34
-rw-r--r--extern/hipew/CMakeLists.txt39
-rw-r--r--extern/hipew/include/hipew.h1207
-rw-r--r--extern/hipew/src/hipew.c533
-rw-r--r--intern/cycles/CMakeLists.txt1
-rw-r--r--intern/cycles/app/CMakeLists.txt4
-rw-r--r--intern/cycles/app/cycles_standalone.cpp80
-rw-r--r--intern/cycles/app/cycles_xml.cpp1
-rw-r--r--intern/cycles/app/oiio_output_driver.cpp71
-rw-r--r--intern/cycles/app/oiio_output_driver.h42
-rw-r--r--intern/cycles/blender/CMakeLists.txt9
-rw-r--r--intern/cycles/blender/addon/engine.py2
-rw-r--r--intern/cycles/blender/addon/properties.py17
-rw-r--r--intern/cycles/blender/addon/ui.py11
-rw-r--r--intern/cycles/blender/blender_curves.cpp15
-rw-r--r--intern/cycles/blender/blender_device.cpp4
-rw-r--r--intern/cycles/blender/blender_display_driver.cpp (renamed from intern/cycles/blender/blender_gpu_display.cpp)125
-rw-r--r--intern/cycles/blender/blender_display_driver.h (renamed from intern/cycles/blender/blender_gpu_display.h)37
-rw-r--r--intern/cycles/blender/blender_geometry.cpp6
-rw-r--r--intern/cycles/blender/blender_output_driver.cpp127
-rw-r--r--intern/cycles/blender/blender_output_driver.h40
-rw-r--r--intern/cycles/blender/blender_python.cpp9
-rw-r--r--intern/cycles/blender/blender_session.cpp217
-rw-r--r--intern/cycles/blender/blender_session.h27
-rw-r--r--intern/cycles/blender/blender_shader.cpp17
-rw-r--r--intern/cycles/blender/blender_util.h59
-rw-r--r--intern/cycles/bvh/bvh_embree.cpp2
-rw-r--r--intern/cycles/cmake/external_libs.cmake9
-rw-r--r--intern/cycles/cmake/macros.cmake14
-rw-r--r--intern/cycles/device/CMakeLists.txt64
-rw-r--r--intern/cycles/device/cpu/device_impl.cpp153
-rw-r--r--intern/cycles/device/cpu/device_impl.h7
-rw-r--r--intern/cycles/device/cuda/device_impl.cpp140
-rw-r--r--intern/cycles/device/cuda/device_impl.h5
-rw-r--r--intern/cycles/device/cuda/graphics_interop.cpp15
-rw-r--r--intern/cycles/device/cuda/graphics_interop.h2
-rw-r--r--intern/cycles/device/cuda/queue.cpp54
-rw-r--r--intern/cycles/device/cuda/queue.h2
-rw-r--r--intern/cycles/device/device.cpp43
-rw-r--r--intern/cycles/device/device.h7
-rw-r--r--intern/cycles/device/device_graphics_interop.h19
-rw-r--r--intern/cycles/device/device_memory.cpp4
-rw-r--r--intern/cycles/device/device_memory.h95
-rw-r--r--intern/cycles/device/device_queue.cpp14
-rw-r--r--intern/cycles/device/device_queue.h2
-rw-r--r--intern/cycles/device/dummy/device.cpp2
-rw-r--r--intern/cycles/device/hip/device.cpp276
-rw-r--r--intern/cycles/device/hip/device.h37
-rw-r--r--intern/cycles/device/hip/device_impl.cpp1343
-rw-r--r--intern/cycles/device/hip/device_impl.h153
-rw-r--r--intern/cycles/device/hip/graphics_interop.cpp105
-rw-r--r--intern/cycles/device/hip/graphics_interop.h64
-rw-r--r--intern/cycles/device/hip/kernel.cpp69
-rw-r--r--intern/cycles/device/hip/kernel.h54
-rw-r--r--intern/cycles/device/hip/queue.cpp209
-rw-r--r--intern/cycles/device/hip/queue.h68
-rw-r--r--intern/cycles/device/hip/util.cpp61
-rw-r--r--intern/cycles/device/hip/util.h63
-rw-r--r--intern/cycles/device/multi/device.cpp8
-rw-r--r--intern/cycles/device/optix/device_impl.cpp44
-rw-r--r--intern/cycles/device/optix/device_impl.h3
-rw-r--r--intern/cycles/integrator/CMakeLists.txt4
-rw-r--r--intern/cycles/integrator/path_trace.cpp92
-rw-r--r--intern/cycles/integrator/path_trace.h38
-rw-r--r--intern/cycles/integrator/path_trace_display.cpp (renamed from intern/cycles/render/gpu_display.cpp)101
-rw-r--r--intern/cycles/integrator/path_trace_display.h (renamed from intern/cycles/render/gpu_display.h)102
-rw-r--r--intern/cycles/integrator/path_trace_tile.cpp107
-rw-r--r--intern/cycles/integrator/path_trace_tile.h43
-rw-r--r--intern/cycles/integrator/path_trace_work.cpp8
-rw-r--r--intern/cycles/integrator/path_trace_work.h14
-rw-r--r--intern/cycles/integrator/path_trace_work_cpu.cpp34
-rw-r--r--intern/cycles/integrator/path_trace_work_cpu.h8
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.cpp61
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.h24
-rw-r--r--intern/cycles/integrator/render_scheduler.cpp8
-rw-r--r--intern/cycles/integrator/render_scheduler.h4
-rw-r--r--intern/cycles/integrator/shader_eval.cpp8
-rw-r--r--intern/cycles/kernel/CMakeLists.txt116
-rw-r--r--intern/cycles/kernel/bvh/bvh.h53
-rw-r--r--intern/cycles/kernel/device/gpu/parallel_active_index.h8
-rw-r--r--intern/cycles/kernel/device/gpu/parallel_prefix_sum.h6
-rw-r--r--intern/cycles/kernel/device/gpu/parallel_reduce.h6
-rw-r--r--intern/cycles/kernel/device/gpu/parallel_sorted_index.h6
-rw-r--r--intern/cycles/kernel/device/hip/compat.h121
-rw-r--r--intern/cycles/kernel/device/hip/config.h57
-rw-r--r--intern/cycles/kernel/device/hip/globals.h49
-rw-r--r--intern/cycles/kernel/device/hip/kernel.cpp28
-rw-r--r--intern/cycles/kernel/device/optix/kernel.cu87
-rw-r--r--intern/cycles/kernel/geom/geom_curve_intersect.h6
-rw-r--r--intern/cycles/kernel/geom/geom_motion_triangle.h13
-rw-r--r--intern/cycles/kernel/integrator/integrator_shade_surface.h42
-rw-r--r--intern/cycles/kernel/integrator/integrator_shade_volume.h4
-rw-r--r--intern/cycles/kernel/integrator/integrator_state.h18
-rw-r--r--intern/cycles/kernel/integrator/integrator_state_template.h10
-rw-r--r--intern/cycles/kernel/integrator/integrator_state_util.h11
-rw-r--r--intern/cycles/kernel/kernel_accumulate.h2
-rw-r--r--intern/cycles/kernel/kernel_bake.h21
-rw-r--r--intern/cycles/kernel/kernel_film.h2
-rw-r--r--intern/cycles/kernel/kernel_jitter.h30
-rw-r--r--intern/cycles/kernel/kernel_shader.h13
-rw-r--r--intern/cycles/kernel/kernel_types.h1
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp1
-rw-r--r--intern/cycles/kernel/osl/osl_services.h1
-rw-r--r--intern/cycles/kernel/shaders/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/shaders/node_float_curve.osl32
-rw-r--r--intern/cycles/kernel/shaders/node_hair_info.osl2
-rw-r--r--intern/cycles/kernel/svm/svm.h4
-rw-r--r--intern/cycles/kernel/svm/svm_geometry.h2
-rw-r--r--intern/cycles/kernel/svm/svm_ramp.h66
-rw-r--r--intern/cycles/kernel/svm/svm_types.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt4
-rw-r--r--intern/cycles/render/attribute.cpp5
-rw-r--r--intern/cycles/render/buffers.cpp1
-rw-r--r--intern/cycles/render/display_driver.h131
-rw-r--r--intern/cycles/render/film.cpp3
-rw-r--r--intern/cycles/render/integrator.h2
-rw-r--r--intern/cycles/render/nodes.cpp89
-rw-r--r--intern/cycles/render/nodes.h12
-rw-r--r--intern/cycles/render/osl.cpp4
-rw-r--r--intern/cycles/render/output_driver.h82
-rw-r--r--intern/cycles/render/session.cpp170
-rw-r--r--intern/cycles/render/session.h32
-rw-r--r--intern/cycles/render/tile.cpp72
-rw-r--r--intern/cycles/render/tile.h7
-rw-r--r--intern/cycles/util/util_atomic.h2
-rw-r--r--intern/cycles/util/util_debug.cpp15
-rw-r--r--intern/cycles/util/util_debug.h16
-rw-r--r--intern/cycles/util/util_half.h24
-rw-r--r--intern/cycles/util/util_math.h19
-rw-r--r--intern/cycles/util/util_math_intersect.h2
-rw-r--r--intern/cycles/util/util_progress.h24
-rw-r--r--intern/ghost/GHOST_IWindow.h2
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerSDL.cpp3
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp4
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp4
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp16
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp6
-rw-r--r--intern/guardedalloc/intern/mallocn_guarded_impl.c7
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_impl.cc2
-rw-r--r--intern/opensubdiv/opensubdiv_capi_type.h2
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default_theme.c31
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils.py2
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py11
-rw-r--r--release/scripts/modules/rna_manual_reference.py82
-rw-r--r--release/scripts/modules/rna_prop_ui.py5
-rw-r--r--release/scripts/presets/keyconfig/Blender.py103
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py315
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py3
-rw-r--r--release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py15
-rw-r--r--release/scripts/startup/bl_operators/assets.py42
-rw-r--r--release/scripts/startup/bl_operators/wm.py699
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py1
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py43
-rw-r--r--release/scripts/startup/bl_ui/space_image.py44
-rw-r--r--release/scripts/startup/bl_ui/space_node.py5
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py3
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py186
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py11
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py5
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py23
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py14
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py34
-rw-r--r--release/scripts/startup/nodeitems_builtins.py137
-rw-r--r--source/blender/blendthumb/src/Dll.cpp87
-rw-r--r--source/blender/blenkernel/BKE_asset.h8
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog.hh360
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog_path.hh143
-rw-r--r--source/blender/blenkernel/BKE_asset_library.h74
-rw-r--r--source/blender/blenkernel/BKE_asset_library.hh54
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh7
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h1
-rw-r--r--source/blender/blenkernel/BKE_colortools.h1
-rw-r--r--source/blender/blenkernel/BKE_displist.h5
-rw-r--r--source/blender/blenkernel/BKE_font.h9
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh76
-rw-r--r--source/blender/blenkernel/BKE_geometry_set_instances.hh9
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h6
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h4
-rw-r--r--source/blender/blenkernel/BKE_material.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh.h5
-rw-r--r--source/blender/blenkernel/BKE_node.h45
-rw-r--r--source/blender/blenkernel/BKE_preferences.h20
-rw-r--r--source/blender/blenkernel/BKE_spline.hh3
-rw-r--r--source/blender/blenkernel/BKE_subdiv.h2
-rw-r--r--source/blender/blenkernel/BKE_subdiv_foreach.h6
-rw-r--r--source/blender/blenkernel/BKE_volume.h12
-rw-r--r--source/blender/blenkernel/CMakeLists.txt11
-rw-r--r--source/blender/blenkernel/intern/asset.cc25
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc791
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path.cc228
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path_test.cc251
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc965
-rw-r--r--source/blender/blenkernel/intern/asset_library.cc141
-rw-r--r--source/blender/blenkernel/intern/asset_library_test.cc82
-rw-r--r--source/blender/blenkernel/intern/asset_test.cc70
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc15
-rw-r--r--source/blender/blenkernel/intern/blendfile.c7
-rw-r--r--source/blender/blenkernel/intern/callbacks.c14
-rw-r--r--source/blender/blenkernel/intern/collection.c2
-rw-r--r--source/blender/blenkernel/intern/colortools.c14
-rw-r--r--source/blender/blenkernel/intern/constraint.c2
-rw-r--r--source/blender/blenkernel/intern/curve.c1
-rw-r--r--source/blender/blenkernel/intern/customdata.c3
-rw-r--r--source/blender/blenkernel/intern/displist.cc19
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c4
-rw-r--r--source/blender/blenkernel/intern/fluid.c2
-rw-r--r--source/blender/blenkernel/intern/font.c27
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc283
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc117
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc139
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc220
-rw-r--r--source/blender/blenkernel/intern/gpencil.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc4
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c12
-rw-r--r--source/blender/blenkernel/intern/ipo.c3
-rw-r--r--source/blender/blenkernel/intern/key.c10
-rw-r--r--source/blender/blenkernel/intern/lib_id.c7
-rw-r--r--source/blender/blenkernel/intern/lib_override.c108
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c2
-rw-r--r--source/blender/blenkernel/intern/main.c1
-rw-r--r--source/blender/blenkernel/intern/material.c30
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc267
-rw-r--r--source/blender/blenkernel/intern/modifier.c2
-rw-r--r--source/blender/blenkernel/intern/nla.c2
-rw-r--r--source/blender/blenkernel/intern/node.cc594
-rw-r--r--source/blender/blenkernel/intern/object.c46
-rw-r--r--source/blender/blenkernel/intern/particle.c6
-rw-r--r--source/blender/blenkernel/intern/preferences.c29
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c6
-rw-r--r--source/blender/blenkernel/intern/scene.c41
-rw-r--r--source/blender/blenkernel/intern/softbody.c2
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc50
-rw-r--r--source/blender/blenkernel/intern/subdiv_mesh.c2
-rw-r--r--source/blender/blenkernel/intern/tracking.c102
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh7
-rw-r--r--source/blender/blenlib/BLI_noise.hh40
-rw-r--r--source/blender/blenlib/BLI_path_util.h4
-rw-r--r--source/blender/blenlib/BLI_resource_scope.hh1
-rw-r--r--source/blender/blenlib/BLI_uuid.h32
-rw-r--r--source/blender/blenlib/intern/fileops.c3
-rw-r--r--source/blender/blenlib/intern/kdtree_impl.h2
-rw-r--r--source/blender/blenlib/intern/list_sort_impl.h2
-rw-r--r--source/blender/blenlib/intern/noise.cc104
-rw-r--r--source/blender/blenlib/intern/path_util.c33
-rw-r--r--source/blender/blenlib/intern/scanfill.c12
-rw-r--r--source/blender/blenlib/intern/uuid.cc72
-rw-r--r--source/blender/blenlib/tests/BLI_mesh_intersect_test.cc6
-rw-r--r--source/blender/blenlib/tests/BLI_path_util_test.cc31
-rw-r--r--source/blender/blenlib/tests/BLI_uuid_test.cc46
-rw-r--r--source/blender/blenloader/BLO_readfile.h25
-rw-r--r--source/blender/blenloader/intern/readblenentry.c33
-rw-r--r--source/blender/blenloader/intern/readfile.c26
-rw-r--r--source/blender/blenloader/intern/versioning_280.c6
-rw-r--r--source/blender/blenloader/intern/versioning_290.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c349
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c3
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c12
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c7
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.c3
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers.c3
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c6
-rw-r--r--source/blender/compositor/COM_defines.h26
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc7
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc6
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h2
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc124
-rw-r--r--source/blender/compositor/intern/COM_Converter.h6
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc4
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc70
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h6
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc2
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h59
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc125
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h102
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc42
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.h4
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc12
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h2
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.cc8
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_SetAlphaNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc59
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc37
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ZCombineNode.cc12
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.cc23
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.cc32
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cc23
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_ConstantOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ConstantOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc11
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_DespeckleOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.cc3
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_DotproductOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.cc30
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.h1
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.cc34
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.cc31
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.cc11
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc21
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.cc17
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.cc17
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.cc24
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc24
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.cc35
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.h1
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_PixelateOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h23
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.cc17
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc19
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h1
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cc9
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.h3
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc138
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h25
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc274
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h75
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc24
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h1
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc40
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.cc205
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.h20
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.cc37
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.h27
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc25
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc68
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.cc15
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc19
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc15
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h2
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.cc2
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc2
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h6
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_uv.c56
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.c16
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h3
-rw-r--r--source/blender/draw/engines/overlay/shaders/grid_frag.glsl15
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c61
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c89
-rw-r--r--source/blender/draw/intern/draw_common.h4
-rw-r--r--source/blender/draw/intern/draw_hair.c25
-rw-r--r--source/blender/draw/intern/draw_hair_private.h6
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl6
-rw-r--r--source/blender/editors/animation/anim_filter.c30
-rw-r--r--source/blender/editors/animation/anim_ipo_utils.c7
-rw-r--r--source/blender/editors/animation/anim_ops.c9
-rw-r--r--source/blender/editors/animation/drivers.c2
-rw-r--r--source/blender/editors/animation/keyframes_draw.c18
-rw-r--r--source/blender/editors/animation/keyframing.c30
-rw-r--r--source/blender/editors/animation/keyingsets.c2
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c35
-rw-r--r--source/blender/editors/armature/pose_transform.c2
-rw-r--r--source/blender/editors/asset/CMakeLists.txt2
-rw-r--r--source/blender/editors/asset/ED_asset_catalog.hh35
-rw-r--r--source/blender/editors/asset/ED_asset_mark_clear.h15
-rw-r--r--source/blender/editors/asset/intern/asset_catalog.cc81
-rw-r--r--source/blender/editors/asset/intern/asset_list.cc4
-rw-r--r--source/blender/editors/asset/intern/asset_mark_clear.cc3
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc175
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_fileselect.h1
-rw-r--r--source/blender/editors/include/ED_image.h10
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h2
-rw-r--r--source/blender/editors/include/ED_text.h2
-rw-r--r--source/blender/editors/include/ED_time_scrub_ui.h3
-rw-r--r--source/blender/editors/include/ED_uvedit.h17
-rw-r--r--source/blender/editors/include/UI_icons.h10
-rw-r--r--source/blender/editors/include/UI_interface.h19
-rw-r--r--source/blender/editors/include/UI_interface.hh35
-rw-r--r--source/blender/editors/include/UI_tree_view.hh257
-rw-r--r--source/blender/editors/include/UI_view2d.h1
-rw-r--r--source/blender/editors/interface/CMakeLists.txt3
-rw-r--r--source/blender/editors/interface/interface.c59
-rw-r--r--source/blender/editors/interface/interface_dropboxes.cc66
-rw-r--r--source/blender/editors/interface/interface_handlers.c20
-rw-r--r--source/blender/editors/interface/interface_icons.c42
-rw-r--r--source/blender/editors/interface/interface_intern.h19
-rw-r--r--source/blender/editors/interface/interface_layout.c85
-rw-r--r--source/blender/editors/interface/interface_ops.c58
-rw-r--r--source/blender/editors/interface/interface_query.c13
-rw-r--r--source/blender/editors/interface/interface_view.cc133
-rw-r--r--source/blender/editors/interface/interface_widgets.c39
-rw-r--r--source/blender/editors/interface/tree_view.cc381
-rw-r--r--source/blender/editors/interface/view2d.c8
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c210
-rw-r--r--source/blender/editors/object/object_add.c13
-rw-r--r--source/blender/editors/object/object_edit.c2
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c161
-rw-r--r--source/blender/editors/render/render_shading.c2
-rw-r--r--source/blender/editors/screen/area.c9
-rw-r--r--source/blender/editors/screen/screen_context.c11
-rw-r--r--source/blender/editors/screen/screen_intern.h2
-rw-r--r--source/blender/editors/screen/workspace_edit.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c7
-rw-r--r--source/blender/editors/space_action/action_data.c51
-rw-r--r--source/blender/editors/space_action/space_action.c2
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c4
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc230
-rw-r--r--source/blender/editors/space_file/file_intern.h18
-rw-r--r--source/blender/editors/space_file/file_ops.c27
-rw-r--r--source/blender/editors/space_file/file_panels.c32
-rw-r--r--source/blender/editors/space_file/filelist.c402
-rw-r--r--source/blender/editors/space_file/filelist.h12
-rw-r--r--source/blender/editors/space_file/filesel.c19
-rw-r--r--source/blender/editors/space_file/fsmenu.c2
-rw-r--r--source/blender/editors/space_file/space_file.c11
-rw-r--r--source/blender/editors/space_graph/space_graph.c35
-rw-r--r--source/blender/editors/space_image/image_draw.c60
-rw-r--r--source/blender/editors/space_image/space_image.c16
-rw-r--r--source/blender/editors/space_info/info_ops.c2
-rw-r--r--source/blender/editors/space_nla/nla_draw.c2
-rw-r--r--source/blender/editors/space_nla/space_nla.c2
-rw-r--r--source/blender/editors/space_node/drawnode.cc53
-rw-r--r--source/blender/editors/space_node/node_draw.cc52
-rw-r--r--source/blender/editors/space_node/node_relationships.cc5
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c11
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c143
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c39
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h7
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c16
-rw-r--r--source/blender/editors/space_text/text_draw.c20
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c34
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c85
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h1
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c2
-rw-r--r--source/blender/editors/transform/transform.c13
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c8
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c44
-rw-r--r--source/blender/editors/transform/transform_convert_object.c14
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c7
-rw-r--r--source/blender/editors/transform/transform_gizmo_2d.c5
-rw-r--r--source/blender/editors/transform/transform_mode_timescale.c2
-rw-r--r--source/blender/editors/transform/transform_snap.c8
-rw-r--r--source/blender/editors/transform/transform_snap_object.c2
-rw-r--r--source/blender/editors/util/CMakeLists.txt2
-rw-r--r--source/blender/editors/uvedit/uvedit_islands.c175
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c91
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp4
-rw-r--r--source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp2
-rw-r--r--source/blender/functions/CMakeLists.txt2
-rw-r--r--source/blender/functions/FN_field.hh9
-rw-r--r--source/blender/functions/intern/field.cc58
-rw-r--r--source/blender/functions/intern/multi_function_procedure_executor.cc16
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c6
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h3
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c28
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c16
-rw-r--r--source/blender/gpu/CMakeLists.txt5
-rw-r--r--source/blender/gpu/GPU_shader.h15
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c16
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c7
-rw-r--r--source/blender/gpu/intern/gpu_select.c4
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.cc4
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c12
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc2
-rw-r--r--source/blender/gpu/opengl/gl_state.cc1
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl30
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl18
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl10
-rw-r--r--source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl (renamed from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl)33
-rw-r--r--source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl (renamed from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl)17
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl33
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl5
-rw-r--r--source/blender/gpu/tests/gpu_shader_builtin_test.cc2
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp2
-rw-r--r--source/blender/imbuf/intern/IMB_indexer.h2
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp171
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_multi.h15
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_stub.cpp25
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c4
-rw-r--r--source/blender/imbuf/intern/readimage.c2
-rw-r--r--source/blender/imbuf/intern/rotate.c2
-rw-r--r--source/blender/imbuf/intern/targa.c8
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc6
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h10
-rw-r--r--source/blender/io/alembic/intern/abc_reader_camera.h8
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.h10
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.cc20
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.h10
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.h12
-rw-r--r--source/blender/io/alembic/intern/abc_reader_transform.h8
-rw-r--r--source/blender/makesdna/DNA_asset_types.h14
-rw-r--r--source/blender/makesdna/DNA_cloth_types.h2
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h4
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h9
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h6
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h104
-rw-r--r--source/blender/makesdna/DNA_scene_types.h7
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h45
-rw-r--r--source/blender/makesdna/DNA_space_types.h61
-rw-r--r--source/blender/makesdna/DNA_tracking_types.h4
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h13
-rw-r--r--source/blender/makesdna/DNA_uuid_types.h6
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/RNA_enum_items.h1
-rw-r--r--source/blender/makesrna/intern/rna_access.c1
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c33
-rw-r--r--source/blender/makesrna/intern/rna_asset.c125
-rw-r--r--source/blender/makesrna/intern/rna_brush.c7
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c37
-rw-r--r--source/blender/makesrna/intern/rna_image.c4
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h11
-rw-r--r--source/blender/makesrna/intern/rna_material_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_nla.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c343
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c4
-rw-r--r--source/blender/makesrna/intern/rna_rigidbody.c7
-rw-r--r--source/blender/makesrna/intern/rna_scene.c32
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c13
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c102
-rw-r--r--source/blender/makesrna/intern/rna_space.c152
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c38
-rw-r--r--source/blender/makesrna/intern/rna_volume.c12
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c9
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc231
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc37
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c4
-rw-r--r--source/blender/nodes/CMakeLists.txt270
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/NOD_function.h6
-rw-r--r--source/blender/nodes/NOD_geometry.h21
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh2
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh181
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh26
-rw-r--r--source/blender/nodes/NOD_shader.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h42
-rw-r--r--source/blender/nodes/composite/node_composite_tree.cc (renamed from source/blender/nodes/composite/node_composite_tree.c)82
-rw-r--r--source/blender/nodes/composite/node_composite_util.cc (renamed from source/blender/nodes/composite/node_composite_util.c)7
-rw-r--r--source/blender/nodes/composite/node_composite_util.hh (renamed from source/blender/nodes/composite/node_composite_util.h)11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alphaOver.cc (renamed from source/blender/nodes/composite/nodes/node_composite_alphaOver.c)26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc (renamed from source/blender/nodes/composite/nodes/node_composite_antialiasing.c)5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc (renamed from source/blender/nodes/composite/nodes/node_composite_bilateralblur.c)6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc (renamed from source/blender/nodes/composite/nodes/node_composite_blur.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc (renamed from source/blender/nodes/composite/nodes/node_composite_bokehblur.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc (renamed from source/blender/nodes/composite/nodes/node_composite_bokehimage.c)18
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc (renamed from source/blender/nodes/composite/nodes/node_composite_boxmask.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc (renamed from source/blender/nodes/composite/nodes/node_composite_brightness.c)27
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channelMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_channelMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_chromaMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_colorMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorSpill.cc (renamed from source/blender/nodes/composite/nodes/node_composite_colorSpill.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc (renamed from source/blender/nodes/composite/nodes/node_composite_colorbalance.c)37
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc (renamed from source/blender/nodes/composite/nodes/node_composite_colorcorrection.c)30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_common.cc (renamed from source/blender/nodes/composite/nodes/node_composite_common.c)12
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc (renamed from source/blender/nodes/composite/nodes/node_composite_composite.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cornerpin.cc (renamed from source/blender/nodes/composite/nodes/node_composite_cornerpin.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc (renamed from source/blender/nodes/composite/nodes/node_composite_crop.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc (renamed from source/blender/nodes/composite/nodes/node_composite_curves.c)43
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_defocus.cc (renamed from source/blender/nodes/composite/nodes/node_composite_defocus.c)10
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc (renamed from source/blender/nodes/composite/nodes/node_composite_denoise.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc (renamed from source/blender/nodes/composite/nodes/node_composite_despeckle.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diffMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_diffMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc (renamed from source/blender/nodes/composite/nodes/node_composite_dilate.c)5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc (renamed from source/blender/nodes/composite/nodes/node_composite_directionalblur.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_displace.cc (renamed from source/blender/nodes/composite/nodes/node_composite_displace.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_distanceMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc (renamed from source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc (renamed from source/blender/nodes/composite/nodes/node_composite_ellipsemask.c)5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc (renamed from source/blender/nodes/composite/nodes/node_composite_exposure.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc (renamed from source/blender/nodes/composite/nodes/node_composite_filter.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_flip.cc (renamed from source/blender/nodes/composite/nodes/node_composite_flip.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc (renamed from source/blender/nodes/composite/nodes/node_composite_gamma.c)24
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_glare.cc (renamed from source/blender/nodes/composite/nodes/node_composite_glare.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc (renamed from source/blender/nodes/composite/nodes/node_composite_hueSatVal.c)34
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc (renamed from source/blender/nodes/composite/nodes/node_composite_huecorrect.c)29
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_idMask.cc (renamed from source/blender/nodes/composite/nodes/node_composite_idMask.c)21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc (renamed from source/blender/nodes/composite/nodes/node_composite_image.c)108
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.cc (renamed from source/blender/nodes/composite/nodes/node_composite_inpaint.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc (renamed from source/blender/nodes/composite/nodes/node_composite_invert.c)19
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.cc (renamed from source/blender/nodes/composite/nodes/node_composite_keying.c)7
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc (renamed from source/blender/nodes/composite/nodes/node_composite_keyingscreen.c)10
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc (renamed from source/blender/nodes/composite/nodes/node_composite_lensdist.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc (renamed from source/blender/nodes/composite/nodes/node_composite_levels.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc (renamed from source/blender/nodes/composite/nodes/node_composite_lummaMatte.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mapRange.cc (renamed from source/blender/nodes/composite/nodes/node_composite_mapRange.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mapUV.cc (renamed from source/blender/nodes/composite/nodes/node_composite_mapUV.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mapValue.cc (renamed from source/blender/nodes/composite/nodes/node_composite_mapValue.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc (renamed from source/blender/nodes/composite/nodes/node_composite_mask.c)19
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.cc (renamed from source/blender/nodes/composite/nodes/node_composite_math.c)21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mixrgb.cc (renamed from source/blender/nodes/composite/nodes/node_composite_mixrgb.c)26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc (renamed from source/blender/nodes/composite/nodes/node_composite_movieclip.c)31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc (renamed from source/blender/nodes/composite/nodes/node_composite_moviedistortion.c)12
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc (renamed from source/blender/nodes/composite/nodes/node_composite_normal.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.cc (renamed from source/blender/nodes/composite/nodes/node_composite_normalize.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_outputFile.cc (renamed from source/blender/nodes/composite/nodes/node_composite_outputFile.c)81
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc (renamed from source/blender/nodes/composite/nodes/node_composite_pixelate.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc (renamed from source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c)6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_posterize.cc (renamed from source/blender/nodes/composite/nodes/node_composite_posterize.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc (renamed from source/blender/nodes/composite/nodes/node_composite_premulkey.c)21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc (renamed from source/blender/nodes/composite/nodes/node_composite_rgb.c)17
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rotate.cc (renamed from source/blender/nodes/composite/nodes/node_composite_rotate.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc (renamed from source/blender/nodes/composite/nodes/node_composite_scale.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc (renamed from source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c)55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc (renamed from source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c)53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc (renamed from source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c)52
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc (renamed from source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c)52
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc (renamed from source/blender/nodes/composite/nodes/node_composite_setalpha.c)26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_splitViewer.cc (renamed from source/blender/nodes/composite/nodes/node_composite_splitViewer.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc (renamed from source/blender/nodes/composite/nodes/node_composite_stabilize2d.c)4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sunbeams.cc (renamed from source/blender/nodes/composite/nodes/node_composite_sunbeams.c)5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switch.cc (renamed from source/blender/nodes/composite/nodes/node_composite_switch.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc (renamed from source/blender/nodes/composite/nodes/node_composite_switchview.c)37
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc (renamed from source/blender/nodes/composite/nodes/node_composite_texture.c)30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc (renamed from source/blender/nodes/composite/nodes/node_composite_tonemap.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc (renamed from source/blender/nodes/composite/nodes/node_composite_trackpos.c)23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_transform.cc (renamed from source/blender/nodes/composite/nodes/node_composite_transform.c)2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc (renamed from source/blender/nodes/composite/nodes/node_composite_translate.c)5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_valToRgb.cc (renamed from source/blender/nodes/composite/nodes/node_composite_valToRgb.c)44
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc (renamed from source/blender/nodes/composite/nodes/node_composite_value.c)17
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_vecBlur.cc (renamed from source/blender/nodes/composite/nodes/node_composite_vecBlur.c)6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc (renamed from source/blender/nodes/composite/nodes/node_composite_viewer.c)24
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_zcombine.cc (renamed from source/blender/nodes/composite/nodes/node_composite_zcombine.c)32
-rw-r--r--source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc (renamed from source/blender/nodes/function/nodes/node_fn_random_float.cc)13
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_special_characters.cc74
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_value.cc299
-rw-r--r--source/blender/nodes/function/nodes/node_fn_rotate_euler.cc138
-rw-r--r--source/blender/nodes/function/nodes/node_fn_string_length.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_string_substring.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_value_to_string.cc1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc2
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh4
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc)26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc)3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc144
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc302
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc392
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc)3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_edge_split.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc)1
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_instance.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_scale.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_separate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_translate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_raycast.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc73
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc67
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc70
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc594
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_position.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc109
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc207
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc897
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc189
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_proximity.cc235
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_components.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc306
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc198
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc16
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc19
-rw-r--r--source/blender/nodes/intern/node_common.cc (renamed from source/blender/nodes/intern/node_common.c)256
-rw-r--r--source/blender/nodes/intern/node_socket.cc2
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc103
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_brightness.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc140
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hair_info.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc5
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_curves.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c4
-rw-r--r--source/blender/python/intern/bpy_interface.c2
-rw-r--r--source/blender/python/intern/bpy_props.c2
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/render/RE_pipeline.h3
-rw-r--r--source/blender/render/intern/engine.c11
-rw-r--r--source/blender/render/intern/pipeline.c4
-rw-r--r--source/blender/render/intern/render_result.c100
-rw-r--r--source/blender/render/intern/render_result.h3
-rw-r--r--source/blender/render/intern/texture_image.c12
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h1
-rw-r--r--source/blender/sequencer/intern/effects.c1
-rw-r--r--source/blender/sequencer/intern/modifier.c111
-rw-r--r--source/blender/sequencer/intern/sequencer.c14
-rw-r--r--source/blender/sequencer/intern/strip_relations.c2
-rw-r--r--source/blender/sequencer/intern/strip_transform.c2
-rw-r--r--source/blender/windowmanager/WM_api.h7
-rw-r--r--source/blender/windowmanager/WM_types.h7
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c39
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c6
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c5
-rw-r--r--source/blender/windowmanager/intern/wm_files.c22
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c102
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c8
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c7
-rw-r--r--source/creator/creator_signals.c2
m---------source/tools0
-rw-r--r--tests/performance/api/device.py2
-rw-r--r--tests/python/CMakeLists.txt20
-rw-r--r--tests/python/bl_blendfile_liblink.py78
-rwxr-xr-xtests/python/modules/render_report.py6
867 files changed, 27159 insertions, 6890 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e807b84e22..c4b8bf6dcd4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -419,6 +419,8 @@ mark_as_advanced(WITH_CYCLES_NATIVE_ONLY)
option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON)
option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" ON)
+option(WITH_CYCLES_DEVICE_HIP "Enable Cycles HIP support" OFF)
+mark_as_advanced(WITH_CYCLES_DEVICE_HIP)
mark_as_advanced(WITH_CYCLES_DEVICE_CUDA)
option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON)
@@ -821,6 +823,11 @@ if(NOT WITH_CUDA_DYNLOAD)
endif()
endif()
+if(WITH_CYCLES_DEVICE_HIP)
+ # Currently HIP must be dynamically loaded, this may change in future toolkits
+ set(WITH_HIP_DYNLOAD ON)
+endif()
+
#-----------------------------------------------------------------------------
# Check check if submodules are cloned
@@ -1850,6 +1857,9 @@ elseif(WITH_CYCLES_STANDALONE)
if(WITH_CUDA_DYNLOAD)
add_subdirectory(extern/cuew)
endif()
+ if(WITH_HIP_DYNLOAD)
+ add_subdirectory(extern/hipew)
+ endif()
if(NOT WITH_SYSTEM_GLEW)
add_subdirectory(extern/glew)
endif()
diff --git a/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py
new file mode 100644
index 00000000000..f3cc95dec61
--- /dev/null
+++ b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py
@@ -0,0 +1,40 @@
+"""
+This method enables conversions between Local and Pose space for bones in
+the middle of updating the armature without having to update dependencies
+after each change, by manually carrying updated matrices in a recursive walk.
+"""
+
+def set_pose_matrices(obj, matrix_map):
+ "Assign pose space matrices of all bones at once, ignoring constraints."
+
+ def rec(pbone, parent_matrix):
+ matrix = matrix_map[pbone.name]
+
+ ## Instead of:
+ # pbone.matrix = matrix
+ # bpy.context.view_layer.update()
+
+ # Compute and assign local matrix, using the new parent matrix
+ if pbone.parent:
+ pbone.matrix_basis = pbone.bone.convert_local_to_pose(
+ matrix,
+ pbone.bone.matrix_local,
+ parent_matrix=parent_matrix,
+ parent_matrix_local=pbone.parent.bone.matrix_local,
+ invert=True
+ )
+ else:
+ pbone.matrix_basis = pbone.bone.convert_local_to_pose(
+ matrix,
+ pbone.bone.matrix_local,
+ invert=True
+ )
+
+ # Recursively process children, passing the new matrix through
+ for child in pbone.children:
+ rec(child, matrix)
+
+ # Scan all bone trees from their roots
+ for pbone in obj.pose.bones:
+ if not pbone.parent:
+ rec(pbone, None)
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 7f7d91f0765..1fdc8e60167 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -67,9 +67,12 @@ endif()
if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV)
add_subdirectory(clew)
- if(WITH_CUDA_DYNLOAD)
+ if((WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) AND WITH_CUDA_DYNLOAD)
add_subdirectory(cuew)
endif()
+ if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD)
+ add_subdirectory(hipew)
+ endif()
endif()
if(WITH_GHOST_X11 AND WITH_GHOST_XDND)
diff --git a/extern/audaspace/bindings/C/AUD_Sound.cpp b/extern/audaspace/bindings/C/AUD_Sound.cpp
index aa246b9a047..8a3c9d1bbc9 100644
--- a/extern/audaspace/bindings/C/AUD_Sound.cpp
+++ b/extern/audaspace/bindings/C/AUD_Sound.cpp
@@ -102,26 +102,30 @@ AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_i
if(file)
{
- auto streams = file->queryStreams();
+ try
+ {
+ auto streams = file->queryStreams();
- size_t size = sizeof(AUD_StreamInfo) * streams.size();
+ size_t size = sizeof(AUD_StreamInfo) * streams.size();
- if(!size)
- {
- *stream_infos = nullptr;
- return 0;
- }
+ if(!size)
+ {
+ *stream_infos = nullptr;
+ return 0;
+ }
- *stream_infos = reinterpret_cast<AUD_StreamInfo*>(std::malloc(size));
- std::memcpy(*stream_infos, streams.data(), size);
+ *stream_infos = reinterpret_cast<AUD_StreamInfo*>(std::malloc(size));
+ std::memcpy(*stream_infos, streams.data(), size);
- return streams.size();
- }
- else
- {
- *stream_infos = nullptr;
- return 0;
+ return streams.size();
+ }
+ catch(Exception&)
+ {
+ }
}
+
+ *stream_infos = nullptr;
+ return 0;
}
AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs)
diff --git a/extern/hipew/CMakeLists.txt b/extern/hipew/CMakeLists.txt
new file mode 100644
index 00000000000..d215ea8c691
--- /dev/null
+++ b/extern/hipew/CMakeLists.txt
@@ -0,0 +1,39 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2021, Blender Foundation
+# All rights reserved.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ include
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ src/hipew.c
+
+ include/hipew.h
+)
+
+set(LIB
+)
+
+blender_add_lib(extern_hipew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/hipew/include/hipew.h b/extern/hipew/include/hipew.h
new file mode 100644
index 00000000000..02fffc331bf
--- /dev/null
+++ b/extern/hipew/include/hipew.h
@@ -0,0 +1,1207 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#ifndef __HIPEW_H__
+#define __HIPEW_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+#define HIP_IPC_HANDLE_SIZE 64
+#define hipHostMallocPortable 0x01
+#define hipHostMallocMapped 0x02
+#define hipHostMallocWriteCombined 0x04
+#define hipHostRegisterPortable 0x01
+#define hipHostRegisterMapped 0x02
+#define hipHostRegisterIoMemory 0x04
+#define hipCooperativeLaunchMultiDeviceNoPreSync 0x01
+#define hipCooperativeLaunchMultiDeviceNoPostSync 0x02
+#define hipArrayLayered 0x01
+#define hipArraySurfaceLoadStore 0x02
+#define hipArrayCubemap 0x04
+#define hipArrayTextureGather 0x08
+#define HIP_TRSA_OVERRIDE_FORMAT 0x01
+#define HIP_TRSF_READ_AS_INTEGER 0x01
+#define HIP_TRSF_NORMALIZED_COORDINATES 0x02
+#define HIP_LAUNCH_PARAM_END ((void*)0x00)
+#define HIP_LAUNCH_PARAM_BUFFER_POINTER ((void*)0x01)
+#define HIP_LAUNCH_PARAM_BUFFER_SIZE ((void*)0x02)
+
+/* Functions which changed 3.1 -> 3.2 for 64 bit stuff,
+ * the cuda library has both the old ones for compatibility and new
+ * ones with _v2 postfix,
+ */
+#define hipModuleGetGlobal hipModuleGetGlobal
+#define hipMemGetInfo hipMemGetInfo
+#define hipMemAllocPitch hipMemAllocPitch
+#define hipMemGetAddressRange hipMemGetAddressRange
+#define hipMemcpyHtoD hipMemcpyHtoD
+#define hipMemcpyDtoH hipMemcpyDtoH
+#define hipMemcpyDtoD hipMemcpyDtoD
+#define hipMemcpyHtoA hipMemcpyHtoA
+#define hipMemcpyAtoH hipMemcpyAtoH
+#define hipMemcpyHtoDAsync hipMemcpyHtoDAsync
+#define hipMemcpyDtoHAsync hipMemcpyDtoHAsync
+#define hipMemcpyDtoDAsync hipMemcpyDtoDAsync
+#define hipMemsetD8 hipMemsetD8
+#define hipMemsetD16 hipMemsetD16
+#define hipMemsetD32 hipMemsetD32
+#define hipArrayCreate hipArrayCreate
+#define hipArray3DCreate hipArray3DCreate
+#define hipTexRefSetAddress hipTexRefSetAddress
+#define hipTexRefGetAddress hipTexRefGetAddress
+#define hipStreamDestroy hipStreamDestroy
+#define hipEventDestroy hipEventDestroy
+#define hipTexRefSetAddress2D hipTexRefSetAddress2D
+
+/* Types. */
+#ifdef _MSC_VER
+typedef unsigned __int32 hipuint32_t;
+typedef unsigned __int64 hipuint64_t;
+#else
+#include <stdint.h>
+typedef uint32_t hipuint32_t;
+typedef uint64_t hipuint64_t;
+#endif
+
+#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined (__aarch64__)
+typedef unsigned long long hipDeviceptr_t;
+#else
+typedef unsigned int hipDeviceptr_t;
+#endif
+
+
+#ifdef _WIN32
+# define HIPAPI __stdcall
+# define HIP_CB __stdcall
+#else
+# define HIPAPI
+# define HIP_CB
+#endif
+
+typedef int hipDevice_t;
+typedef struct ihipCtx_t* hipCtx_t;
+typedef struct ihipModule_t* hipModule_t;
+typedef struct ihipModuleSymbol_t* hipFunction_t;
+typedef struct hipArray* hArray;
+typedef struct hipMipmappedArray_st* hipMipmappedArray_t;
+typedef struct ihipEvent_t* hipEvent_t;
+typedef struct ihipStream_t* hipStream_t;
+typedef unsigned long long hipTextureObject_t;
+
+typedef struct HIPuuid_st {
+ char bytes[16];
+} HIPuuid;
+
+typedef enum hipChannelFormatKind {
+ hipChannelFormatKindSigned = 0,
+ hipChannelFormatKindUnsigned = 1,
+ hipChannelFormatKindFloat = 2,
+ hipChannelFormatKindNone = 3,
+}hipChannelFormatKind;
+
+typedef struct hipChannelFormatDesc {
+ int x;
+ int y;
+ int z;
+ int w;
+ enum hipChannelFormatKind f;
+}hipChannelFormatDesc;
+
+typedef enum hipTextureFilterMode {
+ hipFilterModePoint = 0,
+ hipFilterModeLinear = 1,
+} hipTextureFilterMode;
+
+typedef enum hipArray_Format {
+ HIP_AD_FORMAT_UNSIGNED_INT8 = 0x01,
+ HIP_AD_FORMAT_SIGNED_INT8 = 0x08,
+ HIP_AD_FORMAT_UNSIGNED_INT16 = 0x02,
+ HIP_AD_FORMAT_SIGNED_INT16 = 0x09,
+ HIP_AD_FORMAT_UNSIGNED_INT32 = 0x03,
+ HIP_AD_FORMAT_SIGNED_INT32 = 0x0a,
+ HIP_AD_FORMAT_HALF = 0x10,
+ HIP_AD_FORMAT_FLOAT = 0x20,
+} hipArray_Format;
+
+typedef enum hipTextureAddressMode {
+ hipAddressModeWrap = 0,
+ hipAddressModeClamp = 1,
+ hipAddressModeMirror = 2,
+ hipAddressModeBorder = 3,
+} hipTextureAddressMode;
+
+/**
+ * hip texture reference
+ */
+typedef struct textureReference {
+ int normalized;
+ //enum hipTextureReadMode readMode;// used only for driver API's
+ enum hipTextureFilterMode filterMode;
+ enum hipTextureAddressMode addressMode[3]; // Texture address mode for up to 3 dimensions
+ struct hipChannelFormatDesc channelDesc;
+ int sRGB; // Perform sRGB->linear conversion during texture read
+ unsigned int maxAnisotropy; // Limit to the anisotropy ratio
+ enum hipTextureFilterMode mipmapFilterMode;
+ float mipmapLevelBias;
+ float minMipmapLevelClamp;
+ float maxMipmapLevelClamp;
+
+ hipTextureObject_t textureObject;
+ int numChannels;
+ enum hipArray_Format format;
+}textureReference;
+
+typedef textureReference* hipTexRef;
+
+typedef enum hipMemoryType {
+ hipMemoryTypeHost = 0x00,
+ hipMemoryTypeDevice = 0x01,
+ hipMemoryTypeArray = 0x02,
+ hipMemoryTypeUnified = 0x03,
+} hipMemoryType;
+
+/**
+ * Pointer attributes
+ */
+typedef struct hipPointerAttribute_t {
+ enum hipMemoryType memoryType;
+ int device;
+ void* devicePointer;
+ void* hostPointer;
+ int isManaged;
+ unsigned allocationFlags; /* flags specified when memory was allocated*/
+ /* peers? */
+} hipPointerAttribute_t;
+
+typedef struct ihipIpcEventHandle_t {
+ char reserved[HIP_IPC_HANDLE_SIZE];
+} ihipIpcEventHandle_t;
+
+typedef struct hipIpcMemHandle_st {
+ char reserved[HIP_IPC_HANDLE_SIZE];
+} hipIpcMemHandle_t;
+
+typedef enum HIPipcMem_flags_enum {
+ hipIpcMemLazyEnablePeerAccess = 0x1,
+} HIPipcMem_flags;
+
+typedef enum HIPmemAttach_flags_enum {
+ hipMemAttachGlobal = 0x1,
+ hipMemAttachHost = 0x2,
+ HIP_MEM_ATTACH_SINGLE = 0x4,
+} HIPmemAttach_flags;
+
+typedef enum HIPctx_flags_enum {
+ hipDeviceScheduleAuto = 0x00,
+ hipDeviceScheduleSpin = 0x01,
+ hipDeviceScheduleYield = 0x02,
+ hipDeviceScheduleBlockingSync = 0x04,
+ hipDeviceScheduleMask = 0x07,
+ hipDeviceMapHost = 0x08,
+ hipDeviceLmemResizeToMax = 0x10,
+} HIPctx_flags;
+
+typedef enum HIPstream_flags_enum {
+ hipStreamDefault = 0x0,
+ hipStreamNonBlocking = 0x1,
+} HIPstream_flags;
+
+typedef enum HIPevent_flags_enum {
+ hipEventDefault = 0x0,
+ hipEventBlockingSync = 0x1,
+ hipEventDisableTiming = 0x2,
+ hipEventInterprocess = 0x4,
+} HIPevent_flags;
+
+typedef enum HIPstreamWaitValue_flags_enum {
+ HIP_STREAM_WAIT_VALUE_GEQ = 0x0,
+ HIP_STREAM_WAIT_VALUE_EQ = 0x1,
+ HIP_STREAM_WAIT_VALUE_AND = 0x2,
+ HIP_STREAM_WAIT_VALUE_NOR = 0x3,
+ HIP_STREAM_WAIT_VALUE_FLUSH = (1 << 30),
+} HIPstreamWaitValue_flags;
+
+typedef enum HIPstreamWriteValue_flags_enum {
+ HIP_STREAM_WRITE_VALUE_DEFAULT = 0x0,
+ HIP_STREAM_WRITE_VALUE_NO_MEMORY_BARRIER = 0x1,
+} HIPstreamWriteValue_flags;
+
+typedef enum HIPstreamBatchMemOpType_enum {
+ HIP_STREAM_MEM_OP_WAIT_VALUE_32 = 1,
+ HIP_STREAM_MEM_OP_WRITE_VALUE_32 = 2,
+ HIP_STREAM_MEM_OP_WAIT_VALUE_64 = 4,
+ HIP_STREAM_MEM_OP_WRITE_VALUE_64 = 5,
+ HIP_STREAM_MEM_OP_FLUSH_REMOTE_WRITES = 3,
+} HIPstreamBatchMemOpType;
+
+
+typedef union HIPstreamBatchMemOpParams_union {
+ HIPstreamBatchMemOpType operation;
+ struct HIPstreamMemOpWaitValueParams_st {
+ HIPstreamBatchMemOpType operation;
+ hipDeviceptr_t address;
+ union {
+ hipuint32_t value;
+ hipuint64_t value64;
+ };
+ unsigned int flags;
+ hipDeviceptr_t alias;
+ } waitValue;
+ struct HIPstreamMemOpWriteValueParams_st {
+ HIPstreamBatchMemOpType operation;
+ hipDeviceptr_t address;
+ union {
+ hipuint32_t value;
+ hipuint64_t value64;
+ };
+ unsigned int flags;
+ hipDeviceptr_t alias;
+ } writeValue;
+ struct HIPstreamMemOpFlushRemoteWritesParams_st {
+ HIPstreamBatchMemOpType operation;
+ unsigned int flags;
+ } flushRemoteWrites;
+ hipuint64_t pad[6];
+} HIPstreamBatchMemOpParams;
+
+typedef enum HIPoccupancy_flags_enum {
+ hipOccupancyDefault = 0x0,
+ HIP_OCCUPANCY_DISABLE_CACHING_OVERRIDE = 0x1,
+} HIPoccupancy_flags;
+
+typedef enum hipDeviceAttribute_t {
+ hipDeviceAttributeCudaCompatibleBegin = 0,
+ hipDeviceAttributeEccEnabled = hipDeviceAttributeCudaCompatibleBegin, ///< Whether ECC support is enabled.
+ hipDeviceAttributeAccessPolicyMaxWindowSize, ///< Cuda only. The maximum size of the window policy in bytes.
+ hipDeviceAttributeAsyncEngineCount, ///< Cuda only. Asynchronous engines number.
+ hipDeviceAttributeCanMapHostMemory, ///< Whether host memory can be mapped into device address space
+ hipDeviceAttributeCanUseHostPointerForRegisteredMem,///< Cuda only. Device can access host registered memory
+ ///< at the same virtual address as the CPU
+ hipDeviceAttributeClockRate, ///< Peak clock frequency in kilohertz.
+ hipDeviceAttributeComputeMode, ///< Compute mode that device is currently in.
+ hipDeviceAttributeComputePreemptionSupported, ///< Cuda only. Device supports Compute Preemption.
+ hipDeviceAttributeConcurrentKernels, ///< Device can possibly execute multiple kernels concurrently.
+ hipDeviceAttributeConcurrentManagedAccess, ///< Device can coherently access managed memory concurrently with the CPU
+ hipDeviceAttributeCooperativeLaunch, ///< Support cooperative launch
+ hipDeviceAttributeCooperativeMultiDeviceLaunch, ///< Support cooperative launch on multiple devices
+ hipDeviceAttributeDeviceOverlap, ///< Cuda only. Device can concurrently copy memory and execute a kernel.
+ ///< Deprecated. Use instead asyncEngineCount.
+ hipDeviceAttributeDirectManagedMemAccessFromHost, ///< Host can directly access managed memory on
+ ///< the device without migration
+ hipDeviceAttributeGlobalL1CacheSupported, ///< Cuda only. Device supports caching globals in L1
+ hipDeviceAttributeHostNativeAtomicSupported, ///< Cuda only. Link between the device and the host supports native atomic operations
+ hipDeviceAttributeIntegrated, ///< Device is integrated GPU
+ hipDeviceAttributeIsMultiGpuBoard, ///< Multiple GPU devices.
+ hipDeviceAttributeKernelExecTimeout, ///< Run time limit for kernels executed on the device
+ hipDeviceAttributeL2CacheSize, ///< Size of L2 cache in bytes. 0 if the device doesn't have L2 cache.
+ hipDeviceAttributeLocalL1CacheSupported, ///< caching locals in L1 is supported
+ hipDeviceAttributeLuid, ///< Cuda only. 8-byte locally unique identifier in 8 bytes. Undefined on TCC and non-Windows platforms
+ hipDeviceAttributeLuidDeviceNodeMask, ///< Cuda only. Luid device node mask. Undefined on TCC and non-Windows platforms
+ hipDeviceAttributeComputeCapabilityMajor, ///< Major compute capability version number.
+ hipDeviceAttributeManagedMemory, ///< Device supports allocating managed memory on this system
+ hipDeviceAttributeMaxBlocksPerMultiProcessor, ///< Cuda only. Max block size per multiprocessor
+ hipDeviceAttributeMaxBlockDimX, ///< Max block size in width.
+ hipDeviceAttributeMaxBlockDimY, ///< Max block size in height.
+ hipDeviceAttributeMaxBlockDimZ, ///< Max block size in depth.
+ hipDeviceAttributeMaxGridDimX, ///< Max grid size in width.
+ hipDeviceAttributeMaxGridDimY, ///< Max grid size in height.
+ hipDeviceAttributeMaxGridDimZ, ///< Max grid size in depth.
+ hipDeviceAttributeMaxSurface1D, ///< Maximum size of 1D surface.
+ hipDeviceAttributeMaxSurface1DLayered, ///< Cuda only. Maximum dimensions of 1D layered surface.
+ hipDeviceAttributeMaxSurface2D, ///< Maximum dimension (width, height) of 2D surface.
+ hipDeviceAttributeMaxSurface2DLayered, ///< Cuda only. Maximum dimensions of 2D layered surface.
+ hipDeviceAttributeMaxSurface3D, ///< Maximum dimension (width, height, depth) of 3D surface.
+ hipDeviceAttributeMaxSurfaceCubemap, ///< Cuda only. Maximum dimensions of Cubemap surface.
+ hipDeviceAttributeMaxSurfaceCubemapLayered, ///< Cuda only. Maximum dimension of Cubemap layered surface.
+ hipDeviceAttributeMaxTexture1DWidth, ///< Maximum size of 1D texture.
+ hipDeviceAttributeMaxTexture1DLayered, ///< Cuda only. Maximum dimensions of 1D layered texture.
+ hipDeviceAttributeMaxTexture1DLinear, ///< Maximum number of elements allocatable in a 1D linear texture.
+ ///< Use cudaDeviceGetTexture1DLinearMaxWidth() instead on Cuda.
+ hipDeviceAttributeMaxTexture1DMipmap, ///< Cuda only. Maximum size of 1D mipmapped texture.
+ hipDeviceAttributeMaxTexture2DWidth, ///< Maximum dimension width of 2D texture.
+ hipDeviceAttributeMaxTexture2DHeight, ///< Maximum dimension hight of 2D texture.
+ hipDeviceAttributeMaxTexture2DGather, ///< Cuda only. Maximum dimensions of 2D texture if gather operations performed.
+ hipDeviceAttributeMaxTexture2DLayered, ///< Cuda only. Maximum dimensions of 2D layered texture.
+ hipDeviceAttributeMaxTexture2DLinear, ///< Cuda only. Maximum dimensions (width, height, pitch) of 2D textures bound to pitched memory.
+ hipDeviceAttributeMaxTexture2DMipmap, ///< Cuda only. Maximum dimensions of 2D mipmapped texture.
+ hipDeviceAttributeMaxTexture3DWidth, ///< Maximum dimension width of 3D texture.
+ hipDeviceAttributeMaxTexture3DHeight, ///< Maximum dimension height of 3D texture.
+ hipDeviceAttributeMaxTexture3DDepth, ///< Maximum dimension depth of 3D texture.
+ hipDeviceAttributeMaxTexture3DAlt, ///< Cuda only. Maximum dimensions of alternate 3D texture.
+ hipDeviceAttributeMaxTextureCubemap, ///< Cuda only. Maximum dimensions of Cubemap texture
+ hipDeviceAttributeMaxTextureCubemapLayered, ///< Cuda only. Maximum dimensions of Cubemap layered texture.
+ hipDeviceAttributeMaxThreadsDim, ///< Maximum dimension of a block
+ hipDeviceAttributeMaxThreadsPerBlock, ///< Maximum number of threads per block.
+ hipDeviceAttributeMaxThreadsPerMultiProcessor, ///< Maximum resident threads per multiprocessor.
+ hipDeviceAttributeMaxPitch, ///< Maximum pitch in bytes allowed by memory copies
+ hipDeviceAttributeMemoryBusWidth, ///< Global memory bus width in bits.
+ hipDeviceAttributeMemoryClockRate, ///< Peak memory clock frequency in kilohertz.
+ hipDeviceAttributeComputeCapabilityMinor, ///< Minor compute capability version number.
+ hipDeviceAttributeMultiGpuBoardGroupID, ///< Cuda only. Unique ID of device group on the same multi-GPU board
+ hipDeviceAttributeMultiprocessorCount, ///< Number of multiprocessors on the device.
+ hipDeviceAttributeName, ///< Device name.
+ hipDeviceAttributePageableMemoryAccess, ///< Device supports coherently accessing pageable memory
+ ///< without calling hipHostRegister on it
+ hipDeviceAttributePageableMemoryAccessUsesHostPageTables, ///< Device accesses pageable memory via the host's page tables
+ hipDeviceAttributePciBusId, ///< PCI Bus ID.
+ hipDeviceAttributePciDeviceId, ///< PCI Device ID.
+ hipDeviceAttributePciDomainID, ///< PCI Domain ID.
+ hipDeviceAttributePersistingL2CacheMaxSize, ///< Cuda11 only. Maximum l2 persisting lines capacity in bytes
+ hipDeviceAttributeMaxRegistersPerBlock, ///< 32-bit registers available to a thread block. This number is shared
+ ///< by all thread blocks simultaneously resident on a multiprocessor.
+ hipDeviceAttributeMaxRegistersPerMultiprocessor, ///< 32-bit registers available per block.
+ hipDeviceAttributeReservedSharedMemPerBlock, ///< Cuda11 only. Shared memory reserved by CUDA driver per block.
+ hipDeviceAttributeMaxSharedMemoryPerBlock, ///< Maximum shared memory available per block in bytes.
+ hipDeviceAttributeSharedMemPerBlockOptin, ///< Cuda only. Maximum shared memory per block usable by special opt in.
+ hipDeviceAttributeSharedMemPerMultiprocessor, ///< Cuda only. Shared memory available per multiprocessor.
+ hipDeviceAttributeSingleToDoublePrecisionPerfRatio, ///< Cuda only. Performance ratio of single precision to double precision.
+ hipDeviceAttributeStreamPrioritiesSupported, ///< Cuda only. Whether to support stream priorities.
+ hipDeviceAttributeSurfaceAlignment, ///< Cuda only. Alignment requirement for surfaces
+ hipDeviceAttributeTccDriver, ///< Cuda only. Whether device is a Tesla device using TCC driver
+ hipDeviceAttributeTextureAlignment, ///< Alignment requirement for textures
+ hipDeviceAttributeTexturePitchAlignment, ///< Pitch alignment requirement for 2D texture references bound to pitched memory;
+ hipDeviceAttributeTotalConstantMemory, ///< Constant memory size in bytes.
+ hipDeviceAttributeTotalGlobalMem, ///< Global memory available on devicice.
+ hipDeviceAttributeUnifiedAddressing, ///< Cuda only. An unified address space shared with the host.
+ hipDeviceAttributeUuid, ///< Cuda only. Unique ID in 16 byte.
+ hipDeviceAttributeWarpSize, ///< Warp size in threads.
+ hipDeviceAttributeCudaCompatibleEnd = 9999,
+ hipDeviceAttributeAmdSpecificBegin = 10000,
+ hipDeviceAttributeClockInstructionRate = hipDeviceAttributeAmdSpecificBegin, ///< Frequency in khz of the timer used by the device-side "clock*"
+ hipDeviceAttributeArch, ///< Device architecture
+ hipDeviceAttributeMaxSharedMemoryPerMultiprocessor, ///< Maximum Shared Memory PerMultiprocessor.
+ hipDeviceAttributeGcnArch, ///< Device gcn architecture
+ hipDeviceAttributeGcnArchName, ///< Device gcnArch name in 256 bytes
+ hipDeviceAttributeHdpMemFlushCntl, ///< Address of the HDP_MEM_COHERENCY_FLUSH_CNTL register
+ hipDeviceAttributeHdpRegFlushCntl, ///< Address of the HDP_REG_COHERENCY_FLUSH_CNTL register
+ hipDeviceAttributeCooperativeMultiDeviceUnmatchedFunc, ///< Supports cooperative launch on multiple
+ ///< devices with unmatched functions
+ hipDeviceAttributeCooperativeMultiDeviceUnmatchedGridDim, ///< Supports cooperative launch on multiple
+ ///< devices with unmatched grid dimensions
+ hipDeviceAttributeCooperativeMultiDeviceUnmatchedBlockDim, ///< Supports cooperative launch on multiple
+ ///< devices with unmatched block dimensions
+ hipDeviceAttributeCooperativeMultiDeviceUnmatchedSharedMem, ///< Supports cooperative launch on multiple
+ ///< devices with unmatched shared memories
+ hipDeviceAttributeIsLargeBar, ///< Whether it is LargeBar
+ hipDeviceAttributeAsicRevision, ///< Revision of the GPU in this device
+ hipDeviceAttributeCanUseStreamWaitValue, ///< '1' if Device supports hipStreamWaitValue32() and
+ ///< hipStreamWaitValue64() , '0' otherwise.
+ hipDeviceAttributeAmdSpecificEnd = 19999,
+ hipDeviceAttributeVendorSpecificBegin = 20000,
+ // Extended attributes for vendors
+} hipDeviceAttribute_t;
+
+typedef struct HIPdevprop_st {
+ int maxThreadsPerBlock;
+ int maxThreadsDim[3];
+ int maxGridSize[3];
+ int sharedMemPerBlock;
+ int totalConstantMemory;
+ int SIMDWidth;
+ int memPitch;
+ int regsPerBlock;
+ int clockRate;
+ int textureAlign;
+} HIPdevprop;
+
+typedef enum HIPpointer_attribute_enum {
+ HIP_POINTER_ATTRIBUTE_CONTEXT = 1,
+ HIP_POINTER_ATTRIBUTE_MEMORY_TYPE = 2,
+ HIP_POINTER_ATTRIBUTE_DEVICE_POINTER = 3,
+ HIP_POINTER_ATTRIBUTE_HOST_POINTER = 4,
+ HIP_POINTER_ATTRIBUTE_SYNC_MEMOPS = 6,
+ HIP_POINTER_ATTRIBUTE_BUFFER_ID = 7,
+ HIP_POINTER_ATTRIBUTE_IS_MANAGED = 8,
+ HIP_POINTER_ATTRIBUTE_DEVICE_ORDINAL = 9,
+} HIPpointer_attribute;
+
+typedef enum hipFunction_attribute {
+ HIP_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK = 0,
+ HIP_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES = 1,
+ HIP_FUNC_ATTRIBUTE_CONST_SIZE_BYTES = 2,
+ HIP_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES = 3,
+ HIP_FUNC_ATTRIBUTE_NUM_REGS = 4,
+ HIP_FUNC_ATTRIBUTE_PTX_VERSION = 5,
+ HIP_FUNC_ATTRIBUTE_BINARY_VERSION = 6,
+ HIP_FUNC_ATTRIBUTE_CACHE_MODE_CA = 7,
+ HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES = 8,
+ HIP_FUNC_ATTRIBUTE_PREFERRED_SHARED_MEMORY_CARVEOUT = 9,
+ HIP_FUNC_ATTRIBUTE_MAX,
+} hipFunction_attribute;
+
+typedef enum hipFuncCache_t {
+ hipFuncCachePreferNone = 0x00,
+ hipFuncCachePreferShared = 0x01,
+ hipFuncCachePreferL1 = 0x02,
+ hipFuncCachePreferEqual = 0x03,
+} hipFuncCache_t;
+
+typedef enum hipSharedMemConfig {
+ hipSharedMemBankSizeDefault = 0x00,
+ hipSharedMemBankSizeFourByte = 0x01,
+ hipSharedMemBankSizeEightByte = 0x02,
+} hipSharedMemConfig;
+
+typedef enum HIPshared_carveout_enum {
+ HIP_SHAREDMEM_CARVEOUT_DEFAULT,
+ HIP_SHAREDMEM_CARVEOUT_MAX_SHARED = 100,
+ HIP_SHAREDMEM_CARVEOUT_MAX_L1 = 0,
+} HIPshared_carveout;
+
+
+
+typedef enum hipComputeMode {
+ hipComputeModeDefault = 0,
+ hipComputeModeProhibited = 2,
+ hipComputeModeExclusiveProcess = 3,
+} hipComputeMode;
+
+typedef enum HIPmem_advise_enum {
+ HIP_MEM_ADVISE_SET_READ_MOSTLY = 1,
+ HIP_MEM_ADVISE_UNSET_READ_MOSTLY = 2,
+ HIP_MEM_ADVISE_SET_PREFERRED_LOCATION = 3,
+ HIP_MEM_ADVISE_UNSET_PREFERRED_LOCATION = 4,
+ HIP_MEM_ADVISE_SET_ACCESSED_BY = 5,
+ HIP_MEM_ADVISE_UNSET_ACCESSED_BY = 6,
+} HIPmem_advise;
+
+typedef enum HIPmem_range_attribute_enum {
+ HIP_MEM_RANGE_ATTRIBUTE_READ_MOSTLY = 1,
+ HIP_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION = 2,
+ HIP_MEM_RANGE_ATTRIBUTE_ACCESSED_BY = 3,
+ HIP_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION = 4,
+} HIPmem_range_attribute;
+
+typedef enum hipJitOption {
+ hipJitOptionMaxRegisters = 0,
+ hipJitOptionThreadsPerBlock,
+ hipJitOptionWallTime,
+ hipJitOptionInfoLogBuffer,
+ hipJitOptionInfoLogBufferSizeBytes,
+ hipJitOptionErrorLogBuffer,
+ hipJitOptionErrorLogBufferSizeBytes,
+ hipJitOptionOptimizationLevel,
+ hipJitOptionTargetFromContext,
+ hipJitOptionTarget,
+ hipJitOptionFallbackStrategy,
+ hipJitOptionGenerateDebugInfo,
+ hipJitOptionLogVerbose,
+ hipJitOptionGenerateLineInfo,
+ hipJitOptionCacheMode,
+ hipJitOptionSm3xOpt,
+ hipJitOptionFastCompile,
+ hipJitOptionNumOptions,
+} hipJitOption;
+
+typedef enum HIPjit_target_enum {
+ HIP_TARGET_COMPUTE_20 = 20,
+ HIP_TARGET_COMPUTE_21 = 21,
+ HIP_TARGET_COMPUTE_30 = 30,
+ HIP_TARGET_COMPUTE_32 = 32,
+ HIP_TARGET_COMPUTE_35 = 35,
+ HIP_TARGET_COMPUTE_37 = 37,
+ HIP_TARGET_COMPUTE_50 = 50,
+ HIP_TARGET_COMPUTE_52 = 52,
+ HIP_TARGET_COMPUTE_53 = 53,
+ HIP_TARGET_COMPUTE_60 = 60,
+ HIP_TARGET_COMPUTE_61 = 61,
+ HIP_TARGET_COMPUTE_62 = 62,
+ HIP_TARGET_COMPUTE_70 = 70,
+ HIP_TARGET_COMPUTE_73 = 73,
+ HIP_TARGET_COMPUTE_75 = 75,
+} HIPjit_target;
+
+typedef enum HIPjit_fallback_enum {
+ HIP_PREFER_PTX = 0,
+ HIP_PREFER_BINARY,
+} HIPjit_fallback;
+
+typedef enum HIPjit_cacheMode_enum {
+ HIP_JIT_CACHE_OPTION_NONE = 0,
+ HIP_JIT_CACHE_OPTION_CG,
+ HIP_JIT_CACHE_OPTION_CA,
+} HIPjit_cacheMode;
+
+typedef enum HIPjitInputType_enum {
+ HIP_JIT_INPUT_HIPBIN = 0,
+ HIP_JIT_INPUT_PTX,
+ HIP_JIT_INPUT_FATBINARY,
+ HIP_JIT_INPUT_OBJECT,
+ HIP_JIT_INPUT_LIBRARY,
+ HIP_JIT_NUM_INPUT_TYPES,
+} HIPjitInputType;
+
+typedef struct HIPlinkState_st* HIPlinkState;
+
+typedef enum hipGLDeviceList {
+ hipGLDeviceListAll = 1, ///< All hip devices used by current OpenGL context.
+ hipGLDeviceListCurrentFrame = 2, ///< Hip devices used by current OpenGL context in current
+ ///< frame
+ hipGLDeviceListNextFrame = 3 ///< Hip devices used by current OpenGL context in next
+ ///< frame.
+} hipGLDeviceList;
+
+typedef enum hipGraphicsRegisterFlags {
+ hipGraphicsRegisterFlagsNone = 0,
+ hipGraphicsRegisterFlagsReadOnly = 1, ///< HIP will not write to this registered resource
+ hipGraphicsRegisterFlagsWriteDiscard =
+ 2, ///< HIP will only write and will not read from this registered resource
+ hipGraphicsRegisterFlagsSurfaceLoadStore = 4, ///< HIP will bind this resource to a surface
+ hipGraphicsRegisterFlagsTextureGather =
+ 8 ///< HIP will perform texture gather operations on this registered resource
+} hipGraphicsRegisterFlags;
+
+typedef enum HIPgraphicsRegisterFlags_enum {
+ HIP_GRAPHICS_REGISTER_FLAGS_NONE = 0x00,
+ HIP_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 0x01,
+ HIP_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 0x02,
+ HIP_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 0x04,
+ HIP_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 0x08,
+} HIPgraphicsRegisterFlags;
+
+typedef enum HIPgraphicsMapResourceFlags_enum {
+ HIP_GRAPHICS_MAP_RESOURCE_FLAGS_NONE = 0x00,
+ HIP_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01,
+ HIP_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02,
+} HIPgraphicsMapResourceFlags;
+
+typedef enum HIParray_cubemap_face_enum {
+ HIP_HIPBEMAP_FACE_POSITIVE_X = 0x00,
+ HIP_HIPBEMAP_FACE_NEGATIVE_X = 0x01,
+ HIP_HIPBEMAP_FACE_POSITIVE_Y = 0x02,
+ HIP_HIPBEMAP_FACE_NEGATIVE_Y = 0x03,
+ HIP_HIPBEMAP_FACE_POSITIVE_Z = 0x04,
+ HIP_HIPBEMAP_FACE_NEGATIVE_Z = 0x05,
+} HIParray_cubemap_face;
+
+typedef enum hipLimit_t {
+ HIP_LIMIT_STACK_SIZE = 0x00,
+ HIP_LIMIT_PRINTF_FIFO_SIZE = 0x01,
+ hipLimitMallocHeapSize = 0x02,
+ HIP_LIMIT_DEV_RUNTIME_SYNC_DEPTH = 0x03,
+ HIP_LIMIT_DEV_RUNTIME_PENDING_LAUNCH_COUNT = 0x04,
+ HIP_LIMIT_MAX,
+} hipLimit_t;
+
+typedef enum hipResourceType {
+ hipResourceTypeArray = 0x00,
+ hipResourceTypeMipmappedArray = 0x01,
+ hipResourceTypeLinear = 0x02,
+ hipResourceTypePitch2D = 0x03,
+} hipResourceType;
+
+typedef enum hipError_t {
+ hipSuccess = 0,
+ hipErrorInvalidValue = 1,
+ hipErrorOutOfMemory = 2,
+ hipErrorNotInitialized = 3,
+ hipErrorDeinitialized = 4,
+ hipErrorProfilerDisabled = 5,
+ hipErrorProfilerNotInitialized = 6,
+ hipErrorProfilerAlreadyStarted = 7,
+ hipErrorProfilerAlreadyStopped = 8,
+ hipErrorNoDevice = 100,
+ hipErrorInvalidDevice = 101,
+ hipErrorInvalidImage = 200,
+ hipErrorInvalidContext = 201,
+ hipErrorContextAlreadyCurrent = 202,
+ hipErrorMapFailed = 205,
+ hipErrorUnmapFailed = 206,
+ hipErrorArrayIsMapped = 207,
+ hipErrorAlreadyMapped = 208,
+ hipErrorNoBinaryForGpu = 209,
+ hipErrorAlreadyAcquired = 210,
+ hipErrorNotMapped = 211,
+ hipErrorNotMappedAsArray = 212,
+ hipErrorNotMappedAsPointer = 213,
+ hipErrorECCNotCorrectable = 214,
+ hipErrorUnsupportedLimit = 215,
+ hipErrorContextAlreadyInUse = 216,
+ hipErrorPeerAccessUnsupported = 217,
+ hipErrorInvalidKernelFile = 218,
+ hipErrorInvalidGraphicsContext = 219,
+ hipErrorInvalidSource = 300,
+ hipErrorFileNotFound = 301,
+ hipErrorSharedObjectSymbolNotFound = 302,
+ hipErrorSharedObjectInitFailed = 303,
+ hipErrorOperatingSystem = 304,
+ hipErrorInvalidHandle = 400,
+ hipErrorNotFound = 500,
+ hipErrorNotReady = 600,
+ hipErrorIllegalAddress = 700,
+ hipErrorLaunchOutOfResources = 701,
+ hipErrorLaunchTimeOut = 702,
+ hipErrorPeerAccessAlreadyEnabled = 704,
+ hipErrorPeerAccessNotEnabled = 705,
+ hipErrorSetOnActiveProcess = 708,
+ hipErrorAssert = 710,
+ hipErrorHostMemoryAlreadyRegistered = 712,
+ hipErrorHostMemoryNotRegistered = 713,
+ hipErrorLaunchFailure = 719,
+ hipErrorCooperativeLaunchTooLarge = 720,
+ hipErrorNotSupported = 801,
+ hipErrorUnknown = 999,
+} hipError_t;
+
+/**
+ * Stream CallBack struct
+ */
+typedef void (*hipStreamCallback_t)(hipStream_t stream, hipError_t status, void* userData);
+
+typedef enum HIPdevice_P2PAttribute_enum {
+ HIP_DEVICE_P2P_ATTRIBUTE_PERFORMANCE_RANK = 0x01,
+ HIP_DEVICE_P2P_ATTRIBUTE_ACCESS_SUPPORTED = 0x02,
+ HIP_DEVICE_P2P_ATTRIBUTE_NATIVE_ATOMIC_SUPPORTED = 0x03,
+ HIP_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED = 0x04,
+} HIPdevice_P2PAttribute;
+
+typedef struct hipGraphicsResource_st* hipGraphicsResource;
+
+typedef struct hip_Memcpy2D {
+ size_t srcXInBytes;
+ size_t srcY;
+ hipMemoryType srcMemoryType;
+ const void* srcHost;
+ hipDeviceptr_t srcDevice;
+ hArray * srcArray;
+ size_t srcPitch;
+ size_t dstXInBytes;
+ size_t dstY;
+ hipMemoryType dstMemoryType;
+ void* dstHost;
+ hipDeviceptr_t dstDevice;
+ hArray * dstArray;
+ size_t dstPitch;
+ size_t WidthInBytes;
+ size_t Height;
+} hip_Memcpy2D;
+
+typedef enum hipDeviceP2PAttr {
+ hipDevP2PAttrPerformanceRank = 0,
+ hipDevP2PAttrAccessSupported,
+ hipDevP2PAttrNativeAtomicSupported,
+ hipDevP2PAttrHipArrayAccessSupported
+} hipDeviceP2PAttr;
+
+typedef struct HIP_MEMCPY3D {
+ size_t srcXInBytes;
+ size_t srcY;
+ size_t srcZ;
+ size_t srcLOD;
+ hipMemoryType srcMemoryType;
+ const void* srcHost;
+ hipDeviceptr_t srcDevice;
+ hArray * srcArray;
+ void* reserved0;
+ size_t srcPitch;
+ size_t srcHeight;
+ size_t dstXInBytes;
+ size_t dstY;
+ size_t dstZ;
+ size_t dstLOD;
+ hipMemoryType dstMemoryType;
+ void* dstHost;
+ hipDeviceptr_t dstDevice;
+ hArray * dstArray;
+ void* reserved1;
+ size_t dstPitch;
+ size_t dstHeight;
+ size_t WidthInBytes;
+ size_t Height;
+ size_t Depth;
+} HIP_MEMCPY3D;
+
+typedef struct HIP_MEMCPY3D_PEER_st {
+ size_t srcXInBytes;
+ size_t srcY;
+ size_t srcZ;
+ size_t srcLOD;
+ hipMemoryType srcMemoryType;
+ const void* srcHost;
+ hipDeviceptr_t srcDevice;
+ hArray * srcArray;
+ hipCtx_t srcContext;
+ size_t srcPitch;
+ size_t srcHeight;
+ size_t dstXInBytes;
+ size_t dstY;
+ size_t dstZ;
+ size_t dstLOD;
+ hipMemoryType dstMemoryType;
+ void* dstHost;
+ hipDeviceptr_t dstDevice;
+ hArray * dstArray;
+ hipCtx_t dstContext;
+ size_t dstPitch;
+ size_t dstHeight;
+ size_t WidthInBytes;
+ size_t Height;
+ size_t Depth;
+} HIP_MEMCPY3D_PEER;
+
+typedef struct HIP_ARRAY_DESCRIPTOR {
+ size_t Width;
+ size_t Height;
+ hipArray_Format Format;
+ unsigned int NumChannels;
+} HIP_ARRAY_DESCRIPTOR;
+
+typedef struct HIP_ARRAY3D_DESCRIPTOR {
+ size_t Width;
+ size_t Height;
+ size_t Depth;
+ hipArray_Format Format;
+ unsigned int NumChannels;
+ unsigned int Flags;
+} HIP_ARRAY3D_DESCRIPTOR;
+
+typedef struct HIP_RESOURCE_DESC_st {
+ hipResourceType resType;
+ union {
+ struct {
+ hArray * h_Array;
+ } array;
+ struct {
+ hipMipmappedArray_t hMipmappedArray;
+ } mipmap;
+ struct {
+ hipDeviceptr_t devPtr;
+ hipArray_Format format;
+ unsigned int numChannels;
+ size_t sizeInBytes;
+ } linear;
+ struct {
+ hipDeviceptr_t devPtr;
+ hipArray_Format format;
+ unsigned int numChannels;
+ size_t width;
+ size_t height;
+ size_t pitchInBytes;
+ } pitch2D;
+ struct {
+ int reserved[32];
+ } reserved;
+ } res;
+ unsigned int flags;
+} hipResourceDesc;
+
+/**
+ * hip texture resource view formats
+ */
+typedef enum hipResourceViewFormat {
+ hipResViewFormatNone = 0x00,
+ hipResViewFormatUnsignedChar1 = 0x01,
+ hipResViewFormatUnsignedChar2 = 0x02,
+ hipResViewFormatUnsignedChar4 = 0x03,
+ hipResViewFormatSignedChar1 = 0x04,
+ hipResViewFormatSignedChar2 = 0x05,
+ hipResViewFormatSignedChar4 = 0x06,
+ hipResViewFormatUnsignedShort1 = 0x07,
+ hipResViewFormatUnsignedShort2 = 0x08,
+ hipResViewFormatUnsignedShort4 = 0x09,
+ hipResViewFormatSignedShort1 = 0x0a,
+ hipResViewFormatSignedShort2 = 0x0b,
+ hipResViewFormatSignedShort4 = 0x0c,
+ hipResViewFormatUnsignedInt1 = 0x0d,
+ hipResViewFormatUnsignedInt2 = 0x0e,
+ hipResViewFormatUnsignedInt4 = 0x0f,
+ hipResViewFormatSignedInt1 = 0x10,
+ hipResViewFormatSignedInt2 = 0x11,
+ hipResViewFormatSignedInt4 = 0x12,
+ hipResViewFormatHalf1 = 0x13,
+ hipResViewFormatHalf2 = 0x14,
+ hipResViewFormatHalf4 = 0x15,
+ hipResViewFormatFloat1 = 0x16,
+ hipResViewFormatFloat2 = 0x17,
+ hipResViewFormatFloat4 = 0x18,
+ hipResViewFormatUnsignedBlockCompressed1 = 0x19,
+ hipResViewFormatUnsignedBlockCompressed2 = 0x1a,
+ hipResViewFormatUnsignedBlockCompressed3 = 0x1b,
+ hipResViewFormatUnsignedBlockCompressed4 = 0x1c,
+ hipResViewFormatSignedBlockCompressed4 = 0x1d,
+ hipResViewFormatUnsignedBlockCompressed5 = 0x1e,
+ hipResViewFormatSignedBlockCompressed5 = 0x1f,
+ hipResViewFormatUnsignedBlockCompressed6H = 0x20,
+ hipResViewFormatSignedBlockCompressed6H = 0x21,
+ hipResViewFormatUnsignedBlockCompressed7 = 0x22
+}hipResourceViewFormat;
+
+typedef enum HIPresourceViewFormat_enum
+{
+ HIP_RES_VIEW_FORMAT_NONE = 0x00, /**< No resource view format (use underlying resource format) */
+ HIP_RES_VIEW_FORMAT_UINT_1X8 = 0x01, /**< 1 channel unsigned 8-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_2X8 = 0x02, /**< 2 channel unsigned 8-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_4X8 = 0x03, /**< 4 channel unsigned 8-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_1X8 = 0x04, /**< 1 channel signed 8-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_2X8 = 0x05, /**< 2 channel signed 8-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_4X8 = 0x06, /**< 4 channel signed 8-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_1X16 = 0x07, /**< 1 channel unsigned 16-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_2X16 = 0x08, /**< 2 channel unsigned 16-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_4X16 = 0x09, /**< 4 channel unsigned 16-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_1X16 = 0x0a, /**< 1 channel signed 16-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_2X16 = 0x0b, /**< 2 channel signed 16-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_4X16 = 0x0c, /**< 4 channel signed 16-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_1X32 = 0x0d, /**< 1 channel unsigned 32-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_2X32 = 0x0e, /**< 2 channel unsigned 32-bit integers */
+ HIP_RES_VIEW_FORMAT_UINT_4X32 = 0x0f, /**< 4 channel unsigned 32-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_1X32 = 0x10, /**< 1 channel signed 32-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_2X32 = 0x11, /**< 2 channel signed 32-bit integers */
+ HIP_RES_VIEW_FORMAT_SINT_4X32 = 0x12, /**< 4 channel signed 32-bit integers */
+ HIP_RES_VIEW_FORMAT_FLOAT_1X16 = 0x13, /**< 1 channel 16-bit floating point */
+ HIP_RES_VIEW_FORMAT_FLOAT_2X16 = 0x14, /**< 2 channel 16-bit floating point */
+ HIP_RES_VIEW_FORMAT_FLOAT_4X16 = 0x15, /**< 4 channel 16-bit floating point */
+ HIP_RES_VIEW_FORMAT_FLOAT_1X32 = 0x16, /**< 1 channel 32-bit floating point */
+ HIP_RES_VIEW_FORMAT_FLOAT_2X32 = 0x17, /**< 2 channel 32-bit floating point */
+ HIP_RES_VIEW_FORMAT_FLOAT_4X32 = 0x18, /**< 4 channel 32-bit floating point */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC1 = 0x19, /**< Block compressed 1 */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC2 = 0x1a, /**< Block compressed 2 */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC3 = 0x1b, /**< Block compressed 3 */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC4 = 0x1c, /**< Block compressed 4 unsigned */
+ HIP_RES_VIEW_FORMAT_SIGNED_BC4 = 0x1d, /**< Block compressed 4 signed */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC5 = 0x1e, /**< Block compressed 5 unsigned */
+ HIP_RES_VIEW_FORMAT_SIGNED_BC5 = 0x1f, /**< Block compressed 5 signed */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC6H = 0x20, /**< Block compressed 6 unsigned half-float */
+ HIP_RES_VIEW_FORMAT_SIGNED_BC6H = 0x21, /**< Block compressed 6 signed half-float */
+ HIP_RES_VIEW_FORMAT_UNSIGNED_BC7 = 0x22 /**< Block compressed 7 */
+} HIPresourceViewFormat;
+
+/**
+ * hip resource view descriptor
+ */
+struct hipResourceViewDesc {
+ enum hipResourceViewFormat format;
+ size_t width;
+ size_t height;
+ size_t depth;
+ unsigned int firstMipmapLevel;
+ unsigned int lastMipmapLevel;
+ unsigned int firstLayer;
+ unsigned int lastLayer;
+};
+
+
+typedef struct hipTextureDesc_st {
+ hipTextureAddressMode addressMode[3];
+ hipTextureFilterMode filterMode;
+ unsigned int flags;
+ unsigned int maxAnisotropy;
+ hipTextureFilterMode mipmapFilterMode;
+ float mipmapLevelBias;
+ float minMipmapLevelClamp;
+ float maxMipmapLevelClamp;
+ float borderColor[4];
+ int reserved[12];
+} hipTextureDesc;
+
+/**
+ * Resource view descriptor
+ */
+typedef struct HIP_RESOURCE_VIEW_DESC_st {
+ hipResourceViewFormat format;
+ size_t width;
+ size_t height;
+ size_t depth;
+ unsigned int firstMipmapLevel;
+ unsigned int lastMipmapLevel;
+ unsigned int firstLayer;
+ unsigned int lastLayer;
+ unsigned int reserved[16];
+} HIP_RESOURCE_VIEW_DESC;
+
+typedef struct HIP_POINTER_ATTRIBUTE_P2P_TOKENS_st {
+ unsigned long long p2pToken;
+ unsigned int vaSpaceToken;
+} HIP_POINTER_ATTRIBUTE_P2P_TOKENS;
+
+
+typedef unsigned int GLenum;
+typedef unsigned int GLuint;
+typedef int GLint;
+
+typedef enum HIPGLDeviceList_enum {
+ HIP_GL_DEVICE_LIST_ALL = 0x01,
+ HIP_GL_DEVICE_LIST_CURRENT_FRAME = 0x02,
+ HIP_GL_DEVICE_LIST_NEXT_FRAME = 0x03,
+} HIPGLDeviceList;
+
+typedef enum HIPGLmap_flags_enum {
+ HIP_GL_MAP_RESOURCE_FLAGS_NONE = 0x00,
+ HIP_GL_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01,
+ HIP_GL_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02,
+} HIPGLmap_flags;
+
+
+/* Function types. */
+typedef hipError_t HIPAPI thipGetErrorName(hipError_t error, const char** pStr);
+typedef hipError_t HIPAPI thipInit(unsigned int Flags);
+typedef hipError_t HIPAPI thipDriverGetVersion(int* driverVersion);
+typedef hipError_t HIPAPI thipGetDevice(hipDevice_t* device, int ordinal);
+typedef hipError_t HIPAPI thipGetDeviceCount(int* count);
+typedef hipError_t HIPAPI thipDeviceGetName(char* name, int len, hipDevice_t dev);
+typedef hipError_t HIPAPI thipDeviceGetAttribute(int* pi, hipDeviceAttribute_t attrib, hipDevice_t dev);
+typedef hipError_t HIPAPI thipDeviceComputeCapability(int* major, int* minor, hipDevice_t dev);
+typedef hipError_t HIPAPI thipDevicePrimaryCtxRetain(hipCtx_t* pctx, hipDevice_t dev);
+typedef hipError_t HIPAPI thipDevicePrimaryCtxRelease(hipDevice_t dev);
+typedef hipError_t HIPAPI thipDevicePrimaryCtxSetFlags(hipDevice_t dev, unsigned int flags);
+typedef hipError_t HIPAPI thipDevicePrimaryCtxGetState(hipDevice_t dev, unsigned int* flags, int* active);
+typedef hipError_t HIPAPI thipDevicePrimaryCtxReset(hipDevice_t dev);
+typedef hipError_t HIPAPI thipCtxCreate(hipCtx_t* pctx, unsigned int flags, hipDevice_t dev);
+typedef hipError_t HIPAPI thipCtxDestroy(hipCtx_t ctx);
+typedef hipError_t HIPAPI thipCtxPushCurrent(hipCtx_t ctx);
+typedef hipError_t HIPAPI thipCtxPopCurrent(hipCtx_t* pctx);
+typedef hipError_t HIPAPI thipCtxSetCurrent(hipCtx_t ctx);
+typedef hipError_t HIPAPI thipCtxGetCurrent(hipCtx_t* pctx);
+typedef hipError_t HIPAPI thipCtxGetDevice(hipDevice_t* device);
+typedef hipError_t HIPAPI thipCtxGetFlags(unsigned int* flags);
+typedef hipError_t HIPAPI thipCtxSynchronize(void);
+typedef hipError_t HIPAPI thipDeviceSynchronize(void);
+typedef hipError_t HIPAPI thipCtxGetCacheConfig(hipFuncCache_t* pconfig);
+typedef hipError_t HIPAPI thipCtxSetCacheConfig(hipFuncCache_t config);
+typedef hipError_t HIPAPI thipCtxGetSharedMemConfig(hipSharedMemConfig* pConfig);
+typedef hipError_t HIPAPI thipCtxSetSharedMemConfig(hipSharedMemConfig config);
+typedef hipError_t HIPAPI thipCtxGetApiVersion(hipCtx_t ctx, unsigned int* version);
+typedef hipError_t HIPAPI thipModuleLoad(hipModule_t* module, const char* fname);
+typedef hipError_t HIPAPI thipModuleLoadData(hipModule_t* module, const void* image);
+typedef hipError_t HIPAPI thipModuleLoadDataEx(hipModule_t* module, const void* image, unsigned int numOptions, hipJitOption* options, void** optionValues);
+typedef hipError_t HIPAPI thipModuleUnload(hipModule_t hmod);
+typedef hipError_t HIPAPI thipModuleGetFunction(hipFunction_t* hfunc, hipModule_t hmod, const char* name);
+typedef hipError_t HIPAPI thipModuleGetGlobal(hipDeviceptr_t* dptr, size_t* bytes, hipModule_t hmod, const char* name);
+typedef hipError_t HIPAPI thipModuleGetTexRef(textureReference** pTexRef, hipModule_t hmod, const char* name);
+typedef hipError_t HIPAPI thipMemGetInfo(size_t* free, size_t* total);
+typedef hipError_t HIPAPI thipMalloc(hipDeviceptr_t* dptr, size_t bytesize);
+typedef hipError_t HIPAPI thipMemAllocPitch(hipDeviceptr_t* dptr, size_t* pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes);
+typedef hipError_t HIPAPI thipFree(hipDeviceptr_t dptr);
+typedef hipError_t HIPAPI thipMemGetAddressRange(hipDeviceptr_t* pbase, size_t* psize, hipDeviceptr_t dptr);
+typedef hipError_t HIPAPI thipHostMalloc(void** pp, size_t bytesize);
+typedef hipError_t HIPAPI thipHostFree(void* p);
+typedef hipError_t HIPAPI thipMemHostAlloc(void** pp, size_t bytesize, unsigned int Flags);
+typedef hipError_t HIPAPI thipHostGetDevicePointer(hipDeviceptr_t* pdptr, void* p, unsigned int Flags);
+typedef hipError_t HIPAPI thipHostGetFlags(unsigned int* pFlags, void* p);
+typedef hipError_t HIPAPI thipMallocManaged(hipDeviceptr_t* dptr, size_t bytesize, unsigned int flags);
+typedef hipError_t HIPAPI thipDeviceGetByPCIBusId(hipDevice_t* dev, const char* pciBusId);
+typedef hipError_t HIPAPI thipDeviceGetPCIBusId(char* pciBusId, int len, hipDevice_t dev);
+typedef hipError_t HIPAPI thipMemHostUnregister(void* p);
+typedef hipError_t HIPAPI thipMemcpy(hipDeviceptr_t dst, hipDeviceptr_t src, size_t ByteCount);
+typedef hipError_t HIPAPI thipMemcpyPeer(hipDeviceptr_t dstDevice, hipCtx_t dstContext, hipDeviceptr_t srcDevice, hipCtx_t srcContext, size_t ByteCount);
+typedef hipError_t HIPAPI thipMemcpyHtoD(hipDeviceptr_t dstDevice, void* srcHost, size_t ByteCount);
+typedef hipError_t HIPAPI thipMemcpyDtoH(void* dstHost, hipDeviceptr_t srcDevice, size_t ByteCount);
+typedef hipError_t HIPAPI thipMemcpyDtoD(hipDeviceptr_t dstDevice, hipDeviceptr_t srcDevice, size_t ByteCount);
+typedef hipError_t HIPAPI thipDrvMemcpy2DUnaligned(const hip_Memcpy2D* pCopy);
+typedef hipError_t HIPAPI thipMemcpyParam2D(const hip_Memcpy2D* pCopy);
+typedef hipError_t HIPAPI thipDrvMemcpy3D(const HIP_MEMCPY3D* pCopy);
+typedef hipError_t HIPAPI thipMemcpyHtoDAsync(hipDeviceptr_t dstDevice, const void* srcHost, size_t ByteCount, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemcpyDtoHAsync(void* dstHost, hipDeviceptr_t srcDevice, size_t ByteCount, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemcpyParam2DAsync(const hip_Memcpy2D* pCopy, hipStream_t hStream);
+typedef hipError_t HIPAPI thipDrvMemcpy3DAsync(const HIP_MEMCPY3D* pCopy, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD8(hipDeviceptr_t dstDevice, unsigned char uc, size_t N);
+typedef hipError_t HIPAPI thipMemsetD16(hipDeviceptr_t dstDevice, unsigned short us, size_t N);
+typedef hipError_t HIPAPI thipMemsetD32(hipDeviceptr_t dstDevice, unsigned int ui, size_t N);
+typedef hipError_t HIPAPI thipMemsetD8Async(hipDeviceptr_t dstDevice, unsigned char uc, size_t N, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD16Async(hipDeviceptr_t dstDevice, unsigned short us, size_t N, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD32Async(hipDeviceptr_t dstDevice, unsigned int ui, size_t N, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD2D8Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned char uc, size_t Width, size_t Height, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD2D16Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned short us, size_t Width, size_t Height, hipStream_t hStream);
+typedef hipError_t HIPAPI thipMemsetD2D32Async(hipDeviceptr_t dstDevice, size_t dstPitch, unsigned int ui, size_t Width, size_t Height, hipStream_t hStream);
+typedef hipError_t HIPAPI thipArrayCreate(hArray ** pHandle, const HIP_ARRAY_DESCRIPTOR* pAllocateArray);
+typedef hipError_t HIPAPI thipArrayDestroy(hArray hArray);
+typedef hipError_t HIPAPI thipArray3DCreate(hArray * pHandle, const HIP_ARRAY3D_DESCRIPTOR* pAllocateArray);
+typedef hipError_t HIPAPI hipPointerGetAttributes(hipPointerAttribute_t* attributes, const void* ptr);
+typedef hipError_t HIPAPI thipStreamCreateWithFlags(hipStream_t* phStream, unsigned int Flags);
+typedef hipError_t HIPAPI thipStreamCreateWithPriority(hipStream_t* phStream, unsigned int flags, int priority);
+typedef hipError_t HIPAPI thipStreamGetPriority(hipStream_t hStream, int* priority);
+typedef hipError_t HIPAPI thipStreamGetFlags(hipStream_t hStream, unsigned int* flags);
+typedef hipError_t HIPAPI thipStreamWaitEvent(hipStream_t hStream, hipEvent_t hEvent, unsigned int Flags);
+typedef hipError_t HIPAPI thipStreamAddCallback(hipStream_t hStream, hipStreamCallback_t callback, void* userData, unsigned int flags);
+typedef hipError_t HIPAPI thipStreamQuery(hipStream_t hStream);
+typedef hipError_t HIPAPI thipStreamSynchronize(hipStream_t hStream);
+typedef hipError_t HIPAPI thipStreamDestroy(hipStream_t hStream);
+typedef hipError_t HIPAPI thipEventCreateWithFlags(hipEvent_t* phEvent, unsigned int Flags);
+typedef hipError_t HIPAPI thipEventRecord(hipEvent_t hEvent, hipStream_t hStream);
+typedef hipError_t HIPAPI thipEventQuery(hipEvent_t hEvent);
+typedef hipError_t HIPAPI thipEventSynchronize(hipEvent_t hEvent);
+typedef hipError_t HIPAPI thipEventDestroy(hipEvent_t hEvent);
+typedef hipError_t HIPAPI thipEventElapsedTime(float* pMilliseconds, hipEvent_t hStart, hipEvent_t hEnd);
+typedef hipError_t HIPAPI thipFuncGetAttribute(int* pi, hipFunction_attribute attrib, hipFunction_t hfunc);
+typedef hipError_t HIPAPI thipFuncSetCacheConfig(hipFunction_t hfunc, hipFuncCache_t config);
+typedef hipError_t HIPAPI thipModuleLaunchKernel(hipFunction_t f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, hipStream_t hStream, void** kernelParams, void** extra);
+typedef hipError_t HIPAPI thipDrvOccupancyMaxActiveBlocksPerMultiprocessor(int* numBlocks, hipFunction_t func, int blockSize, size_t dynamicSMemSize);
+typedef hipError_t HIPAPI thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(int* numBlocks, hipFunction_t func, int blockSize, size_t dynamicSMemSize, unsigned int flags);
+typedef hipError_t HIPAPI thipModuleOccupancyMaxPotentialBlockSize(int* minGridSize, int* blockSize, hipFunction_t func, size_t dynamicSMemSize, int blockSizeLimit);
+typedef hipError_t HIPAPI thipTexRefSetArray(hipTexRef hTexRef, hArray * hArray, unsigned int Flags);
+typedef hipError_t HIPAPI thipTexRefSetAddress(size_t* ByteOffset, hipTexRef hTexRef, hipDeviceptr_t dptr, size_t bytes);
+typedef hipError_t HIPAPI thipTexRefSetAddress2D(hipTexRef hTexRef, const HIP_ARRAY_DESCRIPTOR* desc, hipDeviceptr_t dptr, size_t Pitch);
+typedef hipError_t HIPAPI thipTexRefSetFormat(hipTexRef hTexRef, hipArray_Format fmt, int NumPackedComponents);
+typedef hipError_t HIPAPI thipTexRefSetAddressMode(hipTexRef hTexRef, int dim, hipTextureAddressMode am);
+typedef hipError_t HIPAPI thipTexRefSetFilterMode(hipTexRef hTexRef, hipTextureFilterMode fm);
+typedef hipError_t HIPAPI thipTexRefSetFlags(hipTexRef hTexRef, unsigned int Flags);
+typedef hipError_t HIPAPI thipTexRefGetAddress(hipDeviceptr_t* pdptr, hipTexRef hTexRef);
+typedef hipError_t HIPAPI thipTexRefGetArray(hArray ** phArray, hipTexRef hTexRef);
+typedef hipError_t HIPAPI thipTexRefGetAddressMode(hipTextureAddressMode* pam, hipTexRef hTexRef, int dim);
+typedef hipError_t HIPAPI thipTexObjectCreate(hipTextureObject_t* pTexObject, const hipResourceDesc* pResDesc, const hipTextureDesc* pTexDesc, const HIP_RESOURCE_VIEW_DESC* pResViewDesc);
+typedef hipError_t HIPAPI thipTexObjectDestroy(hipTextureObject_t texObject);
+typedef hipError_t HIPAPI thipDeviceCanAccessPeer(int* canAccessPeer, hipDevice_t dev, hipDevice_t peerDev);
+typedef hipError_t HIPAPI thipCtxEnablePeerAccess(hipCtx_t peerContext, unsigned int Flags);
+typedef hipError_t HIPAPI thipCtxDisablePeerAccess(hipCtx_t peerContext);
+typedef hipError_t HIPAPI thipDeviceGetP2PAttribute(int* value, hipDeviceP2PAttr attrib, hipDevice_t srcDevice, hipDevice_t dstDevice);
+typedef hipError_t HIPAPI thipGraphicsUnregisterResource(hipGraphicsResource resource);
+typedef hipError_t HIPAPI thipGraphicsResourceGetMappedMipmappedArray(hipMipmappedArray_t* pMipmappedArray, hipGraphicsResource resource);
+typedef hipError_t HIPAPI thipGraphicsResourceGetMappedPointer(hipDeviceptr_t* pDevPtr, size_t* pSize, hipGraphicsResource resource);
+typedef hipError_t HIPAPI thipGraphicsMapResources(unsigned int count, hipGraphicsResource* resources, hipStream_t hStream);
+typedef hipError_t HIPAPI thipGraphicsUnmapResources(unsigned int count, hipGraphicsResource* resources, hipStream_t hStream);
+typedef hipError_t HIPAPI thipGraphicsGLRegisterBuffer(hipGraphicsResource* pCudaResource, GLuint buffer, unsigned int Flags);
+typedef hipError_t HIPAPI thipGLGetDevices(unsigned int* pHipDeviceCount, int* pHipDevices, unsigned int hipDeviceCount, hipGLDeviceList deviceList);
+
+
+/* Function declarations. */
+extern thipGetErrorName *hipGetErrorName;
+extern thipInit *hipInit;
+extern thipDriverGetVersion *hipDriverGetVersion;
+extern thipGetDevice *hipGetDevice;
+extern thipGetDeviceCount *hipGetDeviceCount;
+extern thipDeviceGetName *hipDeviceGetName;
+extern thipDeviceGetAttribute *hipDeviceGetAttribute;
+extern thipDeviceComputeCapability *hipDeviceComputeCapability;
+extern thipDevicePrimaryCtxRetain *hipDevicePrimaryCtxRetain;
+extern thipDevicePrimaryCtxRelease *hipDevicePrimaryCtxRelease;
+extern thipDevicePrimaryCtxSetFlags *hipDevicePrimaryCtxSetFlags;
+extern thipDevicePrimaryCtxGetState *hipDevicePrimaryCtxGetState;
+extern thipDevicePrimaryCtxReset *hipDevicePrimaryCtxReset;
+extern thipCtxCreate *hipCtxCreate;
+extern thipCtxDestroy *hipCtxDestroy;
+extern thipCtxPushCurrent *hipCtxPushCurrent;
+extern thipCtxPopCurrent *hipCtxPopCurrent;
+extern thipCtxSetCurrent *hipCtxSetCurrent;
+extern thipCtxGetCurrent *hipCtxGetCurrent;
+extern thipCtxGetDevice *hipCtxGetDevice;
+extern thipCtxGetFlags *hipCtxGetFlags;
+extern thipCtxSynchronize *hipCtxSynchronize;
+extern thipDeviceSynchronize *hipDeviceSynchronize;
+extern thipCtxGetCacheConfig *hipCtxGetCacheConfig;
+extern thipCtxSetCacheConfig *hipCtxSetCacheConfig;
+extern thipCtxGetSharedMemConfig *hipCtxGetSharedMemConfig;
+extern thipCtxSetSharedMemConfig *hipCtxSetSharedMemConfig;
+extern thipCtxGetApiVersion *hipCtxGetApiVersion;
+extern thipModuleLoad *hipModuleLoad;
+extern thipModuleLoadData *hipModuleLoadData;
+extern thipModuleLoadDataEx *hipModuleLoadDataEx;
+extern thipModuleUnload *hipModuleUnload;
+extern thipModuleGetFunction *hipModuleGetFunction;
+extern thipModuleGetGlobal *hipModuleGetGlobal;
+extern thipModuleGetTexRef *hipModuleGetTexRef;
+extern thipMemGetInfo *hipMemGetInfo;
+extern thipMalloc *hipMalloc;
+extern thipMemAllocPitch *hipMemAllocPitch;
+extern thipFree *hipFree;
+extern thipMemGetAddressRange *hipMemGetAddressRange;
+extern thipHostMalloc *hipHostMalloc;
+extern thipHostFree *hipHostFree;
+extern thipHostGetDevicePointer *hipHostGetDevicePointer;
+extern thipHostGetFlags *hipHostGetFlags;
+extern thipMallocManaged *hipMallocManaged;
+extern thipDeviceGetByPCIBusId *hipDeviceGetByPCIBusId;
+extern thipDeviceGetPCIBusId *hipDeviceGetPCIBusId;
+extern thipMemcpyPeer *hipMemcpyPeer;
+extern thipMemcpyHtoD *hipMemcpyHtoD;
+extern thipMemcpyDtoH *hipMemcpyDtoH;
+extern thipMemcpyDtoD *hipMemcpyDtoD;
+extern thipDrvMemcpy2DUnaligned *hipDrvMemcpy2DUnaligned;
+extern thipMemcpyParam2D *hipMemcpyParam2D;
+extern thipDrvMemcpy3D *hipDrvMemcpy3D;
+extern thipMemcpyHtoDAsync *hipMemcpyHtoDAsync;
+extern thipMemcpyDtoHAsync *hipMemcpyDtoHAsync;
+extern thipMemcpyParam2DAsync *hipMemcpyParam2DAsync;
+extern thipDrvMemcpy3DAsync *hipDrvMemcpy3DAsync;
+extern thipMemsetD8 *hipMemsetD8;
+extern thipMemsetD16 *hipMemsetD16;
+extern thipMemsetD32 *hipMemsetD32;
+extern thipMemsetD8Async *hipMemsetD8Async;
+extern thipMemsetD16Async *hipMemsetD16Async;
+extern thipMemsetD32Async *hipMemsetD32Async;
+extern thipArrayCreate *hipArrayCreate;
+extern thipArrayDestroy *hipArrayDestroy;
+extern thipArray3DCreate *hipArray3DCreate;
+extern thipStreamCreateWithFlags *hipStreamCreateWithFlags;
+extern thipStreamCreateWithPriority *hipStreamCreateWithPriority;
+extern thipStreamGetPriority *hipStreamGetPriority;
+extern thipStreamGetFlags *hipStreamGetFlags;
+extern thipStreamWaitEvent *hipStreamWaitEvent;
+extern thipStreamAddCallback *hipStreamAddCallback;
+extern thipStreamQuery *hipStreamQuery;
+extern thipStreamSynchronize *hipStreamSynchronize;
+extern thipStreamDestroy *hipStreamDestroy;
+extern thipEventCreateWithFlags *hipEventCreateWithFlags;
+extern thipEventRecord *hipEventRecord;
+extern thipEventQuery *hipEventQuery;
+extern thipEventSynchronize *hipEventSynchronize;
+extern thipEventDestroy *hipEventDestroy;
+extern thipEventElapsedTime *hipEventElapsedTime;
+extern thipFuncGetAttribute *hipFuncGetAttribute;
+extern thipFuncSetCacheConfig *hipFuncSetCacheConfig;
+extern thipModuleLaunchKernel *hipModuleLaunchKernel;
+extern thipDrvOccupancyMaxActiveBlocksPerMultiprocessor *hipDrvOccupancyMaxActiveBlocksPerMultiprocessor;
+extern thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags *hipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags;
+extern thipModuleOccupancyMaxPotentialBlockSize *hipModuleOccupancyMaxPotentialBlockSize;
+extern thipTexRefSetArray *hipTexRefSetArray;
+extern thipTexRefSetAddress *hipTexRefSetAddress;
+extern thipTexRefSetAddress2D *hipTexRefSetAddress2D;
+extern thipTexRefSetFormat *hipTexRefSetFormat;
+extern thipTexRefSetAddressMode *hipTexRefSetAddressMode;
+extern thipTexRefSetFilterMode *hipTexRefSetFilterMode;
+extern thipTexRefSetFlags *hipTexRefSetFlags;
+extern thipTexRefGetAddress *hipTexRefGetAddress;
+extern thipTexRefGetArray *hipTexRefGetArray;
+extern thipTexRefGetAddressMode *hipTexRefGetAddressMode;
+extern thipTexObjectCreate *hipTexObjectCreate;
+extern thipTexObjectDestroy *hipTexObjectDestroy;
+extern thipDeviceCanAccessPeer *hipDeviceCanAccessPeer;
+extern thipCtxEnablePeerAccess *hipCtxEnablePeerAccess;
+extern thipCtxDisablePeerAccess *hipCtxDisablePeerAccess;
+extern thipDeviceGetP2PAttribute *hipDeviceGetP2PAttribute;
+extern thipGraphicsUnregisterResource *hipGraphicsUnregisterResource;
+extern thipGraphicsResourceGetMappedMipmappedArray *hipGraphicsResourceGetMappedMipmappedArray;
+extern thipGraphicsResourceGetMappedPointer *hipGraphicsResourceGetMappedPointer;
+extern thipGraphicsMapResources *hipGraphicsMapResources;
+extern thipGraphicsUnmapResources *hipGraphicsUnmapResources;
+
+extern thipGraphicsGLRegisterBuffer *hipGraphicsGLRegisterBuffer;
+extern thipGLGetDevices *hipGLGetDevices;
+
+
+enum {
+ HIPEW_SUCCESS = 0,
+ HIPEW_ERROR_OPEN_FAILED = -1,
+ HIPEW_ERROR_ATEXIT_FAILED = -2,
+};
+
+enum {
+ HIPEW_INIT_HIP = 1,
+};
+
+int hipewInit(hipuint32_t flags);
+const char *hipewErrorString(hipError_t result);
+const char *hipewCompilerPath(void);
+int hipewCompilerVersion(void);
+int hipewNvrtcVersion(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HIPEW_H__ */
diff --git a/extern/hipew/src/hipew.c b/extern/hipew/src/hipew.c
new file mode 100644
index 00000000000..9d5a63f869a
--- /dev/null
+++ b/extern/hipew/src/hipew.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+#ifdef _MSC_VER
+# if _MSC_VER < 1900
+# define snprintf _snprintf
+# endif
+# define popen _popen
+# define pclose _pclose
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <hipew.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# define VC_EXTRALEAN
+# include <windows.h>
+
+/* Utility macros. */
+
+typedef HMODULE DynamicLibrary;
+
+# define dynamic_library_open(path) LoadLibraryA(path)
+# define dynamic_library_close(lib) FreeLibrary(lib)
+# define dynamic_library_find(lib, symbol) GetProcAddress(lib, symbol)
+#else
+# include <dlfcn.h>
+
+typedef void* DynamicLibrary;
+
+# define dynamic_library_open(path) dlopen(path, RTLD_NOW)
+# define dynamic_library_close(lib) dlclose(lib)
+# define dynamic_library_find(lib, symbol) dlsym(lib, symbol)
+#endif
+
+#define _LIBRARY_FIND_CHECKED(lib, name) \
+ name = (t##name *)dynamic_library_find(lib, #name); \
+ assert(name);
+
+#define _LIBRARY_FIND(lib, name) \
+ name = (t##name *)dynamic_library_find(lib, #name);
+
+#define HIP_LIBRARY_FIND_CHECKED(name) \
+ _LIBRARY_FIND_CHECKED(hip_lib, name)
+#define HIP_LIBRARY_FIND(name) _LIBRARY_FIND(hip_lib, name)
+
+
+static DynamicLibrary hip_lib;
+
+/* Function definitions. */
+thipGetErrorName *hipGetErrorName;
+thipInit *hipInit;
+thipDriverGetVersion *hipDriverGetVersion;
+thipGetDevice *hipGetDevice;
+thipGetDeviceCount *hipGetDeviceCount;
+thipDeviceGetName *hipDeviceGetName;
+thipDeviceGetAttribute *hipDeviceGetAttribute;
+thipDeviceComputeCapability *hipDeviceComputeCapability;
+thipDevicePrimaryCtxRetain *hipDevicePrimaryCtxRetain;
+thipDevicePrimaryCtxRelease *hipDevicePrimaryCtxRelease;
+thipDevicePrimaryCtxSetFlags *hipDevicePrimaryCtxSetFlags;
+thipDevicePrimaryCtxGetState *hipDevicePrimaryCtxGetState;
+thipDevicePrimaryCtxReset *hipDevicePrimaryCtxReset;
+thipCtxCreate *hipCtxCreate;
+thipCtxDestroy *hipCtxDestroy;
+thipCtxPushCurrent *hipCtxPushCurrent;
+thipCtxPopCurrent *hipCtxPopCurrent;
+thipCtxSetCurrent *hipCtxSetCurrent;
+thipCtxGetCurrent *hipCtxGetCurrent;
+thipCtxGetDevice *hipCtxGetDevice;
+thipCtxGetFlags *hipCtxGetFlags;
+thipCtxSynchronize *hipCtxSynchronize;
+thipDeviceSynchronize *hipDeviceSynchronize;
+thipCtxGetCacheConfig *hipCtxGetCacheConfig;
+thipCtxSetCacheConfig *hipCtxSetCacheConfig;
+thipCtxGetSharedMemConfig *hipCtxGetSharedMemConfig;
+thipCtxSetSharedMemConfig *hipCtxSetSharedMemConfig;
+thipCtxGetApiVersion *hipCtxGetApiVersion;
+thipModuleLoad *hipModuleLoad;
+thipModuleLoadData *hipModuleLoadData;
+thipModuleLoadDataEx *hipModuleLoadDataEx;
+thipModuleUnload *hipModuleUnload;
+thipModuleGetFunction *hipModuleGetFunction;
+thipModuleGetGlobal *hipModuleGetGlobal;
+thipModuleGetTexRef *hipModuleGetTexRef;
+thipMemGetInfo *hipMemGetInfo;
+thipMalloc *hipMalloc;
+thipMemAllocPitch *hipMemAllocPitch;
+thipFree *hipFree;
+thipMemGetAddressRange *hipMemGetAddressRange;
+thipHostMalloc *hipHostMalloc;
+thipHostFree *hipHostFree;
+thipHostGetDevicePointer *hipHostGetDevicePointer;
+thipHostGetFlags *hipHostGetFlags;
+thipMallocManaged *hipMallocManaged;
+thipDeviceGetByPCIBusId *hipDeviceGetByPCIBusId;
+thipDeviceGetPCIBusId *hipDeviceGetPCIBusId;
+thipMemcpyPeer *hipMemcpyPeer;
+thipMemcpyHtoD *hipMemcpyHtoD;
+thipMemcpyDtoH *hipMemcpyDtoH;
+thipMemcpyDtoD *hipMemcpyDtoD;
+thipDrvMemcpy2DUnaligned *hipDrvMemcpy2DUnaligned;
+thipMemcpyParam2D *hipMemcpyParam2D;
+thipDrvMemcpy3D *hipDrvMemcpy3D;
+thipMemcpyHtoDAsync *hipMemcpyHtoDAsync;
+thipMemcpyDtoHAsync *hipMemcpyDtoHAsync;
+thipMemcpyParam2DAsync *hipMemcpyParam2DAsync;
+thipDrvMemcpy3DAsync *hipDrvMemcpy3DAsync;
+thipMemsetD8 *hipMemsetD8;
+thipMemsetD16 *hipMemsetD16;
+thipMemsetD32 *hipMemsetD32;
+thipMemsetD8Async *hipMemsetD8Async;
+thipMemsetD16Async *hipMemsetD16Async;
+thipMemsetD32Async *hipMemsetD32Async;
+thipArrayCreate *hipArrayCreate;
+thipArrayDestroy *hipArrayDestroy;
+thipArray3DCreate *hipArray3DCreate;
+thipStreamCreateWithFlags *hipStreamCreateWithFlags;
+thipStreamCreateWithPriority *hipStreamCreateWithPriority;
+thipStreamGetPriority *hipStreamGetPriority;
+thipStreamGetFlags *hipStreamGetFlags;
+thipStreamWaitEvent *hipStreamWaitEvent;
+thipStreamAddCallback *hipStreamAddCallback;
+thipStreamQuery *hipStreamQuery;
+thipStreamSynchronize *hipStreamSynchronize;
+thipStreamDestroy *hipStreamDestroy;
+thipEventCreateWithFlags *hipEventCreateWithFlags;
+thipEventRecord *hipEventRecord;
+thipEventQuery *hipEventQuery;
+thipEventSynchronize *hipEventSynchronize;
+thipEventDestroy *hipEventDestroy;
+thipEventElapsedTime *hipEventElapsedTime;
+thipFuncGetAttribute *hipFuncGetAttribute;
+thipFuncSetCacheConfig *hipFuncSetCacheConfig;
+thipModuleLaunchKernel *hipModuleLaunchKernel;
+thipDrvOccupancyMaxActiveBlocksPerMultiprocessor *hipDrvOccupancyMaxActiveBlocksPerMultiprocessor;
+thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags *hipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags;
+thipModuleOccupancyMaxPotentialBlockSize *hipModuleOccupancyMaxPotentialBlockSize;
+thipTexRefSetArray *hipTexRefSetArray;
+thipTexRefSetAddress *hipTexRefSetAddress;
+thipTexRefSetAddress2D *hipTexRefSetAddress2D;
+thipTexRefSetFormat *hipTexRefSetFormat;
+thipTexRefSetAddressMode *hipTexRefSetAddressMode;
+thipTexRefSetFilterMode *hipTexRefSetFilterMode;
+thipTexRefSetFlags *hipTexRefSetFlags;
+thipTexRefGetAddress *hipTexRefGetAddress;
+thipTexRefGetArray *hipTexRefGetArray;
+thipTexRefGetAddressMode *hipTexRefGetAddressMode;
+thipTexObjectCreate *hipTexObjectCreate;
+thipTexObjectDestroy *hipTexObjectDestroy;
+thipDeviceCanAccessPeer *hipDeviceCanAccessPeer;
+
+thipCtxEnablePeerAccess *hipCtxEnablePeerAccess;
+thipCtxDisablePeerAccess *hipCtxDisablePeerAccess;
+thipDeviceGetP2PAttribute *hipDeviceGetP2PAttribute;
+thipGraphicsUnregisterResource *hipGraphicsUnregisterResource;
+thipGraphicsMapResources *hipGraphicsMapResources;
+thipGraphicsUnmapResources *hipGraphicsUnmapResources;
+thipGraphicsResourceGetMappedPointer *hipGraphicsResourceGetMappedPointer;
+
+thipGraphicsGLRegisterBuffer *hipGraphicsGLRegisterBuffer;
+thipGLGetDevices *hipGLGetDevices;
+
+
+
+static DynamicLibrary dynamic_library_open_find(const char **paths) {
+ int i = 0;
+ while (paths[i] != NULL) {
+ DynamicLibrary lib = dynamic_library_open(paths[i]);
+ if (lib != NULL) {
+ return lib;
+ }
+ ++i;
+ }
+ return NULL;
+}
+
+/* Implementation function. */
+static void hipewHipExit(void) {
+ if (hip_lib != NULL) {
+ /* Ignore errors. */
+ dynamic_library_close(hip_lib);
+ hip_lib = NULL;
+ }
+}
+
+static int hipewHipInit(void) {
+ /* Library paths. */
+#ifdef _WIN32
+ /* Expected in c:/windows/system or similar, no path needed. */
+ const char *hip_paths[] = {"amdhip64.dll", NULL};
+#elif defined(__APPLE__)
+ /* Default installation path. */
+ const char *hip_paths[] = {"", NULL};
+#else
+ const char *hip_paths[] = {"/opt/rocm/hip/lib/libamdhip64.so", NULL};
+#endif
+ static int initialized = 0;
+ static int result = 0;
+ int error, driver_version;
+
+ if (initialized) {
+ return result;
+ }
+
+ initialized = 1;
+
+ error = atexit(hipewHipExit);
+ if (error) {
+ result = HIPEW_ERROR_ATEXIT_FAILED;
+ return result;
+ }
+
+ /* Load library. */
+ hip_lib = dynamic_library_open_find(hip_paths);
+
+ if (hip_lib == NULL) {
+ result = HIPEW_ERROR_OPEN_FAILED;
+ return result;
+ }
+
+ /* Fetch all function pointers. */
+ HIP_LIBRARY_FIND_CHECKED(hipGetErrorName);
+ HIP_LIBRARY_FIND_CHECKED(hipInit);
+ HIP_LIBRARY_FIND_CHECKED(hipDriverGetVersion);
+ HIP_LIBRARY_FIND_CHECKED(hipGetDevice);
+ HIP_LIBRARY_FIND_CHECKED(hipGetDeviceCount);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceGetName);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceGetAttribute);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceComputeCapability);
+ HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRetain);
+ HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRelease);
+ HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxSetFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxGetState);
+ HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxReset);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxCreate);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxDestroy);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxPushCurrent);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxPopCurrent);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxSetCurrent);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetCurrent);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetDevice);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxSynchronize);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceSynchronize);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetCacheConfig);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxSetCacheConfig);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetSharedMemConfig);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxSetSharedMemConfig);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxGetApiVersion);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleLoad);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleLoadData);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleLoadDataEx);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleUnload);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleGetFunction);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleGetGlobal);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleGetTexRef);
+ HIP_LIBRARY_FIND_CHECKED(hipMemGetInfo);
+ HIP_LIBRARY_FIND_CHECKED(hipMalloc);
+ HIP_LIBRARY_FIND_CHECKED(hipMemAllocPitch);
+ HIP_LIBRARY_FIND_CHECKED(hipFree);
+ HIP_LIBRARY_FIND_CHECKED(hipMemGetAddressRange);
+ HIP_LIBRARY_FIND_CHECKED(hipHostMalloc);
+ HIP_LIBRARY_FIND_CHECKED(hipHostFree);
+ HIP_LIBRARY_FIND_CHECKED(hipHostGetDevicePointer);
+ HIP_LIBRARY_FIND_CHECKED(hipHostGetFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipMallocManaged);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceGetByPCIBusId);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceGetPCIBusId);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyPeer);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoD);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoH);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoD);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2D);
+ HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3D);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoDAsync);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoHAsync);
+ HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy2DUnaligned);
+ HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2DAsync);
+ HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3DAsync);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD8);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD16);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD32);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD8Async);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD16Async);
+ HIP_LIBRARY_FIND_CHECKED(hipMemsetD32Async);
+ HIP_LIBRARY_FIND_CHECKED(hipArrayCreate);
+ HIP_LIBRARY_FIND_CHECKED(hipArrayDestroy);
+ HIP_LIBRARY_FIND_CHECKED(hipArray3DCreate);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithPriority);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamGetPriority);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamGetFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamWaitEvent);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamAddCallback);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamQuery);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamSynchronize);
+ HIP_LIBRARY_FIND_CHECKED(hipStreamDestroy);
+ HIP_LIBRARY_FIND_CHECKED(hipEventCreateWithFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipEventRecord);
+ HIP_LIBRARY_FIND_CHECKED(hipEventQuery);
+ HIP_LIBRARY_FIND_CHECKED(hipEventSynchronize);
+ HIP_LIBRARY_FIND_CHECKED(hipEventDestroy);
+ HIP_LIBRARY_FIND_CHECKED(hipEventElapsedTime);
+ HIP_LIBRARY_FIND_CHECKED(hipFuncGetAttribute);
+ HIP_LIBRARY_FIND_CHECKED(hipFuncSetCacheConfig);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleLaunchKernel);
+ HIP_LIBRARY_FIND_CHECKED(hipModuleOccupancyMaxPotentialBlockSize);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetArray);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress2D);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFormat);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddressMode);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFilterMode);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFlags);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddress);
+ HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddressMode);
+ HIP_LIBRARY_FIND_CHECKED(hipTexObjectCreate);
+ HIP_LIBRARY_FIND_CHECKED(hipTexObjectDestroy);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceCanAccessPeer);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxEnablePeerAccess);
+ HIP_LIBRARY_FIND_CHECKED(hipCtxDisablePeerAccess);
+ HIP_LIBRARY_FIND_CHECKED(hipDeviceGetP2PAttribute);
+#ifdef _WIN32
+ HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnregisterResource);
+ HIP_LIBRARY_FIND_CHECKED(hipGraphicsMapResources);
+ HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnmapResources);
+ HIP_LIBRARY_FIND_CHECKED(hipGraphicsResourceGetMappedPointer);
+ HIP_LIBRARY_FIND_CHECKED(hipGraphicsGLRegisterBuffer);
+ HIP_LIBRARY_FIND_CHECKED(hipGLGetDevices);
+#endif
+ result = HIPEW_SUCCESS;
+ return result;
+}
+
+
+
+int hipewInit(hipuint32_t flags) {
+ int result = HIPEW_SUCCESS;
+
+ if (flags & HIPEW_INIT_HIP) {
+ result = hipewHipInit();
+ if (result != HIPEW_SUCCESS) {
+ return result;
+ }
+ }
+
+ return result;
+}
+
+
+const char *hipewErrorString(hipError_t result) {
+ switch (result) {
+ case hipSuccess: return "No errors";
+ case hipErrorInvalidValue: return "Invalid value";
+ case hipErrorOutOfMemory: return "Out of memory";
+ case hipErrorNotInitialized: return "Driver not initialized";
+ case hipErrorDeinitialized: return "Driver deinitialized";
+ case hipErrorProfilerDisabled: return "Profiler disabled";
+ case hipErrorProfilerNotInitialized: return "Profiler not initialized";
+ case hipErrorProfilerAlreadyStarted: return "Profiler already started";
+ case hipErrorProfilerAlreadyStopped: return "Profiler already stopped";
+ case hipErrorNoDevice: return "No HIP-capable device available";
+ case hipErrorInvalidDevice: return "Invalid device";
+ case hipErrorInvalidImage: return "Invalid kernel image";
+ case hipErrorInvalidContext: return "Invalid context";
+ case hipErrorContextAlreadyCurrent: return "Context already current";
+ case hipErrorMapFailed: return "Map failed";
+ case hipErrorUnmapFailed: return "Unmap failed";
+ case hipErrorArrayIsMapped: return "Array is mapped";
+ case hipErrorAlreadyMapped: return "Already mapped";
+ case hipErrorNoBinaryForGpu: return "No binary for GPU";
+ case hipErrorAlreadyAcquired: return "Already acquired";
+ case hipErrorNotMapped: return "Not mapped";
+ case hipErrorNotMappedAsArray: return "Mapped resource not available for access as an array";
+ case hipErrorNotMappedAsPointer: return "Mapped resource not available for access as a pointer";
+ case hipErrorECCNotCorrectable: return "Uncorrectable ECC error detected";
+ case hipErrorUnsupportedLimit: return "hipLimit_t not supported by device";
+ case hipErrorContextAlreadyInUse: return "Context already in use";
+ case hipErrorPeerAccessUnsupported: return "Peer access unsupported";
+ case hipErrorInvalidKernelFile: return "Invalid ptx";
+ case hipErrorInvalidGraphicsContext: return "Invalid graphics context";
+ case hipErrorInvalidSource: return "Invalid source";
+ case hipErrorFileNotFound: return "File not found";
+ case hipErrorSharedObjectSymbolNotFound: return "Link to a shared object failed to resolve";
+ case hipErrorSharedObjectInitFailed: return "Shared object initialization failed";
+ case hipErrorOperatingSystem: return "Operating system";
+ case hipErrorInvalidHandle: return "Invalid handle";
+ case hipErrorNotFound: return "Not found";
+ case hipErrorNotReady: return "HIP not ready";
+ case hipErrorIllegalAddress: return "Illegal address";
+ case hipErrorLaunchOutOfResources: return "Launch exceeded resources";
+ case hipErrorLaunchTimeOut: return "Launch exceeded timeout";
+ case hipErrorPeerAccessAlreadyEnabled: return "Peer access already enabled";
+ case hipErrorPeerAccessNotEnabled: return "Peer access not enabled";
+ case hipErrorSetOnActiveProcess: return "Primary context active";
+ case hipErrorAssert: return "Assert";
+ case hipErrorHostMemoryAlreadyRegistered: return "Host memory already registered";
+ case hipErrorHostMemoryNotRegistered: return "Host memory not registered";
+ case hipErrorLaunchFailure: return "Launch failed";
+ case hipErrorCooperativeLaunchTooLarge: return "Cooperative launch too large";
+ case hipErrorNotSupported: return "Not supported";
+ case hipErrorUnknown: return "Unknown error";
+ default: return "Unknown HIP error value";
+ }
+}
+
+static void path_join(const char *path1,
+ const char *path2,
+ int maxlen,
+ char *result) {
+#if defined(WIN32) || defined(_WIN32)
+ const char separator = '\\';
+#else
+ const char separator = '/';
+#endif
+ int n = snprintf(result, maxlen, "%s%c%s", path1, separator, path2);
+ if (n != -1 && n < maxlen) {
+ result[n] = '\0';
+ }
+ else {
+ result[maxlen - 1] = '\0';
+ }
+}
+
+static int path_exists(const char *path) {
+ struct stat st;
+ if (stat(path, &st)) {
+ return 0;
+ }
+ return 1;
+}
+
+const char *hipewCompilerPath(void) {
+ #ifdef _WIN32
+ const char *hipPath = getenv("HIP_ROCCLR_HOME");
+ const char *windowsCommand = "perl ";
+ const char *executable = "bin/hipcc";
+
+ static char hipcc[65536];
+ static char finalCommand[65536];
+ if(hipPath) {
+ path_join(hipPath, executable, sizeof(hipcc), hipcc);
+ if(path_exists(hipcc)) {
+ snprintf(finalCommand, sizeof(hipcc), "%s %s", windowsCommand, hipcc);
+ return finalCommand;
+ } else {
+ printf("Could not find hipcc. Make sure HIP_ROCCLR_HOME points to the directory holding /bin/hipcc");
+ }
+ }
+ #else
+ const char *hipPath = "opt/rocm/hip/bin";
+ const char *executable = "hipcc";
+
+ static char hipcc[65536];
+ if(hipPath) {
+ path_join(hipPath, executable, sizeof(hipcc), hipcc);
+ if(path_exists(hipcc)){
+ return hipcc;
+ }
+ }
+ #endif
+
+ {
+#ifdef _WIN32
+ FILE *handle = popen("where hipcc", "r");
+#else
+ FILE *handle = popen("which hipcc", "r");
+#endif
+ if (handle) {
+ char buffer[4096] = {0};
+ int len = fread(buffer, 1, sizeof(buffer) - 1, handle);
+ buffer[len] = '\0';
+ pclose(handle);
+ if (buffer[0]) {
+ return "hipcc";
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int hipewCompilerVersion(void) {
+ const char *path = hipewCompilerPath();
+ const char *marker = "Hip compilation tools, release ";
+ FILE *pipe;
+ int major, minor;
+ char *versionstr;
+ char buf[128];
+ char output[65536] = "\0";
+ char command[65536] = "\0";
+
+ if (path == NULL) {
+ return 0;
+ }
+
+ /* get --version output */
+ strcat(command, "\"");
+ strncat(command, path, sizeof(command) - 1);
+ strncat(command, "\" --version", sizeof(command) - strlen(path) - 1);
+ pipe = popen(command, "r");
+ if (!pipe) {
+ fprintf(stderr, "HIP: failed to run compiler to retrieve version");
+ return 0;
+ }
+
+ while (!feof(pipe)) {
+ if (fgets(buf, sizeof(buf), pipe) != NULL) {
+ strncat(output, buf, sizeof(output) - strlen(output) - 1);
+ }
+ }
+
+ pclose(pipe);
+ return 40;
+}
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 17096d441f0..2018c1d9648 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -297,6 +297,7 @@ endif()
if(WITH_CYCLES_STANDALONE)
set(WITH_CYCLES_DEVICE_CUDA TRUE)
+ set(WITH_CYCLES_DEVICE_HIP TRUE)
endif()
# TODO(sergey): Consider removing it, only causes confusion in interface.
set(WITH_CYCLES_DEVICE_MULTI TRUE)
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index f9dc5f00802..3ed3f54ef9f 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -64,6 +64,8 @@ if(WITH_CYCLES_STANDALONE)
cycles_standalone.cpp
cycles_xml.cpp
cycles_xml.h
+ oiio_output_driver.cpp
+ oiio_output_driver.h
)
add_executable(cycles ${SRC} ${INC} ${INC_SYS})
unset(SRC)
@@ -73,7 +75,7 @@ if(WITH_CYCLES_STANDALONE)
if(APPLE)
if(WITH_OPENCOLORIO)
- set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit")
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit -framework Carbon")
endif()
if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
# OpenImageDenoise uses BNNS from the Accelerate framework.
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index 270096d70b0..00dc140648a 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -36,6 +36,9 @@
#include "util/util_unique_ptr.h"
#include "util/util_version.h"
+#include "app/cycles_xml.h"
+#include "app/oiio_output_driver.h"
+
#ifdef WITH_CYCLES_STANDALONE_GUI
# include "util/util_view.h"
#endif
@@ -53,7 +56,8 @@ struct Options {
SessionParams session_params;
bool quiet;
bool show_help, interactive, pause;
- string output_path;
+ string output_filepath;
+ string output_pass;
} options;
static void session_print(const string &str)
@@ -89,30 +93,6 @@ static void session_print_status()
session_print(status);
}
-static bool write_render(const uchar *pixels, int w, int h, int channels)
-{
- string msg = string_printf("Writing image %s", options.output_path.c_str());
- session_print(msg);
-
- unique_ptr<ImageOutput> out = unique_ptr<ImageOutput>(ImageOutput::create(options.output_path));
- if (!out) {
- return false;
- }
-
- ImageSpec spec(w, h, channels, TypeDesc::UINT8);
- if (!out->open(options.output_path, spec)) {
- return false;
- }
-
- /* conversion for different top/bottom convention */
- out->write_image(
- TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride);
-
- out->close();
-
- return true;
-}
-
static BufferParams &session_buffer_params()
{
static BufferParams buffer_params;
@@ -147,9 +127,14 @@ static void scene_init()
static void session_init()
{
- options.session_params.write_render_cb = write_render;
+ options.output_pass = "combined";
options.session = new Session(options.session_params, options.scene_params);
+ if (!options.output_filepath.empty()) {
+ options.session->set_output_driver(make_unique<OIIOOutputDriver>(
+ options.output_filepath, options.output_pass, session_print));
+ }
+
if (options.session_params.background && !options.quiet)
options.session->progress.set_update_callback(function_bind(&session_print_status));
#ifdef WITH_CYCLES_STANDALONE_GUI
@@ -160,7 +145,12 @@ static void session_init()
/* load scene */
scene_init();
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ /* add pass for output. */
+ Pass *pass = options.scene->create_node<Pass>();
+ pass->set_name(ustring(options.output_pass.c_str()));
+ pass->set_type(PASS_COMBINED);
+
+ options.session->reset(options.session_params, session_buffer_params());
options.session->start();
}
@@ -222,9 +212,7 @@ static void display_info(Progress &progress)
static void display()
{
- static DeviceDrawParams draw_params = DeviceDrawParams();
-
- options.session->draw(session_buffer_params(), draw_params);
+ options.session->draw();
display_info(options.session->progress);
}
@@ -254,7 +242,7 @@ static void motion(int x, int y, int button)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
@@ -271,7 +259,7 @@ static void resize(int width, int height)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
@@ -283,7 +271,7 @@ static void keyboard(unsigned char key)
/* Reset */
else if (key == 'r')
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
/* Cancel */
else if (key == 27) // escape
@@ -320,7 +308,7 @@ static void keyboard(unsigned char key)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
/* Set Max Bounces */
@@ -346,7 +334,7 @@ static void keyboard(unsigned char key)
options.session->scene->integrator->set_max_bounce(bounce);
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
#endif
@@ -361,11 +349,13 @@ static int files_parse(int argc, const char *argv[])
static void options_parse(int argc, const char **argv)
{
- options.width = 0;
- options.height = 0;
+ options.width = 1024;
+ options.height = 512;
options.filepath = "";
options.session = NULL;
options.quiet = false;
+ options.session_params.use_auto_tile = false;
+ options.session_params.tile_size = 0;
/* device names */
string device_names = "";
@@ -411,7 +401,7 @@ static void options_parse(int argc, const char **argv)
&options.session_params.samples,
"Number of samples to render",
"--output %s",
- &options.output_path,
+ &options.output_filepath,
"File path to write output image",
"--threads %d",
&options.session_params.threads,
@@ -422,12 +412,9 @@ static void options_parse(int argc, const char **argv)
"--height %d",
&options.height,
"Window height in pixel",
- "--tile-width %d",
- &options.session_params.tile_size.x,
- "Tile width in pixels",
- "--tile-height %d",
- &options.session_params.tile_size.y,
- "Tile height in pixels",
+ "--tile-size %d",
+ &options.session_params.tile_size,
+ "Tile size in pixels",
"--list-devices",
&list,
"List information about all available devices",
@@ -489,8 +476,9 @@ static void options_parse(int argc, const char **argv)
options.session_params.background = true;
#endif
- /* Use progressive rendering */
- options.session_params.progressive = true;
+ if (options.session_params.tile_size > 0) {
+ options.session_params.use_auto_tile = true;
+ }
/* find matching device */
DeviceType device_type = Device::type_from_string(devicename.c_str());
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index 54f97fddbd9..0b83c60f32d 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -333,6 +333,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node
}
snode = (ShaderNode *)node_type->create(node_type);
+ snode->set_owner(graph);
}
xml_read_node(graph_reader, snode, node);
diff --git a/intern/cycles/app/oiio_output_driver.cpp b/intern/cycles/app/oiio_output_driver.cpp
new file mode 100644
index 00000000000..d791c89772f
--- /dev/null
+++ b/intern/cycles/app/oiio_output_driver.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "app/oiio_output_driver.h"
+
+CCL_NAMESPACE_BEGIN
+
+OIIOOutputDriver::OIIOOutputDriver(const string_view filepath,
+ const string_view pass,
+ LogFunction log)
+ : filepath_(filepath), pass_(pass), log_(log)
+{
+}
+
+OIIOOutputDriver::~OIIOOutputDriver()
+{
+}
+
+void OIIOOutputDriver::write_render_tile(const Tile &tile)
+{
+ /* Only write the full buffer, no intermediate tiles. */
+ if (!(tile.size == tile.full_size)) {
+ return;
+ }
+
+ log_(string_printf("Writing image %s", filepath_.c_str()));
+
+ unique_ptr<ImageOutput> image_output(ImageOutput::create(filepath_));
+ if (image_output == nullptr) {
+ log_("Failed to create image file");
+ return;
+ }
+
+ const int width = tile.size.x;
+ const int height = tile.size.y;
+
+ ImageSpec spec(width, height, 4, TypeDesc::FLOAT);
+ if (!image_output->open(filepath_, spec)) {
+ log_("Failed to create image file");
+ return;
+ }
+
+ vector<float> pixels(width * height * 4);
+ if (!tile.get_pass_pixels(pass_, 4, pixels.data())) {
+ log_("Failed to read render pass pixels");
+ return;
+ }
+
+ /* Manipulate offset and stride to convert from bottom-up to top-down convention. */
+ image_output->write_image(TypeDesc::FLOAT,
+ pixels.data() + (height - 1) * width * 4,
+ AutoStride,
+ -width * 4 * sizeof(float),
+ AutoStride);
+ image_output->close();
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h
new file mode 100644
index 00000000000..cdc4085d962
--- /dev/null
+++ b/intern/cycles/app/oiio_output_driver.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/output_driver.h"
+
+#include "util/util_function.h"
+#include "util/util_image.h"
+#include "util/util_string.h"
+#include "util/util_unique_ptr.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class OIIOOutputDriver : public OutputDriver {
+ public:
+ typedef function<void(const string &)> LogFunction;
+
+ OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log);
+ virtual ~OIIOOutputDriver();
+
+ void write_render_tile(const Tile &tile) override;
+
+ protected:
+ string filepath_;
+ string pass_;
+ LogFunction log_;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index 5bdcfd56a4d..a0442b3394b 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -31,13 +31,14 @@ set(INC_SYS
set(SRC
blender_camera.cpp
blender_device.cpp
+ blender_display_driver.cpp
blender_image.cpp
blender_geometry.cpp
- blender_gpu_display.cpp
blender_light.cpp
blender_mesh.cpp
blender_object.cpp
blender_object_cull.cpp
+ blender_output_driver.cpp
blender_particles.cpp
blender_curves.cpp
blender_logging.cpp
@@ -51,10 +52,11 @@ set(SRC
CCL_api.h
blender_device.h
- blender_gpu_display.h
+ blender_display_driver.h
blender_id_map.h
blender_image.h
blender_object_cull.h
+ blender_output_driver.h
blender_sync.h
blender_session.h
blender_texture.h
@@ -95,6 +97,9 @@ set(ADDON_FILES
add_definitions(${GL_DEFINITIONS})
+if(WITH_CYCLES_DEVICE_HIP)
+ add_definitions(-DWITH_HIP)
+endif()
if(WITH_MOD_FLUID)
add_definitions(-DWITH_FLUID)
endif()
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index e0e8ca10bef..d729cb1ee69 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -28,7 +28,7 @@ def _configure_argument_parser():
action='store_true')
parser.add_argument("--cycles-device",
help="Set the device to use for Cycles, overriding user preferences and the scene setting."
- "Valid options are 'CPU', 'CUDA' or 'OPTIX'."
+ "Valid options are 'CPU', 'CUDA', 'OPTIX', or 'HIP'"
"Additionally, you can append '+CPU' to any GPU type for hybrid rendering.",
default=None)
return parser
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 5fb0eeed925..cea70033784 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -111,6 +111,7 @@ enum_device_type = (
('CPU', "CPU", "CPU", 0),
('CUDA', "CUDA", "CUDA", 1),
('OPTIX', "OptiX", "OptiX", 3),
+ ("HIP", "HIP", "HIP", 4)
)
enum_texture_limit = (
@@ -123,7 +124,7 @@ enum_texture_limit = (
('4096', "4096", "Limit texture size to 4096 pixels", 6),
('8192', "8192", "Limit texture size to 8192 pixels", 7),
)
-
+
# NOTE: Identifiers are expected to be an upper case version of identifiers from `Pass::get_type_enum()`
enum_view3d_shading_render_pass = (
('', "General", ""),
@@ -739,7 +740,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
use_auto_tile: BoolProperty(
name="Auto Tiles",
- description="Automatically split image into tiles",
+ description="Automatically render high resolution images in tiles to reduce memory usage, using the specified tile size. Tiles are cached to disk while rendering to save memory",
default=True,
)
tile_size: IntProperty(
@@ -1266,12 +1267,16 @@ class CyclesPreferences(bpy.types.AddonPreferences):
def get_device_types(self, context):
import _cycles
- has_cuda, has_optix = _cycles.get_device_types()
+ has_cuda, has_optix, has_hip = _cycles.get_device_types()
+
list = [('NONE', "None", "Don't use compute device", 0)]
if has_cuda:
list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1))
if has_optix:
list.append(('OPTIX', "OptiX", "Use OptiX for GPU acceleration", 3))
+ if has_hip:
+ list.append(('HIP', "HIP", "Use HIP for GPU acceleration", 4))
+
return list
compute_device_type: EnumProperty(
@@ -1296,7 +1301,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
def update_device_entries(self, device_list):
for device in device_list:
- if not device[1] in {'CUDA', 'OPTIX', 'CPU'}:
+ if not device[1] in {'CUDA', 'OPTIX', 'CPU', 'HIP'}:
continue
# Try to find existing Device entry
entry = self.find_existing_device_entry(device)
@@ -1330,7 +1335,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
elif entry.type == 'CPU':
cpu_devices.append(entry)
# Extend all GPU devices with CPU.
- if compute_device_type != 'CPU':
+ if compute_device_type != 'CPU' and compute_device_type != 'HIP':
devices.extend(cpu_devices)
return devices
@@ -1340,7 +1345,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
import _cycles
# Ensure `self.devices` is not re-allocated when the second call to
# get_devices_for_type is made, freeing items from the first list.
- for device_type in ('CUDA', 'OPTIX', 'OPENCL'):
+ for device_type in ('CUDA', 'OPTIX', 'HIP'):
self.update_device_entries(_cycles.available_devices(device_type))
# Deprecated: use refresh_devices instead.
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index d02627b9936..c4a1844480c 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -99,6 +99,11 @@ def use_cuda(context):
return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU')
+def use_hip(context):
+ cscene = context.scene.cycles
+
+ return (get_device_type(context) == 'HIP' and cscene.device == 'GPU')
+
def use_optix(context):
cscene = context.scene.cycles
@@ -613,8 +618,8 @@ class CYCLES_RENDER_PT_performance_threads(CyclesButtonsPanel, Panel):
sub.prop(rd, "threads")
-class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel):
- bl_label = "Tiles"
+class CYCLES_RENDER_PT_performance_memory(CyclesButtonsPanel, Panel):
+ bl_label = "Memory"
bl_parent_id = "CYCLES_RENDER_PT_performance"
def draw(self, context):
@@ -2107,7 +2112,7 @@ classes = (
CYCLES_RENDER_PT_film_transparency,
CYCLES_RENDER_PT_performance,
CYCLES_RENDER_PT_performance_threads,
- CYCLES_RENDER_PT_performance_tiles,
+ CYCLES_RENDER_PT_performance_memory,
CYCLES_RENDER_PT_performance_acceleration_structure,
CYCLES_RENDER_PT_performance_final_render,
CYCLES_RENDER_PT_performance_viewport,
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 6fe5ea41fff..b6b4f206620 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -283,10 +283,13 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
return;
Attribute *attr_intercept = NULL;
+ Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH))
+ attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
@@ -336,6 +339,10 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
num_curve_keys++;
}
+ if (attr_length != NULL) {
+ attr_length->add(CData->curve_length[curve]);
+ }
+
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(num_curves, 0));
}
@@ -657,11 +664,15 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Add requested attributes. */
Attribute *attr_intercept = NULL;
+ Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
}
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
+ attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
+ }
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
}
@@ -714,6 +725,10 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
}
}
+ if (attr_length) {
+ attr_length->add(length);
+ }
+
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp
index ce1770f18a3..7bed33855c2 100644
--- a/intern/cycles/blender/blender_device.cpp
+++ b/intern/cycles/blender/blender_device.cpp
@@ -26,6 +26,7 @@ enum ComputeDevice {
COMPUTE_DEVICE_CPU = 0,
COMPUTE_DEVICE_CUDA = 1,
COMPUTE_DEVICE_OPTIX = 3,
+ COMPUTE_DEVICE_HIP = 4,
COMPUTE_DEVICE_NUM
};
@@ -81,6 +82,9 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
mask |= DEVICE_MASK_OPTIX;
}
+ else if (compute_device == COMPUTE_DEVICE_HIP) {
+ mask |= DEVICE_MASK_HIP;
+ }
vector<DeviceInfo> devices = Device::available_devices(mask);
/* Match device preferences and available devices. */
diff --git a/intern/cycles/blender/blender_gpu_display.cpp b/intern/cycles/blender/blender_display_driver.cpp
index c5c3a2bd155..f55a8ce8c4e 100644
--- a/intern/cycles/blender/blender_gpu_display.cpp
+++ b/intern/cycles/blender/blender_display_driver.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "blender/blender_gpu_display.h"
+#include "blender/blender_display_driver.h"
#include "device/device.h"
#include "util/util_logging.h"
@@ -273,17 +273,17 @@ uint BlenderDisplaySpaceShader::get_shader_program()
}
/* --------------------------------------------------------------------
- * BlenderGPUDisplay.
+ * BlenderDisplayDriver.
*/
-BlenderGPUDisplay::BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene)
+BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene)
: b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene))
{
/* Create context while on the main thread. */
gl_context_create();
}
-BlenderGPUDisplay::~BlenderGPUDisplay()
+BlenderDisplayDriver::~BlenderDisplayDriver()
{
gl_resources_destroy();
}
@@ -292,19 +292,18 @@ BlenderGPUDisplay::~BlenderGPUDisplay()
* Update procedure.
*/
-bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams &params,
+bool BlenderDisplayDriver::update_begin(const Params &params,
int texture_width,
int texture_height)
{
- /* Note that it's the responsibility of BlenderGPUDisplay to ensure updating and drawing
+ /* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing
* the texture does not happen at the same time. This is achieved indirectly.
*
* When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
* This same lock is also held when do_draw() is called, which together ensure mutual
* exclusion.
*
- * This locking is not performed at the GPU display level, because that would cause lock
- * inversion. */
+ * This locking is not performed on the Cycles side, because that would cause lock inversion. */
if (!gl_context_enable()) {
return false;
}
@@ -361,7 +360,7 @@ bool BlenderGPUDisplay::do_update_begin(const GPUDisplayParams &params,
return true;
}
-void BlenderGPUDisplay::do_update_end()
+void BlenderDisplayDriver::update_end()
{
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
@@ -370,53 +369,17 @@ void BlenderGPUDisplay::do_update_end()
}
/* --------------------------------------------------------------------
- * Texture update from CPU buffer.
- */
-
-void BlenderGPUDisplay::do_copy_pixels_to_texture(
- const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height)
-{
- /* This call copies pixels to a Pixel Buffer Object (PBO) which is much cheaper from CPU time
- * point of view than to copy data directly to the OpenGL texture.
- *
- * The possible downside of this approach is that it might require a higher peak memory when
- * doing partial updates of the texture (although, in practice even partial updates might peak
- * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */
-
- half4 *mapped_rgba_pixels = map_texture_buffer();
- if (!mapped_rgba_pixels) {
- return;
- }
-
- if (texture_x == 0 && texture_y == 0 && pixels_width == texture_.width &&
- pixels_height == texture_.height) {
- const size_t size_in_bytes = sizeof(half4) * texture_.width * texture_.height;
- memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes);
- }
- else {
- const half4 *rgba_row = rgba_pixels;
- half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_.width + texture_x;
- for (int y = 0; y < pixels_height;
- ++y, rgba_row += pixels_width, mapped_rgba_row += texture_.width) {
- memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width);
- }
- }
-
- unmap_texture_buffer();
-}
-
-/* --------------------------------------------------------------------
* Texture buffer mapping.
*/
-half4 *BlenderGPUDisplay::do_map_texture_buffer()
+half4 *BlenderDisplayDriver::map_texture_buffer()
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
if (!mapped_rgba_pixels) {
- LOG(ERROR) << "Error mapping BlenderGPUDisplay pixel buffer object.";
+ LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object.";
}
if (texture_.need_clear) {
@@ -431,7 +394,7 @@ half4 *BlenderGPUDisplay::do_map_texture_buffer()
return mapped_rgba_pixels;
}
-void BlenderGPUDisplay::do_unmap_texture_buffer()
+void BlenderDisplayDriver::unmap_texture_buffer()
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
@@ -442,9 +405,9 @@ void BlenderGPUDisplay::do_unmap_texture_buffer()
* Graphics interoperability.
*/
-DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get()
+BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get()
{
- DeviceGraphicsInteropDestination interop_dst;
+ GraphicsInterop interop_dst;
interop_dst.buffer_width = texture_.buffer_width;
interop_dst.buffer_height = texture_.buffer_height;
@@ -456,12 +419,12 @@ DeviceGraphicsInteropDestination BlenderGPUDisplay::do_graphics_interop_get()
return interop_dst;
}
-void BlenderGPUDisplay::graphics_interop_activate()
+void BlenderDisplayDriver::graphics_interop_activate()
{
gl_context_enable();
}
-void BlenderGPUDisplay::graphics_interop_deactivate()
+void BlenderDisplayDriver::graphics_interop_deactivate()
{
gl_context_disable();
}
@@ -470,27 +433,21 @@ void BlenderGPUDisplay::graphics_interop_deactivate()
* Drawing.
*/
-void BlenderGPUDisplay::clear()
+void BlenderDisplayDriver::clear()
{
texture_.need_clear = true;
}
-void BlenderGPUDisplay::set_zoom(float zoom_x, float zoom_y)
+void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y)
{
zoom_ = make_float2(zoom_x, zoom_y);
}
-void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
+void BlenderDisplayDriver::draw(const Params &params)
{
/* See do_update_begin() for why no locking is required here. */
const bool transparent = true; // TODO(sergey): Derive this from Film.
- if (texture_.need_clear) {
- /* Texture is requested to be cleared and was not yet cleared.
- * Do early return which should be equivalent of drawing all-zero texture. */
- return;
- }
-
if (!gl_draw_resources_ensure()) {
return;
}
@@ -499,6 +456,16 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
gl_context_mutex_.lock();
}
+ if (texture_.need_clear) {
+ /* Texture is requested to be cleared and was not yet cleared.
+ *
+ * Do early return which should be equivalent of drawing all-zero texture.
+ * Watch out for the lock though so that the clear happening during update is properly
+ * synchronized here. */
+ gl_context_mutex_.unlock();
+ return;
+ }
+
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
}
@@ -524,7 +491,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
const float zoomed_width = params.size.x * zoom_.x;
const float zoomed_height = params.size.y * zoom_.y;
if (texture_.width != params.size.x || texture_.height != params.size.y) {
- /* Resolution divider is different from 1, force enarest interpolation. */
+ /* Resolution divider is different from 1, force nearest interpolation. */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) {
@@ -580,7 +547,7 @@ void BlenderGPUDisplay::do_draw(const GPUDisplayParams &params)
}
}
-void BlenderGPUDisplay::gl_context_create()
+void BlenderDisplayDriver::gl_context_create()
{
/* When rendering in viewport there is no render context available via engine.
* Check whether own context is to be created here.
@@ -609,7 +576,7 @@ void BlenderGPUDisplay::gl_context_create()
}
}
-bool BlenderGPUDisplay::gl_context_enable()
+bool BlenderDisplayDriver::gl_context_enable()
{
if (use_gl_context_) {
if (!gl_context_) {
@@ -624,7 +591,7 @@ bool BlenderGPUDisplay::gl_context_enable()
return true;
}
-void BlenderGPUDisplay::gl_context_disable()
+void BlenderDisplayDriver::gl_context_disable()
{
if (use_gl_context_) {
if (gl_context_) {
@@ -637,7 +604,7 @@ void BlenderGPUDisplay::gl_context_disable()
RE_engine_render_context_disable(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
}
-void BlenderGPUDisplay::gl_context_dispose()
+void BlenderDisplayDriver::gl_context_dispose()
{
if (gl_context_) {
const bool drw_state = DRW_opengl_context_release();
@@ -649,7 +616,7 @@ void BlenderGPUDisplay::gl_context_dispose()
}
}
-bool BlenderGPUDisplay::gl_draw_resources_ensure()
+bool BlenderDisplayDriver::gl_draw_resources_ensure()
{
if (!texture_.gl_id) {
/* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
@@ -676,7 +643,7 @@ bool BlenderGPUDisplay::gl_draw_resources_ensure()
return true;
}
-void BlenderGPUDisplay::gl_resources_destroy()
+void BlenderDisplayDriver::gl_resources_destroy()
{
gl_context_enable();
@@ -699,7 +666,7 @@ void BlenderGPUDisplay::gl_resources_destroy()
gl_context_dispose();
}
-bool BlenderGPUDisplay::gl_texture_resources_ensure()
+bool BlenderDisplayDriver::gl_texture_resources_ensure()
{
if (texture_.creation_attempted) {
return texture_.is_created;
@@ -736,7 +703,7 @@ bool BlenderGPUDisplay::gl_texture_resources_ensure()
return true;
}
-void BlenderGPUDisplay::texture_update_if_needed()
+void BlenderDisplayDriver::texture_update_if_needed()
{
if (!texture_.need_update) {
return;
@@ -750,7 +717,7 @@ void BlenderGPUDisplay::texture_update_if_needed()
texture_.need_update = false;
}
-void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams &params)
+void BlenderDisplayDriver::vertex_buffer_update(const Params &params)
{
/* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
* rendered. */
@@ -763,23 +730,23 @@ void BlenderGPUDisplay::vertex_buffer_update(const GPUDisplayParams &params)
vpointer[0] = 0.0f;
vpointer[1] = 0.0f;
- vpointer[2] = params.offset.x;
- vpointer[3] = params.offset.y;
+ vpointer[2] = params.full_offset.x;
+ vpointer[3] = params.full_offset.y;
vpointer[4] = 1.0f;
vpointer[5] = 0.0f;
- vpointer[6] = (float)params.size.x + params.offset.x;
- vpointer[7] = params.offset.y;
+ vpointer[6] = (float)params.size.x + params.full_offset.x;
+ vpointer[7] = params.full_offset.y;
vpointer[8] = 1.0f;
vpointer[9] = 1.0f;
- vpointer[10] = (float)params.size.x + params.offset.x;
- vpointer[11] = (float)params.size.y + params.offset.y;
+ vpointer[10] = (float)params.size.x + params.full_offset.x;
+ vpointer[11] = (float)params.size.y + params.full_offset.y;
vpointer[12] = 0.0f;
vpointer[13] = 1.0f;
- vpointer[14] = params.offset.x;
- vpointer[15] = (float)params.size.y + params.offset.y;
+ vpointer[14] = params.full_offset.x;
+ vpointer[15] = (float)params.size.y + params.full_offset.y;
glUnmapBuffer(GL_ARRAY_BUFFER);
}
diff --git a/intern/cycles/blender/blender_gpu_display.h b/intern/cycles/blender/blender_display_driver.h
index 89420567037..558997c6b4f 100644
--- a/intern/cycles/blender/blender_gpu_display.h
+++ b/intern/cycles/blender/blender_display_driver.h
@@ -22,12 +22,14 @@
#include "RNA_blender_cpp.h"
-#include "render/gpu_display.h"
+#include "render/display_driver.h"
+
+#include "util/util_thread.h"
#include "util/util_unique_ptr.h"
CCL_NAMESPACE_BEGIN
-/* Base class of shader used for GPU display rendering. */
+/* Base class of shader used for display driver rendering. */
class BlenderDisplayShader {
public:
static constexpr const char *position_attribute_name = "pos";
@@ -96,11 +98,11 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader {
uint shader_program_ = 0;
};
-/* GPU display implementation which is specific for Blender viewport integration. */
-class BlenderGPUDisplay : public GPUDisplay {
+/* Display driver implementation which is specific for Blender viewport integration. */
+class BlenderDisplayDriver : public DisplayDriver {
public:
- BlenderGPUDisplay(BL::RenderEngine &b_engine, BL::Scene &b_scene);
- ~BlenderGPUDisplay();
+ BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene);
+ ~BlenderDisplayDriver();
virtual void graphics_interop_activate() override;
virtual void graphics_interop_deactivate() override;
@@ -110,22 +112,15 @@ class BlenderGPUDisplay : public GPUDisplay {
void set_zoom(float zoom_x, float zoom_y);
protected:
- virtual bool do_update_begin(const GPUDisplayParams &params,
- int texture_width,
- int texture_height) override;
- virtual void do_update_end() override;
+ virtual bool update_begin(const Params &params, int texture_width, int texture_height) override;
+ virtual void update_end() override;
- virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels,
- int texture_x,
- int texture_y,
- int pixels_width,
- int pixels_height) override;
- virtual void do_draw(const GPUDisplayParams &params) override;
+ virtual half4 *map_texture_buffer() override;
+ virtual void unmap_texture_buffer() override;
- virtual half4 *do_map_texture_buffer() override;
- virtual void do_unmap_texture_buffer() override;
+ virtual GraphicsInterop graphics_interop_get() override;
- virtual DeviceGraphicsInteropDestination do_graphics_interop_get() override;
+ virtual void draw(const Params &params) override;
/* Helper function which allocates new GPU context. */
void gl_context_create();
@@ -152,13 +147,13 @@ class BlenderGPUDisplay : public GPUDisplay {
* This buffer is used to render texture in the viewport.
*
* NOTE: The buffer needs to be bound. */
- void vertex_buffer_update(const GPUDisplayParams &params);
+ void vertex_buffer_update(const Params &params);
BL::RenderEngine b_engine_;
/* OpenGL context which is used the render engine doesn't have its own. */
void *gl_context_ = nullptr;
- /* The when Blender RenderEngine side context is not available and the GPUDisplay is to create
+ /* The when Blender RenderEngine side context is not available and the DisplayDriver is to create
* its own context. */
bool use_gl_context_ = false;
/* Mutex used to guard the `gl_context_`. */
diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp
index fca8cb9eda3..7b49bb7fbb7 100644
--- a/intern/cycles/blender/blender_geometry.cpp
+++ b/intern/cycles/blender/blender_geometry.cpp
@@ -80,8 +80,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
{
/* Test if we can instance or if the object is modified. */
Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair);
- BL::ID b_key_id = (BKE_object_is_modified(b_ob_info.real_object)) ? b_ob_info.real_object :
- b_ob_info.object_data;
+ BL::ID b_key_id = (b_ob_info.is_real_object_data() &&
+ BKE_object_is_modified(b_ob_info.real_object)) ?
+ b_ob_info.real_object :
+ b_ob_info.object_data;
GeometryKey key(b_key_id.ptr.data, geom_type);
/* Find shader indices. */
diff --git a/intern/cycles/blender/blender_output_driver.cpp b/intern/cycles/blender/blender_output_driver.cpp
new file mode 100644
index 00000000000..f380b7b3bb1
--- /dev/null
+++ b/intern/cycles/blender/blender_output_driver.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "blender/blender_output_driver.h"
+
+CCL_NAMESPACE_BEGIN
+
+BlenderOutputDriver::BlenderOutputDriver(BL::RenderEngine &b_engine) : b_engine_(b_engine)
+{
+}
+
+BlenderOutputDriver::~BlenderOutputDriver()
+{
+}
+
+bool BlenderOutputDriver::read_render_tile(const Tile &tile)
+{
+ /* Get render result. */
+ BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x,
+ tile.offset.y,
+ tile.size.x,
+ tile.size.y,
+ tile.layer.c_str(),
+ tile.view.c_str());
+
+ /* Can happen if the intersected rectangle gives 0 width or height. */
+ if (b_rr.ptr.data == NULL) {
+ return false;
+ }
+
+ BL::RenderResult::layers_iterator b_single_rlay;
+ b_rr.layers.begin(b_single_rlay);
+
+ /* layer will be missing if it was disabled in the UI */
+ if (b_single_rlay == b_rr.layers.end()) {
+ return false;
+ }
+
+ BL::RenderLayer b_rlay = *b_single_rlay;
+
+ vector<float> pixels(tile.size.x * tile.size.y * 4);
+
+ /* Copy each pass.
+ * TODO:copy only the required ones for better performance? */
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
+ tile.set_pass_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect());
+ }
+
+ b_engine_.end_result(b_rr, false, false, false);
+
+ return true;
+}
+
+bool BlenderOutputDriver::update_render_tile(const Tile &tile)
+{
+ /* Use final write for preview renders, otherwise render result wouldn't be be updated
+ * quickly on Blender side. For all other cases we use the display driver. */
+ if (b_engine_.is_preview()) {
+ write_render_tile(tile);
+ return true;
+ }
+ else {
+ /* Don't highlight full-frame tile. */
+ if (!(tile.size == tile.full_size)) {
+ b_engine_.tile_highlight_clear_all();
+ b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true);
+ }
+
+ return false;
+ }
+}
+
+void BlenderOutputDriver::write_render_tile(const Tile &tile)
+{
+ b_engine_.tile_highlight_clear_all();
+
+ /* Get render result. */
+ BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x,
+ tile.offset.y,
+ tile.size.x,
+ tile.size.y,
+ tile.layer.c_str(),
+ tile.view.c_str());
+
+ /* Can happen if the intersected rectangle gives 0 width or height. */
+ if (b_rr.ptr.data == NULL) {
+ return;
+ }
+
+ BL::RenderResult::layers_iterator b_single_rlay;
+ b_rr.layers.begin(b_single_rlay);
+
+ /* Layer will be missing if it was disabled in the UI. */
+ if (b_single_rlay == b_rr.layers.end()) {
+ return;
+ }
+
+ BL::RenderLayer b_rlay = *b_single_rlay;
+
+ vector<float> pixels(tile.size.x * tile.size.y * 4);
+
+ /* Copy each pass. */
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
+ if (!tile.get_pass_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) {
+ memset(&pixels[0], 0, pixels.size() * sizeof(float));
+ }
+
+ b_pass.rect(&pixels[0]);
+ }
+
+ b_engine_.end_result(b_rr, true, false, true);
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/blender_output_driver.h
new file mode 100644
index 00000000000..8a1cf92d7c7
--- /dev/null
+++ b/intern/cycles/blender/blender_output_driver.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_blender_cpp.h"
+
+#include "render/output_driver.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BlenderOutputDriver : public OutputDriver {
+ public:
+ BlenderOutputDriver(BL::RenderEngine &b_engine);
+ ~BlenderOutputDriver();
+
+ virtual void write_render_tile(const Tile &tile) override;
+ virtual bool update_render_tile(const Tile &tile) override;
+ virtual bool read_render_tile(const Tile &tile) override;
+
+ protected:
+ BL::RenderEngine b_engine_;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 694d8454422..d681517c9e1 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -911,14 +911,16 @@ static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*
static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
{
vector<DeviceType> device_types = Device::available_types();
- bool has_cuda = false, has_optix = false;
+ bool has_cuda = false, has_optix = false, has_hip = false;
foreach (DeviceType device_type, device_types) {
has_cuda |= (device_type == DEVICE_CUDA);
has_optix |= (device_type == DEVICE_OPTIX);
+ has_hip |= (device_type == DEVICE_HIP);
}
- PyObject *list = PyTuple_New(2);
+ PyObject *list = PyTuple_New(3);
PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda));
PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix));
+ PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_hip));
return list;
}
@@ -944,6 +946,9 @@ static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg)
else if (override == "OPTIX") {
BlenderSession::device_override = DEVICE_MASK_OPTIX;
}
+ else if (override == "HIP") {
+ BlenderSession::device_override = DEVICE_MASK_HIP;
+ }
else {
printf("\nError: %s is not a valid Cycles device.\n", override.c_str());
Py_RETURN_FALSE;
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index d65d89a7ddd..3be7ff32bd8 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -42,7 +42,8 @@
#include "util/util_progress.h"
#include "util/util_time.h"
-#include "blender/blender_gpu_display.h"
+#include "blender/blender_display_driver.h"
+#include "blender/blender_output_driver.h"
#include "blender/blender_session.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
@@ -71,7 +72,8 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
width(0),
height(0),
preview_osl(preview_osl),
- python_thread_state(NULL)
+ python_thread_state(NULL),
+ use_developer_ui(false)
{
/* offline render */
background = true;
@@ -156,11 +158,13 @@ void BlenderSession::create_session()
b_v3d, b_rv3d, scene->camera, width, height);
session->reset(session_params, buffer_params);
- /* Create GPU display. */
+ /* Create GPU display.
+ * TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */
if (!b_engine.is_preview() && !headless) {
- unique_ptr<BlenderGPUDisplay> gpu_display = make_unique<BlenderGPUDisplay>(b_engine, b_scene);
- gpu_display_ = gpu_display.get();
- session->set_gpu_display(move(gpu_display));
+ unique_ptr<BlenderDisplayDriver> display_driver = make_unique<BlenderDisplayDriver>(b_engine,
+ b_scene);
+ display_driver_ = display_driver.get();
+ session->set_display_driver(move(display_driver));
}
/* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform
@@ -277,94 +281,6 @@ void BlenderSession::free_session()
session = nullptr;
}
-void BlenderSession::read_render_tile()
-{
- const int2 tile_offset = session->get_render_tile_offset();
- const int2 tile_size = session->get_render_tile_size();
-
- /* get render result */
- BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x,
- tile_offset.y,
- tile_size.x,
- tile_size.y,
- b_rlay_name.c_str(),
- b_rview_name.c_str());
-
- /* can happen if the intersected rectangle gives 0 width or height */
- if (b_rr.ptr.data == NULL) {
- return;
- }
-
- BL::RenderResult::layers_iterator b_single_rlay;
- b_rr.layers.begin(b_single_rlay);
-
- /* layer will be missing if it was disabled in the UI */
- if (b_single_rlay == b_rr.layers.end())
- return;
-
- BL::RenderLayer b_rlay = *b_single_rlay;
-
- vector<float> pixels(tile_size.x * tile_size.y * 4);
-
- /* Copy each pass.
- * TODO:copy only the required ones for better performance? */
- for (BL::RenderPass &b_pass : b_rlay.passes) {
- session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect());
- }
-}
-
-void BlenderSession::write_render_tile()
-{
- const int2 tile_offset = session->get_render_tile_offset();
- const int2 tile_size = session->get_render_tile_size();
-
- const string_view render_layer_name = session->get_render_tile_layer();
- const string_view render_view_name = session->get_render_tile_view();
-
- b_engine.tile_highlight_clear_all();
-
- /* get render result */
- BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x,
- tile_offset.y,
- tile_size.x,
- tile_size.y,
- render_layer_name.c_str(),
- render_view_name.c_str());
-
- /* can happen if the intersected rectangle gives 0 width or height */
- if (b_rr.ptr.data == NULL) {
- return;
- }
-
- BL::RenderResult::layers_iterator b_single_rlay;
- b_rr.layers.begin(b_single_rlay);
-
- /* layer will be missing if it was disabled in the UI */
- if (b_single_rlay == b_rr.layers.end()) {
- return;
- }
-
- BL::RenderLayer b_rlay = *b_single_rlay;
-
- write_render_result(b_rlay);
-
- b_engine.end_result(b_rr, true, false, true);
-}
-
-void BlenderSession::update_render_tile()
-{
- if (!session->has_multiple_render_tiles()) {
- /* Don't highlight full-frame tile. */
- return;
- }
-
- const int2 tile_offset = session->get_render_tile_offset();
- const int2 tile_size = session->get_render_tile_size();
-
- b_engine.tile_highlight_clear_all();
- b_engine.tile_highlight_set(tile_offset.x, tile_offset.y, tile_size.x, tile_size.y, true);
-}
-
void BlenderSession::full_buffer_written(string_view filename)
{
full_buffer_files_.emplace_back(filename);
@@ -438,18 +354,8 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
return;
}
- /* set callback to write out render results */
- session->write_render_tile_cb = [&]() { write_render_tile(); };
-
- /* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender
- * side. */
- /* TODO(sergey): Investigate whether GPUDisplay can be used for the preview as well. */
- if (b_engine.is_preview()) {
- session->update_render_tile_cb = [&]() { write_render_tile(); };
- }
- else {
- session->update_render_tile_cb = [&]() { update_render_tile(); };
- }
+ /* Create driver to write out render results. */
+ session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); };
@@ -557,6 +463,11 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
/* free result without merging */
b_engine.end_result(b_rr, true, false, false);
+ /* When tiled rendering is used there will be no "write" done for the tile. Forcefully clear
+ * highlighted tiles now, so that the highlight will be removed while processing full frame from
+ * file. */
+ b_engine.tile_highlight_clear_all();
+
double total_time, render_time;
session->progress.get_time(total_time, render_time);
VLOG(1) << "Total render time: " << total_time;
@@ -581,12 +492,17 @@ void BlenderSession::render_frame_finish()
for (string_view filename : full_buffer_files_) {
session->process_full_buffer_from_disk(filename);
+ if (check_and_report_session_error()) {
+ break;
+ }
+ }
+
+ for (string_view filename : full_buffer_files_) {
path_remove(filename);
}
- /* clear callback */
- session->write_render_tile_cb = function_null;
- session->update_render_tile_cb = function_null;
+ /* Clear driver. */
+ session->set_output_driver(nullptr);
session->full_buffer_written_cb = function_null;
}
@@ -692,9 +608,8 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
pass->set_type(bake_type_to_pass(bake_type, bake_filter));
pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR));
- session->read_render_tile_cb = [&]() { read_render_tile(); };
- session->write_render_tile_cb = [&]() { write_render_tile(); };
- session->set_gpu_display(nullptr);
+ session->set_display_driver(nullptr);
+ session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
if (!session->progress.get_cancel()) {
/* Sync scene. */
@@ -737,43 +652,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
session->wait();
}
- session->read_render_tile_cb = function_null;
- session->write_render_tile_cb = function_null;
-}
-
-void BlenderSession::write_render_result(BL::RenderLayer &b_rlay)
-{
- if (!session->copy_render_tile_from_device()) {
- return;
- }
-
- const int2 tile_size = session->get_render_tile_size();
- vector<float> pixels(tile_size.x * tile_size.y * 4);
-
- /* Copy each pass. */
- for (BL::RenderPass &b_pass : b_rlay.passes) {
- if (!session->get_render_tile_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) {
- memset(&pixels[0], 0, pixels.size() * sizeof(float));
- }
-
- b_pass.rect(&pixels[0]);
- }
-}
-
-void BlenderSession::update_render_result(BL::RenderLayer &b_rlay)
-{
- if (!session->copy_render_tile_from_device()) {
- return;
- }
-
- const int2 tile_size = session->get_render_tile_size();
- vector<float> pixels(tile_size.x * tile_size.y * 4);
-
- /* Copy combined pass. */
- BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
- if (session->get_render_tile_pixels("Combined", b_combined_pass.channels(), &pixels[0])) {
- b_combined_pass.rect(&pixels[0]);
- }
+ session->set_output_driver(nullptr);
}
void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
@@ -881,7 +760,7 @@ void BlenderSession::draw(BL::SpaceImageEditor &space_image)
}
BL::Array<float, 2> zoom = space_image.zoom();
- gpu_display_->set_zoom(zoom[0], zoom[1]);
+ display_driver_->set_zoom(zoom[0], zoom[1]);
session->draw();
}
@@ -988,8 +867,9 @@ void BlenderSession::update_status_progress()
get_status(status, substatus);
get_progress(progress, total_time, render_time);
- if (progress > 0)
- remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
+ if (progress > 0) {
+ remaining_time = session->get_estimated_remaining_time();
+ }
if (background) {
if (scene)
@@ -1027,20 +907,27 @@ void BlenderSession::update_status_progress()
last_progress = progress;
}
- if (session->progress.get_error()) {
- string error = session->progress.get_error_message();
- if (error != last_error) {
- /* TODO(sergey): Currently C++ RNA API doesn't let us to
- * use mnemonic name for the variable. Would be nice to
- * have this figured out.
- *
- * For until then, 1 << 5 means RPT_ERROR.
- */
- b_engine.report(1 << 5, error.c_str());
- b_engine.error_set(error.c_str());
- last_error = error;
- }
+ check_and_report_session_error();
+}
+
+bool BlenderSession::check_and_report_session_error()
+{
+ if (!session->progress.get_error()) {
+ return false;
}
+
+ const string error = session->progress.get_error_message();
+ if (error != last_error) {
+ /* TODO(sergey): Currently C++ RNA API doesn't let us to use mnemonic name for the variable.
+ * Would be nice to have this figured out.
+ *
+ * For until then, 1 << 5 means RPT_ERROR. */
+ b_engine.report(1 << 5, error.c_str());
+ b_engine.error_set(error.c_str());
+ last_error = error;
+ }
+
+ return true;
}
void BlenderSession::tag_update()
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index 11e2657a325..fef6ad1adfc 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -29,7 +29,7 @@
CCL_NAMESPACE_BEGIN
-class BlenderGPUDisplay;
+class BlenderDisplayDriver;
class BlenderSync;
class ImageMetaData;
class Scene;
@@ -70,20 +70,7 @@ class BlenderSession {
const int bake_width,
const int bake_height);
- void write_render_result(BL::RenderLayer &b_rlay);
- void write_render_tile();
-
- void update_render_tile();
-
void full_buffer_written(string_view filename);
-
- /* update functions are used to update display buffer only after sample was rendered
- * only needed for better visual feedback */
- void update_render_result(BL::RenderLayer &b_rlay);
-
- /* read functions for baking input */
- void read_render_tile();
-
/* interactive updates */
void synchronize(BL::Depsgraph &b_depsgraph);
@@ -110,8 +97,7 @@ class BlenderSession {
BL::RenderSettings b_render;
BL::Depsgraph b_depsgraph;
/* NOTE: Blender's scene might become invalid after call
- * free_blender_memory_if_possible().
- */
+ * #free_blender_memory_if_possible(). */
BL::Scene b_scene;
BL::SpaceView3D b_v3d;
BL::RegionView3D b_rv3d;
@@ -147,6 +133,11 @@ class BlenderSession {
protected:
void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name);
+ /* Check whether session error happened.
+ * If so, it is reported to the render engine and true is returned.
+ * Otherwise false is returned. */
+ bool check_and_report_session_error();
+
void builtin_images_load();
/* Is used after each render layer synchronization is done with the goal
@@ -160,8 +151,8 @@ class BlenderSession {
int last_pass_index = -1;
} draw_state_;
- /* NOTE: The BlenderSession references the GPU display. */
- BlenderGPUDisplay *gpu_display_ = nullptr;
+ /* NOTE: The BlenderSession references the display driver. */
+ BlenderDisplayDriver *display_driver_ = nullptr;
vector<string> full_buffer_files_;
};
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 8c4f789ffd0..0b8aea15d6c 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -279,7 +279,7 @@ static ShaderNode *add_node(Scene *scene,
array<float3> curve_mapping_curves;
float min_x, max_x;
curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, true);
- curvemapping_minmax(mapping, true, &min_x, &max_x);
+ curvemapping_minmax(mapping, 4, &min_x, &max_x);
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
@@ -292,12 +292,25 @@ static ShaderNode *add_node(Scene *scene,
array<float3> curve_mapping_curves;
float min_x, max_x;
curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, false);
- curvemapping_minmax(mapping, false, &min_x, &max_x);
+ curvemapping_minmax(mapping, 3, &min_x, &max_x);
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
node = curves;
}
+ else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) {
+ BL::ShaderNodeFloatCurve b_curve_node(b_node);
+ BL::CurveMapping mapping(b_curve_node.mapping());
+ FloatCurveNode *curve = graph->create_node<FloatCurveNode>();
+ array<float> curve_mapping_curve;
+ float min_x, max_x;
+ curvemapping_float_to_array(mapping, curve_mapping_curve, RAMP_TABLE_SIZE);
+ curvemapping_minmax(mapping, 1, &min_x, &max_x);
+ curve->set_min_x(min_x);
+ curve->set_max_x(max_x);
+ curve->set_curve(curve_mapping_curve);
+ node = curve;
+ }
else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) {
RGBRampNode *ramp = graph->create_node<RGBRampNode>();
BL::ShaderNodeValToRGB b_ramp_node(b_node);
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 04008d77d89..77b2bd5ac4f 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -90,26 +90,27 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
}
#endif
- BL::Mesh mesh(PointerRNA_NULL);
- if (b_ob_info.object_data.is_a(&RNA_Mesh)) {
- /* TODO: calc_undeformed is not used. */
- mesh = BL::Mesh(b_ob_info.object_data);
-
- /* Make a copy to split faces if we use autosmooth, otherwise not needed.
- * Also in edit mode do we need to make a copy, to ensure data layers like
- * UV are not empty. */
- if (mesh.is_editmode() ||
- (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) {
+ BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) :
+ BL::Mesh(PointerRNA_NULL);
+
+ if (b_ob_info.is_real_object_data()) {
+ if (mesh) {
+ /* Make a copy to split faces if we use autosmooth, otherwise not needed.
+ * Also in edit mode do we need to make a copy, to ensure data layers like
+ * UV are not empty. */
+ if (mesh.is_editmode() ||
+ (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) {
+ BL::Depsgraph depsgraph(PointerRNA_NULL);
+ mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
+ }
+ }
+ else {
BL::Depsgraph depsgraph(PointerRNA_NULL);
- assert(b_ob_info.is_real_object_data());
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
}
}
else {
- BL::Depsgraph depsgraph(PointerRNA_NULL);
- if (b_ob_info.is_real_object_data()) {
- mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
- }
+ /* TODO: what to do about non-mesh geometry instances? */
}
#if 0
@@ -170,12 +171,11 @@ static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap &curve, float *m
}
static inline void curvemapping_minmax(/*const*/ BL::CurveMapping &cumap,
- bool rgb_curve,
+ int num_curves,
float *min_x,
float *max_x)
{
// const int num_curves = cumap.curves.length(); /* Gives linking error so far. */
- const int num_curves = rgb_curve ? 4 : 3;
*min_x = FLT_MAX;
*max_x = -FLT_MAX;
for (int i = 0; i < num_curves; ++i) {
@@ -195,6 +195,28 @@ static inline void curvemapping_to_array(BL::CurveMapping &cumap, array<float> &
}
}
+static inline void curvemapping_float_to_array(BL::CurveMapping &cumap,
+ array<float> &data,
+ int size)
+{
+ float min = 0.0f, max = 1.0f;
+
+ curvemapping_minmax(cumap, 1, &min, &max);
+
+ const float range = max - min;
+
+ cumap.update();
+
+ BL::CurveMap map = cumap.curves[0];
+
+ data.resize(size);
+
+ for (int i = 0; i < size; i++) {
+ float t = min + (float)i / (float)(size - 1) * range;
+ data[i] = cumap.evaluate(map, t);
+ }
+}
+
static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
array<float3> &data,
int size,
@@ -213,7 +235,8 @@ static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
*
* There might be some better estimations here tho.
*/
- curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x);
+ const int num_curves = rgb_curve ? 4 : 3;
+ curvemapping_minmax(cumap, num_curves, &min_x, &max_x);
const float range_x = max_x - min_x;
diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp
index 96852510b63..20430cb164c 100644
--- a/intern/cycles/bvh/bvh_embree.cpp
+++ b/intern/cycles/bvh/bvh_embree.cpp
@@ -213,7 +213,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
if (ctx->num_hits < ctx->max_hits) {
Intersection current_isect;
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
- for (size_t i = 0; i < ctx->max_hits; ++i) {
+ for (size_t i = 0; i < ctx->num_hits; ++i) {
if (current_isect.object == ctx->isect_s[i].object &&
current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) {
/* This intersection was already recorded, skip it. */
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index da259171844..b966edd4298 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -532,4 +532,13 @@ if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD)
endif()
endif()
+
+###########################################################################
+# HIP
+###########################################################################
+
+if(NOT WITH_HIP_DYNLOAD)
+ set(WITH_HIP_DYNLOAD ON)
+endif()
+
unset(_cycles_lib_dir)
diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake
index 47196dfd1ce..a470fb9c574 100644
--- a/intern/cycles/cmake/macros.cmake
+++ b/intern/cycles/cmake/macros.cmake
@@ -156,10 +156,16 @@ macro(cycles_target_link_libraries target)
${PLATFORM_LINKLIBS}
)
- if(WITH_CUDA_DYNLOAD)
- target_link_libraries(${target} extern_cuew)
- else()
- target_link_libraries(${target} ${CUDA_CUDA_LIBRARY})
+ if(WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX)
+ if(WITH_CUDA_DYNLOAD)
+ target_link_libraries(${target} extern_cuew)
+ else()
+ target_link_libraries(${target} ${CUDA_CUDA_LIBRARY})
+ endif()
+ endif()
+
+ if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD)
+ target_link_libraries(${target} extern_hipew)
endif()
if(CYCLES_STANDALONE_REPOSITORY)
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index d18f4360aef..6d33a6f107f 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -22,16 +22,25 @@ set(INC_SYS
../../../extern/clew/include
)
-if(WITH_CUDA_DYNLOAD)
+if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA)
+ if(WITH_CUDA_DYNLOAD)
+ list(APPEND INC
+ ../../../extern/cuew/include
+ )
+ add_definitions(-DWITH_CUDA_DYNLOAD)
+ else()
+ list(APPEND INC_SYS
+ ${CUDA_TOOLKIT_INCLUDE}
+ )
+ add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}")
+ endif()
+endif()
+
+if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD)
list(APPEND INC
- ../../../extern/cuew/include
- )
- add_definitions(-DWITH_CUDA_DYNLOAD)
-else()
- list(APPEND INC_SYS
- ${CUDA_TOOLKIT_INCLUDE}
+ ../../../extern/hipew/include
)
- add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}")
+ add_definitions(-DWITH_HIP_DYNLOAD)
endif()
set(SRC
@@ -70,6 +79,21 @@ set(SRC_CUDA
cuda/util.h
)
+set(SRC_HIP
+ hip/device.cpp
+ hip/device.h
+ hip/device_impl.cpp
+ hip/device_impl.h
+ hip/graphics_interop.cpp
+ hip/graphics_interop.h
+ hip/kernel.cpp
+ hip/kernel.h
+ hip/queue.cpp
+ hip/queue.h
+ hip/util.cpp
+ hip/util.h
+)
+
set(SRC_DUMMY
dummy/device.cpp
dummy/device.h
@@ -105,13 +129,21 @@ set(LIB
${CYCLES_GL_LIBRARIES}
)
-if(WITH_CUDA_DYNLOAD)
- list(APPEND LIB
- extern_cuew
- )
-else()
+if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA)
+ if(WITH_CUDA_DYNLOAD)
+ list(APPEND LIB
+ extern_cuew
+ )
+ else()
+ list(APPEND LIB
+ ${CUDA_CUDA_LIBRARY}
+ )
+ endif()
+endif()
+
+if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD)
list(APPEND LIB
- ${CUDA_CUDA_LIBRARY}
+ extern_hipew
)
endif()
@@ -120,6 +152,9 @@ add_definitions(${GL_DEFINITIONS})
if(WITH_CYCLES_DEVICE_CUDA)
add_definitions(-DWITH_CUDA)
endif()
+if(WITH_CYCLES_DEVICE_HIP)
+ add_definitions(-DWITH_HIP)
+endif()
if(WITH_CYCLES_DEVICE_OPTIX)
add_definitions(-DWITH_OPTIX)
endif()
@@ -140,6 +175,7 @@ cycles_add_library(cycles_device "${LIB}"
${SRC}
${SRC_CPU}
${SRC_CUDA}
+ ${SRC_HIP}
${SRC_DUMMY}
${SRC_MULTI}
${SRC_OPTIX}
diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp
index 3b0db6bdd0e..d02c18daee9 100644
--- a/intern/cycles/device/cpu/device_impl.cpp
+++ b/intern/cycles/device/cpu/device_impl.cpp
@@ -54,7 +54,6 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_map.h"
-#include "util/util_opengl.h"
#include "util/util_openimagedenoise.h"
#include "util/util_optimization.h"
#include "util/util_progress.h"
@@ -170,7 +169,7 @@ void CPUDevice::mem_copy_to(device_memory &mem)
}
void CPUDevice::mem_copy_from(
- device_memory & /*mem*/, int /*y*/, int /*w*/, int /*h*/, int /*elem*/)
+ device_memory & /*mem*/, size_t /*y*/, size_t /*w*/, size_t /*h*/, size_t /*elem*/)
{
/* no-op */
}
@@ -204,7 +203,7 @@ void CPUDevice::mem_free(device_memory &mem)
}
}
-device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/)
+device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/)
{
return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset));
}
@@ -298,154 +297,6 @@ void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
Device::build_bvh(bvh, progress, refit);
}
-#if 0
-void CPUDevice::render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg)
-{
- const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE;
-
- scoped_timer timer(&tile.buffers->render_time);
-
- Coverage coverage(kg, tile);
- if (use_coverage) {
- coverage.init_path_trace();
- }
-
- float *render_buffer = (float *)tile.buffer;
- int start_sample = tile.start_sample;
- int end_sample = tile.start_sample + tile.num_samples;
-
- /* Needed for Embree. */
- SIMD_SET_FLUSH_TO_ZERO;
-
- for (int sample = start_sample; sample < end_sample; sample++) {
- if (task.get_cancel() || TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- break;
- }
-
- if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) {
- tile.stealing_state = RenderTile::WAS_STOLEN;
- break;
- }
-
- if (tile.task == RenderTile::PATH_TRACE) {
- for (int y = tile.y; y < tile.y + tile.h; y++) {
- for (int x = tile.x; x < tile.x + tile.w; x++) {
- if (use_coverage) {
- coverage.init_pixel(x, y);
- }
- kernels.path_trace(kg, render_buffer, sample, x, y, tile.offset, tile.stride);
- }
- }
- }
- else {
- for (int y = tile.y; y < tile.y + tile.h; y++) {
- for (int x = tile.x; x < tile.x + tile.w; x++) {
- kernels.bake(kg, render_buffer, sample, x, y, tile.offset, tile.stride);
- }
- }
- }
- tile.sample = sample + 1;
-
- if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) {
- const bool stop = adaptive_sampling_filter(kg, tile, sample);
- if (stop) {
- const int num_progress_samples = end_sample - sample;
- tile.sample = end_sample;
- task.update_progress(&tile, tile.w * tile.h * num_progress_samples);
- break;
- }
- }
-
- task.update_progress(&tile, tile.w * tile.h);
- }
- if (use_coverage) {
- coverage.finalize();
- }
-
- if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) {
- adaptive_sampling_post(tile, kg);
- }
-}
-
-void CPUDevice::thread_render(DeviceTask &task)
-{
- if (TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- return;
- }
-
- /* allocate buffer for kernel globals */
- CPUKernelThreadGlobals kg(kernel_globals, get_cpu_osl_memory());
-
- profiler.add_state(&kg.profiler);
-
- /* NLM denoiser. */
- DenoisingTask *denoising = NULL;
-
- /* OpenImageDenoise: we can only denoise with one thread at a time, so to
- * avoid waiting with mutex locks in the denoiser, we let only a single
- * thread acquire denoising tiles. */
- uint tile_types = task.tile_types;
- bool hold_denoise_lock = false;
- if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
- if (!oidn_task_lock.try_lock()) {
- tile_types &= ~RenderTile::DENOISE;
- hold_denoise_lock = true;
- }
- }
-
- RenderTile tile;
- while (task.acquire_tile(this, tile, tile_types)) {
- if (tile.task == RenderTile::PATH_TRACE) {
- render(task, tile, &kg);
- }
- else if (tile.task == RenderTile::BAKE) {
- render(task, tile, &kg);
- }
- else if (tile.task == RenderTile::DENOISE) {
- denoise_openimagedenoise(task, tile);
- task.update_progress(&tile, tile.w * tile.h);
- }
-
- task.release_tile(tile);
-
- if (TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- if (hold_denoise_lock) {
- oidn_task_lock.unlock();
- }
-
- profiler.remove_state(&kg.profiler);
-
- delete denoising;
-}
-
-void CPUDevice::thread_denoise(DeviceTask &task)
-{
- RenderTile tile;
- tile.x = task.x;
- tile.y = task.y;
- tile.w = task.w;
- tile.h = task.h;
- tile.buffer = task.buffer;
- tile.sample = task.sample + task.num_samples;
- tile.num_samples = task.num_samples;
- tile.start_sample = task.sample;
- tile.offset = task.offset;
- tile.stride = task.stride;
- tile.buffers = task.buffers;
-
- denoise_openimagedenoise(task, tile);
-
- task.update_progress(&tile, tile.w * tile.h);
-}
-#endif
-
const CPUKernels *CPUDevice::get_cpu_kernels() const
{
return &kernels;
diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h
index 7d222808652..371d2258104 100644
--- a/intern/cycles/device/cpu/device_impl.h
+++ b/intern/cycles/device/cpu/device_impl.h
@@ -72,10 +72,13 @@ class CPUDevice : public Device {
virtual void mem_alloc(device_memory &mem) override;
virtual void mem_copy_to(device_memory &mem) override;
- virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override;
+ virtual void mem_copy_from(
+ device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override;
virtual void mem_zero(device_memory &mem) override;
virtual void mem_free(device_memory &mem) override;
- virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override;
+ virtual device_ptr mem_alloc_sub_ptr(device_memory &mem,
+ size_t offset,
+ size_t /*size*/) override;
virtual void const_copy_to(const char *name, void *host, size_t size) override;
diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp
index 37fab8f8293..5e1a63c04df 100644
--- a/intern/cycles/device/cuda/device_impl.cpp
+++ b/intern/cycles/device/cuda/device_impl.cpp
@@ -31,7 +31,6 @@
# include "util/util_logging.h"
# include "util/util_map.h"
# include "util/util_md5.h"
-# include "util/util_opengl.h"
# include "util/util_path.h"
# include "util/util_string.h"
# include "util/util_system.h"
@@ -837,7 +836,7 @@ void CUDADevice::mem_copy_to(device_memory &mem)
}
}
-void CUDADevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem)
+void CUDADevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem)
{
if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) {
assert(!"mem_copy_from not supported for textures.");
@@ -891,7 +890,7 @@ void CUDADevice::mem_free(device_memory &mem)
}
}
-device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/)
+device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/)
{
return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset));
}
@@ -1169,141 +1168,6 @@ void CUDADevice::tex_free(device_texture &mem)
}
}
-# if 0
-void CUDADevice::render(DeviceTask &task,
- RenderTile &rtile,
- device_vector<KernelWorkTile> &work_tiles)
-{
- scoped_timer timer(&rtile.buffers->render_time);
-
- if (have_error())
- return;
-
- CUDAContextScope scope(this);
- CUfunction cuRender;
-
- /* Get kernel function. */
- if (rtile.task == RenderTile::BAKE) {
- cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake"));
- }
- else {
- cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace"));
- }
-
- if (have_error()) {
- return;
- }
-
- cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1));
-
- /* Allocate work tile. */
- work_tiles.alloc(1);
-
- KernelWorkTile *wtile = work_tiles.data();
- wtile->x = rtile.x;
- wtile->y = rtile.y;
- wtile->w = rtile.w;
- wtile->h = rtile.h;
- wtile->offset = rtile.offset;
- wtile->stride = rtile.stride;
- wtile->buffer = (float *)(CUdeviceptr)rtile.buffer;
-
- /* Prepare work size. More step samples render faster, but for now we
- * remain conservative for GPUs connected to a display to avoid driver
- * timeouts and display freezing. */
- int min_blocks, num_threads_per_block;
- cuda_assert(
- cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0));
- if (!info.display_device) {
- min_blocks *= 8;
- }
-
- uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h);
-
- /* Render all samples. */
- uint start_sample = rtile.start_sample;
- uint end_sample = rtile.start_sample + rtile.num_samples;
-
- for (int sample = start_sample; sample < end_sample;) {
- /* Setup and copy work tile to device. */
- wtile->start_sample = sample;
- wtile->num_samples = step_samples;
- if (task.adaptive_sampling.use) {
- wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
- }
- wtile->num_samples = min(wtile->num_samples, end_sample - sample);
- work_tiles.copy_to_device();
-
- CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
- uint total_work_size = wtile->w * wtile->h * wtile->num_samples;
- uint num_blocks = divide_up(total_work_size, num_threads_per_block);
-
- /* Launch kernel. */
- void *args[] = {&d_work_tiles, &total_work_size};
-
- cuda_assert(
- cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0));
-
- /* Run the adaptive sampling kernels at selected samples aligned to step samples. */
- uint filter_sample = sample + wtile->num_samples - 1;
- if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) {
- adaptive_sampling_filter(filter_sample, wtile, d_work_tiles);
- }
-
- cuda_assert(cuCtxSynchronize());
-
- /* Update progress. */
- sample += wtile->num_samples;
- rtile.sample = sample;
- task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
-
- if (task.get_cancel()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- /* Finalize adaptive sampling. */
- if (task.adaptive_sampling.use) {
- CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
- adaptive_sampling_post(rtile, wtile, d_work_tiles);
- cuda_assert(cuCtxSynchronize());
- task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
- }
-}
-
-void CUDADevice::thread_run(DeviceTask &task)
-{
- CUDAContextScope scope(this);
-
- if (task.type == DeviceTask::RENDER) {
- device_vector<KernelWorkTile> work_tiles(this, "work_tiles", MEM_READ_ONLY);
-
- /* keep rendering tiles until done */
- RenderTile tile;
- DenoisingTask denoising(this, task);
-
- while (task.acquire_tile(this, tile, task.tile_types)) {
- if (tile.task == RenderTile::PATH_TRACE) {
- render(task, tile, work_tiles);
- }
- else if (tile.task == RenderTile::BAKE) {
- render(task, tile, work_tiles);
- }
-
- task.release_tile(tile);
-
- if (task.get_cancel()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- work_tiles.free();
- }
-}
-# endif
-
unique_ptr<DeviceQueue> CUDADevice::gpu_queue_create()
{
return make_unique<CUDADeviceQueue>(this);
diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h
index 6b27db54ab4..c0316d18ba0 100644
--- a/intern/cycles/device/cuda/device_impl.h
+++ b/intern/cycles/device/cuda/device_impl.h
@@ -26,7 +26,6 @@
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
# else
-# include "util/util_opengl.h"
# include <cuda.h>
# include <cudaGL.h>
# endif
@@ -120,13 +119,13 @@ class CUDADevice : public Device {
void mem_copy_to(device_memory &mem) override;
- void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override;
+ void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override;
void mem_zero(device_memory &mem) override;
void mem_free(device_memory &mem) override;
- device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override;
+ device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override;
virtual void const_copy_to(const char *name, void *host, size_t size) override;
diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp
index e8ca8b90eae..30efefd9b6b 100644
--- a/intern/cycles/device/cuda/graphics_interop.cpp
+++ b/intern/cycles/device/cuda/graphics_interop.cpp
@@ -37,14 +37,15 @@ CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop()
}
}
-void CUDADeviceGraphicsInterop::set_destination(
- const DeviceGraphicsInteropDestination &destination)
+void CUDADeviceGraphicsInterop::set_display_interop(
+ const DisplayDriver::GraphicsInterop &display_interop)
{
- const int64_t new_buffer_area = int64_t(destination.buffer_width) * destination.buffer_height;
+ const int64_t new_buffer_area = int64_t(display_interop.buffer_width) *
+ display_interop.buffer_height;
- need_clear_ = destination.need_clear;
+ need_clear_ = display_interop.need_clear;
- if (opengl_pbo_id_ == destination.opengl_pbo_id && buffer_area_ == new_buffer_area) {
+ if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) {
return;
}
@@ -55,12 +56,12 @@ void CUDADeviceGraphicsInterop::set_destination(
}
const CUresult result = cuGraphicsGLRegisterBuffer(
- &cu_graphics_resource_, destination.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
+ &cu_graphics_resource_, display_interop.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
if (result != CUDA_SUCCESS) {
LOG(ERROR) << "Error registering OpenGL buffer: " << cuewErrorString(result);
}
- opengl_pbo_id_ = destination.opengl_pbo_id;
+ opengl_pbo_id_ = display_interop.opengl_pbo_id;
buffer_area_ = new_buffer_area;
}
diff --git a/intern/cycles/device/cuda/graphics_interop.h b/intern/cycles/device/cuda/graphics_interop.h
index 8a70c8aa71d..ec480f20c86 100644
--- a/intern/cycles/device/cuda/graphics_interop.h
+++ b/intern/cycles/device/cuda/graphics_interop.h
@@ -41,7 +41,7 @@ class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop {
CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete;
CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete;
- virtual void set_destination(const DeviceGraphicsInteropDestination &destination) override;
+ virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override;
virtual device_ptr map() override;
virtual void unmap() override;
diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp
index b7f86c10553..1149a835b14 100644
--- a/intern/cycles/device/cuda/queue.cpp
+++ b/intern/cycles/device/cuda/queue.cpp
@@ -116,18 +116,18 @@ bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *ar
}
/* Launch kernel. */
- cuda_device_assert(cuda_device_,
- cuLaunchKernel(cuda_kernel.function,
- num_blocks,
- 1,
- 1,
- num_threads_per_block,
- 1,
- 1,
- shared_mem_bytes,
- cuda_stream_,
- args,
- 0));
+ assert_success(cuLaunchKernel(cuda_kernel.function,
+ num_blocks,
+ 1,
+ 1,
+ num_threads_per_block,
+ 1,
+ 1,
+ shared_mem_bytes,
+ cuda_stream_,
+ args,
+ 0),
+ "enqueue");
return !(cuda_device_->have_error());
}
@@ -139,7 +139,8 @@ bool CUDADeviceQueue::synchronize()
}
const CUDAContextScope scope(cuda_device_);
- cuda_device_assert(cuda_device_, cuStreamSynchronize(cuda_stream_));
+ assert_success(cuStreamSynchronize(cuda_stream_), "synchronize");
+
debug_synchronize();
return !(cuda_device_->have_error());
@@ -162,9 +163,9 @@ void CUDADeviceQueue::zero_to_device(device_memory &mem)
assert(mem.device_pointer != 0);
const CUDAContextScope scope(cuda_device_);
- cuda_device_assert(
- cuda_device_,
- cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_));
+ assert_success(
+ cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_),
+ "zero_to_device");
}
void CUDADeviceQueue::copy_to_device(device_memory &mem)
@@ -185,10 +186,10 @@ void CUDADeviceQueue::copy_to_device(device_memory &mem)
/* Copy memory to device. */
const CUDAContextScope scope(cuda_device_);
- cuda_device_assert(
- cuda_device_,
+ assert_success(
cuMemcpyHtoDAsync(
- (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_));
+ (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_),
+ "copy_to_device");
}
void CUDADeviceQueue::copy_from_device(device_memory &mem)
@@ -204,10 +205,19 @@ void CUDADeviceQueue::copy_from_device(device_memory &mem)
/* Copy memory from device. */
const CUDAContextScope scope(cuda_device_);
- cuda_device_assert(
- cuda_device_,
+ assert_success(
cuMemcpyDtoHAsync(
- mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_));
+ mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_),
+ "copy_from_device");
+}
+
+void CUDADeviceQueue::assert_success(CUresult result, const char *operation)
+{
+ if (result != CUDA_SUCCESS) {
+ const char *name = cuewErrorString(result);
+ cuda_device_->set_error(string_printf(
+ "%s in CUDA queue %s (%s)", name, operation, debug_active_kernels().c_str()));
+ }
}
unique_ptr<DeviceGraphicsInterop> CUDADeviceQueue::graphics_interop_create()
diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h
index 62e3aa3d6c2..4d1995ed69e 100644
--- a/intern/cycles/device/cuda/queue.h
+++ b/intern/cycles/device/cuda/queue.h
@@ -60,6 +60,8 @@ class CUDADeviceQueue : public DeviceQueue {
protected:
CUDADevice *cuda_device_;
CUstream cuda_stream_;
+
+ void assert_success(CUresult result, const char *operation);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 6ccedcf54ef..81574e8b184 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -25,6 +25,7 @@
#include "device/cpu/device.h"
#include "device/cuda/device.h"
#include "device/dummy/device.h"
+#include "device/hip/device.h"
#include "device/multi/device.h"
#include "device/optix/device.h"
@@ -32,7 +33,6 @@
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_string.h"
#include "util/util_system.h"
#include "util/util_time.h"
@@ -47,6 +47,7 @@ thread_mutex Device::device_mutex;
vector<DeviceInfo> Device::cuda_devices;
vector<DeviceInfo> Device::optix_devices;
vector<DeviceInfo> Device::cpu_devices;
+vector<DeviceInfo> Device::hip_devices;
uint Device::devices_initialized_mask = 0;
/* Device */
@@ -97,6 +98,14 @@ Device *Device::create(const DeviceInfo &info, Stats &stats, Profiler &profiler)
device = device_optix_create(info, stats, profiler);
break;
#endif
+
+#ifdef WITH_HIP
+ case DEVICE_HIP:
+ if (device_hip_init())
+ device = device_hip_create(info, stats, profiler);
+ break;
+#endif
+
default:
break;
}
@@ -118,6 +127,8 @@ DeviceType Device::type_from_string(const char *name)
return DEVICE_OPTIX;
else if (strcmp(name, "MULTI") == 0)
return DEVICE_MULTI;
+ else if (strcmp(name, "HIP") == 0)
+ return DEVICE_HIP;
return DEVICE_NONE;
}
@@ -132,6 +143,8 @@ string Device::string_from_type(DeviceType type)
return "OPTIX";
else if (type == DEVICE_MULTI)
return "MULTI";
+ else if (type == DEVICE_HIP)
+ return "HIP";
return "";
}
@@ -146,6 +159,10 @@ vector<DeviceType> Device::available_types()
#ifdef WITH_OPTIX
types.push_back(DEVICE_OPTIX);
#endif
+#ifdef WITH_HIP
+ types.push_back(DEVICE_HIP);
+#endif
+
return types;
}
@@ -187,6 +204,20 @@ vector<DeviceInfo> Device::available_devices(uint mask)
}
#endif
+#ifdef WITH_HIP
+ if (mask & DEVICE_MASK_HIP) {
+ if (!(devices_initialized_mask & DEVICE_MASK_HIP)) {
+ if (device_hip_init()) {
+ device_hip_info(hip_devices);
+ }
+ devices_initialized_mask |= DEVICE_MASK_HIP;
+ }
+ foreach (DeviceInfo &info, hip_devices) {
+ devices.push_back(info);
+ }
+ }
+#endif
+
if (mask & DEVICE_MASK_CPU) {
if (!(devices_initialized_mask & DEVICE_MASK_CPU)) {
device_cpu_info(cpu_devices);
@@ -227,6 +258,15 @@ string Device::device_capabilities(uint mask)
}
#endif
+#ifdef WITH_HIP
+ if (mask & DEVICE_MASK_HIP) {
+ if (device_hip_init()) {
+ capabilities += "\nHIP device capabilities:\n";
+ capabilities += device_hip_capabilities();
+ }
+ }
+#endif
+
return capabilities;
}
@@ -315,6 +355,7 @@ void Device::free_memory()
devices_initialized_mask = 0;
cuda_devices.free_memory();
optix_devices.free_memory();
+ hip_devices.free_memory();
cpu_devices.free_memory();
}
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index 399d5eb91df..c73d74cdccc 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -51,6 +51,7 @@ enum DeviceType {
DEVICE_CUDA,
DEVICE_MULTI,
DEVICE_OPTIX,
+ DEVICE_HIP,
DEVICE_DUMMY,
};
@@ -58,6 +59,7 @@ enum DeviceTypeMask {
DEVICE_MASK_CPU = (1 << DEVICE_CPU),
DEVICE_MASK_CUDA = (1 << DEVICE_CUDA),
DEVICE_MASK_OPTIX = (1 << DEVICE_OPTIX),
+ DEVICE_MASK_HIP = (1 << DEVICE_HIP),
DEVICE_MASK_ALL = ~0
};
@@ -119,7 +121,7 @@ class Device {
string error_msg;
- virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, int /*offset*/, int /*size*/)
+ virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, size_t /*offset*/, size_t /*size*/)
{
/* Only required for devices that implement denoising. */
assert(false);
@@ -273,7 +275,7 @@ class Device {
virtual void mem_alloc(device_memory &mem) = 0;
virtual void mem_copy_to(device_memory &mem) = 0;
- virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) = 0;
+ virtual void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) = 0;
virtual void mem_zero(device_memory &mem) = 0;
virtual void mem_free(device_memory &mem) = 0;
@@ -284,6 +286,7 @@ class Device {
static vector<DeviceInfo> cuda_devices;
static vector<DeviceInfo> optix_devices;
static vector<DeviceInfo> cpu_devices;
+ static vector<DeviceInfo> hip_devices;
static uint devices_initialized_mask;
};
diff --git a/intern/cycles/device/device_graphics_interop.h b/intern/cycles/device/device_graphics_interop.h
index 671b1c189d7..eaf76077141 100644
--- a/intern/cycles/device/device_graphics_interop.h
+++ b/intern/cycles/device/device_graphics_interop.h
@@ -16,25 +16,12 @@
#pragma once
+#include "render/display_driver.h"
+
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
-/* Information about interoperability destination.
- * Is provided by the GPUDisplay. */
-class DeviceGraphicsInteropDestination {
- public:
- /* Dimensions of the buffer, in pixels. */
- int buffer_width = 0;
- int buffer_height = 0;
-
- /* OpenGL pixel buffer object. */
- int opengl_pbo_id = 0;
-
- /* Clear the entire destination before doing partial write to it. */
- bool need_clear = false;
-};
-
/* Device-side graphics interoperability support.
*
* Takes care of holding all the handlers needed by the device to implement interoperability with
@@ -46,7 +33,7 @@ class DeviceGraphicsInterop {
/* Update this device-side graphics interoperability object with the given destination resource
* information. */
- virtual void set_destination(const DeviceGraphicsInteropDestination &destination) = 0;
+ virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) = 0;
virtual device_ptr map() = 0;
virtual void unmap() = 0;
diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/device_memory.cpp
index c4d45829b83..c0ab2e17cae 100644
--- a/intern/cycles/device/device_memory.cpp
+++ b/intern/cycles/device/device_memory.cpp
@@ -136,7 +136,7 @@ void device_memory::device_copy_to()
}
}
-void device_memory::device_copy_from(int y, int w, int h, int elem)
+void device_memory::device_copy_from(size_t y, size_t w, size_t h, size_t elem)
{
assert(type != MEM_TEXTURE && type != MEM_READ_ONLY && type != MEM_GLOBAL);
device->mem_copy_from(*this, y, w, h, elem);
@@ -181,7 +181,7 @@ bool device_memory::is_resident(Device *sub_device) const
/* Device Sub Ptr */
-device_sub_ptr::device_sub_ptr(device_memory &mem, int offset, int size) : device(mem.device)
+device_sub_ptr::device_sub_ptr(device_memory &mem, size_t offset, size_t size) : device(mem.device)
{
ptr = device->mem_alloc_sub_ptr(mem, offset, size);
}
diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h
index c51594b8580..be6123e09b2 100644
--- a/intern/cycles/device/device_memory.h
+++ b/intern/cycles/device/device_memory.h
@@ -81,154 +81,154 @@ static constexpr size_t datatype_size(DataType datatype)
template<typename T> struct device_type_traits {
static const DataType data_type = TYPE_UNKNOWN;
- static const int num_elements_cpu = sizeof(T);
- static const int num_elements_gpu = sizeof(T);
+ static const size_t num_elements_cpu = sizeof(T);
+ static const size_t num_elements_gpu = sizeof(T);
};
template<> struct device_type_traits<uchar> {
static const DataType data_type = TYPE_UCHAR;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(uchar) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uchar2> {
static const DataType data_type = TYPE_UCHAR;
- static const int num_elements_cpu = 2;
- static const int num_elements_gpu = 2;
+ static const size_t num_elements_cpu = 2;
+ static const size_t num_elements_gpu = 2;
static_assert(sizeof(uchar2) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uchar3> {
static const DataType data_type = TYPE_UCHAR;
- static const int num_elements_cpu = 3;
- static const int num_elements_gpu = 3;
+ static const size_t num_elements_cpu = 3;
+ static const size_t num_elements_gpu = 3;
static_assert(sizeof(uchar3) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uchar4> {
static const DataType data_type = TYPE_UCHAR;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(uchar4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint> {
static const DataType data_type = TYPE_UINT;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(uint) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint2> {
static const DataType data_type = TYPE_UINT;
- static const int num_elements_cpu = 2;
- static const int num_elements_gpu = 2;
+ static const size_t num_elements_cpu = 2;
+ static const size_t num_elements_gpu = 2;
static_assert(sizeof(uint2) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint3> {
static const DataType data_type = TYPE_UINT;
- static const int num_elements_cpu = 3;
- static const int num_elements_gpu = 3;
+ static const size_t num_elements_cpu = 3;
+ static const size_t num_elements_gpu = 3;
static_assert(sizeof(uint3) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint4> {
static const DataType data_type = TYPE_UINT;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(uint4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<int> {
static const DataType data_type = TYPE_INT;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(int) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<int2> {
static const DataType data_type = TYPE_INT;
- static const int num_elements_cpu = 2;
- static const int num_elements_gpu = 2;
+ static const size_t num_elements_cpu = 2;
+ static const size_t num_elements_gpu = 2;
static_assert(sizeof(int2) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<int3> {
static const DataType data_type = TYPE_INT;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 3;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 3;
static_assert(sizeof(int3) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<int4> {
static const DataType data_type = TYPE_INT;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(int4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<float> {
static const DataType data_type = TYPE_FLOAT;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(float) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<float2> {
static const DataType data_type = TYPE_FLOAT;
- static const int num_elements_cpu = 2;
- static const int num_elements_gpu = 2;
+ static const size_t num_elements_cpu = 2;
+ static const size_t num_elements_gpu = 2;
static_assert(sizeof(float2) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<float3> {
static const DataType data_type = TYPE_FLOAT;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 3;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 3;
static_assert(sizeof(float3) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<float4> {
static const DataType data_type = TYPE_FLOAT;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(float4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<half> {
static const DataType data_type = TYPE_HALF;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(half) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<ushort4> {
static const DataType data_type = TYPE_UINT16;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(ushort4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint16_t> {
static const DataType data_type = TYPE_UINT16;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(uint16_t) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<half4> {
static const DataType data_type = TYPE_HALF;
- static const int num_elements_cpu = 4;
- static const int num_elements_gpu = 4;
+ static const size_t num_elements_cpu = 4;
+ static const size_t num_elements_gpu = 4;
static_assert(sizeof(half4) == num_elements_cpu * datatype_size(data_type));
};
template<> struct device_type_traits<uint64_t> {
static const DataType data_type = TYPE_UINT64;
- static const int num_elements_cpu = 1;
- static const int num_elements_gpu = 1;
+ static const size_t num_elements_cpu = 1;
+ static const size_t num_elements_gpu = 1;
static_assert(sizeof(uint64_t) == num_elements_cpu * datatype_size(data_type));
};
@@ -277,6 +277,7 @@ class device_memory {
protected:
friend class CUDADevice;
friend class OptiXDevice;
+ friend class HIPDevice;
/* Only create through subclasses. */
device_memory(Device *device, const char *name, MemoryType type);
@@ -296,7 +297,7 @@ class device_memory {
void device_alloc();
void device_free();
void device_copy_to();
- void device_copy_from(int y, int w, int h, int elem);
+ void device_copy_from(size_t y, size_t w, size_t h, size_t elem);
void device_zero();
bool device_is_cpu();
@@ -565,7 +566,7 @@ template<typename T> class device_vector : public device_memory {
device_copy_from(0, data_width, (data_height == 0) ? 1 : data_height, sizeof(T));
}
- void copy_from_device(int y, int w, int h)
+ void copy_from_device(size_t y, size_t w, size_t h)
{
device_copy_from(y, w, h, sizeof(T));
}
@@ -601,7 +602,7 @@ template<typename T> class device_vector : public device_memory {
class device_sub_ptr {
public:
- device_sub_ptr(device_memory &mem, int offset, int size);
+ device_sub_ptr(device_memory &mem, size_t offset, size_t size);
~device_sub_ptr();
device_ptr operator*() const
diff --git a/intern/cycles/device/device_queue.cpp b/intern/cycles/device/device_queue.cpp
index a89ba68d62c..f2b2f3496e0 100644
--- a/intern/cycles/device/device_queue.cpp
+++ b/intern/cycles/device/device_queue.cpp
@@ -57,8 +57,9 @@ void DeviceQueue::debug_init_execution()
{
if (VLOG_IS_ON(3)) {
last_sync_time_ = time_dt();
- last_kernels_enqueued_ = 0;
}
+
+ last_kernels_enqueued_ = 0;
}
void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size)
@@ -66,8 +67,9 @@ void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size)
if (VLOG_IS_ON(3)) {
VLOG(4) << "GPU queue launch " << device_kernel_as_string(kernel) << ", work_size "
<< work_size;
- last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel);
}
+
+ last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel);
}
void DeviceQueue::debug_synchronize()
@@ -80,8 +82,14 @@ void DeviceQueue::debug_synchronize()
stats_kernel_time_[last_kernels_enqueued_] += elapsed_time;
last_sync_time_ = new_time;
- last_kernels_enqueued_ = 0;
}
+
+ last_kernels_enqueued_ = 0;
+}
+
+string DeviceQueue::debug_active_kernels()
+{
+ return device_kernel_mask_as_string(last_kernels_enqueued_);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device_queue.h b/intern/cycles/device/device_queue.h
index edda3e61d51..e6835b787cf 100644
--- a/intern/cycles/device/device_queue.h
+++ b/intern/cycles/device/device_queue.h
@@ -21,6 +21,7 @@
#include "device/device_graphics_interop.h"
#include "util/util_logging.h"
#include "util/util_map.h"
+#include "util/util_string.h"
#include "util/util_unique_ptr.h"
CCL_NAMESPACE_BEGIN
@@ -101,6 +102,7 @@ class DeviceQueue {
void debug_init_execution();
void debug_enqueue(DeviceKernel kernel, const int work_size);
void debug_synchronize();
+ string debug_active_kernels();
/* Combination of kernels enqueued together sync last synchronize. */
DeviceKernelMask last_kernels_enqueued_;
diff --git a/intern/cycles/device/dummy/device.cpp b/intern/cycles/device/dummy/device.cpp
index 678276ed025..e3cea272300 100644
--- a/intern/cycles/device/dummy/device.cpp
+++ b/intern/cycles/device/dummy/device.cpp
@@ -48,7 +48,7 @@ class DummyDevice : public Device {
{
}
- virtual void mem_copy_from(device_memory &, int, int, int, int) override
+ virtual void mem_copy_from(device_memory &, size_t, size_t, size_t, size_t) override
{
}
diff --git a/intern/cycles/device/hip/device.cpp b/intern/cycles/device/hip/device.cpp
new file mode 100644
index 00000000000..90028ac7f10
--- /dev/null
+++ b/intern/cycles/device/hip/device.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device/hip/device.h"
+
+#include "util/util_logging.h"
+
+#ifdef WITH_HIP
+# include "device/device.h"
+# include "device/hip/device_impl.h"
+
+# include "util/util_string.h"
+# include "util/util_windows.h"
+#endif /* WITH_HIP */
+
+CCL_NAMESPACE_BEGIN
+
+bool device_hip_init()
+{
+#if !defined(WITH_HIP)
+ return false;
+#elif defined(WITH_HIP_DYNLOAD)
+ static bool initialized = false;
+ static bool result = false;
+
+ if (initialized)
+ return result;
+
+ initialized = true;
+ int hipew_result = hipewInit(HIPEW_INIT_HIP);
+ if (hipew_result == HIPEW_SUCCESS) {
+ VLOG(1) << "HIPEW initialization succeeded";
+ if (HIPDevice::have_precompiled_kernels()) {
+ VLOG(1) << "Found precompiled kernels";
+ result = true;
+ }
+ else if (hipewCompilerPath() != NULL) {
+ VLOG(1) << "Found HIPCC " << hipewCompilerPath();
+ result = true;
+ }
+ else {
+ VLOG(1) << "Neither precompiled kernels nor HIPCC was found,"
+ << " unable to use HIP";
+ }
+ }
+ else {
+ VLOG(1) << "HIPEW initialization failed: "
+ << ((hipew_result == HIPEW_ERROR_ATEXIT_FAILED) ? "Error setting up atexit() handler" :
+ "Error opening the library");
+ }
+
+ return result;
+#else /* WITH_HIP_DYNLOAD */
+ return true;
+#endif /* WITH_HIP_DYNLOAD */
+}
+
+Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler)
+{
+#ifdef WITH_HIP
+ return new HIPDevice(info, stats, profiler);
+#else
+ (void)info;
+ (void)stats;
+ (void)profiler;
+
+ LOG(FATAL) << "Request to create HIP device without compiled-in support. Should never happen.";
+
+ return nullptr;
+#endif
+}
+
+#ifdef WITH_HIP
+static hipError_t device_hip_safe_init()
+{
+# ifdef _WIN32
+ __try {
+ return hipInit(0);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER) {
+ /* Ignore crashes inside the HIP driver and hope we can
+ * survive even with corrupted HIP installs. */
+ fprintf(stderr, "Cycles HIP: driver crashed, continuing without HIP.\n");
+ }
+
+ return hipErrorNoDevice;
+# else
+ return hipInit(0);
+# endif
+}
+#endif /* WITH_HIP */
+
+void device_hip_info(vector<DeviceInfo> &devices)
+{
+#ifdef WITH_HIP
+ hipError_t result = device_hip_safe_init();
+ if (result != hipSuccess) {
+ if (result != hipErrorNoDevice)
+ fprintf(stderr, "HIP hipInit: %s\n", hipewErrorString(result));
+ return;
+ }
+
+ int count = 0;
+ result = hipGetDeviceCount(&count);
+ if (result != hipSuccess) {
+ fprintf(stderr, "HIP hipGetDeviceCount: %s\n", hipewErrorString(result));
+ return;
+ }
+
+ vector<DeviceInfo> display_devices;
+
+ for (int num = 0; num < count; num++) {
+ char name[256];
+
+ result = hipDeviceGetName(name, 256, num);
+ if (result != hipSuccess) {
+ fprintf(stderr, "HIP :hipDeviceGetName: %s\n", hipewErrorString(result));
+ continue;
+ }
+
+ int major;
+ hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, num);
+ // TODO : (Arya) What is the last major version we are supporting?
+
+ DeviceInfo info;
+
+ info.type = DEVICE_HIP;
+ info.description = string(name);
+ info.num = num;
+
+ info.has_half_images = (major >= 3);
+ info.has_nanovdb = true;
+ info.denoisers = 0;
+
+ info.has_gpu_queue = true;
+ /* Check if the device has P2P access to any other device in the system. */
+ for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) {
+ if (num != peer_num) {
+ int can_access = 0;
+ hipDeviceCanAccessPeer(&can_access, num, peer_num);
+ info.has_peer_memory = (can_access != 0);
+ }
+ }
+
+ int pci_location[3] = {0, 0, 0};
+ hipDeviceGetAttribute(&pci_location[0], hipDeviceAttributePciDomainID, num);
+ hipDeviceGetAttribute(&pci_location[1], hipDeviceAttributePciBusId, num);
+ hipDeviceGetAttribute(&pci_location[2], hipDeviceAttributePciDeviceId, num);
+ info.id = string_printf("HIP_%s_%04x:%02x:%02x",
+ name,
+ (unsigned int)pci_location[0],
+ (unsigned int)pci_location[1],
+ (unsigned int)pci_location[2]);
+
+ /* If device has a kernel timeout and no compute preemption, we assume
+ * it is connected to a display and will freeze the display while doing
+ * computations. */
+ int timeout_attr = 0, preempt_attr = 0;
+ hipDeviceGetAttribute(&timeout_attr, hipDeviceAttributeKernelExecTimeout, num);
+
+ if (timeout_attr && !preempt_attr) {
+ VLOG(1) << "Device is recognized as display.";
+ info.description += " (Display)";
+ info.display_device = true;
+ display_devices.push_back(info);
+ }
+ else {
+ VLOG(1) << "Device has compute preemption or is not used for display.";
+ devices.push_back(info);
+ }
+ VLOG(1) << "Added device \"" << name << "\" with id \"" << info.id << "\".";
+ }
+
+ if (!display_devices.empty())
+ devices.insert(devices.end(), display_devices.begin(), display_devices.end());
+#else /* WITH_HIP */
+ (void)devices;
+#endif /* WITH_HIP */
+}
+
+string device_hip_capabilities()
+{
+#ifdef WITH_HIP
+ hipError_t result = device_hip_safe_init();
+ if (result != hipSuccess) {
+ if (result != hipErrorNoDevice) {
+ return string("Error initializing HIP: ") + hipewErrorString(result);
+ }
+ return "No HIP device found\n";
+ }
+
+ int count;
+ result = hipGetDeviceCount(&count);
+ if (result != hipSuccess) {
+ return string("Error getting devices: ") + hipewErrorString(result);
+ }
+
+ string capabilities = "";
+ for (int num = 0; num < count; num++) {
+ char name[256];
+ if (hipDeviceGetName(name, 256, num) != hipSuccess) {
+ continue;
+ }
+ capabilities += string("\t") + name + "\n";
+ int value;
+# define GET_ATTR(attr) \
+ { \
+ if (hipDeviceGetAttribute(&value, hipDeviceAttribute##attr, num) == hipSuccess) { \
+ capabilities += string_printf("\t\thipDeviceAttribute" #attr "\t\t\t%d\n", value); \
+ } \
+ } \
+ (void)0
+ /* TODO(sergey): Strip all attributes which are not useful for us
+ * or does not depend on the driver.
+ */
+ GET_ATTR(MaxThreadsPerBlock);
+ GET_ATTR(MaxBlockDimX);
+ GET_ATTR(MaxBlockDimY);
+ GET_ATTR(MaxBlockDimZ);
+ GET_ATTR(MaxGridDimX);
+ GET_ATTR(MaxGridDimY);
+ GET_ATTR(MaxGridDimZ);
+ GET_ATTR(MaxSharedMemoryPerBlock);
+ GET_ATTR(TotalConstantMemory);
+ GET_ATTR(WarpSize);
+ GET_ATTR(MaxPitch);
+ GET_ATTR(MaxRegistersPerBlock);
+ GET_ATTR(ClockRate);
+ GET_ATTR(TextureAlignment);
+ GET_ATTR(MultiprocessorCount);
+ GET_ATTR(KernelExecTimeout);
+ GET_ATTR(Integrated);
+ GET_ATTR(CanMapHostMemory);
+ GET_ATTR(ComputeMode);
+ GET_ATTR(MaxTexture1DWidth);
+ GET_ATTR(MaxTexture2DWidth);
+ GET_ATTR(MaxTexture2DHeight);
+ GET_ATTR(MaxTexture3DWidth);
+ GET_ATTR(MaxTexture3DHeight);
+ GET_ATTR(MaxTexture3DDepth);
+ GET_ATTR(ConcurrentKernels);
+ GET_ATTR(EccEnabled);
+ GET_ATTR(MemoryClockRate);
+ GET_ATTR(MemoryBusWidth);
+ GET_ATTR(L2CacheSize);
+ GET_ATTR(MaxThreadsPerMultiProcessor);
+ GET_ATTR(ComputeCapabilityMajor);
+ GET_ATTR(ComputeCapabilityMinor);
+ GET_ATTR(MaxSharedMemoryPerMultiprocessor);
+ GET_ATTR(ManagedMemory);
+ GET_ATTR(IsMultiGpuBoard);
+# undef GET_ATTR
+ capabilities += "\n";
+ }
+
+ return capabilities;
+
+#else /* WITH_HIP */
+ return "";
+#endif /* WITH_HIP */
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/device/hip/device.h b/intern/cycles/device/hip/device.h
new file mode 100644
index 00000000000..965fd9e484b
--- /dev/null
+++ b/intern/cycles/device/hip/device.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "util/util_string.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Device;
+class DeviceInfo;
+class Profiler;
+class Stats;
+
+bool device_hip_init();
+
+Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler);
+
+void device_hip_info(vector<DeviceInfo> &devices);
+
+string device_hip_capabilities();
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp
new file mode 100644
index 00000000000..0e5ac6ce401
--- /dev/null
+++ b/intern/cycles/device/hip/device_impl.cpp
@@ -0,0 +1,1343 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include <climits>
+# include <limits.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+
+# include "device/hip/device_impl.h"
+
+# include "render/buffers.h"
+
+# include "util/util_debug.h"
+# include "util/util_foreach.h"
+# include "util/util_logging.h"
+# include "util/util_map.h"
+# include "util/util_md5.h"
+# include "util/util_opengl.h"
+# include "util/util_path.h"
+# include "util/util_string.h"
+# include "util/util_system.h"
+# include "util/util_time.h"
+# include "util/util_types.h"
+# include "util/util_windows.h"
+
+CCL_NAMESPACE_BEGIN
+
+class HIPDevice;
+
+bool HIPDevice::have_precompiled_kernels()
+{
+ string fatbins_path = path_get("lib");
+ return path_exists(fatbins_path);
+}
+
+bool HIPDevice::show_samples() const
+{
+ /* The HIPDevice only processes one tile at a time, so showing samples is fine. */
+ return true;
+}
+
+BVHLayoutMask HIPDevice::get_bvh_layout_mask() const
+{
+ return BVH_LAYOUT_BVH2;
+}
+
+void HIPDevice::set_error(const string &error)
+{
+ Device::set_error(error);
+
+ if (first_error) {
+ fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n");
+ fprintf(stderr,
+ "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n");
+ first_error = false;
+ }
+}
+
+HIPDevice::HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler)
+ : Device(info, stats, profiler), texture_info(this, "__texture_info", MEM_GLOBAL)
+{
+ first_error = true;
+
+ hipDevId = info.num;
+ hipDevice = 0;
+ hipContext = 0;
+
+ hipModule = 0;
+
+ need_texture_info = false;
+
+ device_texture_headroom = 0;
+ device_working_headroom = 0;
+ move_texture_to_host = false;
+ map_host_limit = 0;
+ map_host_used = 0;
+ can_map_host = 0;
+ pitch_alignment = 0;
+
+ /* Initialize HIP. */
+ hipError_t result = hipInit(0);
+ if (result != hipSuccess) {
+ set_error(string_printf("Failed to initialize HIP runtime (%s)", hipewErrorString(result)));
+ return;
+ }
+
+ /* Setup device and context. */
+ result = hipGetDevice(&hipDevice, hipDevId);
+ if (result != hipSuccess) {
+ set_error(string_printf("Failed to get HIP device handle from ordinal (%s)",
+ hipewErrorString(result)));
+ return;
+ }
+
+ hip_assert(hipDeviceGetAttribute(&can_map_host, hipDeviceAttributeCanMapHostMemory, hipDevice));
+
+ hip_assert(
+ hipDeviceGetAttribute(&pitch_alignment, hipDeviceAttributeTexturePitchAlignment, hipDevice));
+
+ unsigned int ctx_flags = hipDeviceLmemResizeToMax;
+ if (can_map_host) {
+ ctx_flags |= hipDeviceMapHost;
+ init_host_memory();
+ }
+
+ /* Create context. */
+ result = hipCtxCreate(&hipContext, ctx_flags, hipDevice);
+
+ if (result != hipSuccess) {
+ set_error(string_printf("Failed to create HIP context (%s)", hipewErrorString(result)));
+ return;
+ }
+
+ int major, minor;
+ hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
+ hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
+ hipDevArchitecture = major * 100 + minor * 10;
+
+ /* Pop context set by hipCtxCreate. */
+ hipCtxPopCurrent(NULL);
+}
+
+HIPDevice::~HIPDevice()
+{
+ texture_info.free();
+
+ hip_assert(hipCtxDestroy(hipContext));
+}
+
+bool HIPDevice::support_device(const uint /*kernel_features*/)
+{
+ int major, minor;
+ hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
+ hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
+
+ // TODO : (Arya) What versions do we plan to support?
+ return true;
+}
+
+bool HIPDevice::check_peer_access(Device *peer_device)
+{
+ if (peer_device == this) {
+ return false;
+ }
+ if (peer_device->info.type != DEVICE_HIP && peer_device->info.type != DEVICE_OPTIX) {
+ return false;
+ }
+
+ HIPDevice *const peer_device_hip = static_cast<HIPDevice *>(peer_device);
+
+ int can_access = 0;
+ hip_assert(hipDeviceCanAccessPeer(&can_access, hipDevice, peer_device_hip->hipDevice));
+ if (can_access == 0) {
+ return false;
+ }
+
+ // Ensure array access over the link is possible as well (for 3D textures)
+ hip_assert(hipDeviceGetP2PAttribute(
+ &can_access, hipDevP2PAttrHipArrayAccessSupported, hipDevice, peer_device_hip->hipDevice));
+ if (can_access == 0) {
+ return false;
+ }
+
+ // Enable peer access in both directions
+ {
+ const HIPContextScope scope(this);
+ hipError_t result = hipCtxEnablePeerAccess(peer_device_hip->hipContext, 0);
+ if (result != hipSuccess) {
+ set_error(string_printf("Failed to enable peer access on HIP context (%s)",
+ hipewErrorString(result)));
+ return false;
+ }
+ }
+ {
+ const HIPContextScope scope(peer_device_hip);
+ hipError_t result = hipCtxEnablePeerAccess(hipContext, 0);
+ if (result != hipSuccess) {
+ set_error(string_printf("Failed to enable peer access on HIP context (%s)",
+ hipewErrorString(result)));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool HIPDevice::use_adaptive_compilation()
+{
+ return DebugFlags().hip.adaptive_compile;
+}
+
+/* Common NVCC flags which stays the same regardless of shading model,
+ * kernel sources md5 and only depends on compiler or compilation settings.
+ */
+string HIPDevice::compile_kernel_get_common_cflags(const uint kernel_features)
+{
+ const int machine = system_cpu_bits();
+ const string source_path = path_get("source");
+ const string include_path = source_path;
+ string cflags = string_printf(
+ "-m%d "
+ "--ptxas-options=\"-v\" "
+ "--use_fast_math "
+ "-DHIPCC "
+ "-I\"%s\"",
+ machine,
+ include_path.c_str());
+ if (use_adaptive_compilation()) {
+ cflags += " -D__KERNEL_FEATURES__=" + to_string(kernel_features);
+ }
+ return cflags;
+}
+
+string HIPDevice::compile_kernel(const uint kernel_features,
+ const char *name,
+ const char *base,
+ bool force_ptx)
+{
+ /* Compute kernel name. */
+ int major, minor;
+ hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
+ hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
+
+ /* Attempt to use kernel provided with Blender. */
+ if (!use_adaptive_compilation()) {
+ if (!force_ptx) {
+ const string fatbin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor));
+ VLOG(1) << "Testing for pre-compiled kernel " << fatbin << ".";
+ if (path_exists(fatbin)) {
+ VLOG(1) << "Using precompiled kernel.";
+ return fatbin;
+ }
+ }
+
+ /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */
+ int ptx_major = major, ptx_minor = minor;
+ while (ptx_major >= 3) {
+ const string ptx = path_get(
+ string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor));
+ VLOG(1) << "Testing for pre-compiled kernel " << ptx << ".";
+ if (path_exists(ptx)) {
+ VLOG(1) << "Using precompiled kernel.";
+ return ptx;
+ }
+
+ if (ptx_minor > 0) {
+ ptx_minor--;
+ }
+ else {
+ ptx_major--;
+ ptx_minor = 9;
+ }
+ }
+ }
+
+ /* Try to use locally compiled kernel. */
+ string source_path = path_get("source");
+ const string source_md5 = path_files_md5_hash(source_path);
+
+ /* We include cflags into md5 so changing hip toolkit or changing other
+ * compiler command line arguments makes sure fatbin gets re-built.
+ */
+ string common_cflags = compile_kernel_get_common_cflags(kernel_features);
+ const string kernel_md5 = util_md5_string(source_md5 + common_cflags);
+
+ const char *const kernel_ext = "genco";
+# ifdef _WIN32
+ const char *const options =
+ "save-temps -Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp";
+# else
+ const char *const options =
+ "save-temps -Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp -O3 -ggdb";
+# endif
+ const string include_path = source_path;
+ const char *const kernel_arch = force_ptx ? "compute" : "sm";
+ const string fatbin_file = string_printf(
+ "cycles_%s_%s_%d%d_%s", name, kernel_arch, major, minor, kernel_md5.c_str());
+ const string fatbin = path_cache_get(path_join("kernels", fatbin_file));
+ VLOG(1) << "Testing for locally compiled kernel " << fatbin << ".";
+ if (path_exists(fatbin)) {
+ VLOG(1) << "Using locally compiled kernel.";
+ return fatbin;
+ }
+
+# ifdef _WIN32
+ if (!use_adaptive_compilation() && have_precompiled_kernels()) {
+ if (major < 3) {
+ set_error(
+ string_printf("HIP backend requires compute capability 3.0 or up, but found %d.%d. "
+ "Your GPU is not supported.",
+ major,
+ minor));
+ }
+ else {
+ set_error(
+ string_printf("HIP binary kernel for this graphics card compute "
+ "capability (%d.%d) not found.",
+ major,
+ minor));
+ }
+ return string();
+ }
+# endif
+
+ /* Compile. */
+ const char *const hipcc = hipewCompilerPath();
+ if (hipcc == NULL) {
+ set_error(
+ "HIP hipcc compiler not found. "
+ "Install HIP toolkit in default location.");
+ return string();
+ }
+
+ const int hipcc_hip_version = hipewCompilerVersion();
+ VLOG(1) << "Found hipcc " << hipcc << ", HIP version " << hipcc_hip_version << ".";
+ if (hipcc_hip_version < 40) {
+ printf(
+ "Unsupported HIP version %d.%d detected, "
+ "you need HIP 4.0 or newer.\n",
+ hipcc_hip_version / 10,
+ hipcc_hip_version % 10);
+ return string();
+ }
+
+ double starttime = time_dt();
+
+ path_create_directories(fatbin);
+
+ source_path = path_join(path_join(source_path, "kernel"),
+ path_join("device", path_join(base, string_printf("%s.cpp", name))));
+
+ string command = string_printf("%s -%s -I %s --%s %s -o \"%s\"",
+ hipcc,
+ options,
+ include_path.c_str(),
+ kernel_ext,
+ source_path.c_str(),
+ fatbin.c_str());
+
+ printf("Compiling HIP kernel ...\n%s\n", command.c_str());
+
+# ifdef _WIN32
+ command = "call " + command;
+# endif
+ if (system(command.c_str()) != 0) {
+ set_error(
+ "Failed to execute compilation command, "
+ "see console for details.");
+ return string();
+ }
+
+ /* Verify if compilation succeeded */
+ if (!path_exists(fatbin)) {
+ set_error(
+ "HIP kernel compilation failed, "
+ "see console for details.");
+ return string();
+ }
+
+ printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime);
+
+ return fatbin;
+}
+
+bool HIPDevice::load_kernels(const uint kernel_features)
+{
+ /* TODO(sergey): Support kernels re-load for HIP devices.
+ *
+ * Currently re-loading kernel will invalidate memory pointers,
+ * causing problems in hipCtxSynchronize.
+ */
+ if (hipModule) {
+ VLOG(1) << "Skipping kernel reload, not currently supported.";
+ return true;
+ }
+
+ /* check if hip init succeeded */
+ if (hipContext == 0)
+ return false;
+
+ /* check if GPU is supported */
+ if (!support_device(kernel_features))
+ return false;
+
+ /* get kernel */
+ const char *kernel_name = "kernel";
+ string fatbin = compile_kernel(kernel_features, kernel_name);
+ if (fatbin.empty())
+ return false;
+
+ /* open module */
+ HIPContextScope scope(this);
+
+ string fatbin_data;
+ hipError_t result;
+
+ if (path_read_text(fatbin, fatbin_data))
+ result = hipModuleLoadData(&hipModule, fatbin_data.c_str());
+ else
+ result = hipErrorFileNotFound;
+
+ if (result != hipSuccess)
+ set_error(string_printf(
+ "Failed to load HIP kernel from '%s' (%s)", fatbin.c_str(), hipewErrorString(result)));
+
+ if (result == hipSuccess) {
+ kernels.load(this);
+ reserve_local_memory(kernel_features);
+ }
+
+ return (result == hipSuccess);
+}
+
+void HIPDevice::reserve_local_memory(const uint)
+{
+ /* Together with hipDeviceLmemResizeToMax, this reserves local memory
+ * needed for kernel launches, so that we can reliably figure out when
+ * to allocate scene data in mapped host memory. */
+ size_t total = 0, free_before = 0, free_after = 0;
+
+ {
+ HIPContextScope scope(this);
+ hipMemGetInfo(&free_before, &total);
+ }
+
+ {
+ /* Use the biggest kernel for estimation. */
+ const DeviceKernel test_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE;
+
+ /* Launch kernel, using just 1 block appears sufficient to reserve memory for all
+ * multiprocessors. It would be good to do this in parallel for the multi GPU case
+ * still to make it faster. */
+ HIPDeviceQueue queue(this);
+
+ void *d_path_index = nullptr;
+ void *d_render_buffer = nullptr;
+ int d_work_size = 0;
+ void *args[] = {&d_path_index, &d_render_buffer, &d_work_size};
+
+ queue.init_execution();
+ queue.enqueue(test_kernel, 1, args);
+ queue.synchronize();
+ }
+
+ {
+ HIPContextScope scope(this);
+ hipMemGetInfo(&free_after, &total);
+ }
+
+ VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after)
+ << " bytes. (" << string_human_readable_size(free_before - free_after) << ")";
+
+# if 0
+ /* For testing mapped host memory, fill up device memory. */
+ const size_t keep_mb = 1024;
+
+ while (free_after > keep_mb * 1024 * 1024LL) {
+ hipDeviceptr_t tmp;
+ hip_assert(hipMalloc(&tmp, 10 * 1024 * 1024LL));
+ hipMemGetInfo(&free_after, &total);
+ }
+# endif
+}
+
+void HIPDevice::init_host_memory()
+{
+ /* Limit amount of host mapped memory, because allocating too much can
+ * cause system instability. Leave at least half or 4 GB of system
+ * memory free, whichever is smaller. */
+ size_t default_limit = 4 * 1024 * 1024 * 1024LL;
+ size_t system_ram = system_physical_ram();
+
+ if (system_ram > 0) {
+ if (system_ram / 2 > default_limit) {
+ map_host_limit = system_ram - default_limit;
+ }
+ else {
+ map_host_limit = system_ram / 2;
+ }
+ }
+ else {
+ VLOG(1) << "Mapped host memory disabled, failed to get system RAM";
+ map_host_limit = 0;
+ }
+
+ /* Amount of device memory to keep is free after texture memory
+ * and working memory allocations respectively. We set the working
+ * memory limit headroom lower so that some space is left after all
+ * texture memory allocations. */
+ device_working_headroom = 32 * 1024 * 1024LL; // 32MB
+ device_texture_headroom = 128 * 1024 * 1024LL; // 128MB
+
+ VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit)
+ << " bytes. (" << string_human_readable_size(map_host_limit) << ")";
+}
+
+void HIPDevice::load_texture_info()
+{
+ if (need_texture_info) {
+ /* Unset flag before copying, so this does not loop indefinitely if the copy below calls
+ * into 'move_textures_to_host' (which calls 'load_texture_info' again). */
+ need_texture_info = false;
+ texture_info.copy_to_device();
+ }
+}
+
+void HIPDevice::move_textures_to_host(size_t size, bool for_texture)
+{
+ /* Break out of recursive call, which can happen when moving memory on a multi device. */
+ static bool any_device_moving_textures_to_host = false;
+ if (any_device_moving_textures_to_host) {
+ return;
+ }
+
+ /* Signal to reallocate textures in host memory only. */
+ move_texture_to_host = true;
+
+ while (size > 0) {
+ /* Find suitable memory allocation to move. */
+ device_memory *max_mem = NULL;
+ size_t max_size = 0;
+ bool max_is_image = false;
+
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ foreach (HIPMemMap::value_type &pair, hip_mem_map) {
+ device_memory &mem = *pair.first;
+ HIPMem *cmem = &pair.second;
+
+ /* Can only move textures allocated on this device (and not those from peer devices).
+ * And need to ignore memory that is already on the host. */
+ if (!mem.is_resident(this) || cmem->use_mapped_host) {
+ continue;
+ }
+
+ bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) &&
+ (&mem != &texture_info);
+ bool is_image = is_texture && (mem.data_height > 1);
+
+ /* Can't move this type of memory. */
+ if (!is_texture || cmem->array) {
+ continue;
+ }
+
+ /* For other textures, only move image textures. */
+ if (for_texture && !is_image) {
+ continue;
+ }
+
+ /* Try to move largest allocation, prefer moving images. */
+ if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) {
+ max_is_image = is_image;
+ max_size = mem.device_size;
+ max_mem = &mem;
+ }
+ }
+ lock.unlock();
+
+ /* Move to host memory. This part is mutex protected since
+ * multiple HIP devices could be moving the memory. The
+ * first one will do it, and the rest will adopt the pointer. */
+ if (max_mem) {
+ VLOG(1) << "Move memory from device to host: " << max_mem->name;
+
+ static thread_mutex move_mutex;
+ thread_scoped_lock lock(move_mutex);
+
+ any_device_moving_textures_to_host = true;
+
+ /* Potentially need to call back into multi device, so pointer mapping
+ * and peer devices are updated. This is also necessary since the device
+ * pointer may just be a key here, so cannot be accessed and freed directly.
+ * Unfortunately it does mean that memory is reallocated on all other
+ * devices as well, which is potentially dangerous when still in use (since
+ * a thread rendering on another devices would only be caught in this mutex
+ * if it so happens to do an allocation at the same time as well. */
+ max_mem->device_copy_to();
+ size = (max_size >= size) ? 0 : size - max_size;
+
+ any_device_moving_textures_to_host = false;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* Unset flag before texture info is reloaded, since it should stay in device memory. */
+ move_texture_to_host = false;
+
+ /* Update texture info array with new pointers. */
+ load_texture_info();
+}
+
+HIPDevice::HIPMem *HIPDevice::generic_alloc(device_memory &mem, size_t pitch_padding)
+{
+ HIPContextScope scope(this);
+
+ hipDeviceptr_t device_pointer = 0;
+ size_t size = mem.memory_size() + pitch_padding;
+
+ hipError_t mem_alloc_result = hipErrorOutOfMemory;
+ const char *status = "";
+
+ /* First try allocating in device memory, respecting headroom. We make
+ * an exception for texture info. It is small and frequently accessed,
+ * so treat it as working memory.
+ *
+ * If there is not enough room for working memory, we will try to move
+ * textures to host memory, assuming the performance impact would have
+ * been worse for working memory. */
+ bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info);
+ bool is_image = is_texture && (mem.data_height > 1);
+
+ size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom;
+
+ size_t total = 0, free = 0;
+ hipMemGetInfo(&free, &total);
+
+ /* Move textures to host memory if needed. */
+ if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) {
+ move_textures_to_host(size + headroom - free, is_texture);
+ hipMemGetInfo(&free, &total);
+ }
+
+ /* Allocate in device memory. */
+ if (!move_texture_to_host && (size + headroom) < free) {
+ mem_alloc_result = hipMalloc(&device_pointer, size);
+ if (mem_alloc_result == hipSuccess) {
+ status = " in device memory";
+ }
+ }
+
+ /* Fall back to mapped host memory if needed and possible. */
+
+ void *shared_pointer = 0;
+
+ if (mem_alloc_result != hipSuccess && can_map_host) {
+ if (mem.shared_pointer) {
+ /* Another device already allocated host memory. */
+ mem_alloc_result = hipSuccess;
+ shared_pointer = mem.shared_pointer;
+ }
+ else if (map_host_used + size < map_host_limit) {
+ /* Allocate host memory ourselves. */
+ mem_alloc_result = hipHostMalloc(&shared_pointer, size);
+
+ assert((mem_alloc_result == hipSuccess && shared_pointer != 0) ||
+ (mem_alloc_result != hipSuccess && shared_pointer == 0));
+ }
+
+ if (mem_alloc_result == hipSuccess) {
+ hip_assert(hipHostGetDevicePointer(&device_pointer, shared_pointer, 0));
+ map_host_used += size;
+ status = " in host memory";
+ }
+ }
+
+ if (mem_alloc_result != hipSuccess) {
+ status = " failed, out of device and host memory";
+ set_error("System is out of GPU and shared host memory");
+ }
+
+ if (mem.name) {
+ VLOG(1) << "Buffer allocate: " << mem.name << ", "
+ << string_human_readable_number(mem.memory_size()) << " bytes. ("
+ << string_human_readable_size(mem.memory_size()) << ")" << status;
+ }
+
+ mem.device_pointer = (device_ptr)device_pointer;
+ mem.device_size = size;
+ stats.mem_alloc(size);
+
+ if (!mem.device_pointer) {
+ return NULL;
+ }
+
+ /* Insert into map of allocations. */
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ HIPMem *cmem = &hip_mem_map[&mem];
+ if (shared_pointer != 0) {
+ /* Replace host pointer with our host allocation. Only works if
+ * HIP memory layout is the same and has no pitch padding. Also
+ * does not work if we move textures to host during a render,
+ * since other devices might be using the memory. */
+
+ if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer &&
+ mem.host_pointer != shared_pointer) {
+ memcpy(shared_pointer, mem.host_pointer, size);
+
+ /* A Call to device_memory::host_free() should be preceded by
+ * a call to device_memory::device_free() for host memory
+ * allocated by a device to be handled properly. Two exceptions
+ * are here and a call in OptiXDevice::generic_alloc(), where
+ * the current host memory can be assumed to be allocated by
+ * device_memory::host_alloc(), not by a device */
+
+ mem.host_free();
+ mem.host_pointer = shared_pointer;
+ }
+ mem.shared_pointer = shared_pointer;
+ mem.shared_counter++;
+ cmem->use_mapped_host = true;
+ }
+ else {
+ cmem->use_mapped_host = false;
+ }
+
+ return cmem;
+}
+
+void HIPDevice::generic_copy_to(device_memory &mem)
+{
+ if (!mem.host_pointer || !mem.device_pointer) {
+ return;
+ }
+
+ /* If use_mapped_host of mem is false, the current device only uses device memory allocated by
+ * hipMalloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from
+ * mem.host_pointer. */
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
+ const HIPContextScope scope(this);
+ hip_assert(
+ hipMemcpyHtoD((hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size()));
+ }
+}
+
+void HIPDevice::generic_free(device_memory &mem)
+{
+ if (mem.device_pointer) {
+ HIPContextScope scope(this);
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ const HIPMem &cmem = hip_mem_map[&mem];
+
+ /* If cmem.use_mapped_host is true, reference counting is used
+ * to safely free a mapped host memory. */
+
+ if (cmem.use_mapped_host) {
+ assert(mem.shared_pointer);
+ if (mem.shared_pointer) {
+ assert(mem.shared_counter > 0);
+ if (--mem.shared_counter == 0) {
+ if (mem.host_pointer == mem.shared_pointer) {
+ mem.host_pointer = 0;
+ }
+ hipHostFree(mem.shared_pointer);
+ mem.shared_pointer = 0;
+ }
+ }
+ map_host_used -= mem.device_size;
+ }
+ else {
+ /* Free device memory. */
+ hip_assert(hipFree(mem.device_pointer));
+ }
+
+ stats.mem_free(mem.device_size);
+ mem.device_pointer = 0;
+ mem.device_size = 0;
+
+ hip_mem_map.erase(hip_mem_map.find(&mem));
+ }
+}
+
+void HIPDevice::mem_alloc(device_memory &mem)
+{
+ if (mem.type == MEM_TEXTURE) {
+ assert(!"mem_alloc not supported for textures.");
+ }
+ else if (mem.type == MEM_GLOBAL) {
+ assert(!"mem_alloc not supported for global memory.");
+ }
+ else {
+ generic_alloc(mem);
+ }
+}
+
+void HIPDevice::mem_copy_to(device_memory &mem)
+{
+ if (mem.type == MEM_GLOBAL) {
+ global_free(mem);
+ global_alloc(mem);
+ }
+ else if (mem.type == MEM_TEXTURE) {
+ tex_free((device_texture &)mem);
+ tex_alloc((device_texture &)mem);
+ }
+ else {
+ if (!mem.device_pointer) {
+ generic_alloc(mem);
+ }
+ generic_copy_to(mem);
+ }
+}
+
+void HIPDevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem)
+{
+ if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) {
+ assert(!"mem_copy_from not supported for textures.");
+ }
+ else if (mem.host_pointer) {
+ const size_t size = elem * w * h;
+ const size_t offset = elem * y * w;
+
+ if (mem.device_pointer) {
+ const HIPContextScope scope(this);
+ hip_assert(hipMemcpyDtoH(
+ (char *)mem.host_pointer + offset, (hipDeviceptr_t)mem.device_pointer + offset, size));
+ }
+ else {
+ memset((char *)mem.host_pointer + offset, 0, size);
+ }
+ }
+}
+
+void HIPDevice::mem_zero(device_memory &mem)
+{
+ if (!mem.device_pointer) {
+ mem_alloc(mem);
+ }
+ if (!mem.device_pointer) {
+ return;
+ }
+
+ /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory
+ * regardless of mem.host_pointer and mem.shared_pointer. */
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
+ const HIPContextScope scope(this);
+ hip_assert(hipMemsetD8((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size()));
+ }
+ else if (mem.host_pointer) {
+ memset(mem.host_pointer, 0, mem.memory_size());
+ }
+}
+
+void HIPDevice::mem_free(device_memory &mem)
+{
+ if (mem.type == MEM_GLOBAL) {
+ global_free(mem);
+ }
+ else if (mem.type == MEM_TEXTURE) {
+ tex_free((device_texture &)mem);
+ }
+ else {
+ generic_free(mem);
+ }
+}
+
+device_ptr HIPDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/)
+{
+ return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset));
+}
+
+void HIPDevice::const_copy_to(const char *name, void *host, size_t size)
+{
+ HIPContextScope scope(this);
+ hipDeviceptr_t mem;
+ size_t bytes;
+
+ hip_assert(hipModuleGetGlobal(&mem, &bytes, hipModule, name));
+ assert(bytes == size);
+ hip_assert(hipMemcpyHtoD(mem, host, size));
+}
+
+void HIPDevice::global_alloc(device_memory &mem)
+{
+ if (mem.is_resident(this)) {
+ generic_alloc(mem);
+ generic_copy_to(mem);
+ }
+
+ const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer));
+}
+
+void HIPDevice::global_free(device_memory &mem)
+{
+ if (mem.is_resident(this) && mem.device_pointer) {
+ generic_free(mem);
+ }
+}
+
+void HIPDevice::tex_alloc(device_texture &mem)
+{
+ HIPContextScope scope(this);
+
+ /* General variables for both architectures */
+ string bind_name = mem.name;
+ size_t dsize = datatype_size(mem.data_type);
+ size_t size = mem.memory_size();
+
+ hipTextureAddressMode address_mode = hipAddressModeWrap;
+ switch (mem.info.extension) {
+ case EXTENSION_REPEAT:
+ address_mode = hipAddressModeWrap;
+ break;
+ case EXTENSION_EXTEND:
+ address_mode = hipAddressModeClamp;
+ break;
+ case EXTENSION_CLIP:
+ // TODO : (Arya) setting this to Mode Clamp instead of Mode Border because it's unsupported
+ // in hip
+ address_mode = hipAddressModeClamp;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ hipTextureFilterMode filter_mode;
+ if (mem.info.interpolation == INTERPOLATION_CLOSEST) {
+ filter_mode = hipFilterModePoint;
+ }
+ else {
+ filter_mode = hipFilterModeLinear;
+ }
+
+ /* Image Texture Storage */
+ hipArray_Format format;
+ switch (mem.data_type) {
+ case TYPE_UCHAR:
+ format = HIP_AD_FORMAT_UNSIGNED_INT8;
+ break;
+ case TYPE_UINT16:
+ format = HIP_AD_FORMAT_UNSIGNED_INT16;
+ break;
+ case TYPE_UINT:
+ format = HIP_AD_FORMAT_UNSIGNED_INT32;
+ break;
+ case TYPE_INT:
+ format = HIP_AD_FORMAT_SIGNED_INT32;
+ break;
+ case TYPE_FLOAT:
+ format = HIP_AD_FORMAT_FLOAT;
+ break;
+ case TYPE_HALF:
+ format = HIP_AD_FORMAT_HALF;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ HIPMem *cmem = NULL;
+ hArray array_3d = NULL;
+ size_t src_pitch = mem.data_width * dsize * mem.data_elements;
+ size_t dst_pitch = src_pitch;
+
+ if (!mem.is_resident(this)) {
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ cmem = &hip_mem_map[&mem];
+ cmem->texobject = 0;
+
+ if (mem.data_depth > 1) {
+ array_3d = (hArray)mem.device_pointer;
+ cmem->array = array_3d;
+ }
+ else if (mem.data_height > 0) {
+ dst_pitch = align_up(src_pitch, pitch_alignment);
+ }
+ }
+ else if (mem.data_depth > 1) {
+ /* 3D texture using array, there is no API for linear memory. */
+ HIP_ARRAY3D_DESCRIPTOR desc;
+
+ desc.Width = mem.data_width;
+ desc.Height = mem.data_height;
+ desc.Depth = mem.data_depth;
+ desc.Format = format;
+ desc.NumChannels = mem.data_elements;
+ desc.Flags = 0;
+
+ VLOG(1) << "Array 3D allocate: " << mem.name << ", "
+ << string_human_readable_number(mem.memory_size()) << " bytes. ("
+ << string_human_readable_size(mem.memory_size()) << ")";
+
+ hip_assert(hipArray3DCreate(&array_3d, &desc));
+
+ if (!array_3d) {
+ return;
+ }
+
+ HIP_MEMCPY3D param;
+ memset(&param, 0, sizeof(param));
+ param.dstMemoryType = hipMemoryTypeArray;
+ param.dstArray = &array_3d;
+ param.srcMemoryType = hipMemoryTypeHost;
+ param.srcHost = mem.host_pointer;
+ param.srcPitch = src_pitch;
+ param.WidthInBytes = param.srcPitch;
+ param.Height = mem.data_height;
+ param.Depth = mem.data_depth;
+
+ hip_assert(hipDrvMemcpy3D(&param));
+
+ mem.device_pointer = (device_ptr)array_3d;
+ mem.device_size = size;
+ stats.mem_alloc(size);
+
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ cmem = &hip_mem_map[&mem];
+ cmem->texobject = 0;
+ cmem->array = array_3d;
+ }
+ else if (mem.data_height > 0) {
+ /* 2D texture, using pitch aligned linear memory. */
+ dst_pitch = align_up(src_pitch, pitch_alignment);
+ size_t dst_size = dst_pitch * mem.data_height;
+
+ cmem = generic_alloc(mem, dst_size - mem.memory_size());
+ if (!cmem) {
+ return;
+ }
+
+ hip_Memcpy2D param;
+ memset(&param, 0, sizeof(param));
+ param.dstMemoryType = hipMemoryTypeDevice;
+ param.dstDevice = mem.device_pointer;
+ param.dstPitch = dst_pitch;
+ param.srcMemoryType = hipMemoryTypeHost;
+ param.srcHost = mem.host_pointer;
+ param.srcPitch = src_pitch;
+ param.WidthInBytes = param.srcPitch;
+ param.Height = mem.data_height;
+
+ hip_assert(hipDrvMemcpy2DUnaligned(&param));
+ }
+ else {
+ /* 1D texture, using linear memory. */
+ cmem = generic_alloc(mem);
+ if (!cmem) {
+ return;
+ }
+
+ hip_assert(hipMemcpyHtoD(mem.device_pointer, mem.host_pointer, size));
+ }
+
+ /* Resize once */
+ const uint slot = mem.slot;
+ if (slot >= texture_info.size()) {
+ /* Allocate some slots in advance, to reduce amount
+ * of re-allocations. */
+ texture_info.resize(slot + 128);
+ }
+
+ /* Set Mapping and tag that we need to (re-)upload to device */
+ texture_info[slot] = mem.info;
+ need_texture_info = true;
+
+ if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
+ mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
+ /* Kepler+, bindless textures. */
+ hipResourceDesc resDesc;
+ memset(&resDesc, 0, sizeof(resDesc));
+
+ if (array_3d) {
+ resDesc.resType = hipResourceTypeArray;
+ resDesc.res.array.h_Array = &array_3d;
+ resDesc.flags = 0;
+ }
+ else if (mem.data_height > 0) {
+ resDesc.resType = hipResourceTypePitch2D;
+ resDesc.res.pitch2D.devPtr = mem.device_pointer;
+ resDesc.res.pitch2D.format = format;
+ resDesc.res.pitch2D.numChannels = mem.data_elements;
+ resDesc.res.pitch2D.height = mem.data_height;
+ resDesc.res.pitch2D.width = mem.data_width;
+ resDesc.res.pitch2D.pitchInBytes = dst_pitch;
+ }
+ else {
+ resDesc.resType = hipResourceTypeLinear;
+ resDesc.res.linear.devPtr = mem.device_pointer;
+ resDesc.res.linear.format = format;
+ resDesc.res.linear.numChannels = mem.data_elements;
+ resDesc.res.linear.sizeInBytes = mem.device_size;
+ }
+
+ hipTextureDesc texDesc;
+ memset(&texDesc, 0, sizeof(texDesc));
+ texDesc.addressMode[0] = address_mode;
+ texDesc.addressMode[1] = address_mode;
+ texDesc.addressMode[2] = address_mode;
+ texDesc.filterMode = filter_mode;
+ texDesc.flags = HIP_TRSF_NORMALIZED_COORDINATES;
+
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ cmem = &hip_mem_map[&mem];
+
+ hip_assert(hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
+
+ texture_info[slot].data = (uint64_t)cmem->texobject;
+ }
+ else {
+ texture_info[slot].data = (uint64_t)mem.device_pointer;
+ }
+}
+
+void HIPDevice::tex_free(device_texture &mem)
+{
+ if (mem.device_pointer) {
+ HIPContextScope scope(this);
+ thread_scoped_lock lock(hip_mem_map_mutex);
+ const HIPMem &cmem = hip_mem_map[&mem];
+
+ if (cmem.texobject) {
+ /* Free bindless texture. */
+ hipTexObjectDestroy(cmem.texobject);
+ }
+
+ if (!mem.is_resident(this)) {
+ /* Do not free memory here, since it was allocated on a different device. */
+ hip_mem_map.erase(hip_mem_map.find(&mem));
+ }
+ else if (cmem.array) {
+ /* Free array. */
+ hipArrayDestroy(cmem.array);
+ stats.mem_free(mem.device_size);
+ mem.device_pointer = 0;
+ mem.device_size = 0;
+
+ hip_mem_map.erase(hip_mem_map.find(&mem));
+ }
+ else {
+ lock.unlock();
+ generic_free(mem);
+ }
+ }
+}
+
+# if 0
+void HIPDevice::render(DeviceTask &task,
+ RenderTile &rtile,
+ device_vector<KernelWorkTile> &work_tiles)
+{
+ scoped_timer timer(&rtile.buffers->render_time);
+
+ if (have_error())
+ return;
+
+ HIPContextScope scope(this);
+ hipFunction_t hipRender;
+
+ /* Get kernel function. */
+ if (rtile.task == RenderTile::BAKE) {
+ hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_bake"));
+ }
+ else {
+ hip_assert(hipModuleGetFunction(&hipRender, hipModule, "kernel_hip_path_trace"));
+ }
+
+ if (have_error()) {
+ return;
+ }
+
+ hip_assert(hipFuncSetCacheConfig(hipRender, hipFuncCachePreferL1));
+
+ /* Allocate work tile. */
+ work_tiles.alloc(1);
+
+ KernelWorkTile *wtile = work_tiles.data();
+ wtile->x = rtile.x;
+ wtile->y = rtile.y;
+ wtile->w = rtile.w;
+ wtile->h = rtile.h;
+ wtile->offset = rtile.offset;
+ wtile->stride = rtile.stride;
+ wtile->buffer = (float *)(hipDeviceptr_t)rtile.buffer;
+
+ /* Prepare work size. More step samples render faster, but for now we
+ * remain conservative for GPUs connected to a display to avoid driver
+ * timeouts and display freezing. */
+ int min_blocks, num_threads_per_block;
+ hip_assert(
+ hipModuleOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, hipRender, NULL, 0, 0));
+ if (!info.display_device) {
+ min_blocks *= 8;
+ }
+
+ uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h);
+
+ /* Render all samples. */
+ uint start_sample = rtile.start_sample;
+ uint end_sample = rtile.start_sample + rtile.num_samples;
+
+ for (int sample = start_sample; sample < end_sample;) {
+ /* Setup and copy work tile to device. */
+ wtile->start_sample = sample;
+ wtile->num_samples = step_samples;
+ if (task.adaptive_sampling.use) {
+ wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
+ }
+ wtile->num_samples = min(wtile->num_samples, end_sample - sample);
+ work_tiles.copy_to_device();
+
+ hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer;
+ uint total_work_size = wtile->w * wtile->h * wtile->num_samples;
+ uint num_blocks = divide_up(total_work_size, num_threads_per_block);
+
+ /* Launch kernel. */
+ void *args[] = {&d_work_tiles, &total_work_size};
+
+ hip_assert(
+ hipModuleLaunchKernel(hipRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0));
+
+ /* Run the adaptive sampling kernels at selected samples aligned to step samples. */
+ uint filter_sample = sample + wtile->num_samples - 1;
+ if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) {
+ adaptive_sampling_filter(filter_sample, wtile, d_work_tiles);
+ }
+
+ hip_assert(hipDeviceSynchronize());
+
+ /* Update progress. */
+ sample += wtile->num_samples;
+ rtile.sample = sample;
+ task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
+
+ if (task.get_cancel()) {
+ if (task.need_finish_queue == false)
+ break;
+ }
+ }
+
+ /* Finalize adaptive sampling. */
+ if (task.adaptive_sampling.use) {
+ hipDeviceptr_t d_work_tiles = (hipDeviceptr_t)work_tiles.device_pointer;
+ adaptive_sampling_post(rtile, wtile, d_work_tiles);
+ hip_assert(hipDeviceSynchronize());
+ task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
+ }
+}
+
+void HIPDevice::thread_run(DeviceTask &task)
+{
+ HIPContextScope scope(this);
+
+ if (task.type == DeviceTask::RENDER) {
+ device_vector<KernelWorkTile> work_tiles(this, "work_tiles", MEM_READ_ONLY);
+
+ /* keep rendering tiles until done */
+ RenderTile tile;
+ DenoisingTask denoising(this, task);
+
+ while (task.acquire_tile(this, tile, task.tile_types)) {
+ if (tile.task == RenderTile::PATH_TRACE) {
+ render(task, tile, work_tiles);
+ }
+ else if (tile.task == RenderTile::BAKE) {
+ render(task, tile, work_tiles);
+ }
+
+ task.release_tile(tile);
+
+ if (task.get_cancel()) {
+ if (task.need_finish_queue == false)
+ break;
+ }
+ }
+
+ work_tiles.free();
+ }
+}
+# endif
+
+unique_ptr<DeviceQueue> HIPDevice::gpu_queue_create()
+{
+ return make_unique<HIPDeviceQueue>(this);
+}
+
+bool HIPDevice::should_use_graphics_interop()
+{
+ /* Check whether this device is part of OpenGL context.
+ *
+ * Using HIP device for graphics interoperability which is not part of the OpenGL context is
+ * possible, but from the empiric measurements it can be considerably slower than using naive
+ * pixels copy. */
+
+ HIPContextScope scope(this);
+
+ int num_all_devices = 0;
+ hip_assert(hipGetDeviceCount(&num_all_devices));
+
+ if (num_all_devices == 0) {
+ return false;
+ }
+
+ vector<hipDevice_t> gl_devices(num_all_devices);
+ uint num_gl_devices = 0;
+ hipGLGetDevices(&num_gl_devices, gl_devices.data(), num_all_devices, hipGLDeviceListAll);
+
+ for (hipDevice_t gl_device : gl_devices) {
+ if (gl_device == hipDevice) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int HIPDevice::get_num_multiprocessors()
+{
+ return get_device_default_attribute(hipDeviceAttributeMultiprocessorCount, 0);
+}
+
+int HIPDevice::get_max_num_threads_per_multiprocessor()
+{
+ return get_device_default_attribute(hipDeviceAttributeMaxThreadsPerMultiProcessor, 0);
+}
+
+bool HIPDevice::get_device_attribute(hipDeviceAttribute_t attribute, int *value)
+{
+ HIPContextScope scope(this);
+
+ return hipDeviceGetAttribute(value, attribute, hipDevice) == hipSuccess;
+}
+
+int HIPDevice::get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value)
+{
+ int value = 0;
+ if (!get_device_attribute(attribute, &value)) {
+ return default_value;
+ }
+ return value;
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h
new file mode 100644
index 00000000000..1d138ee9856
--- /dev/null
+++ b/intern/cycles/device/hip/device_impl.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/device.h"
+# include "device/hip/kernel.h"
+# include "device/hip/queue.h"
+# include "device/hip/util.h"
+
+# include "util/util_map.h"
+
+# ifdef WITH_HIP_DYNLOAD
+# include "hipew.h"
+# else
+# include "util/util_opengl.h"
+# endif
+
+CCL_NAMESPACE_BEGIN
+
+class DeviceQueue;
+
+class HIPDevice : public Device {
+
+ friend class HIPContextScope;
+
+ public:
+ hipDevice_t hipDevice;
+ hipCtx_t hipContext;
+ hipModule_t hipModule;
+ size_t device_texture_headroom;
+ size_t device_working_headroom;
+ bool move_texture_to_host;
+ size_t map_host_used;
+ size_t map_host_limit;
+ int can_map_host;
+ int pitch_alignment;
+ int hipDevId;
+ int hipDevArchitecture;
+ bool first_error;
+
+ struct HIPMem {
+ HIPMem() : texobject(0), array(0), use_mapped_host(false)
+ {
+ }
+
+ hipTextureObject_t texobject;
+ hArray array;
+
+ /* If true, a mapped host memory in shared_pointer is being used. */
+ bool use_mapped_host;
+ };
+ typedef map<device_memory *, HIPMem> HIPMemMap;
+ HIPMemMap hip_mem_map;
+ thread_mutex hip_mem_map_mutex;
+
+ /* Bindless Textures */
+ device_vector<TextureInfo> texture_info;
+ bool need_texture_info;
+
+ HIPDeviceKernels kernels;
+
+ static bool have_precompiled_kernels();
+
+ virtual bool show_samples() const override;
+
+ virtual BVHLayoutMask get_bvh_layout_mask() const override;
+
+ void set_error(const string &error) override;
+
+ HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler);
+
+ virtual ~HIPDevice();
+
+ bool support_device(const uint /*kernel_features*/);
+
+ bool check_peer_access(Device *peer_device) override;
+
+ bool use_adaptive_compilation();
+
+ virtual string compile_kernel_get_common_cflags(const uint kernel_features);
+
+ string compile_kernel(const uint kernel_features,
+ const char *name,
+ const char *base = "hip",
+ bool force_ptx = false);
+
+ virtual bool load_kernels(const uint kernel_features) override;
+ void reserve_local_memory(const uint kernel_features);
+
+ void init_host_memory();
+
+ void load_texture_info();
+
+ void move_textures_to_host(size_t size, bool for_texture);
+
+ HIPMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0);
+
+ void generic_copy_to(device_memory &mem);
+
+ void generic_free(device_memory &mem);
+
+ void mem_alloc(device_memory &mem) override;
+
+ void mem_copy_to(device_memory &mem) override;
+
+ void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override;
+
+ void mem_zero(device_memory &mem) override;
+
+ void mem_free(device_memory &mem) override;
+
+ device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override;
+
+ virtual void const_copy_to(const char *name, void *host, size_t size) override;
+
+ void global_alloc(device_memory &mem);
+
+ void global_free(device_memory &mem);
+
+ void tex_alloc(device_texture &mem);
+
+ void tex_free(device_texture &mem);
+
+ /* Graphics resources interoperability. */
+ virtual bool should_use_graphics_interop() override;
+
+ virtual unique_ptr<DeviceQueue> gpu_queue_create() override;
+
+ int get_num_multiprocessors();
+ int get_max_num_threads_per_multiprocessor();
+
+ protected:
+ bool get_device_attribute(hipDeviceAttribute_t attribute, int *value);
+ int get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value);
+};
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/device/hip/graphics_interop.cpp b/intern/cycles/device/hip/graphics_interop.cpp
new file mode 100644
index 00000000000..0d5d71019b3
--- /dev/null
+++ b/intern/cycles/device/hip/graphics_interop.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/hip/graphics_interop.h"
+
+# include "device/hip/device_impl.h"
+# include "device/hip/util.h"
+
+CCL_NAMESPACE_BEGIN
+
+HIPDeviceGraphicsInterop::HIPDeviceGraphicsInterop(HIPDeviceQueue *queue)
+ : queue_(queue), device_(static_cast<HIPDevice *>(queue->device))
+{
+}
+
+HIPDeviceGraphicsInterop::~HIPDeviceGraphicsInterop()
+{
+ HIPContextScope scope(device_);
+
+ if (hip_graphics_resource_) {
+ hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_));
+ }
+}
+
+void HIPDeviceGraphicsInterop::set_display_interop(
+ const DisplayDriver::GraphicsInterop &display_interop)
+{
+ const int64_t new_buffer_area = int64_t(display_interop.buffer_width) *
+ display_interop.buffer_height;
+
+ need_clear_ = display_interop.need_clear;
+
+ if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) {
+ return;
+ }
+
+ HIPContextScope scope(device_);
+
+ if (hip_graphics_resource_) {
+ hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_));
+ }
+
+ const hipError_t result = hipGraphicsGLRegisterBuffer(
+ &hip_graphics_resource_, display_interop.opengl_pbo_id, hipGraphicsRegisterFlagsNone);
+ if (result != hipSuccess) {
+ LOG(ERROR) << "Error registering OpenGL buffer: " << hipewErrorString(result);
+ }
+
+ opengl_pbo_id_ = display_interop.opengl_pbo_id;
+ buffer_area_ = new_buffer_area;
+}
+
+device_ptr HIPDeviceGraphicsInterop::map()
+{
+ if (!hip_graphics_resource_) {
+ return 0;
+ }
+
+ HIPContextScope scope(device_);
+
+ hipDeviceptr_t hip_buffer;
+ size_t bytes;
+
+ hip_device_assert(device_,
+ hipGraphicsMapResources(1, &hip_graphics_resource_, queue_->stream()));
+ hip_device_assert(
+ device_, hipGraphicsResourceGetMappedPointer(&hip_buffer, &bytes, hip_graphics_resource_));
+
+ if (need_clear_) {
+ hip_device_assert(
+ device_,
+ hipMemsetD8Async(static_cast<hipDeviceptr_t>(hip_buffer), 0, bytes, queue_->stream()));
+
+ need_clear_ = false;
+ }
+
+ return static_cast<device_ptr>(hip_buffer);
+}
+
+void HIPDeviceGraphicsInterop::unmap()
+{
+ HIPContextScope scope(device_);
+
+ hip_device_assert(device_,
+ hipGraphicsUnmapResources(1, &hip_graphics_resource_, queue_->stream()));
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/device/hip/graphics_interop.h b/intern/cycles/device/hip/graphics_interop.h
new file mode 100644
index 00000000000..2b2d287ff6c
--- /dev/null
+++ b/intern/cycles/device/hip/graphics_interop.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/device_graphics_interop.h"
+
+# ifdef WITH_HIP_DYNLOAD
+# include "hipew.h"
+# endif
+
+CCL_NAMESPACE_BEGIN
+
+class HIPDevice;
+class HIPDeviceQueue;
+
+class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop {
+ public:
+ explicit HIPDeviceGraphicsInterop(HIPDeviceQueue *queue);
+
+ HIPDeviceGraphicsInterop(const HIPDeviceGraphicsInterop &other) = delete;
+ HIPDeviceGraphicsInterop(HIPDeviceGraphicsInterop &&other) noexcept = delete;
+
+ ~HIPDeviceGraphicsInterop();
+
+ HIPDeviceGraphicsInterop &operator=(const HIPDeviceGraphicsInterop &other) = delete;
+ HIPDeviceGraphicsInterop &operator=(HIPDeviceGraphicsInterop &&other) = delete;
+
+ virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override;
+
+ virtual device_ptr map() override;
+ virtual void unmap() override;
+
+ protected:
+ HIPDeviceQueue *queue_ = nullptr;
+ HIPDevice *device_ = nullptr;
+
+ /* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */
+ uint opengl_pbo_id_ = 0;
+ /* Buffer area in pixels of the corresponding PBO. */
+ int64_t buffer_area_ = 0;
+
+ /* The destination was requested to be cleared. */
+ bool need_clear_ = false;
+
+ hipGraphicsResource hip_graphics_resource_ = nullptr;
+};
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/device/hip/kernel.cpp b/intern/cycles/device/hip/kernel.cpp
new file mode 100644
index 00000000000..9ede8507a0c
--- /dev/null
+++ b/intern/cycles/device/hip/kernel.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/hip/kernel.h"
+# include "device/hip/device_impl.h"
+
+CCL_NAMESPACE_BEGIN
+
+void HIPDeviceKernels::load(HIPDevice *device)
+{
+ hipModule_t hipModule = device->hipModule;
+
+ for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) {
+ HIPDeviceKernel &kernel = kernels_[i];
+
+ /* No mega-kernel used for GPU. */
+ if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
+ continue;
+ }
+
+ const std::string function_name = std::string("kernel_gpu_") +
+ device_kernel_as_string((DeviceKernel)i);
+ hip_device_assert(device,
+ hipModuleGetFunction(&kernel.function, hipModule, function_name.c_str()));
+
+ if (kernel.function) {
+ hip_device_assert(device, hipFuncSetCacheConfig(kernel.function, hipFuncCachePreferL1));
+
+ hip_device_assert(
+ device,
+ hipModuleOccupancyMaxPotentialBlockSize(
+ &kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, 0, 0));
+ }
+ else {
+ LOG(ERROR) << "Unable to load kernel " << function_name;
+ }
+ }
+
+ loaded = true;
+}
+
+const HIPDeviceKernel &HIPDeviceKernels::get(DeviceKernel kernel) const
+{
+ return kernels_[(int)kernel];
+}
+
+bool HIPDeviceKernels::available(DeviceKernel kernel) const
+{
+ return kernels_[(int)kernel].function != nullptr;
+}
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP*/
diff --git a/intern/cycles/device/hip/kernel.h b/intern/cycles/device/hip/kernel.h
new file mode 100644
index 00000000000..3301731f56e
--- /dev/null
+++ b/intern/cycles/device/hip/kernel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef WITH_HIP
+
+# include "device/device_kernel.h"
+
+# ifdef WITH_HIP_DYNLOAD
+# include "hipew.h"
+# endif
+
+CCL_NAMESPACE_BEGIN
+
+class HIPDevice;
+
+/* HIP kernel and associate occupancy information. */
+class HIPDeviceKernel {
+ public:
+ hipFunction_t function = nullptr;
+
+ int num_threads_per_block = 0;
+ int min_blocks = 0;
+};
+
+/* Cache of HIP kernels for each DeviceKernel. */
+class HIPDeviceKernels {
+ public:
+ void load(HIPDevice *device);
+ const HIPDeviceKernel &get(DeviceKernel kernel) const;
+ bool available(DeviceKernel kernel) const;
+
+ protected:
+ HIPDeviceKernel kernels_[DEVICE_KERNEL_NUM];
+ bool loaded = false;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP */
diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp
new file mode 100644
index 00000000000..78c77e5fdae
--- /dev/null
+++ b/intern/cycles/device/hip/queue.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/hip/queue.h"
+
+# include "device/hip/device_impl.h"
+# include "device/hip/graphics_interop.h"
+# include "device/hip/kernel.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* HIPDeviceQueue */
+
+HIPDeviceQueue::HIPDeviceQueue(HIPDevice *device)
+ : DeviceQueue(device), hip_device_(device), hip_stream_(nullptr)
+{
+ const HIPContextScope scope(hip_device_);
+ hip_device_assert(hip_device_, hipStreamCreateWithFlags(&hip_stream_, hipStreamNonBlocking));
+}
+
+HIPDeviceQueue::~HIPDeviceQueue()
+{
+ const HIPContextScope scope(hip_device_);
+ hipStreamDestroy(hip_stream_);
+}
+
+int HIPDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const
+{
+ /* TODO: compute automatically. */
+ /* TODO: must have at least num_threads_per_block. */
+ return 14416128;
+}
+
+int HIPDeviceQueue::num_concurrent_busy_states() const
+{
+ const int max_num_threads = hip_device_->get_num_multiprocessors() *
+ hip_device_->get_max_num_threads_per_multiprocessor();
+
+ if (max_num_threads == 0) {
+ return 65536;
+ }
+
+ return 4 * max_num_threads;
+}
+
+void HIPDeviceQueue::init_execution()
+{
+ /* Synchronize all textures and memory copies before executing task. */
+ HIPContextScope scope(hip_device_);
+ hip_device_->load_texture_info();
+ hip_device_assert(hip_device_, hipDeviceSynchronize());
+
+ debug_init_execution();
+}
+
+bool HIPDeviceQueue::kernel_available(DeviceKernel kernel) const
+{
+ return hip_device_->kernels.available(kernel);
+}
+
+bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[])
+{
+ if (hip_device_->have_error()) {
+ return false;
+ }
+
+ debug_enqueue(kernel, work_size);
+
+ const HIPContextScope scope(hip_device_);
+ const HIPDeviceKernel &hip_kernel = hip_device_->kernels.get(kernel);
+
+ /* Compute kernel launch parameters. */
+ const int num_threads_per_block = hip_kernel.num_threads_per_block;
+ const int num_blocks = divide_up(work_size, num_threads_per_block);
+
+ int shared_mem_bytes = 0;
+
+ switch (kernel) {
+ case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY:
+ case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY:
+ case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY:
+ case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY:
+ case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY:
+ case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY:
+ /* See parall_active_index.h for why this amount of shared memory is needed. */
+ shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int);
+ break;
+ default:
+ break;
+ }
+
+ /* Launch kernel. */
+ hip_device_assert(hip_device_,
+ hipModuleLaunchKernel(hip_kernel.function,
+ num_blocks,
+ 1,
+ 1,
+ num_threads_per_block,
+ 1,
+ 1,
+ shared_mem_bytes,
+ hip_stream_,
+ args,
+ 0));
+ return !(hip_device_->have_error());
+}
+
+bool HIPDeviceQueue::synchronize()
+{
+ if (hip_device_->have_error()) {
+ return false;
+ }
+
+ const HIPContextScope scope(hip_device_);
+ hip_device_assert(hip_device_, hipStreamSynchronize(hip_stream_));
+ debug_synchronize();
+
+ return !(hip_device_->have_error());
+}
+
+void HIPDeviceQueue::zero_to_device(device_memory &mem)
+{
+ assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
+
+ if (mem.memory_size() == 0) {
+ return;
+ }
+
+ /* Allocate on demand. */
+ if (mem.device_pointer == 0) {
+ hip_device_->mem_alloc(mem);
+ }
+
+ /* Zero memory on device. */
+ assert(mem.device_pointer != 0);
+
+ const HIPContextScope scope(hip_device_);
+ hip_device_assert(
+ hip_device_,
+ hipMemsetD8Async((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size(), hip_stream_));
+}
+
+void HIPDeviceQueue::copy_to_device(device_memory &mem)
+{
+ assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
+
+ if (mem.memory_size() == 0) {
+ return;
+ }
+
+ /* Allocate on demand. */
+ if (mem.device_pointer == 0) {
+ hip_device_->mem_alloc(mem);
+ }
+
+ assert(mem.device_pointer != 0);
+ assert(mem.host_pointer != nullptr);
+
+ /* Copy memory to device. */
+ const HIPContextScope scope(hip_device_);
+ hip_device_assert(
+ hip_device_,
+ hipMemcpyHtoDAsync(
+ (hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size(), hip_stream_));
+}
+
+void HIPDeviceQueue::copy_from_device(device_memory &mem)
+{
+ assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
+
+ if (mem.memory_size() == 0) {
+ return;
+ }
+
+ assert(mem.device_pointer != 0);
+ assert(mem.host_pointer != nullptr);
+
+ /* Copy memory from device. */
+ const HIPContextScope scope(hip_device_);
+ hip_device_assert(
+ hip_device_,
+ hipMemcpyDtoHAsync(
+ mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_));
+}
+
+// TODO : (Arya) Enable this after stabilizing dev branch
+unique_ptr<DeviceGraphicsInterop> HIPDeviceQueue::graphics_interop_create()
+{
+ return make_unique<HIPDeviceGraphicsInterop>(this);
+}
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP */
diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h
new file mode 100644
index 00000000000..04c8a5982ce
--- /dev/null
+++ b/intern/cycles/device/hip/queue.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef WITH_HIP
+
+# include "device/device_kernel.h"
+# include "device/device_memory.h"
+# include "device/device_queue.h"
+
+# include "device/hip/util.h"
+
+CCL_NAMESPACE_BEGIN
+
+class HIPDevice;
+class device_memory;
+
+/* Base class for HIP queues. */
+class HIPDeviceQueue : public DeviceQueue {
+ public:
+ HIPDeviceQueue(HIPDevice *device);
+ ~HIPDeviceQueue();
+
+ virtual int num_concurrent_states(const size_t state_size) const override;
+ virtual int num_concurrent_busy_states() const override;
+
+ virtual void init_execution() override;
+
+ virtual bool kernel_available(DeviceKernel kernel) const override;
+
+ virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override;
+
+ virtual bool synchronize() override;
+
+ virtual void zero_to_device(device_memory &mem) override;
+ virtual void copy_to_device(device_memory &mem) override;
+ virtual void copy_from_device(device_memory &mem) override;
+
+ virtual hipStream_t stream()
+ {
+ return hip_stream_;
+ }
+
+ // TODO : (Arya) Enable this after stabilizing the dev branch
+ virtual unique_ptr<DeviceGraphicsInterop> graphics_interop_create() override;
+
+ protected:
+ HIPDevice *hip_device_;
+ hipStream_t hip_stream_;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP */
diff --git a/intern/cycles/device/hip/util.cpp b/intern/cycles/device/hip/util.cpp
new file mode 100644
index 00000000000..44f52c4e17b
--- /dev/null
+++ b/intern/cycles/device/hip/util.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef WITH_HIP
+
+# include "device/hip/util.h"
+# include "device/hip/device_impl.h"
+
+CCL_NAMESPACE_BEGIN
+
+HIPContextScope::HIPContextScope(HIPDevice *device) : device(device)
+{
+ hip_device_assert(device, hipCtxPushCurrent(device->hipContext));
+}
+
+HIPContextScope::~HIPContextScope()
+{
+ hip_device_assert(device, hipCtxPopCurrent(NULL));
+}
+
+# ifndef WITH_HIP_DYNLOAD
+const char *hipewErrorString(hipError_t result)
+{
+ /* We can only give error code here without major code duplication, that
+ * should be enough since dynamic loading is only being disabled by folks
+ * who knows what they're doing anyway.
+ *
+ * NOTE: Avoid call from several threads.
+ */
+ static string error;
+ error = string_printf("%d", result);
+ return error.c_str();
+}
+
+const char *hipewCompilerPath()
+{
+ return CYCLES_HIP_HIPCC_EXECUTABLE;
+}
+
+int hipewCompilerVersion()
+{
+ return (HIP_VERSION / 100) + (HIP_VERSION % 100 / 10);
+}
+# endif
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP */
diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h
new file mode 100644
index 00000000000..0db5174a3db
--- /dev/null
+++ b/intern/cycles/device/hip/util.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef WITH_HIP
+
+# ifdef WITH_HIP_DYNLOAD
+# include "hipew.h"
+# endif
+
+CCL_NAMESPACE_BEGIN
+
+class HIPDevice;
+
+/* Utility to push/pop HIP context. */
+class HIPContextScope {
+ public:
+ HIPContextScope(HIPDevice *device);
+ ~HIPContextScope();
+
+ private:
+ HIPDevice *device;
+};
+
+/* Utility for checking return values of HIP function calls. */
+# define hip_device_assert(hip_device, stmt) \
+ { \
+ hipError_t result = stmt; \
+ if (result != hipSuccess) { \
+ const char *name = hipewErrorString(result); \
+ hip_device->set_error( \
+ string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \
+ } \
+ } \
+ (void)0
+
+# define hip_assert(stmt) hip_device_assert(this, stmt)
+
+# ifndef WITH_HIP_DYNLOAD
+/* Transparently implement some functions, so majority of the file does not need
+ * to worry about difference between dynamically loaded and linked HIP at all. */
+const char *hipewErrorString(hipError_t result);
+const char *hipewCompilerPath();
+int hipewCompilerVersion();
+# endif /* WITH_HIP_DYNLOAD */
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_HIP */
diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp
index 6dbcce2d9a5..4f995abf2c4 100644
--- a/intern/cycles/device/multi/device.cpp
+++ b/intern/cycles/device/multi/device.cpp
@@ -315,14 +315,14 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size - existing_size);
}
- void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override
+ void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override
{
device_ptr key = mem.device_pointer;
- int i = 0, sub_h = h / devices.size();
+ size_t i = 0, sub_h = h / devices.size();
foreach (SubDevice &sub, devices) {
- int sy = y + i * sub_h;
- int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h;
+ size_t sy = y + i * sub_h;
+ size_t sh = (i == (size_t)devices.size() - 1) ? h - sub_h * i : sub_h;
SubDevice *owner_sub = find_matching_mem_device(key, sub);
mem.device = owner_sub->device;
diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp
index b54d423a183..49d4e22143f 100644
--- a/intern/cycles/device/optix/device_impl.cpp
+++ b/intern/cycles/device/optix/device_impl.cpp
@@ -315,6 +315,11 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
group_descs[PG_HITS].hitgroup.moduleAH = optix_module;
group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit";
+ group_descs[PG_HITV].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
+ group_descs[PG_HITV].hitgroup.moduleCH = optix_module;
+ group_descs[PG_HITV].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit";
+ group_descs[PG_HITV].hitgroup.moduleAH = optix_module;
+ group_descs[PG_HITV].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_volume_test";
if (kernel_features & KERNEL_FEATURE_HAIR) {
if (kernel_features & KERNEL_FEATURE_HAIR_THICK) {
@@ -397,6 +402,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH);
trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH);
trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH);
+ trace_css = std::max(trace_css, stack_size[PG_HITV].cssIS + stack_size[PG_HITV].cssAH);
trace_css = std::max(trace_css,
stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH);
trace_css = std::max(trace_css,
@@ -421,6 +427,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD]);
pipeline_groups.push_back(groups[PG_HITS]);
pipeline_groups.push_back(groups[PG_HITL]);
+ pipeline_groups.push_back(groups[PG_HITV]);
if (motion_blur) {
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
@@ -459,6 +466,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD]);
pipeline_groups.push_back(groups[PG_HITS]);
pipeline_groups.push_back(groups[PG_HITL]);
+ pipeline_groups.push_back(groups[PG_HITV]);
if (motion_blur) {
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
@@ -1390,25 +1398,33 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
/* Set user instance ID to object index (but leave low bit blank). */
instance.instanceId = ob->get_device_index() << 1;
- /* Have to have at least one bit in the mask, or else instance would always be culled. */
- instance.visibilityMask = 1;
+ /* Add some of the object visibility bits to the mask.
+ * __prim_visibility contains the combined visibility bits of all instances, so is not
+ * reliable if they differ between instances. But the OptiX visibility mask can only contain
+ * 8 bits, so have to trade-off here and select just a few important ones.
+ */
+ instance.visibilityMask = ob->visibility_for_tracing() & 0xFF;
- if (ob->get_geometry()->has_volume) {
- /* Volumes have a special bit set in the visibility mask so a trace can mask only volumes.
- */
- instance.visibilityMask |= 2;
+ /* Have to have at least one bit in the mask, or else instance would always be culled. */
+ if (0 == instance.visibilityMask) {
+ instance.visibilityMask = 0xFF;
}
- if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
- /* Same applies to curves (so they can be skipped in local trace calls). */
- instance.visibilityMask |= 4;
-
- if (motion_blur && ob->get_geometry()->has_motion_blur() &&
- static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
+ if (ob->get_geometry()->geometry_type == Geometry::HAIR &&
+ static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
+ if (motion_blur && ob->get_geometry()->has_motion_blur()) {
/* Select between motion blur and non-motion blur built-in intersection module. */
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
}
+ else {
+ /* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves,
+ * since it needs to filter out end-caps there).
+ * It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit
+ * programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT.
+ */
+ instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_ANYHIT;
+ }
/* Insert motion traversable if object has motion. */
if (motion_blur && ob->use_motion()) {
@@ -1474,7 +1490,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
/* Disable instance transform if object uses motion transform already. */
- instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
+ instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
/* Get traversable handle to motion transform. */
optixConvertPointerToTraversableHandle(context,
@@ -1491,7 +1507,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
}
else {
/* Disable instance transform if geometry already has it applied to vertex data. */
- instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
+ instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
/* Non-instanced objects read ID from 'prim_object', so distinguish
* them from instanced objects with the low bit set. */
instance.instanceId |= 1;
diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h
index 91ef52e0a5a..3695ac6afc2 100644
--- a/intern/cycles/device/optix/device_impl.h
+++ b/intern/cycles/device/optix/device_impl.h
@@ -40,6 +40,7 @@ enum {
PG_HITD, /* Default hit group. */
PG_HITS, /* __SHADOW_RECORD_ALL__ hit group. */
PG_HITL, /* __BVH_LOCAL__ hit group (only used for triangles). */
+ PG_HITV, /* __VOLUME__ hit group. */
PG_HITD_MOTION,
PG_HITS_MOTION,
PG_CALL_SVM_AO,
@@ -51,7 +52,7 @@ enum {
static const int MISS_PROGRAM_GROUP_OFFSET = PG_MISS;
static const int NUM_MIS_PROGRAM_GROUPS = 1;
static const int HIT_PROGAM_GROUP_OFFSET = PG_HITD;
-static const int NUM_HIT_PROGRAM_GROUPS = 5;
+static const int NUM_HIT_PROGRAM_GROUPS = 6;
static const int CALLABLE_PROGRAM_GROUPS_BASE = PG_CALL_SVM_AO;
static const int NUM_CALLABLE_PROGRAM_GROUPS = 3;
diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt
index bfabd35d7c3..949254606b8 100644
--- a/intern/cycles/integrator/CMakeLists.txt
+++ b/intern/cycles/integrator/CMakeLists.txt
@@ -27,6 +27,8 @@ set(SRC
pass_accessor.cpp
pass_accessor_cpu.cpp
pass_accessor_gpu.cpp
+ path_trace_display.cpp
+ path_trace_tile.cpp
path_trace_work.cpp
path_trace_work_cpu.cpp
path_trace_work_gpu.cpp
@@ -47,6 +49,8 @@ set(SRC_HEADERS
pass_accessor.h
pass_accessor_cpu.h
pass_accessor_gpu.h
+ path_trace_display.h
+ path_trace_tile.h
path_trace_work.h
path_trace_work_cpu.h
path_trace_work_gpu.h
diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp
index b62a06aea43..7624b244175 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -19,8 +19,9 @@
#include "device/cpu/device.h"
#include "device/device.h"
#include "integrator/pass_accessor.h"
+#include "integrator/path_trace_display.h"
+#include "integrator/path_trace_tile.h"
#include "integrator/render_scheduler.h"
-#include "render/gpu_display.h"
#include "render/pass.h"
#include "render/scene.h"
#include "render/tile.h"
@@ -67,11 +68,11 @@ PathTrace::PathTrace(Device *device,
PathTrace::~PathTrace()
{
/* Destroy any GPU resource which was used for graphics interop.
- * Need to have access to the GPUDisplay as it is the only source of drawing context which is
- * used for interop. */
- if (gpu_display_) {
+ * Need to have access to the PathTraceDisplay as it is the only source of drawing context which
+ * is used for interop. */
+ if (display_) {
for (auto &&path_trace_work : path_trace_works_) {
- path_trace_work->destroy_gpu_resources(gpu_display_.get());
+ path_trace_work->destroy_gpu_resources(display_.get());
}
}
}
@@ -94,7 +95,7 @@ bool PathTrace::ready_to_reset()
{
/* The logic here is optimized for the best feedback in the viewport, which implies having a GPU
* display. Of there is no such display, the logic here will break. */
- DCHECK(gpu_display_);
+ DCHECK(display_);
/* The logic here tries to provide behavior which feels the most interactive feel to artists.
* General idea is to be able to reset as quickly as possible, while still providing interactive
@@ -126,8 +127,8 @@ void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_t
/* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation.
* It is requires to inform about reset whenever it happens, so that the redraw state tracking is
* properly updated. */
- if (gpu_display_) {
- gpu_display_->reset(full_params);
+ if (display_) {
+ display_->reset(full_params);
}
render_state_.has_denoised_result = false;
@@ -244,7 +245,7 @@ static void foreach_sliced_buffer_params(const vector<unique_ptr<PathTraceWork>>
const int slice_height = max(lround(height * weight), 1);
/* Disallow negative values to deal with situations when there are more compute devices than
- * scanlines. */
+ * scan-lines. */
const int remaining_height = max(0, height - current_y);
BufferParams slide_params = buffer_params;
@@ -535,25 +536,35 @@ void PathTrace::denoise(const RenderWork &render_work)
render_scheduler_.report_denoise_time(render_work, time_dt() - start_time);
}
-void PathTrace::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
+void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver)
{
- gpu_display_ = move(gpu_display);
+ output_driver_ = move(driver);
}
-void PathTrace::clear_gpu_display()
+void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver)
{
- if (gpu_display_) {
- gpu_display_->clear();
+ if (driver) {
+ display_ = make_unique<PathTraceDisplay>(move(driver));
+ }
+ else {
+ display_ = nullptr;
+ }
+}
+
+void PathTrace::clear_display()
+{
+ if (display_) {
+ display_->clear();
}
}
void PathTrace::draw()
{
- if (!gpu_display_) {
+ if (!display_) {
return;
}
- did_draw_after_reset_ |= gpu_display_->draw();
+ did_draw_after_reset_ |= display_->draw();
}
void PathTrace::update_display(const RenderWork &render_work)
@@ -562,31 +573,32 @@ void PathTrace::update_display(const RenderWork &render_work)
return;
}
- if (!gpu_display_ && !tile_buffer_update_cb) {
+ if (!display_ && !output_driver_) {
VLOG(3) << "Ignore display update.";
return;
}
if (full_params_.width == 0 || full_params_.height == 0) {
- VLOG(3) << "Skipping GPUDisplay update due to 0 size of the render buffer.";
+ VLOG(3) << "Skipping PathTraceDisplay update due to 0 size of the render buffer.";
return;
}
const double start_time = time_dt();
- if (tile_buffer_update_cb) {
+ if (output_driver_) {
VLOG(3) << "Invoke buffer update callback.";
- tile_buffer_update_cb();
+ PathTraceTile tile(*this);
+ output_driver_->update_render_tile(tile);
}
- if (gpu_display_) {
+ if (display_) {
VLOG(3) << "Perform copy to GPUDisplay work.";
const int resolution_divider = render_work.resolution_divider;
const int texture_width = max(1, full_params_.width / resolution_divider);
const int texture_height = max(1, full_params_.height / resolution_divider);
- if (!gpu_display_->update_begin(texture_width, texture_height)) {
+ if (!display_->update_begin(texture_width, texture_height)) {
LOG(ERROR) << "Error beginning GPUDisplay update.";
return;
}
@@ -600,10 +612,10 @@ void PathTrace::update_display(const RenderWork &render_work)
* all works in parallel. */
const int num_samples = get_num_samples_in_buffer();
for (auto &&path_trace_work : path_trace_works_) {
- path_trace_work->copy_to_gpu_display(gpu_display_.get(), pass_mode, num_samples);
+ path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples);
}
- gpu_display_->update_end();
+ display_->update_end();
}
render_scheduler_.report_display_update_time(render_work, time_dt() - start_time);
@@ -753,20 +765,26 @@ bool PathTrace::is_cancel_requested()
void PathTrace::tile_buffer_write()
{
- if (!tile_buffer_write_cb) {
+ if (!output_driver_) {
return;
}
- tile_buffer_write_cb();
+ PathTraceTile tile(*this);
+ output_driver_->write_render_tile(tile);
}
void PathTrace::tile_buffer_read()
{
- if (!tile_buffer_read_cb) {
+ if (!device_scene_->data.bake.use) {
return;
}
- if (tile_buffer_read_cb()) {
+ if (!output_driver_) {
+ return;
+ }
+
+ PathTraceTile tile(*this);
+ if (output_driver_->read_render_tile(tile)) {
tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_render_buffers_to_device();
});
@@ -801,7 +819,7 @@ void PathTrace::tile_buffer_write_to_disk()
}
if (!tile_manager_.write_tile(*buffers)) {
- LOG(ERROR) << "Error writing tile to file.";
+ device_->set_error("Error writing tile to file");
}
}
@@ -894,7 +912,14 @@ void PathTrace::process_full_buffer_from_disk(string_view filename)
DenoiseParams denoise_params;
if (!tile_manager_.read_full_buffer_from_disk(filename, &full_frame_buffers, &denoise_params)) {
- LOG(ERROR) << "Error reading tiles from file.";
+ const string error_message = "Error reading tiles from file";
+ if (progress_) {
+ progress_->set_error(error_message);
+ progress_->set_cancel(error_message);
+ }
+ else {
+ LOG(ERROR) << error_message;
+ }
return;
}
@@ -998,6 +1023,11 @@ int2 PathTrace::get_render_tile_offset() const
return make_int2(tile.x, tile.y);
}
+int2 PathTrace::get_render_size() const
+{
+ return tile_manager_.get_size();
+}
+
const BufferParams &PathTrace::get_render_tile_params() const
{
if (full_frame_state_.render_buffers) {
@@ -1028,6 +1058,8 @@ static const char *device_type_for_description(const DeviceType type)
return "CUDA";
case DEVICE_OPTIX:
return "OptiX";
+ case DEVICE_HIP:
+ return "HIP";
case DEVICE_DUMMY:
return "Dummy";
case DEVICE_MULTI:
diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h
index fc7713e6df9..dbb22c204d9 100644
--- a/intern/cycles/integrator/path_trace.h
+++ b/intern/cycles/integrator/path_trace.h
@@ -31,12 +31,14 @@ CCL_NAMESPACE_BEGIN
class AdaptiveSampling;
class Device;
class DeviceScene;
+class DisplayDriver;
class Film;
class RenderBuffers;
class RenderScheduler;
class RenderWork;
+class PathTraceDisplay;
+class OutputDriver;
class Progress;
-class GPUDisplay;
class TileManager;
/* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of
@@ -98,13 +100,16 @@ class PathTrace {
* Use this to configure the adaptive sampler before rendering any samples. */
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling);
- /* Set GPU display which takes care of drawing the render result. */
- void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
+ /* Sets output driver for render buffer output. */
+ void set_output_driver(unique_ptr<OutputDriver> driver);
- /* Clear the GPU display by filling it in with all zeroes. */
- void clear_gpu_display();
+ /* Set display driver for interactive render buffer display. */
+ void set_display_driver(unique_ptr<DisplayDriver> driver);
- /* Perform drawing of the current state of the GPUDisplay. */
+ /* Clear the display buffer by filling it in with all zeroes. */
+ void clear_display();
+
+ /* Perform drawing of the current state of the DisplayDriver. */
void draw();
/* Cancel rendering process as soon as possible, without waiting for full tile to be sampled.
@@ -157,6 +162,7 @@ class PathTrace {
* instead. */
int2 get_render_tile_size() const;
int2 get_render_tile_offset() const;
+ int2 get_render_size() const;
/* Get buffer parameters of the current tile.
*
@@ -168,18 +174,6 @@ class PathTrace {
* times, and so on. */
string full_report() const;
- /* Callback which communicates an updates state of the render buffer of the current big tile.
- * Is called during path tracing to communicate work-in-progress state of the final buffer. */
- function<void(void)> tile_buffer_update_cb;
-
- /* Callback which communicates final rendered buffer. Is called after path-tracing is done. */
- function<void(void)> tile_buffer_write_cb;
-
- /* Callback which initializes rendered buffer. Is called before path-tracing starts.
- *
- * This is used for baking. */
- function<bool(void)> tile_buffer_read_cb;
-
/* Callback which is called to report current rendering progress.
*
* It is supposed to be cheaper than buffer update/write, hence can be called more often.
@@ -252,7 +246,11 @@ class PathTrace {
RenderScheduler &render_scheduler_;
TileManager &tile_manager_;
- unique_ptr<GPUDisplay> gpu_display_;
+ /* Display driver for interactive render buffer display. */
+ unique_ptr<PathTraceDisplay> display_;
+
+ /* Output driver to write render buffer to. */
+ unique_ptr<OutputDriver> output_driver_;
/* Per-compute device descriptors of work which is responsible for path tracing on its configured
* device. */
@@ -286,7 +284,7 @@ class PathTrace {
/* Parameters of the big tile with the current resolution divider applied. */
BufferParams effective_big_tile_params;
- /* Denosier was run and there are denoised versions of the passes in the render buffers. */
+ /* Denoiser was run and there are denoised versions of the passes in the render buffers. */
bool has_denoised_result = false;
/* Current tile has been written (to either disk or callback.
diff --git a/intern/cycles/render/gpu_display.cpp b/intern/cycles/integrator/path_trace_display.cpp
index a8f0cc50583..28f0a7f7745 100644
--- a/intern/cycles/render/gpu_display.cpp
+++ b/intern/cycles/integrator/path_trace_display.cpp
@@ -14,20 +14,25 @@
* limitations under the License.
*/
-#include "render/gpu_display.h"
+#include "integrator/path_trace_display.h"
#include "render/buffers.h"
+
#include "util/util_logging.h"
CCL_NAMESPACE_BEGIN
-void GPUDisplay::reset(const BufferParams &buffer_params)
+PathTraceDisplay::PathTraceDisplay(unique_ptr<DisplayDriver> driver) : driver_(move(driver))
+{
+}
+
+void PathTraceDisplay::reset(const BufferParams &buffer_params)
{
thread_scoped_lock lock(mutex_);
- const GPUDisplayParams old_params = params_;
+ const DisplayDriver::Params old_params = params_;
- params_.offset = make_int2(buffer_params.full_x, buffer_params.full_y);
+ params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y);
params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height);
params_.size = make_int2(buffer_params.width, buffer_params.height);
@@ -44,7 +49,7 @@ void GPUDisplay::reset(const BufferParams &buffer_params)
texture_state_.is_outdated = true;
}
-void GPUDisplay::mark_texture_updated()
+void PathTraceDisplay::mark_texture_updated()
{
texture_state_.is_outdated = false;
texture_state_.is_usable = true;
@@ -54,7 +59,7 @@ void GPUDisplay::mark_texture_updated()
* Update procedure.
*/
-bool GPUDisplay::update_begin(int texture_width, int texture_height)
+bool PathTraceDisplay::update_begin(int texture_width, int texture_height)
{
DCHECK(!update_state_.is_active);
@@ -66,15 +71,15 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height)
/* Get parameters within a mutex lock, to avoid reset() modifying them at the same time.
* The update itself is non-blocking however, for better performance and to avoid
* potential deadlocks due to locks held by the subclass. */
- GPUDisplayParams params;
+ DisplayDriver::Params params;
{
thread_scoped_lock lock(mutex_);
params = params_;
texture_state_.size = make_int2(texture_width, texture_height);
}
- if (!do_update_begin(params, texture_width, texture_height)) {
- LOG(ERROR) << "GPUDisplay implementation could not begin update.";
+ if (!driver_->update_begin(params, texture_width, texture_height)) {
+ LOG(ERROR) << "PathTraceDisplay implementation could not begin update.";
return false;
}
@@ -83,7 +88,7 @@ bool GPUDisplay::update_begin(int texture_width, int texture_height)
return true;
}
-void GPUDisplay::update_end()
+void PathTraceDisplay::update_end()
{
DCHECK(update_state_.is_active);
@@ -92,12 +97,12 @@ void GPUDisplay::update_end()
return;
}
- do_update_end();
+ driver_->update_end();
update_state_.is_active = false;
}
-int2 GPUDisplay::get_texture_size() const
+int2 PathTraceDisplay::get_texture_size() const
{
return texture_state_.size;
}
@@ -106,25 +111,54 @@ int2 GPUDisplay::get_texture_size() const
* Texture update from CPU buffer.
*/
-void GPUDisplay::copy_pixels_to_texture(
+void PathTraceDisplay::copy_pixels_to_texture(
const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height)
{
DCHECK(update_state_.is_active);
if (!update_state_.is_active) {
- LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update.";
+ LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update.";
return;
}
mark_texture_updated();
- do_copy_pixels_to_texture(rgba_pixels, texture_x, texture_y, pixels_width, pixels_height);
+
+ /* This call copies pixels to a mapped texture buffer which is typically much cheaper from CPU
+ * time point of view than to copy data directly to a texture.
+ *
+ * The possible downside of this approach is that it might require a higher peak memory when
+ * doing partial updates of the texture (although, in practice even partial updates might peak
+ * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */
+ half4 *mapped_rgba_pixels = map_texture_buffer();
+ if (!mapped_rgba_pixels) {
+ return;
+ }
+
+ const int texture_width = texture_state_.size.x;
+ const int texture_height = texture_state_.size.y;
+
+ if (texture_x == 0 && texture_y == 0 && pixels_width == texture_width &&
+ pixels_height == texture_height) {
+ const size_t size_in_bytes = sizeof(half4) * texture_width * texture_height;
+ memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes);
+ }
+ else {
+ const half4 *rgba_row = rgba_pixels;
+ half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_width + texture_x;
+ for (int y = 0; y < pixels_height;
+ ++y, rgba_row += pixels_width, mapped_rgba_row += texture_width) {
+ memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width);
+ }
+ }
+
+ unmap_texture_buffer();
}
/* --------------------------------------------------------------------
* Texture buffer mapping.
*/
-half4 *GPUDisplay::map_texture_buffer()
+half4 *PathTraceDisplay::map_texture_buffer()
{
DCHECK(!texture_buffer_state_.is_mapped);
DCHECK(update_state_.is_active);
@@ -135,11 +169,11 @@ half4 *GPUDisplay::map_texture_buffer()
}
if (!update_state_.is_active) {
- LOG(ERROR) << "Attempt to copy pixels data outside of GPUDisplay update.";
+ LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update.";
return nullptr;
}
- half4 *mapped_rgba_pixels = do_map_texture_buffer();
+ half4 *mapped_rgba_pixels = driver_->map_texture_buffer();
if (mapped_rgba_pixels) {
texture_buffer_state_.is_mapped = true;
@@ -148,7 +182,7 @@ half4 *GPUDisplay::map_texture_buffer()
return mapped_rgba_pixels;
}
-void GPUDisplay::unmap_texture_buffer()
+void PathTraceDisplay::unmap_texture_buffer()
{
DCHECK(texture_buffer_state_.is_mapped);
@@ -160,14 +194,14 @@ void GPUDisplay::unmap_texture_buffer()
texture_buffer_state_.is_mapped = false;
mark_texture_updated();
- do_unmap_texture_buffer();
+ driver_->unmap_texture_buffer();
}
/* --------------------------------------------------------------------
* Graphics interoperability.
*/
-DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get()
+DisplayDriver::GraphicsInterop PathTraceDisplay::graphics_interop_get()
{
DCHECK(!texture_buffer_state_.is_mapped);
DCHECK(update_state_.is_active);
@@ -175,38 +209,45 @@ DeviceGraphicsInteropDestination GPUDisplay::graphics_interop_get()
if (texture_buffer_state_.is_mapped) {
LOG(ERROR)
<< "Attempt to use graphics interoperability mode while the texture buffer is mapped.";
- return DeviceGraphicsInteropDestination();
+ return DisplayDriver::GraphicsInterop();
}
if (!update_state_.is_active) {
- LOG(ERROR) << "Attempt to use graphics interoperability outside of GPUDisplay update.";
- return DeviceGraphicsInteropDestination();
+ LOG(ERROR) << "Attempt to use graphics interoperability outside of PathTraceDisplay update.";
+ return DisplayDriver::GraphicsInterop();
}
/* Assume that interop will write new values to the texture. */
mark_texture_updated();
- return do_graphics_interop_get();
+ return driver_->graphics_interop_get();
}
-void GPUDisplay::graphics_interop_activate()
+void PathTraceDisplay::graphics_interop_activate()
{
+ driver_->graphics_interop_activate();
}
-void GPUDisplay::graphics_interop_deactivate()
+void PathTraceDisplay::graphics_interop_deactivate()
{
+ driver_->graphics_interop_deactivate();
}
/* --------------------------------------------------------------------
* Drawing.
*/
-bool GPUDisplay::draw()
+void PathTraceDisplay::clear()
+{
+ driver_->clear();
+}
+
+bool PathTraceDisplay::draw()
{
/* Get parameters within a mutex lock, to avoid reset() modifying them at the same time.
* The drawing itself is non-blocking however, for better performance and to avoid
* potential deadlocks due to locks held by the subclass. */
- GPUDisplayParams params;
+ DisplayDriver::Params params;
bool is_usable;
bool is_outdated;
@@ -218,7 +259,7 @@ bool GPUDisplay::draw()
}
if (is_usable) {
- do_draw(params);
+ driver_->draw(params);
}
return !is_outdated;
diff --git a/intern/cycles/render/gpu_display.h b/intern/cycles/integrator/path_trace_display.h
index a01348d28d5..24aaa0df6b1 100644
--- a/intern/cycles/render/gpu_display.h
+++ b/intern/cycles/integrator/path_trace_display.h
@@ -16,52 +16,30 @@
#pragma once
-#include "device/device_graphics_interop.h"
+#include "render/display_driver.h"
+
#include "util/util_half.h"
#include "util/util_thread.h"
#include "util/util_types.h"
+#include "util/util_unique_ptr.h"
CCL_NAMESPACE_BEGIN
class BufferParams;
-/* GPUDisplay class takes care of drawing render result in a viewport. The render result is stored
- * in a GPU-side texture, which is updated from a path tracer and drawn by an application.
+/* PathTraceDisplay is used for efficient render buffer display.
*
- * The base GPUDisplay does some special texture state tracking, which allows render Session to
- * make decisions on whether reset for an updated state is possible or not. This state should only
- * be tracked in a base class and a particular implementation should not worry about it.
+ * The host applications implements a DisplayDriver, storing a render pass in a GPU-side
+ * textures. This texture is continuously updated by the path tracer and drawn by the host
+ * application.
*
- * The subclasses should only implement the pure virtual methods, which allows them to not worry
- * about parent method calls, which helps them to be as small and reliable as possible. */
-
-class GPUDisplayParams {
- public:
- /* Offset of the display within a viewport.
- * For example, set to a lower-bottom corner of border render in Blender's viewport. */
- int2 offset = make_int2(0, 0);
-
- /* Full viewport size.
- *
- * NOTE: Is not affected by the resolution divider. */
- int2 full_size = make_int2(0, 0);
-
- /* Effective vieport size.
- * In the case of border render, size of the border rectangle.
- *
- * NOTE: Is not affected by the resolution divider. */
- int2 size = make_int2(0, 0);
-
- bool modified(const GPUDisplayParams &other) const
- {
- return !(offset == other.offset && full_size == other.full_size && size == other.size);
- }
-};
+ * PathTraceDisplay is a wrapper around the DisplayDriver, adding thread safety, state tracking
+ * and error checking. */
-class GPUDisplay {
+class PathTraceDisplay {
public:
- GPUDisplay() = default;
- virtual ~GPUDisplay() = default;
+ PathTraceDisplay(unique_ptr<DisplayDriver> driver);
+ virtual ~PathTraceDisplay() = default;
/* Reset the display for the new state of render session. Is called whenever session is reset,
* which happens on changes like viewport navigation or viewport dimension change.
@@ -69,11 +47,6 @@ class GPUDisplay {
* This call will configure parameters for a changed buffer and reset the texture state. */
void reset(const BufferParams &buffer_params);
- const GPUDisplayParams &get_params() const
- {
- return params_;
- }
-
/* --------------------------------------------------------------------
* Update procedure.
*
@@ -94,7 +67,8 @@ class GPUDisplay {
/* --------------------------------------------------------------------
* Texture update from CPU buffer.
*
- * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`.
+ * NOTE: The PathTraceDisplay should be marked for an update being in process with
+ * `update_begin()`.
*
* Most portable implementation, which must be supported by all platforms. Might not be the most
* efficient one.
@@ -115,7 +89,8 @@ class GPUDisplay {
* This functionality is used to update GPU-side texture content without need to maintain CPU
* side buffer on the caller.
*
- * NOTE: The GPUDisplay should be marked for an update being in process with `update_begin()`.
+ * NOTE: The PathTraceDisplay should be marked for an update being in process with
+ * `update_begin()`.
*
* NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means
* that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and
@@ -145,14 +120,14 @@ class GPUDisplay {
* that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and
* `unmap_texture_buffer()` calls. */
- /* Get GPUDisplay graphics interoperability information which acts as a destination for the
+ /* Get PathTraceDisplay graphics interoperability information which acts as a destination for the
* device API. */
- DeviceGraphicsInteropDestination graphics_interop_get();
+ DisplayDriver::GraphicsInterop graphics_interop_get();
/* (De)activate GPU display for graphics interoperability outside of regular display update
* routines. */
- virtual void graphics_interop_activate();
- virtual void graphics_interop_deactivate();
+ void graphics_interop_activate();
+ void graphics_interop_deactivate();
/* --------------------------------------------------------------------
* Drawing.
@@ -163,47 +138,26 @@ class GPUDisplay {
* This call might happen in parallel with draw, but can never happen in parallel with the
* update.
*
- * The actual zero-ing can be deferred to a later moment. What is important is that after clear
+ * The actual zeroing can be deferred to a later moment. What is important is that after clear
* and before pixels update the drawing texture will be fully empty, and that partial update
* after clear will write new pixel values for an updating area, leaving everything else zeroed.
*
* If the GPU display supports graphics interoperability then the zeroing the display is to be
- * delegated to the device via the `DeviceGraphicsInteropDestination`. */
- virtual void clear() = 0;
+ * delegated to the device via the `DisplayDriver::GraphicsInterop`. */
+ void clear();
/* Draw the current state of the texture.
*
* Returns true if this call did draw an updated state of the texture. */
bool draw();
- protected:
- /* Implementation-specific calls which subclasses are to implement.
- * These `do_foo()` method corresponds to their `foo()` calls, but they are purely virtual to
- * simplify their particular implementation. */
- virtual bool do_update_begin(const GPUDisplayParams &params,
- int texture_width,
- int texture_height) = 0;
- virtual void do_update_end() = 0;
-
- virtual void do_copy_pixels_to_texture(const half4 *rgba_pixels,
- int texture_x,
- int texture_y,
- int pixels_width,
- int pixels_height) = 0;
-
- virtual half4 *do_map_texture_buffer() = 0;
- virtual void do_unmap_texture_buffer() = 0;
-
- /* Note that this might be called in parallel to do_update_begin() and do_update_end(),
- * the subclass is responsible for appropriate mutex locks to avoid multiple threads
- * editing and drawing the texture at the same time. */
- virtual void do_draw(const GPUDisplayParams &params) = 0;
-
- virtual DeviceGraphicsInteropDestination do_graphics_interop_get() = 0;
-
private:
+ /* Display driver implemented by the host application. */
+ unique_ptr<DisplayDriver> driver_;
+
+ /* Current display parameters */
thread_mutex mutex_;
- GPUDisplayParams params_;
+ DisplayDriver::Params params_;
/* Mark texture as its content has been updated.
* Used from places which knows that the texture content has been brought up-to-date, so that the
diff --git a/intern/cycles/integrator/path_trace_tile.cpp b/intern/cycles/integrator/path_trace_tile.cpp
new file mode 100644
index 00000000000..540f4aa5f68
--- /dev/null
+++ b/intern/cycles/integrator/path_trace_tile.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "integrator/path_trace_tile.h"
+#include "integrator/pass_accessor_cpu.h"
+#include "integrator/path_trace.h"
+
+#include "render/buffers.h"
+#include "render/film.h"
+#include "render/pass.h"
+#include "render/scene.h"
+
+CCL_NAMESPACE_BEGIN
+
+PathTraceTile::PathTraceTile(PathTrace &path_trace)
+ : OutputDriver::Tile(path_trace.get_render_tile_offset(),
+ path_trace.get_render_tile_size(),
+ path_trace.get_render_size(),
+ path_trace.get_render_tile_params().layer,
+ path_trace.get_render_tile_params().view),
+ path_trace_(path_trace),
+ copied_from_device_(false)
+{
+}
+
+bool PathTraceTile::get_pass_pixels(const string_view pass_name,
+ const int num_channels,
+ float *pixels) const
+{
+ /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
+ * is happening while this function runs. */
+
+ if (!copied_from_device_) {
+ /* Copy from device on demand. */
+ path_trace_.copy_render_tile_from_device();
+ const_cast<PathTraceTile *>(this)->copied_from_device_ = true;
+ }
+
+ const BufferParams &buffer_params = path_trace_.get_render_tile_params();
+
+ const BufferPass *pass = buffer_params.find_pass(pass_name);
+ if (pass == nullptr) {
+ return false;
+ }
+
+ const bool has_denoised_result = path_trace_.has_denoised_result();
+ if (pass->mode == PassMode::DENOISED && !has_denoised_result) {
+ pass = buffer_params.find_pass(pass->type);
+ if (pass == nullptr) {
+ /* Happens when denoised result pass is requested but is never written by the kernel. */
+ return false;
+ }
+ }
+
+ pass = buffer_params.get_actual_display_pass(pass);
+
+ const float exposure = buffer_params.exposure;
+ const int num_samples = path_trace_.get_num_render_tile_samples();
+
+ PassAccessor::PassAccessInfo pass_access_info(*pass);
+ pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher;
+ pass_access_info.use_approximate_shadow_catcher_background =
+ pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background;
+
+ const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
+ const PassAccessor::Destination destination(pixels, num_channels);
+
+ return path_trace_.get_render_tile_pixels(pass_accessor, destination);
+}
+
+bool PathTraceTile::set_pass_pixels(const string_view pass_name,
+ const int num_channels,
+ const float *pixels) const
+{
+ /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
+ * is happening while this function runs. */
+
+ const BufferParams &buffer_params = path_trace_.get_render_tile_params();
+ const BufferPass *pass = buffer_params.find_pass(pass_name);
+ if (!pass) {
+ return false;
+ }
+
+ const float exposure = buffer_params.exposure;
+ const int num_samples = 1;
+
+ const PassAccessor::PassAccessInfo pass_access_info(*pass);
+ PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
+ PassAccessor::Source source(pixels, num_channels);
+
+ return path_trace_.set_render_tile_pixels(pass_accessor, source);
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/integrator/path_trace_tile.h b/intern/cycles/integrator/path_trace_tile.h
new file mode 100644
index 00000000000..fd3e2969f6c
--- /dev/null
+++ b/intern/cycles/integrator/path_trace_tile.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "render/output_driver.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* PathTraceTile
+ *
+ * Implementation of OutputDriver::Tile interface for path tracer. */
+
+class PathTrace;
+
+class PathTraceTile : public OutputDriver::Tile {
+ public:
+ PathTraceTile(PathTrace &path_trace);
+
+ bool get_pass_pixels(const string_view pass_name, const int num_channels, float *pixels) const;
+ bool set_pass_pixels(const string_view pass_name,
+ const int num_channels,
+ const float *pixels) const;
+
+ private:
+ PathTrace &path_trace_;
+ bool copied_from_device_;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp
index d9634acac10..c29177907c9 100644
--- a/intern/cycles/integrator/path_trace_work.cpp
+++ b/intern/cycles/integrator/path_trace_work.cpp
@@ -16,12 +16,12 @@
#include "device/device.h"
+#include "integrator/path_trace_display.h"
#include "integrator/path_trace_work.h"
#include "integrator/path_trace_work_cpu.h"
#include "integrator/path_trace_work_gpu.h"
#include "render/buffers.h"
#include "render/film.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "kernel/kernel_types.h"
@@ -185,12 +185,12 @@ PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMod
return pass_access_info;
}
-PassAccessor::Destination PathTraceWork::get_gpu_display_destination_template(
- const GPUDisplay *gpu_display) const
+PassAccessor::Destination PathTraceWork::get_display_destination_template(
+ const PathTraceDisplay *display) const
{
PassAccessor::Destination destination(film_->get_display_pass());
- const int2 display_texture_size = gpu_display->get_texture_size();
+ const int2 display_texture_size = display->get_texture_size();
const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x;
const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y;
diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h
index 8c9c8811199..404165b7c55 100644
--- a/intern/cycles/integrator/path_trace_work.h
+++ b/intern/cycles/integrator/path_trace_work.h
@@ -28,7 +28,7 @@ class BufferParams;
class Device;
class DeviceScene;
class Film;
-class GPUDisplay;
+class PathTraceDisplay;
class RenderBuffers;
class PathTraceWork {
@@ -83,11 +83,9 @@ class PathTraceWork {
* noisy pass mode will be passed here when it is known that the buffer does not have denoised
* passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is
* not used then this function will fall-back to the noisy pass instead. */
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) = 0;
+ virtual void copy_to_display(PathTraceDisplay *display, PassMode pass_mode, int num_samples) = 0;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) = 0;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) = 0;
/* Copy data from/to given render buffers.
* Will copy pixels from a corresponding place (from multi-device point of view) of the render
@@ -104,7 +102,7 @@ class PathTraceWork {
* - Copies work's render buffer to its device. */
void copy_from_render_buffers(const RenderBuffers *render_buffers);
- /* Special version of the `copy_from_render_buffers()` which only copies denosied passes from the
+ /* Special version of the `copy_from_render_buffers()` which only copies denoised passes from the
* given render buffers, leaving rest of the passes.
*
* Same notes about device copying applies to this call as well. */
@@ -162,8 +160,8 @@ class PathTraceWork {
/* Get destination which offset and stride are configured so that writing to it will write to a
* proper location of GPU display texture, taking current tile and device slice into account. */
- PassAccessor::Destination get_gpu_display_destination_template(
- const GPUDisplay *gpu_display) const;
+ PassAccessor::Destination get_display_destination_template(
+ const PathTraceDisplay *display) const;
/* Device which will be used for path tracing.
* Note that it is an actual render device (and never is a multi-device). */
diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp
index b9a33b64051..18a5365453d 100644
--- a/intern/cycles/integrator/path_trace_work_cpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_cpu.cpp
@@ -19,10 +19,12 @@
#include "device/cpu/kernel.h"
#include "device/device.h"
+#include "kernel/kernel_path_state.h"
+
#include "integrator/pass_accessor_cpu.h"
+#include "integrator/path_trace_display.h"
#include "render/buffers.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_atomic.h"
@@ -116,13 +118,17 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global
const KernelWorkTile &work_tile,
const int samples_num)
{
- const bool has_shadow_catcher = device_scene_->data.integrator.has_shadow_catcher;
const bool has_bake = device_scene_->data.bake.use;
- IntegratorStateCPU integrator_states[2] = {};
+ IntegratorStateCPU integrator_states[2];
IntegratorStateCPU *state = &integrator_states[0];
- IntegratorStateCPU *shadow_catcher_state = &integrator_states[1];
+ IntegratorStateCPU *shadow_catcher_state = nullptr;
+
+ if (device_scene_->data.integrator.has_shadow_catcher) {
+ shadow_catcher_state = &integrator_states[1];
+ path_state_init_queues(kernel_globals, shadow_catcher_state);
+ }
KernelWorkTile sample_work_tile = work_tile;
float *render_buffer = buffers_->buffer.data();
@@ -147,7 +153,7 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global
kernels_.integrator_megakernel(kernel_globals, state, render_buffer);
- if (has_shadow_catcher) {
+ if (shadow_catcher_state) {
kernels_.integrator_megakernel(kernel_globals, shadow_catcher_state, render_buffer);
}
@@ -155,14 +161,14 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global
}
}
-void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkCPU::copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
- half4 *rgba_half = gpu_display->map_texture_buffer();
+ half4 *rgba_half = display->map_texture_buffer();
if (!rgba_half) {
- /* TODO(sergey): Look into using copy_to_gpu_display() if mapping failed. Might be needed for
- * some implementations of GPUDisplay which can not map memory? */
+ /* TODO(sergey): Look into using copy_to_display() if mapping failed. Might be needed for
+ * some implementations of PathTraceDisplay which can not map memory? */
return;
}
@@ -172,7 +178,7 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples);
- PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
+ PassAccessor::Destination destination = get_display_destination_template(display);
destination.pixels_half_rgba = rgba_half;
tbb::task_arena local_arena = local_tbb_arena_create(device_);
@@ -180,10 +186,10 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination);
});
- gpu_display->unmap_texture_buffer();
+ display->unmap_texture_buffer();
}
-void PathTraceWorkCPU::destroy_gpu_resources(GPUDisplay * /*gpu_display*/)
+void PathTraceWorkCPU::destroy_gpu_resources(PathTraceDisplay * /*display*/)
{
}
diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h
index ab729bbf879..d011e8d05bd 100644
--- a/intern/cycles/integrator/path_trace_work_cpu.h
+++ b/intern/cycles/integrator/path_trace_work_cpu.h
@@ -50,10 +50,10 @@ class PathTraceWorkCPU : public PathTraceWork {
int start_sample,
int samples_num) override;
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) override;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
+ virtual void copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples) override;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;
diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp
index 135466becc6..17c49f244d2 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_gpu.cpp
@@ -15,12 +15,12 @@
*/
#include "integrator/path_trace_work_gpu.h"
+#include "integrator/path_trace_display.h"
#include "device/device.h"
#include "integrator/pass_accessor_gpu.h"
#include "render/buffers.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_logging.h"
#include "util/util_tbb.h"
@@ -46,7 +46,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device,
queued_paths_(device, "queued_paths", MEM_READ_WRITE),
num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE),
work_tiles_(device, "work_tiles", MEM_READ_WRITE),
- gpu_display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
+ display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))),
min_num_active_paths_(queue_->num_concurrent_busy_states()),
max_active_path_index_(0)
@@ -95,8 +95,8 @@ void PathTraceWorkGPU::alloc_integrator_soa()
#define KERNEL_STRUCT_END(name) \
break; \
}
-#define KERNEL_STRUCT_END_ARRAY(name, array_size) \
- if (array_index == array_size - 1) { \
+#define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \
+ if (array_index == gpu_array_size - 1) { \
break; \
} \
}
@@ -652,7 +652,7 @@ int PathTraceWorkGPU::get_num_active_paths()
bool PathTraceWorkGPU::should_use_graphics_interop()
{
/* There are few aspects with the graphics interop when using multiple devices caused by the fact
- * that the GPUDisplay has a single texture:
+ * that the PathTraceDisplay has a single texture:
*
* CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when
* attempting to register OpenGL PBO which has been mapped. Which makes sense, because
@@ -678,9 +678,9 @@ bool PathTraceWorkGPU::should_use_graphics_interop()
return interop_use_;
}
-void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkGPU::copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
if (device_->have_error()) {
/* Don't attempt to update GPU display if the device has errors: the error state will make
@@ -694,7 +694,7 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
}
if (should_use_graphics_interop()) {
- if (copy_to_gpu_display_interop(gpu_display, pass_mode, num_samples)) {
+ if (copy_to_display_interop(display, pass_mode, num_samples)) {
return;
}
@@ -703,12 +703,12 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
interop_use_ = false;
}
- copy_to_gpu_display_naive(gpu_display, pass_mode, num_samples);
+ copy_to_display_naive(display, pass_mode, num_samples);
}
-void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
const int full_x = effective_buffer_params_.full_x;
const int full_y = effective_buffer_params_.full_y;
@@ -725,43 +725,42 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
* NOTE: allocation happens to the final resolution so that no re-allocation happens on every
* change of the resolution divider. However, if the display becomes smaller, shrink the
* allocated memory as well. */
- if (gpu_display_rgba_half_.data_width != final_width ||
- gpu_display_rgba_half_.data_height != final_height) {
- gpu_display_rgba_half_.alloc(final_width, final_height);
+ if (display_rgba_half_.data_width != final_width ||
+ display_rgba_half_.data_height != final_height) {
+ display_rgba_half_.alloc(final_width, final_height);
/* TODO(sergey): There should be a way to make sure device-side memory is allocated without
* transferring zeroes to the device. */
- queue_->zero_to_device(gpu_display_rgba_half_);
+ queue_->zero_to_device(display_rgba_half_);
}
PassAccessor::Destination destination(film_->get_display_pass());
- destination.d_pixels_half_rgba = gpu_display_rgba_half_.device_pointer;
+ destination.d_pixels_half_rgba = display_rgba_half_.device_pointer;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
- gpu_display_rgba_half_.copy_from_device();
+ queue_->copy_from_device(display_rgba_half_);
+ queue_->synchronize();
- gpu_display->copy_pixels_to_texture(
- gpu_display_rgba_half_.data(), texture_x, texture_y, width, height);
+ display->copy_pixels_to_texture(display_rgba_half_.data(), texture_x, texture_y, width, height);
}
-bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+bool PathTraceWorkGPU::copy_to_display_interop(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
if (!device_graphics_interop_) {
device_graphics_interop_ = queue_->graphics_interop_create();
}
- const DeviceGraphicsInteropDestination graphics_interop_dst =
- gpu_display->graphics_interop_get();
- device_graphics_interop_->set_destination(graphics_interop_dst);
+ const DisplayDriver::GraphicsInterop graphics_interop_dst = display->graphics_interop_get();
+ device_graphics_interop_->set_display_interop(graphics_interop_dst);
const device_ptr d_rgba_half = device_graphics_interop_->map();
if (!d_rgba_half) {
return false;
}
- PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
+ PassAccessor::Destination destination = get_display_destination_template(display);
destination.d_pixels_half_rgba = d_rgba_half;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
@@ -771,14 +770,14 @@ bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
return true;
}
-void PathTraceWorkGPU::destroy_gpu_resources(GPUDisplay *gpu_display)
+void PathTraceWorkGPU::destroy_gpu_resources(PathTraceDisplay *display)
{
if (!device_graphics_interop_) {
return;
}
- gpu_display->graphics_interop_activate();
+ display->graphics_interop_activate();
device_graphics_interop_ = nullptr;
- gpu_display->graphics_interop_deactivate();
+ display->graphics_interop_deactivate();
}
void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination,
diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h
index 38788122b0d..9212537d2fd 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.h
+++ b/intern/cycles/integrator/path_trace_work_gpu.h
@@ -48,10 +48,10 @@ class PathTraceWorkGPU : public PathTraceWork {
int start_sample,
int samples_num) override;
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) override;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
+ virtual void copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples) override;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;
@@ -88,16 +88,16 @@ class PathTraceWorkGPU : public PathTraceWork {
int get_num_active_paths();
- /* Check whether graphics interop can be used for the GPUDisplay update. */
+ /* Check whether graphics interop can be used for the PathTraceDisplay update. */
bool should_use_graphics_interop();
- /* Naive implementation of the `copy_to_gpu_display()` which performs film conversion on the
- * device, then copies pixels to the host and pushes them to the `gpu_display`. */
- void copy_to_gpu_display_naive(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
+ /* Naive implementation of the `copy_to_display()` which performs film conversion on the
+ * device, then copies pixels to the host and pushes them to the `display`. */
+ void copy_to_display_naive(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
- /* Implementation of `copy_to_gpu_display()` which uses driver's OpenGL/GPU interoperability
+ /* Implementation of `copy_to_display()` which uses driver's OpenGL/GPU interoperability
* functionality, avoiding copy of pixels to the host. */
- bool copy_to_gpu_display_interop(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
+ bool copy_to_display_interop(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
/* Synchronously run film conversion kernel and store display result in the given destination. */
void get_render_tile_film_pixels(const PassAccessor::Destination &destination,
@@ -139,9 +139,9 @@ class PathTraceWorkGPU : public PathTraceWork {
/* Temporary buffer for passing work tiles to kernel. */
device_vector<KernelWorkTile> work_tiles_;
- /* Temporary buffer used by the copy_to_gpu_display() whenever graphics interoperability is not
+ /* Temporary buffer used by the copy_to_display() whenever graphics interoperability is not
* available. Is allocated on-demand. */
- device_vector<half4> gpu_display_rgba_half_;
+ device_vector<half4> display_rgba_half_;
unique_ptr<DeviceGraphicsInterop> device_graphics_interop_;
diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp
index 3e5b3417a6a..322d3d5f94c 100644
--- a/intern/cycles/integrator/render_scheduler.cpp
+++ b/intern/cycles/integrator/render_scheduler.cpp
@@ -384,7 +384,7 @@ bool RenderScheduler::set_postprocess_render_work(RenderWork *render_work)
}
if (denoiser_params_.use && !state_.last_work_tile_was_denoised) {
- render_work->tile.denoise = true;
+ render_work->tile.denoise = !tile_manager_.has_multiple_tiles();
any_scheduled = true;
}
@@ -903,6 +903,12 @@ bool RenderScheduler::work_need_denoise(bool &delayed, bool &ready_to_display)
return false;
}
+ /* When multiple tiles are used the full frame will be denoised.
+ * Avoid per-tile denoising to save up render time. */
+ if (tile_manager_.has_multiple_tiles()) {
+ return false;
+ }
+
if (done()) {
/* Always denoise at the last sample. */
return true;
diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h
index b7b598fb10c..c4ab15e54ba 100644
--- a/intern/cycles/integrator/render_scheduler.h
+++ b/intern/cycles/integrator/render_scheduler.h
@@ -31,7 +31,7 @@ class RenderWork {
int resolution_divider = 1;
/* Initialize render buffers.
- * Includes steps like zero-ing the buffer on the device, and optional reading of pixels from the
+ * Includes steps like zeroing the buffer on the device, and optional reading of pixels from the
* baking target. */
bool init_render_buffers = false;
@@ -344,7 +344,7 @@ class RenderScheduler {
/* Number of rendered samples on top of the start sample. */
int num_rendered_samples = 0;
- /* Point in time the latest GPUDisplay work has been scheduled. */
+ /* Point in time the latest PathTraceDisplay work has been scheduled. */
double last_display_update_time = 0.0;
/* Value of -1 means display was never updated. */
int last_display_update_sample = -1;
diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp
index d35ff4cd03f..a14e41ec5be 100644
--- a/intern/cycles/integrator/shader_eval.cpp
+++ b/intern/cycles/integrator/shader_eval.cpp
@@ -149,14 +149,14 @@ bool ShaderEval::eval_gpu(Device *device,
/* Execute work on GPU in chunk, so we can cancel.
* TODO : query appropriate size from device.*/
- const int chunk_size = 65536;
+ const int64_t chunk_size = 65536;
- const int work_size = output.size();
+ const int64_t work_size = output.size();
void *d_input = (void *)input.device_pointer;
void *d_output = (void *)output.device_pointer;
- for (int d_offset = 0; d_offset < work_size; d_offset += chunk_size) {
- int d_work_size = min(chunk_size, work_size - d_offset);
+ for (int64_t d_offset = 0; d_offset < work_size; d_offset += chunk_size) {
+ int64_t d_work_size = std::min(chunk_size, work_size - d_offset);
void *args[] = {&d_input, &d_output, &d_offset, &d_work_size};
queue->enqueue(kernel, d_work_size, args);
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 4196539a9b1..7b56216e887 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -35,6 +35,10 @@ set(SRC_DEVICE_CUDA
device/cuda/kernel.cu
)
+set(SRC_DEVICE_HIP
+ device/hip/kernel.cpp
+)
+
set(SRC_DEVICE_OPTIX
device/optix/kernel.cu
device/optix/kernel_shader_raytrace.cu
@@ -106,6 +110,12 @@ set(SRC_DEVICE_CUDA_HEADERS
device/cuda/globals.h
)
+set(SRC_DEVICE_HIP_HEADERS
+ device/hip/compat.h
+ device/hip/config.h
+ device/hip/globals.h
+)
+
set(SRC_DEVICE_OPTIX_HEADERS
device/optix/compat.h
device/optix/globals.h
@@ -458,6 +468,104 @@ if(WITH_CYCLES_CUDA_BINARIES)
cycles_set_solution_folder(cycles_kernel_cuda)
endif()
+####################################################### START
+
+# HIP module
+
+if(WITH_CYCLES_HIP_BINARIES)
+ # 64 bit only
+ set(HIP_BITS 64)
+
+ # HIP version
+ execute_process(COMMAND ${HIP_HIPCC_EXECUTABLE} "--version" OUTPUT_VARIABLE HIPCC_OUT)
+ string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" HIP_VERSION_MAJOR "${HIPCC_OUT}")
+ string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" HIP_VERSION_MINOR "${HIPCC_OUT}")
+ set(HIP_VERSION "${HIP_VERSION_MAJOR}${HIP_VERSION_MINOR}")
+
+
+ message(WARNING
+ "HIP version ${HIP_VERSION_MAJOR}.${HIP_VERSION_MINOR} detected")
+
+ # build for each arch
+ set(hip_sources device/hip/kernel.cpp
+ ${SRC_HEADERS}
+ ${SRC_DEVICE_HIP_HEADERS}
+ ${SRC_BVH_HEADERS}
+ ${SRC_SVM_HEADERS}
+ ${SRC_GEOM_HEADERS}
+ ${SRC_INTEGRATOR_HEADERS}
+ ${SRC_CLOSURE_HEADERS}
+ ${SRC_UTIL_HEADERS}
+ )
+ set(hip_fatbins)
+
+ macro(CYCLES_HIP_KERNEL_ADD arch prev_arch name flags sources experimental)
+ if(${arch} MATCHES "compute_.*")
+ set(format "ptx")
+ else()
+ set(format "fatbin")
+ endif()
+ set(hip_file ${name}_${arch}.${format})
+
+ set(kernel_sources ${sources})
+ if(NOT ${prev_arch} STREQUAL "none")
+ if(${prev_arch} MATCHES "compute_.*")
+ set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.ptx)
+ else()
+ set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.fatbin)
+ endif()
+ endif()
+
+ set(hip_kernel_src "/device/hip/${name}.cpp")
+
+ set(hip_flags ${flags}
+ -D CCL_NAMESPACE_BEGIN=
+ -D CCL_NAMESPACE_END=
+ -D HIPCC
+ -m ${HIP_BITS}
+ -I ${CMAKE_CURRENT_SOURCE_DIR}/..
+ -I ${CMAKE_CURRENT_SOURCE_DIR}/device/hip
+ --use_fast_math
+ -o ${CMAKE_CURRENT_BINARY_DIR}/${hip_file})
+
+ if(${experimental})
+ set(hip_flags ${hip_flags} -D __KERNEL_EXPERIMENTAL__)
+ set(name ${name}_experimental)
+ endif()
+
+ if(WITH_CYCLES_DEBUG)
+ set(hip_flags ${hip_flags} -D __KERNEL_DEBUG__)
+ endif()
+
+ if(WITH_NANOVDB)
+ set(hip_flags ${hip_flags}
+ -D WITH_NANOVDB
+ -I "${NANOVDB_INCLUDE_DIR}")
+ endif()
+ endmacro()
+
+ set(prev_arch "none")
+ foreach(arch ${CYCLES_HIP_BINARIES_ARCH})
+ set(hip_hipcc_executable ${HIP_HIPCC_EXECUTABLE})
+ set(hip_toolkit_root_dir ${HIP_TOOLKIT_ROOT_DIR})
+ if(DEFINED hip_hipcc_executable AND DEFINED hip_toolkit_root_dir)
+ # Compile regular kernel
+ CYCLES_HIP_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${hip_sources}" FALSE)
+
+ if(WITH_CYCLES_HIP_BUILD_SERIAL)
+ set(prev_arch ${arch})
+ endif()
+
+ unset(hip_hipcc_executable)
+ unset(hip_toolkit_root_dir)
+ endif()
+ endforeach()
+
+ add_custom_target(cycles_kernel_hip ALL DEPENDS ${hip_fatbins})
+ cycles_set_solution_folder(cycles_kernel_hip)
+endif()
+
+####################################################### END
# OptiX PTX modules
if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
@@ -602,11 +710,13 @@ endif()
cycles_add_library(cycles_kernel "${LIB}"
${SRC_DEVICE_CPU}
${SRC_DEVICE_CUDA}
+ ${SRC_DEVICE_HIP}
${SRC_DEVICE_OPTIX}
${SRC_HEADERS}
${SRC_DEVICE_CPU_HEADERS}
${SRC_DEVICE_GPU_HEADERS}
${SRC_DEVICE_CUDA_HEADERS}
+ ${SRC_DEVICE_HIP_HEADERS}
${SRC_DEVICE_OPTIX_HEADERS}
${SRC_BVH_HEADERS}
${SRC_CLOSURE_HEADERS}
@@ -621,6 +731,7 @@ source_group("geom" FILES ${SRC_GEOM_HEADERS})
source_group("integrator" FILES ${SRC_INTEGRATOR_HEADERS})
source_group("kernel" FILES ${SRC_HEADERS})
source_group("device\\cpu" FILES ${SRC_DEVICE_CPU} ${SRC_DEVICE_CPU_HEADERS})
+source_group("device\\hip" FILES ${SRC_DEVICE_HIP} ${SRC_DEVICE_HIP_HEADERS})
source_group("device\\gpu" FILES ${SRC_DEVICE_GPU_HEADERS})
source_group("device\\cuda" FILES ${SRC_DEVICE_CUDA} ${SRC_DEVICE_CUDA_HEADERS})
source_group("device\\optix" FILES ${SRC_DEVICE_OPTIX} ${SRC_DEVICE_OPTIX_HEADERS})
@@ -632,14 +743,19 @@ endif()
if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
add_dependencies(cycles_kernel cycles_kernel_optix)
endif()
+if(WITH_CYCLES_HIP)
+ add_dependencies(cycles_kernel cycles_kernel_hip)
+endif()
# Install kernel source for runtime compilation
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda)
+delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda)
+delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure)
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 539e9fd05fb..0b44cc5db34 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -167,15 +167,25 @@ ccl_device_intersect bool scene_intersect(const KernelGlobals *kg,
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
+ uint ray_mask = visibility & 0xFF;
+ uint ray_flags = OPTIX_RAY_FLAG_NONE;
+ if (0 == ray_mask && (visibility & ~0xFF) != 0) {
+ ray_mask = 0xFF;
+ ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
+ }
+ else if (visibility & PATH_RAY_SHADOW_OPAQUE) {
+ ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
+ }
+
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
ray->D,
0.0f,
ray->t,
ray->time,
- 0xF,
- OPTIX_RAY_FLAG_NONE,
- 0, // SBT offset for PG_HITD
+ ray_mask,
+ ray_flags,
+ 0, /* SBT offset for PG_HITD */
0,
0,
p0,
@@ -251,11 +261,11 @@ ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg,
uint p2 = ((uint64_t)local_isect) & 0xFFFFFFFF;
uint p3 = (((uint64_t)local_isect) >> 32) & 0xFFFFFFFF;
uint p4 = local_object;
- // Is set to zero on miss or if ray is aborted, so can be used as return value
+ /* Is set to zero on miss or if ray is aborted, so can be used as return value. */
uint p5 = max_hits;
if (local_isect) {
- local_isect->num_hits = 0; // Initialize hit count to zero
+ local_isect->num_hits = 0; /* Initialize hit count to zero. */
}
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
@@ -263,11 +273,10 @@ ccl_device_intersect bool scene_intersect_local(const KernelGlobals *kg,
0.0f,
ray->t,
ray->time,
- // Skip curves
- 0x3,
- // Need to always call into __anyhit__kernel_optix_local_hit
+ 0xFF,
+ /* Need to always call into __anyhit__kernel_optix_local_hit. */
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
- 2, // SBT offset for PG_HITL
+ 2, /* SBT offset for PG_HITL */
0,
0,
p0,
@@ -365,17 +374,22 @@ ccl_device_intersect bool scene_intersect_shadow_all(const KernelGlobals *kg,
uint p4 = visibility;
uint p5 = false;
- *num_hits = 0; // Initialize hit count to zero
+ uint ray_mask = visibility & 0xFF;
+ if (0 == ray_mask && (visibility & ~0xFF) != 0) {
+ ray_mask = 0xFF;
+ }
+
+ *num_hits = 0; /* Initialize hit count to zero. */
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
ray->D,
0.0f,
ray->t,
ray->time,
- 0xF,
- // Need to always call into __anyhit__kernel_optix_shadow_all_hit
+ ray_mask,
+ /* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
- 1, // SBT offset for PG_HITS
+ 1, /* SBT offset for PG_HITS */
0,
0,
p0,
@@ -444,16 +458,21 @@ ccl_device_intersect bool scene_intersect_volume(const KernelGlobals *kg,
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
+ uint ray_mask = visibility & 0xFF;
+ if (0 == ray_mask && (visibility & ~0xFF) != 0) {
+ ray_mask = 0xFF;
+ }
+
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
ray->D,
0.0f,
ray->t,
ray->time,
- // Skip everything but volumes
- 0x2,
- OPTIX_RAY_FLAG_NONE,
- 0, // SBT offset for PG_HITD
+ ray_mask,
+ /* Need to always call into __anyhit__kernel_optix_volume_test. */
+ OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
+ 3, /* SBT offset for PG_HITV */
0,
0,
p0,
diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h
index 85500bf4d07..db4a4bf71e0 100644
--- a/intern/cycles/kernel/device/gpu/parallel_active_index.h
+++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h
@@ -21,11 +21,15 @@ CCL_NAMESPACE_BEGIN
/* Given an array of states, build an array of indices for which the states
* are active.
*
- * Shared memory requirement is sizeof(int) * (number_of_warps + 1) */
+ * Shared memory requirement is `sizeof(int) * (number_of_warps + 1)`. */
#include "util/util_atomic.h"
-#define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512
+#ifdef __HIP__
+# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 1024
+#else
+# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512
+#endif
template<uint blocksize, typename IsActiveOp>
__device__ void gpu_parallel_active_index_array(const uint num_states,
diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h
index f609520b8b4..a1349e82efb 100644
--- a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h
+++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h
@@ -27,7 +27,11 @@ CCL_NAMESPACE_BEGIN
#include "util/util_atomic.h"
-#define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512
+#ifdef __HIP__
+# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 1024
+#else
+# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512
+#endif
template<uint blocksize> __device__ void gpu_parallel_prefix_sum(int *values, const int num_values)
{
diff --git a/intern/cycles/kernel/device/gpu/parallel_reduce.h b/intern/cycles/kernel/device/gpu/parallel_reduce.h
index 65b1990dbb8..b60dceb2ed0 100644
--- a/intern/cycles/kernel/device/gpu/parallel_reduce.h
+++ b/intern/cycles/kernel/device/gpu/parallel_reduce.h
@@ -26,7 +26,11 @@ CCL_NAMESPACE_BEGIN
* the overall cost of the algorithm while keeping the work complexity O(n) and
* the step complexity O(log n). (Brent's Theorem optimization) */
-#define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512
+#ifdef __HIP__
+# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 1024
+#else
+# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512
+#endif
template<uint blocksize, typename InputT, typename OutputT, typename ConvertOp>
__device__ void gpu_parallel_sum(
diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h
index 99b35468517..9bca1fad22f 100644
--- a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h
+++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h
@@ -26,7 +26,11 @@ CCL_NAMESPACE_BEGIN
#include "util/util_atomic.h"
-#define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512
+#ifdef __HIP__
+# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 1024
+#else
+# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512
+#endif
#define GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY (~0)
template<uint blocksize, typename GetKeyOp>
diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h
new file mode 100644
index 00000000000..95338fe7d6e
--- /dev/null
+++ b/intern/cycles/kernel/device/hip/compat.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define __KERNEL_GPU__
+#define __KERNEL_HIP__
+#define CCL_NAMESPACE_BEGIN
+#define CCL_NAMESPACE_END
+
+#ifndef ATTR_FALLTHROUGH
+# define ATTR_FALLTHROUGH
+#endif
+
+#ifdef __HIPCC_RTC__
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+#else
+# include <stdint.h>
+#endif
+
+#ifdef CYCLES_HIPBIN_CC
+# define FLT_MIN 1.175494350822287507969e-38f
+# define FLT_MAX 340282346638528859811704183484516925440.0f
+# define FLT_EPSILON 1.192092896e-07F
+#endif
+
+/* Qualifiers */
+
+#define ccl_device __device__ __inline__
+#define ccl_device_inline __device__ __inline__
+#define ccl_device_forceinline __device__ __forceinline__
+#define ccl_device_noinline __device__ __noinline__
+#define ccl_device_noinline_cpu ccl_device
+#define ccl_global
+#define ccl_static_constant __constant__
+#define ccl_device_constant __constant__ __device__
+#define ccl_constant const
+#define ccl_gpu_shared __shared__
+#define ccl_private
+#define ccl_may_alias
+#define ccl_addr_space
+#define ccl_restrict __restrict__
+#define ccl_loop_no_unroll
+#define ccl_align(n) __align__(n)
+#define ccl_optional_struct_init
+
+#define kernel_assert(cond)
+
+/* Types */
+#ifdef __HIP__
+# include "hip/hip_fp16.h"
+# include "hip/hip_runtime.h"
+#endif
+
+#ifdef _MSC_VER
+# include <immintrin.h>
+#endif
+
+#define ccl_gpu_thread_idx_x (threadIdx.x)
+#define ccl_gpu_block_dim_x (blockDim.x)
+#define ccl_gpu_block_idx_x (blockIdx.x)
+#define ccl_gpu_grid_dim_x (gridDim.x)
+#define ccl_gpu_warp_size (warpSize)
+
+#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x)
+#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x)
+
+/* GPU warp synchronization */
+
+#define ccl_gpu_syncthreads() __syncthreads()
+#define ccl_gpu_ballot(predicate) __ballot(predicate)
+#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down(var, detla)
+#define ccl_gpu_popc(x) __popc(x)
+
+/* GPU texture objects */
+typedef hipTextureObject_t ccl_gpu_tex_object;
+
+template<typename T>
+ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj,
+ const float x,
+ const float y)
+{
+ return tex2D<T>(texobj, x, y);
+}
+
+template<typename T>
+ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj,
+ const float x,
+ const float y,
+ const float z)
+{
+ return tex3D<T>(texobj, x, y, z);
+}
+
+/* Use fast math functions */
+
+#define cosf(x) __cosf(((float)(x)))
+#define sinf(x) __sinf(((float)(x)))
+#define powf(x, y) __powf(((float)(x)), ((float)(y)))
+#define tanf(x) __tanf(((float)(x)))
+#define logf(x) __logf(((float)(x)))
+#define expf(x) __expf(((float)(x)))
+
+/* Types */
+
+#include "util/util_half.h"
+#include "util/util_types.h"
diff --git a/intern/cycles/kernel/device/hip/config.h b/intern/cycles/kernel/device/hip/config.h
new file mode 100644
index 00000000000..2fde0d46015
--- /dev/null
+++ b/intern/cycles/kernel/device/hip/config.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Device data taken from HIP occupancy calculator.
+ *
+ * Terminology
+ * - HIP GPUs have multiple streaming multiprocessors
+ * - Each multiprocessor executes multiple thread blocks
+ * - Each thread block contains a number of threads, also known as the block size
+ * - Multiprocessors have a fixed number of registers, and the amount of registers
+ * used by each threads limits the number of threads per block.
+ */
+
+/* Launch Bound Definitions */
+#define GPU_MULTIPRESSOR_MAX_REGISTERS 65536
+#define GPU_MULTIPROCESSOR_MAX_BLOCKS 64
+#define GPU_BLOCK_MAX_THREADS 1024
+#define GPU_THREAD_MAX_REGISTERS 255
+
+#define GPU_KERNEL_BLOCK_NUM_THREADS 1024
+#define GPU_KERNEL_MAX_REGISTERS 64
+
+/* Compute number of threads per block and minimum blocks per multiprocessor
+ * given the maximum number of registers per thread. */
+
+#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \
+ extern "C" __global__ void __launch_bounds__(block_num_threads, \
+ GPU_MULTIPRESSOR_MAX_REGISTERS / \
+ (block_num_threads * thread_num_registers))
+
+/* sanity checks */
+
+#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS
+# error "Maximum number of threads per block exceeded"
+#endif
+
+#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \
+ GPU_MULTIPROCESSOR_MAX_BLOCKS
+# error "Maximum number of blocks per multiprocessor exceeded"
+#endif
+
+#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS
+# error "Maximum number of registers per thread exceeded"
+#endif
diff --git a/intern/cycles/kernel/device/hip/globals.h b/intern/cycles/kernel/device/hip/globals.h
new file mode 100644
index 00000000000..39978ae7899
--- /dev/null
+++ b/intern/cycles/kernel/device/hip/globals.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Constant Globals */
+
+#pragma once
+
+#include "kernel/kernel_profiling.h"
+#include "kernel/kernel_types.h"
+
+#include "kernel/integrator/integrator_state.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Not actually used, just a NULL pointer that gets passed everywhere, which we
+ * hope gets optimized out by the compiler. */
+struct KernelGlobals {
+ /* NOTE: Keep the size in sync with SHADOW_STACK_MAX_HITS. */
+ int unused[1];
+};
+
+/* Global scene data and textures */
+__constant__ KernelData __data;
+#define KERNEL_TEX(type, name) __attribute__((used)) const __constant__ __device__ type *name;
+#include "kernel/kernel_textures.h"
+
+/* Integrator state */
+__constant__ IntegratorStateGPU __integrator_state;
+
+/* Abstraction macros */
+#define kernel_data __data
+#define kernel_tex_fetch(t, index) t[(index)]
+#define kernel_tex_array(t) (t)
+#define kernel_integrator_state __integrator_state
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/device/hip/kernel.cpp b/intern/cycles/kernel/device/hip/kernel.cpp
new file mode 100644
index 00000000000..c801320a2e1
--- /dev/null
+++ b/intern/cycles/kernel/device/hip/kernel.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* HIP kernel entry points */
+
+#ifdef __HIP_DEVICE_COMPILE__
+
+# include "kernel/device/hip/compat.h"
+# include "kernel/device/hip/config.h"
+# include "kernel/device/hip/globals.h"
+
+# include "kernel/device/gpu/image.h"
+# include "kernel/device/gpu/kernel.h"
+
+#endif
diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu
index c1e36febfc0..7a79e0c4823 100644
--- a/intern/cycles/kernel/device/optix/kernel.cu
+++ b/intern/cycles/kernel/device/optix/kernel.cu
@@ -19,7 +19,7 @@
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
-#include "kernel/device/gpu/image.h" // Texture lookup uses normal CUDA intrinsics
+#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */
#include "kernel/integrator/integrator_state.h"
#include "kernel/integrator/integrator_state_flow.h"
@@ -44,18 +44,18 @@ template<typename T> ccl_device_forceinline T *get_payload_ptr_2()
template<bool always = false> ccl_device_forceinline uint get_object_id()
{
#ifdef __OBJECT_MOTION__
- // Always get the the instance ID from the TLAS
- // There might be a motion transform node between TLAS and BLAS which does not have one
+ /* Always get the the instance ID from the TLAS.
+ * There might be a motion transform node between TLAS and BLAS which does not have one. */
uint object = optixGetInstanceIdFromHandle(optixGetTransformListHandle(0));
#else
uint object = optixGetInstanceId();
#endif
- // Choose between always returning object ID or only for instances
+ /* Choose between always returning object ID or only for instances. */
if (always || (object & 1) == 0)
- // Can just remove the low bit since instance always contains object ID
+ /* Can just remove the low bit since instance always contains object ID. */
return object >> 1;
else
- // Set to OBJECT_NONE if this is not an instanced object
+ /* Set to OBJECT_NONE if this is not an instanced object. */
return OBJECT_NONE;
}
@@ -93,23 +93,30 @@ extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_st
extern "C" __global__ void __miss__kernel_optix_miss()
{
- // 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss
+ /* 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss. */
optixSetPayload_0(__float_as_uint(optixGetRayTmax()));
optixSetPayload_5(PRIMITIVE_NONE);
}
extern "C" __global__ void __anyhit__kernel_optix_local_hit()
{
+#ifdef __HAIR__
+ if (!optixIsTriangleHit()) {
+ /* Ignore curves. */
+ return optixIgnoreIntersection();
+ }
+#endif
+
#ifdef __BVH_LOCAL__
const uint object = get_object_id<true>();
if (object != optixGetPayload_4() /* local_object */) {
- // Only intersect with matching object
+ /* Only intersect with matching object. */
return optixIgnoreIntersection();
}
const uint max_hits = optixGetPayload_5();
if (max_hits == 0) {
- // Special case for when no hit information is requested, just report that something was hit
+ /* Special case for when no hit information is requested, just report that something was hit */
optixSetPayload_5(true);
return optixTerminateRay();
}
@@ -136,8 +143,9 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
}
else {
if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) {
- // Record closest intersection only
- // Do not terminate ray here, since there is no guarantee about distance ordering in any-hit
+ /* Record closest intersection only.
+ * Do not terminate ray here, since there is no guarantee about distance ordering in any-hit.
+ */
return optixIgnoreIntersection();
}
@@ -154,14 +162,14 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
isect->u = 1.0f - barycentrics.y - barycentrics.x;
isect->v = barycentrics.x;
- // Record geometric normal
+ /* Record geometric normal. */
const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim);
const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0));
const float3 tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1));
const float3 tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2));
local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
- // Continue tracing (without this the trace call would return after the first hit)
+ /* Continue tracing (without this the trace call would return after the first hit). */
optixIgnoreIntersection();
#endif
}
@@ -190,7 +198,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
u = __uint_as_float(optixGetAttribute_0());
v = __uint_as_float(optixGetAttribute_1());
- // Filter out curve endcaps
+ /* Filter out curve endcaps. */
if (u == 0.0f || u == 1.0f) {
ignore_intersection = true;
}
@@ -241,10 +249,10 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
isect->type = kernel_tex_fetch(__prim_type, prim);
# ifdef __TRANSPARENT_SHADOWS__
- // Detect if this surface has a shader with transparent shadows
+ /* Detect if this surface has a shader with transparent shadows. */
if (!shader_transparent_shadow(NULL, isect) || max_hits == 0) {
# endif
- // If no transparent shadows, all light is blocked and we can stop immediately
+ /* If no transparent shadows, all light is blocked and we can stop immediately. */
optixSetPayload_5(true);
return optixTerminateRay();
# ifdef __TRANSPARENT_SHADOWS__
@@ -252,24 +260,39 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
# endif
}
- // Continue tracing
+ /* Continue tracing. */
optixIgnoreIntersection();
#endif
}
-extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
+extern "C" __global__ void __anyhit__kernel_optix_volume_test()
{
- uint visibility = optixGetPayload_4();
+#ifdef __HAIR__
+ if (!optixIsTriangleHit()) {
+ /* Ignore curves. */
+ return optixIgnoreIntersection();
+ }
+#endif
+
#ifdef __VISIBILITY_FLAG__
const uint prim = optixGetPrimitiveIndex();
+ const uint visibility = optixGetPayload_4();
if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) {
return optixIgnoreIntersection();
}
#endif
+ const uint object = get_object_id<true>();
+ if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) {
+ return optixIgnoreIntersection();
+ }
+}
+
+extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
+{
#ifdef __HAIR__
if (!optixIsTriangleHit()) {
- // Filter out curve endcaps
+ /* Filter out curve endcaps. */
const float u = __uint_as_float(optixGetAttribute_0());
if (u == 0.0f || u == 1.0f) {
return optixIgnoreIntersection();
@@ -277,18 +300,26 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
}
#endif
- // Shadow ray early termination
+#ifdef __VISIBILITY_FLAG__
+ const uint prim = optixGetPrimitiveIndex();
+ const uint visibility = optixGetPayload_4();
+ if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) {
+ return optixIgnoreIntersection();
+ }
+
+ /* Shadow ray early termination. */
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
return optixTerminateRay();
}
+#endif
}
extern "C" __global__ void __closesthit__kernel_optix_hit()
{
- optixSetPayload_0(__float_as_uint(optixGetRayTmax())); // Intersection distance
+ optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */
optixSetPayload_3(optixGetPrimitiveIndex());
optixSetPayload_4(get_object_id());
- // Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index
+ /* Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index. */
optixSetPayload_5(kernel_tex_fetch(__prim_type, optixGetPrimitiveIndex()));
if (optixIsTriangleHit()) {
@@ -297,7 +328,7 @@ extern "C" __global__ void __closesthit__kernel_optix_hit()
optixSetPayload_2(__float_as_uint(barycentrics.x));
}
else {
- optixSetPayload_1(optixGetAttribute_0()); // Same as 'optixGetCurveParameter()'
+ optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */
optixSetPayload_2(optixGetAttribute_1());
}
}
@@ -311,7 +342,7 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type
float3 P = optixGetObjectRayOrigin();
float3 dir = optixGetObjectRayDirection();
- // The direction is not normalized by default, but the curve intersection routine expects that
+ /* The direction is not normalized by default, but the curve intersection routine expects that */
float len;
dir = normalize_len(dir, &len);
@@ -323,15 +354,15 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type
Intersection isect;
isect.t = optixGetRayTmax();
- // Transform maximum distance into object space
+ /* Transform maximum distance into object space. */
if (isect.t != FLT_MAX)
isect.t *= len;
if (curve_intersect(NULL, &isect, P, dir, isect.t, visibility, object, prim, time, type)) {
optixReportIntersection(isect.t / len,
type & PRIMITIVE_ALL,
- __float_as_int(isect.u), // Attribute_0
- __float_as_int(isect.v)); // Attribute_1
+ __float_as_int(isect.u), /* Attribute_0 */
+ __float_as_int(isect.v)); /* Attribute_1 */
}
}
diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h
index 213f3e62ee0..a068e93790a 100644
--- a/intern/cycles/kernel/geom/geom_curve_intersect.h
+++ b/intern/cycles/kernel/geom/geom_curve_intersect.h
@@ -713,7 +713,7 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg,
P = transform_point(&tfm, P);
D = transform_direction(&tfm, D * t);
- D = normalize_len(D, &t);
+ D = safe_normalize_len(D, &t);
}
int prim = kernel_tex_fetch(__prim_index, isect_prim);
@@ -764,8 +764,10 @@ ccl_device_inline void curve_shader_setup(const KernelGlobals *kg,
/* Thick curves, compute normal using direction from inside the curve.
* This could be optimized by recording the normal in the intersection,
* however for Optix this would go beyond the size of the payload. */
+ /* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small
+ * radius). In this case use the view direction to approximate the normal. */
const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, sd->u));
- const float3 Ng = normalize(P - P_inside);
+ const float3 Ng = (!isequal_float3(P, P_inside)) ? normalize(P - P_inside) : -sd->I;
sd->N = Ng;
sd->Ng = Ng;
diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h
index eb4a39e062b..239bd0a37b2 100644
--- a/intern/cycles/kernel/geom/geom_motion_triangle.h
+++ b/intern/cycles/kernel/geom/geom_motion_triangle.h
@@ -41,7 +41,18 @@ ccl_device_inline int find_attribute_motion(const KernelGlobals *kg,
uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
while (attr_map.x != id) {
- attr_offset += ATTR_PRIM_TYPES;
+ if (UNLIKELY(attr_map.x == ATTR_STD_NONE)) {
+ if (UNLIKELY(attr_map.y == 0)) {
+ return (int)ATTR_STD_NOT_FOUND;
+ }
+ else {
+ /* Chain jump to a different part of the table. */
+ attr_offset = attr_map.z;
+ }
+ }
+ else {
+ attr_offset += ATTR_PRIM_TYPES;
+ }
attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
}
diff --git a/intern/cycles/kernel/integrator/integrator_shade_surface.h b/intern/cycles/kernel/integrator/integrator_shade_surface.h
index 73b7cad32be..a24473addcc 100644
--- a/intern/cycles/kernel/integrator/integrator_shade_surface.h
+++ b/intern/cycles/kernel/integrator/integrator_shade_surface.h
@@ -365,19 +365,16 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS,
#ifdef __VOLUME__
if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
+ const int path_flag = INTEGRATOR_STATE(path, flag);
- {
- const int path_flag = INTEGRATOR_STATE(path, flag);
#ifdef __SUBSURFACE__
- /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
- if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
+ /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
+ if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
#endif
- {
- /* Evaluate shader. */
- PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL);
- shader_eval_surface<node_feature_mask>(
- INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag);
- }
+ {
+ /* Evaluate shader. */
+ PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL);
+ shader_eval_surface<node_feature_mask>(INTEGRATOR_STATE_PASS, &sd, render_buffer, path_flag);
}
#ifdef __SUBSURFACE__
@@ -417,17 +414,20 @@ ccl_device bool integrate_surface(INTEGRATOR_STATE_ARGS,
/* Perform path termination. Most paths have already been terminated in
* the intersect_closest kernel, this is just for emission and for dividing
- * throughput by the probability at the right moment. */
- const int path_flag = INTEGRATOR_STATE(path, flag);
- const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
- 0.0f :
- path_state_continuation_probability(INTEGRATOR_STATE_PASS,
- path_flag);
- if (probability == 0.0f) {
- return false;
- }
- else if (probability != 1.0f) {
- INTEGRATOR_STATE_WRITE(path, throughput) /= probability;
+ * throughput by the probability at the right moment.
+ *
+ * Also ensure we don't do it twice for SSS at both the entry and exit point. */
+ if (!(path_flag & PATH_RAY_SUBSURFACE)) {
+ const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
+ 0.0f :
+ path_state_continuation_probability(INTEGRATOR_STATE_PASS,
+ path_flag);
+ if (probability == 0.0f) {
+ return false;
+ }
+ else if (probability != 1.0f) {
+ INTEGRATOR_STATE_WRITE(path, throughput) /= probability;
+ }
}
#ifdef __DENOISING_FEATURES__
diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h
index 095a28ac505..dac3efb3996 100644
--- a/intern/cycles/kernel/integrator/integrator_shade_volume.h
+++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h
@@ -74,7 +74,7 @@ ccl_device_inline bool shadow_volume_shader_sample(INTEGRATOR_STATE_ARGS,
ShaderData *ccl_restrict sd,
float3 *ccl_restrict extinction)
{
- shader_eval_volume(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) {
+ shader_eval_volume<true>(INTEGRATOR_STATE_PASS, sd, PATH_RAY_SHADOW, [=](const int i) {
return integrator_state_read_shadow_volume_stack(INTEGRATOR_STATE_PASS, i);
});
@@ -93,7 +93,7 @@ ccl_device_inline bool volume_shader_sample(INTEGRATOR_STATE_ARGS,
VolumeShaderCoefficients *coeff)
{
const int path_flag = INTEGRATOR_STATE(path, flag);
- shader_eval_volume(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) {
+ shader_eval_volume<false>(INTEGRATOR_STATE_PASS, sd, path_flag, [=](const int i) {
return integrator_state_read_volume_stack(INTEGRATOR_STATE_PASS, i);
});
diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h
index 094446be02c..f745ad3f4b9 100644
--- a/intern/cycles/kernel/integrator/integrator_state.h
+++ b/intern/cycles/kernel/integrator/integrator_state.h
@@ -60,7 +60,15 @@ CCL_NAMESPACE_BEGIN
* TODO: these could be made dynamic depending on the features used in the scene. */
#define INTEGRATOR_VOLUME_STACK_SIZE VOLUME_STACK_SIZE
-#define INTEGRATOR_SHADOW_ISECT_SIZE 4
+
+#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024
+#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4
+
+#ifdef __KERNEL_CPU__
+# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU
+#else
+# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU
+#endif
/* Data structures */
@@ -74,9 +82,9 @@ typedef struct IntegratorStateCPU {
#define KERNEL_STRUCT_END(name) \
} \
name;
-#define KERNEL_STRUCT_END_ARRAY(name, size) \
+#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \
} \
- name[size];
+ name[cpu_size];
#include "kernel/integrator/integrator_state_template.h"
#undef KERNEL_STRUCT_BEGIN
#undef KERNEL_STRUCT_MEMBER
@@ -103,9 +111,9 @@ typedef struct IntegratorStateGPU {
#define KERNEL_STRUCT_END(name) \
} \
name;
-#define KERNEL_STRUCT_END_ARRAY(name, size) \
+#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \
} \
- name[size];
+ name[gpu_size];
#include "kernel/integrator/integrator_state_template.h"
#undef KERNEL_STRUCT_BEGIN
#undef KERNEL_STRUCT_MEMBER
diff --git a/intern/cycles/kernel/integrator/integrator_state_template.h b/intern/cycles/kernel/integrator/integrator_state_template.h
index 41dd1bfcdbf..0d8126c64aa 100644
--- a/intern/cycles/kernel/integrator/integrator_state_template.h
+++ b/intern/cycles/kernel/integrator/integrator_state_template.h
@@ -107,7 +107,7 @@ KERNEL_STRUCT_END(subsurface)
KERNEL_STRUCT_BEGIN(volume_stack)
KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME)
-KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE)
+KERNEL_STRUCT_END_ARRAY(volume_stack, INTEGRATOR_VOLUME_STACK_SIZE, INTEGRATOR_VOLUME_STACK_SIZE)
/********************************* Shadow Path State **************************/
@@ -153,11 +153,15 @@ KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACIN
KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING)
/* TODO: exclude for GPU. */
KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float3, Ng, KERNEL_FEATURE_PATH_TRACING)
-KERNEL_STRUCT_END_ARRAY(shadow_isect, INTEGRATOR_SHADOW_ISECT_SIZE)
+KERNEL_STRUCT_END_ARRAY(shadow_isect,
+ INTEGRATOR_SHADOW_ISECT_SIZE_CPU,
+ INTEGRATOR_SHADOW_ISECT_SIZE_GPU)
/**************************** Shadow Volume Stack *****************************/
KERNEL_STRUCT_BEGIN(shadow_volume_stack)
KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME)
-KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, INTEGRATOR_VOLUME_STACK_SIZE)
+KERNEL_STRUCT_END_ARRAY(shadow_volume_stack,
+ INTEGRATOR_VOLUME_STACK_SIZE,
+ INTEGRATOR_VOLUME_STACK_SIZE)
diff --git a/intern/cycles/kernel/integrator/integrator_state_util.h b/intern/cycles/kernel/integrator/integrator_state_util.h
index cdf412fe22f..08d6cb00114 100644
--- a/intern/cycles/kernel/integrator/integrator_state_util.h
+++ b/intern/cycles/kernel/integrator/integrator_state_util.h
@@ -217,10 +217,10 @@ ccl_device_inline void integrator_state_copy_only(const IntegratorState to_state
while (false) \
;
-# define KERNEL_STRUCT_END_ARRAY(name, array_size) \
+# define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \
++index; \
} \
- while (index < array_size) \
+ while (index < gpu_array_size) \
;
# include "kernel/integrator/integrator_state_template.h"
@@ -264,7 +264,12 @@ ccl_device_inline void integrator_state_shadow_catcher_split(INTEGRATOR_STATE_AR
IntegratorStateCPU *ccl_restrict split_state = state + 1;
- *split_state = *state;
+ /* Only copy the required subset, since shadow intersections are big and irrelevant here. */
+ split_state->path = state->path;
+ split_state->ray = state->ray;
+ split_state->isect = state->isect;
+ memcpy(split_state->volume_stack, state->volume_stack, sizeof(state->volume_stack));
+ split_state->shadow_path = state->shadow_path;
split_state->path.flag |= PATH_RAY_SHADOW_CATCHER_PASS;
#endif
diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h
index 9e12d24dcf4..f4d00e4c20c 100644
--- a/intern/cycles/kernel/kernel_accumulate.h
+++ b/intern/cycles/kernel/kernel_accumulate.h
@@ -386,7 +386,7 @@ ccl_device_inline void kernel_accum_light(INTEGRATOR_STATE_CONST_ARGS,
{
/* The throughput for shadow paths already contains the light shader evaluation. */
float3 contribution = INTEGRATOR_STATE(shadow_path, throughput);
- kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce) - 1);
+ kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(shadow_path, bounce));
ccl_global float *buffer = kernel_accum_pixel_render_buffer(INTEGRATOR_STATE_PASS,
render_buffer);
diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h
index e025bcd6674..abb1ba455e6 100644
--- a/intern/cycles/kernel/kernel_bake.h
+++ b/intern/cycles/kernel/kernel_bake.h
@@ -42,6 +42,16 @@ ccl_device void kernel_displace_evaluate(const KernelGlobals *kg,
object_inverse_dir_transform(kg, &sd, &D);
+#ifdef __KERNEL_DEBUG_NAN__
+ if (!isfinite3_safe(D)) {
+ kernel_assert(!"Cycles displacement with non-finite value detected");
+ }
+#endif
+
+ /* Ensure finite displacement, preventing BVH from becoming degenerate and avoiding possible
+ * traversal issues caused by non-finite math. */
+ D = ensure_finite3(D);
+
/* Write output. */
output[offset] += make_float4(D.x, D.y, D.z, 0.0f);
}
@@ -66,7 +76,16 @@ ccl_device void kernel_background_evaluate(const KernelGlobals *kg,
const int path_flag = PATH_RAY_EMISSION;
shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT>(
INTEGRATOR_STATE_PASS_NULL, &sd, NULL, path_flag);
- const float3 color = shader_background_eval(&sd);
+ float3 color = shader_background_eval(&sd);
+
+#ifdef __KERNEL_DEBUG_NAN__
+ if (!isfinite3_safe(color)) {
+ kernel_assert(!"Cycles background with non-finite value detected");
+ }
+#endif
+
+ /* Ensure finite color, avoiding possible numerical instabilities in the path tracing kernels. */
+ color = ensure_finite3(color);
/* Write output. */
output[offset] += make_float4(color.x, color.y, color.z, 0.0f);
diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h
index 715d764fb31..e8f4a21878e 100644
--- a/intern/cycles/kernel/kernel_film.h
+++ b/intern/cycles/kernel/kernel_film.h
@@ -394,7 +394,7 @@ film_calculate_shadow_catcher(const KernelFilmConvert *ccl_restrict kfilm_conver
/* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual
* shadow catcher objects in the scene. In this case there will be no auxiliary passes required
- * for the devision (to save up memory). So delay the asserts to this point so that the number of
+ * for the decision (to save up memory). So delay the asserts to this point so that the number of
* samples check handles such configuration. */
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
kernel_assert(kfilm_convert->pass_combined != PASS_UNUSED);
diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h
index 354e8115538..1beaf3cc2b2 100644
--- a/intern/cycles/kernel/kernel_jitter.h
+++ b/intern/cycles/kernel/kernel_jitter.h
@@ -74,10 +74,6 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p)
ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_hash, uint dimension)
{
- /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D
- * the x part is used as the sample (TODO(@leesonw): Add using both x and y parts
- * independently). */
-
/* Perform Owen shuffle of the sample number to reorder the samples. */
#ifdef _SIMPLE_HASH_
const uint rv = cmj_hash_simple(dimension, rng_hash);
@@ -95,7 +91,10 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha
const uint sample_set = s / NUM_PMJ_SAMPLES;
const uint d = (dimension + sample_set);
const uint dim = d % NUM_PMJ_PATTERNS;
- int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES));
+
+ /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D
+ * the x part is used for even dims and the y for odd. */
+ int index = 2 * ((dim >> 1) * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)) + (dim & 1);
float fx = kernel_tex_fetch(__sample_pattern_lut, index);
@@ -104,12 +103,11 @@ ccl_device float pmj_sample_1D(const KernelGlobals *kg, uint sample, uint rng_ha
# ifdef _SIMPLE_HASH_
float dx = cmj_randfloat_simple(d, rng_hash);
# else
- /* Only jitter within the grid interval. */
float dx = cmj_randfloat(d, rng_hash);
# endif
- fx = fx + dx * (1.0f / NUM_PMJ_SAMPLES);
+ /* Jitter sample locations and map back into [0 1]. */
+ fx = fx + dx;
fx = fx - floorf(fx);
-
#else
# warning "Not using Cranley-Patterson Rotation."
#endif
@@ -136,7 +134,7 @@ ccl_device void pmj_sample_2D(
/* Based on the sample number a sample pattern is selected and offset by the dimension. */
const uint sample_set = s / NUM_PMJ_SAMPLES;
const uint d = (dimension + sample_set);
- const uint dim = d % NUM_PMJ_PATTERNS;
+ uint dim = d % NUM_PMJ_PATTERNS;
int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES));
float fx = kernel_tex_fetch(__sample_pattern_lut, index);
@@ -151,17 +149,17 @@ ccl_device void pmj_sample_2D(
float dx = cmj_randfloat(d, rng_hash);
float dy = cmj_randfloat(d + 1, rng_hash);
# endif
- /* Only jitter within the grid cells. */
- fx = fx + dx * (1.0f / NUM_PMJ_DIVISIONS);
- fy = fy + dy * (1.0f / NUM_PMJ_DIVISIONS);
- fx = fx - floorf(fx);
- fy = fy - floorf(fy);
+ /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */
+ float sx = fx + dx;
+ float sy = fy + dy;
+ sx = sx - floorf(sx);
+ sy = sy - floorf(sy);
#else
# warning "Not using Cranley Patterson Rotation."
#endif
- (*x) = fx;
- (*y) = fy;
+ (*x) = sx;
+ (*y) = sy;
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 3052bb53040..e7133724c85 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -186,8 +186,8 @@ ccl_device_inline float _shader_bsdf_multi_eval(const KernelGlobals *kg,
float sum_sample_weight,
const uint light_shader_flags)
{
- /* this is the veach one-sample model with balance heuristic, some pdf
- * factors drop out when using balance heuristic weighting */
+ /* This is the veach one-sample model with balance heuristic,
+ * some PDF factors drop out when using balance heuristic weighting. */
for (int i = 0; i < sd->num_closure; i++) {
const ShaderClosure *sc = &sd->closure[i];
@@ -750,7 +750,7 @@ ccl_device int shader_phase_sample_closure(const KernelGlobals *kg,
/* Volume Evaluation */
-template<typename StackReadOp>
+template<const bool shadow, typename StackReadOp>
ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS,
ShaderData *ccl_restrict sd,
const int path_flag,
@@ -815,8 +815,11 @@ ccl_device_inline void shader_eval_volume(INTEGRATOR_STATE_CONST_ARGS,
# endif
/* Merge closures to avoid exceeding number of closures limit. */
- if (i > 0)
- shader_merge_volume_closures(sd);
+ if (!shadow) {
+ if (i > 0) {
+ shader_merge_volume_closures(sd);
+ }
+ }
}
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 66b7310ab65..3cc42bf7a85 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -572,6 +572,7 @@ typedef enum AttributeStandard {
ATTR_STD_MOTION_VERTEX_NORMAL,
ATTR_STD_PARTICLE,
ATTR_STD_CURVE_INTERCEPT,
+ ATTR_STD_CURVE_LENGTH,
ATTR_STD_CURVE_RANDOM,
ATTR_STD_PTEX_FACE_ID,
ATTR_STD_PTEX_UV,
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 396f42080e4..4fc46a255a8 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -107,6 +107,7 @@ ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
ustring OSLRenderServices::u_is_curve("geom:is_curve");
ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
+ustring OSLRenderServices::u_curve_length("geom:curve_length");
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h
index 58accb46e7d..2a5400282b3 100644
--- a/intern/cycles/kernel/osl/osl_services.h
+++ b/intern/cycles/kernel/osl/osl_services.h
@@ -294,6 +294,7 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_is_smooth;
static ustring u_is_curve;
static ustring u_curve_thickness;
+ static ustring u_curve_length;
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_path_ray_length;
diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt
index 02be7813369..6b62e7bb52f 100644
--- a/intern/cycles/kernel/shaders/CMakeLists.txt
+++ b/intern/cycles/kernel/shaders/CMakeLists.txt
@@ -41,6 +41,7 @@ set(SRC_OSL
node_vector_displacement.osl
node_emission.osl
node_environment_texture.osl
+ node_float_curve.osl
node_fresnel.osl
node_gamma.osl
node_geometry.osl
diff --git a/intern/cycles/kernel/shaders/node_float_curve.osl b/intern/cycles/kernel/shaders/node_float_curve.osl
new file mode 100644
index 00000000000..f1f05fd88a9
--- /dev/null
+++ b/intern/cycles/kernel/shaders/node_float_curve.osl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011-2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "node_ramp_util.h"
+#include "stdcycles.h"
+
+shader node_float_curve(float ramp[] = {0.0},
+ float min_x = 0.0,
+ float max_x = 1.0,
+ float ValueIn = 0.0,
+ float Factor = 0.0,
+ output float ValueOut = 0.0)
+{
+ float c = (ValueIn - min_x) / (max_x - min_x);
+
+ ValueOut = rgb_ramp_lookup(ramp, c, 1, 1);
+
+ ValueOut = mix(ValueIn, ValueOut, Factor);
+}
diff --git a/intern/cycles/kernel/shaders/node_hair_info.osl b/intern/cycles/kernel/shaders/node_hair_info.osl
index ee08ea57e68..ddc2e28b83a 100644
--- a/intern/cycles/kernel/shaders/node_hair_info.osl
+++ b/intern/cycles/kernel/shaders/node_hair_info.osl
@@ -18,12 +18,14 @@
shader node_hair_info(output float IsStrand = 0.0,
output float Intercept = 0.0,
+ output float Length = 0.0,
output float Thickness = 0.0,
output normal TangentNormal = N,
output float Random = 0)
{
getattribute("geom:is_curve", IsStrand);
getattribute("geom:curve_intercept", Intercept);
+ getattribute("geom:curve_length", Length);
getattribute("geom:curve_thickness", Thickness);
getattribute("geom:curve_tangent_normal", TangentNormal);
getattribute("geom:curve_random", Random);
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 4aee1ef11b3..ad609b15f86 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -493,11 +493,13 @@ ccl_device void svm_eval_nodes(INTEGRATOR_STATE_CONST_ARGS,
case NODE_IES:
svm_node_ies(kg, sd, stack, node);
break;
-
case NODE_RGB_CURVES:
case NODE_VECTOR_CURVES:
offset = svm_node_curves(kg, sd, stack, node, offset);
break;
+ case NODE_FLOAT_CURVE:
+ offset = svm_node_curve(kg, sd, stack, node, offset);
+ break;
case NODE_TANGENT:
svm_node_tangent(kg, sd, stack, node);
break;
diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h
index 10e9f291d0e..432529eb061 100644
--- a/intern/cycles/kernel/svm/svm_geometry.h
+++ b/intern/cycles/kernel/svm/svm_geometry.h
@@ -213,6 +213,8 @@ ccl_device_noinline void svm_node_hair_info(
}
case NODE_INFO_CURVE_INTERCEPT:
break; /* handled as attribute */
+ case NODE_INFO_CURVE_LENGTH:
+ break; /* handled as attribute */
case NODE_INFO_CURVE_RANDOM:
break; /* handled as attribute */
case NODE_INFO_CURVE_THICKNESS: {
diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h
index e92df3c093c..563e5bcb5e4 100644
--- a/intern/cycles/kernel/svm/svm_ramp.h
+++ b/intern/cycles/kernel/svm/svm_ramp.h
@@ -21,6 +21,48 @@ CCL_NAMESPACE_BEGIN
/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
+ccl_device_inline float fetch_float(const KernelGlobals *kg, int offset)
+{
+ uint4 node = kernel_tex_fetch(__svm_nodes, offset);
+ return __uint_as_float(node.x);
+}
+
+ccl_device_inline float float_ramp_lookup(const KernelGlobals *kg,
+ int offset,
+ float f,
+ bool interpolate,
+ bool extrapolate,
+ int table_size)
+{
+ if ((f < 0.0f || f > 1.0f) && extrapolate) {
+ float t0, dy;
+ if (f < 0.0f) {
+ t0 = fetch_float(kg, offset);
+ dy = t0 - fetch_float(kg, offset + 1);
+ f = -f;
+ }
+ else {
+ t0 = fetch_float(kg, offset + table_size - 1);
+ dy = t0 - fetch_float(kg, offset + table_size - 2);
+ f = f - 1.0f;
+ }
+ return t0 + dy * f * (table_size - 1);
+ }
+
+ f = saturate(f) * (table_size - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = clamp(float_to_int(f), 0, table_size - 1);
+ float t = f - (float)i;
+
+ float a = fetch_float(kg, offset + i);
+
+ if (interpolate && t > 0.0f)
+ a = (1.0f - t) * a + t * fetch_float(kg, offset + i + 1);
+
+ return a;
+}
+
ccl_device_inline float4 rgb_ramp_lookup(const KernelGlobals *kg,
int offset,
float f,
@@ -105,6 +147,30 @@ ccl_device_noinline int svm_node_curves(
return offset;
}
+ccl_device_noinline int svm_node_curve(
+ const KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int offset)
+{
+ uint fac_offset, value_in_offset, out_offset;
+ svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset);
+
+ uint table_size = read_node(kg, &offset).x;
+
+ float fac = stack_load_float(stack, fac_offset);
+ float in = stack_load_float(stack, value_in_offset);
+
+ const float min = __int_as_float(node.z), max = __int_as_float(node.w);
+ const float range = max - min;
+ const float relpos = (in - min) / range;
+
+ float v = float_ramp_lookup(kg, offset, relpos, true, true, table_size);
+
+ in = (1.0f - fac) * in + fac * v;
+ stack_store_float(stack, out_offset, in);
+
+ offset += table_size;
+ return offset;
+}
+
CCL_NAMESPACE_END
#endif /* __SVM_RAMP_H__ */
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index c053be96c51..59a0e33acbc 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -122,6 +122,7 @@ typedef enum ShaderNodeType {
NODE_AOV_START,
NODE_AOV_COLOR,
NODE_AOV_VALUE,
+ NODE_FLOAT_CURVE,
/* NOTE: for best OpenCL performance, item definition in the enum must
* match the switch case order in svm.h. */
} ShaderNodeType;
@@ -173,6 +174,7 @@ typedef enum NodeParticleInfo {
typedef enum NodeHairInfo {
NODE_INFO_CURVE_IS_STRAND,
NODE_INFO_CURVE_INTERCEPT,
+ NODE_INFO_CURVE_LENGTH,
NODE_INFO_CURVE_THICKNESS,
/* Fade for minimum hair width transiency. */
// NODE_INFO_CURVE_FADE,
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 6edb5261b32..323222b8c85 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -35,7 +35,6 @@ set(SRC
denoising.cpp
film.cpp
geometry.cpp
- gpu_display.cpp
graph.cpp
hair.cpp
image.cpp
@@ -78,9 +77,10 @@ set(SRC_HEADERS
colorspace.h
constant_fold.h
denoising.h
+ display_driver.h
+ output_driver.h
film.h
geometry.h
- gpu_display.h
graph.h
hair.h
image.h
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index ea5a5f50f2d..aaf21ad9fd2 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -342,6 +342,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "particle";
case ATTR_STD_CURVE_INTERCEPT:
return "curve_intercept";
+ case ATTR_STD_CURVE_LENGTH:
+ return "curve_length";
case ATTR_STD_CURVE_RANDOM:
return "curve_random";
case ATTR_STD_PTEX_FACE_ID:
@@ -586,6 +588,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_CURVE_INTERCEPT:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY);
break;
+ case ATTR_STD_CURVE_LENGTH:
+ attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
+ break;
case ATTR_STD_CURVE_RANDOM:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
break;
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index 1882510cd70..3682b55049a 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -22,7 +22,6 @@
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_time.h"
#include "util/util_types.h"
diff --git a/intern/cycles/render/display_driver.h b/intern/cycles/render/display_driver.h
new file mode 100644
index 00000000000..85f305034d7
--- /dev/null
+++ b/intern/cycles/render/display_driver.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "util/util_half.h"
+#include "util/util_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Display driver for efficient interactive display of renders.
+ *
+ * Host applications implement this interface for viewport rendering. For best performance, we
+ * recommend:
+ * - Allocating a texture on the GPU to be interactively updated
+ * - Using the graphics interop mechanism to avoid CPU-GPU copying overhead
+ * - Using a dedicated or thread-safe graphics API context for updates, to avoid
+ * blocking the host application.
+ */
+class DisplayDriver {
+ public:
+ DisplayDriver() = default;
+ virtual ~DisplayDriver() = default;
+
+ /* Render buffer parameters. */
+ struct Params {
+ public:
+ /* Render resolution, ignoring progressive resolution changes.
+ * The texture buffer should be allocated with this size. */
+ int2 size = make_int2(0, 0);
+
+ /* For border rendering, the full resolution of the render, and the offset within that larger
+ * render. */
+ int2 full_size = make_int2(0, 0);
+ int2 full_offset = make_int2(0, 0);
+
+ bool modified(const Params &other) const
+ {
+ return !(full_offset == other.full_offset && full_size == other.full_size &&
+ size == other.size);
+ }
+ };
+
+ /* Update the render from the rendering thread.
+ *
+ * Cycles periodically updates the render to be displayed. For multithreaded updates with
+ * potentially multiple rendering devices, it will call these methods as follows.
+ *
+ * if (driver.update_begin(params, width, height)) {
+ * parallel_for_each(rendering_device) {
+ * buffer = driver.map_texture_buffer();
+ * if (buffer) {
+ * fill(buffer);
+ * driver.unmap_texture_buffer();
+ * }
+ * }
+ * driver.update_end();
+ * }
+ *
+ * The parameters may dynamically change due to camera changes in the scene, and resources should
+ * be re-allocated accordingly.
+ *
+ * The width and height passed to update_begin() are the effective render resolution taking into
+ * account progressive resolution changes, which may be equal to or smaller than the params.size.
+ * For efficiency, changes in this resolution should be handled without re-allocating resources,
+ * but rather by using a subset of the full resolution buffer. */
+ virtual bool update_begin(const Params &params, int width, int height) = 0;
+ virtual void update_end() = 0;
+
+ virtual half4 *map_texture_buffer() = 0;
+ virtual void unmap_texture_buffer() = 0;
+
+ /* Optionally return a handle to a native graphics API texture buffer. If supported,
+ * the rendering device may write directly to this buffer instead of calling
+ * map_texture_buffer() and unmap_texture_buffer(). */
+ class GraphicsInterop {
+ public:
+ /* Dimensions of the buffer, in pixels. */
+ int buffer_width = 0;
+ int buffer_height = 0;
+
+ /* OpenGL pixel buffer object. */
+ int opengl_pbo_id = 0;
+
+ /* Clear the entire buffer before doing partial write to it. */
+ bool need_clear = false;
+ };
+
+ virtual GraphicsInterop graphics_interop_get()
+ {
+ return GraphicsInterop();
+ }
+
+ /* (De)activate graphics context required for editing or deleting the graphics interop
+ * object.
+ *
+ * For example, destruction of the CUDA object associated with an OpenGL requires the
+ * OpenGL context to be active. */
+ virtual void graphics_interop_activate(){};
+ virtual void graphics_interop_deactivate(){};
+
+ /* Clear the display buffer by filling it with zeros. */
+ virtual void clear() = 0;
+
+ /* Draw the render using the native graphics API.
+ *
+ * Note that this may be called in parallel to updates. The implementation is responsible for
+ * mutex locking or other mechanisms to avoid conflicts.
+ *
+ * The parameters may have changed since the last update. The implementation is responsible for
+ * deciding to skip or adjust render display for such changes.
+ *
+ * Host application drawing the render buffer should use Session.draw(), which will
+ * call this method. */
+ virtual void draw(const Params &params) = 0;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp
index 8e14b338bd3..ad3336ca089 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -434,7 +434,8 @@ void Film::update_passes(Scene *scene, bool add_sample_count_pass)
const ObjectManager *object_manager = scene->object_manager;
Integrator *integrator = scene->integrator;
- if (!is_modified() && !object_manager->need_update() && !integrator->is_modified()) {
+ if (!is_modified() && !object_manager->need_update() && !integrator->is_modified() &&
+ !background->is_modified()) {
return;
}
diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h
index 32e108d62ca..5ad419e02ca 100644
--- a/intern/cycles/render/integrator.h
+++ b/intern/cycles/render/integrator.h
@@ -19,7 +19,7 @@
#include "kernel/kernel_types.h"
-#include "device/device_denoise.h" /* For the paramaters and type enum. */
+#include "device/device_denoise.h" /* For the parameters and type enum. */
#include "graph/node.h"
#include "integrator/adaptive_sampling.h"
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 03b79d7de3e..1629895ff6e 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -4368,6 +4368,7 @@ NODE_DEFINE(HairInfoNode)
SOCKET_OUT_FLOAT(is_strand, "Is Strand");
SOCKET_OUT_FLOAT(intercept, "Intercept");
+ SOCKET_OUT_FLOAT(size, "Length");
SOCKET_OUT_FLOAT(thickness, "Thickness");
SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal");
#if 0 /* Output for minimum hair width transparency - deactivated. */
@@ -4390,6 +4391,9 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
if (!intercept_out->links.empty())
attributes->add(ATTR_STD_CURVE_INTERCEPT);
+ if (!output("Length")->links.empty())
+ attributes->add(ATTR_STD_CURVE_LENGTH);
+
if (!output("Random")->links.empty())
attributes->add(ATTR_STD_CURVE_RANDOM);
}
@@ -4412,6 +4416,12 @@ void HairInfoNode::compile(SVMCompiler &compiler)
compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
}
+ out = output("Length");
+ if (!out->links.empty()) {
+ int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH);
+ compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
+ }
+
out = output("Thickness");
if (!out->links.empty()) {
compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out));
@@ -6372,7 +6382,7 @@ void BumpNode::constant_fold(const ConstantFolder &folder)
/* TODO(sergey): Ignore bump with zero strength. */
}
-/* Curve node */
+/* Curves node */
CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type)
{
@@ -6521,6 +6531,83 @@ void VectorCurvesNode::compile(OSLCompiler &compiler)
CurvesNode::compile(compiler, "node_vector_curves");
}
+/* FloatCurveNode */
+
+NODE_DEFINE(FloatCurveNode)
+{
+ NodeType *type = NodeType::add("float_curve", create, NodeType::SHADER);
+
+ SOCKET_FLOAT_ARRAY(curve, "Curve", array<float>());
+ SOCKET_FLOAT(min_x, "Min X", 0.0f);
+ SOCKET_FLOAT(max_x, "Max X", 1.0f);
+
+ SOCKET_IN_FLOAT(fac, "Factor", 0.0f);
+ SOCKET_IN_FLOAT(value, "Value", 0.0f);
+
+ SOCKET_OUT_FLOAT(value, "Value");
+
+ return type;
+}
+
+FloatCurveNode::FloatCurveNode() : ShaderNode(get_node_type())
+{
+}
+
+void FloatCurveNode::constant_fold(const ConstantFolder &folder)
+{
+ ShaderInput *value_in = input("Value");
+ ShaderInput *fac_in = input("Factor");
+
+ /* evaluate fully constant node */
+ if (folder.all_inputs_constant()) {
+ if (curve.size() == 0) {
+ return;
+ }
+
+ float pos = (value - min_x) / (max_x - min_x);
+ float result = float_ramp_lookup(curve.data(), pos, true, true, curve.size());
+
+ folder.make_constant(value + fac * (result - value));
+ }
+ /* remove no-op node */
+ else if (!fac_in->link && fac == 0.0f) {
+ /* link is not null because otherwise all inputs are constant */
+ folder.bypass(value_in->link);
+ }
+}
+
+void FloatCurveNode::compile(SVMCompiler &compiler)
+{
+ if (curve.size() == 0)
+ return;
+
+ ShaderInput *value_in = input("Value");
+ ShaderInput *fac_in = input("Factor");
+ ShaderOutput *value_out = output("Value");
+
+ compiler.add_node(NODE_FLOAT_CURVE,
+ compiler.encode_uchar4(compiler.stack_assign(fac_in),
+ compiler.stack_assign(value_in),
+ compiler.stack_assign(value_out)),
+ __float_as_int(min_x),
+ __float_as_int(max_x));
+
+ compiler.add_node(curve.size());
+ for (int i = 0; i < curve.size(); i++)
+ compiler.add_node(make_float4(curve[i]));
+}
+
+void FloatCurveNode::compile(OSLCompiler &compiler)
+{
+ if (curve.size() == 0)
+ return;
+
+ compiler.parameter_array("ramp", curve.data(), curve.size());
+ compiler.parameter(this, "min_x");
+ compiler.parameter(this, "max_x");
+ compiler.add(this, "node_float_curve");
+}
+
/* RGBRampNode */
NODE_DEFINE(RGBRampNode)
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 22bdb06b059..5ac72835ac5 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -1398,6 +1398,18 @@ class VectorCurvesNode : public CurvesNode {
void constant_fold(const ConstantFolder &folder);
};
+class FloatCurveNode : public ShaderNode {
+ public:
+ SHADER_NODE_CLASS(FloatCurveNode)
+ void constant_fold(const ConstantFolder &folder);
+
+ NODE_SOCKET_API_ARRAY(array<float>, curve)
+ NODE_SOCKET_API(float, min_x)
+ NODE_SOCKET_API(float, max_x)
+ NODE_SOCKET_API(float, fac)
+ NODE_SOCKET_API(float, value)
+};
+
class RGBRampNode : public ShaderNode {
public:
SHADER_NODE_CLASS(RGBRampNode)
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index d28b222c10e..5a43b641872 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -727,8 +727,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
}
}
- /* create shader of the appropriate type. OSL only distinguishes between "surface"
- * and "displacement" atm */
+ /* Create shader of the appropriate type. OSL only distinguishes between "surface"
+ * and "displacement" at the moment. */
if (current_type == SHADER_TYPE_SURFACE)
ss->Shader("surface", name, id(node).c_str());
else if (current_type == SHADER_TYPE_VOLUME)
diff --git a/intern/cycles/render/output_driver.h b/intern/cycles/render/output_driver.h
new file mode 100644
index 00000000000..b7e980d71d4
--- /dev/null
+++ b/intern/cycles/render/output_driver.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "util/util_math.h"
+#include "util/util_string.h"
+#include "util/util_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Output driver for reading render buffers.
+ *
+ * Host applications implement this interface for outputting render buffers for offline rendering.
+ * Drivers can be used to copy the buffers into the host application or write them directly to
+ * disk. This interface may also be used for interactive display, however the DisplayDriver is more
+ * efficient for that purpose.
+ */
+class OutputDriver {
+ public:
+ OutputDriver() = default;
+ virtual ~OutputDriver() = default;
+
+ class Tile {
+ public:
+ Tile(const int2 offset,
+ const int2 size,
+ const int2 full_size,
+ const string_view layer,
+ const string_view view)
+ : offset(offset), size(size), full_size(full_size), layer(layer), view(view)
+ {
+ }
+ virtual ~Tile() = default;
+
+ const int2 offset;
+ const int2 size;
+ const int2 full_size;
+ const string layer;
+ const string view;
+
+ virtual bool get_pass_pixels(const string_view pass_name,
+ const int num_channels,
+ float *pixels) const = 0;
+ virtual bool set_pass_pixels(const string_view pass_name,
+ const int num_channels,
+ const float *pixels) const = 0;
+ };
+
+ /* Write tile once it has finished rendering. */
+ virtual void write_render_tile(const Tile &tile) = 0;
+
+ /* Update tile while rendering is in progress. Return true if any update
+ * was performed. */
+ virtual bool update_render_tile(const Tile & /* tile */)
+ {
+ return false;
+ }
+
+ /* For baking, read render pass PASS_BAKE_PRIMITIVE and PASS_BAKE_DIFFERENTIAL
+ * to determine which shading points to use for baking at each pixel. Return
+ * true if any data was read. */
+ virtual bool read_render_tile(const Tile & /* tile */)
+ {
+ return false;
+ }
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 823c34ed519..550188b196a 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -25,12 +25,13 @@
#include "render/bake.h"
#include "render/buffers.h"
#include "render/camera.h"
-#include "render/gpu_display.h"
+#include "render/display_driver.h"
#include "render/graph.h"
#include "render/integrator.h"
#include "render/light.h"
#include "render/mesh.h"
#include "render/object.h"
+#include "render/output_driver.h"
#include "render/scene.h"
#include "render/session.h"
@@ -38,7 +39,6 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_task.h"
#include "util/util_time.h"
@@ -65,25 +65,6 @@ Session::Session(const SessionParams &params_, const SceneParams &scene_params)
path_trace_ = make_unique<PathTrace>(
device, scene->film, &scene->dscene, render_scheduler_, tile_manager_);
path_trace_->set_progress(&progress);
- path_trace_->tile_buffer_update_cb = [&]() {
- if (!update_render_tile_cb) {
- return;
- }
- update_render_tile_cb();
- };
- path_trace_->tile_buffer_write_cb = [&]() {
- if (!write_render_tile_cb) {
- return;
- }
- write_render_tile_cb();
- };
- path_trace_->tile_buffer_read_cb = [&]() -> bool {
- if (!read_render_tile_cb) {
- return false;
- }
- read_render_tile_cb();
- return true;
- };
path_trace_->progress_update_cb = [&]() { update_status_time(); };
tile_manager_.full_buffer_written_cb = [&](string_view filename) {
@@ -98,24 +79,6 @@ Session::~Session()
{
cancel();
- /* TODO(sergey): Bring the passes in viewport back.
- * It is unclear why there is such an exception needed though. */
-#if 0
- if (buffers && params.write_render_cb) {
- /* Copy to display buffer and write out image if requested */
- delete display;
-
- display = new DisplayBuffer(device, false);
- display->reset(buffers->params);
- copy_to_display_buffer(params.samples);
-
- int w = display->draw_width;
- int h = display->draw_height;
- uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h);
- params.write_render_cb((uchar *)pixels, w, h, 4);
- }
-#endif
-
/* Make sure path tracer is destroyed before the device. This is needed because destruction might
* need to access device for device memory free. */
/* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the
@@ -163,7 +126,7 @@ bool Session::ready_to_reset()
void Session::run_main_render_loop()
{
- path_trace_->clear_gpu_display();
+ path_trace_->clear_display();
while (true) {
RenderWork render_work = run_update_for_next_iteration();
@@ -397,8 +360,8 @@ int2 Session::get_effective_tile_size() const
/* TODO(sergey): Take available memory into account, and if there is enough memory do not tile
* and prefer optimal performance. */
-
- return make_int2(params.tile_size, params.tile_size);
+ const int tile_size = tile_manager_.compute_render_tile_size(params.tile_size);
+ return make_int2(tile_size, tile_size);
}
void Session::do_delayed_reset()
@@ -515,9 +478,33 @@ void Session::set_pause(bool pause)
}
}
-void Session::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
+void Session::set_output_driver(unique_ptr<OutputDriver> driver)
{
- path_trace_->set_gpu_display(move(gpu_display));
+ path_trace_->set_output_driver(move(driver));
+}
+
+void Session::set_display_driver(unique_ptr<DisplayDriver> driver)
+{
+ path_trace_->set_display_driver(move(driver));
+}
+
+double Session::get_estimated_remaining_time() const
+{
+ const float completed = progress.get_progress();
+ if (completed == 0.0f) {
+ return 0.0;
+ }
+
+ double total_time, render_time;
+ progress.get_time(total_time, render_time);
+ double remaining = (1.0 - (double)completed) * (render_time / (double)completed);
+
+ const double time_limit = render_scheduler_.get_time_limit();
+ if (time_limit != 0.0) {
+ remaining = min(remaining, max(time_limit - render_time, 0.0));
+ }
+
+ return remaining;
}
void Session::wait()
@@ -619,101 +606,6 @@ void Session::collect_statistics(RenderStats *render_stats)
}
/* --------------------------------------------------------------------
- * Tile and tile pixels access.
- */
-
-bool Session::has_multiple_render_tiles() const
-{
- return tile_manager_.has_multiple_tiles();
-}
-
-int2 Session::get_render_tile_size() const
-{
- return path_trace_->get_render_tile_size();
-}
-
-int2 Session::get_render_tile_offset() const
-{
- return path_trace_->get_render_tile_offset();
-}
-
-string_view Session::get_render_tile_layer() const
-{
- const BufferParams &buffer_params = path_trace_->get_render_tile_params();
- return buffer_params.layer;
-}
-
-string_view Session::get_render_tile_view() const
-{
- const BufferParams &buffer_params = path_trace_->get_render_tile_params();
- return buffer_params.view;
-}
-
-bool Session::copy_render_tile_from_device()
-{
- return path_trace_->copy_render_tile_from_device();
-}
-
-bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels)
-{
- /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
- * is happening while this function runs. */
-
- const BufferParams &buffer_params = path_trace_->get_render_tile_params();
-
- const BufferPass *pass = buffer_params.find_pass(pass_name);
- if (pass == nullptr) {
- return false;
- }
-
- const bool has_denoised_result = path_trace_->has_denoised_result();
- if (pass->mode == PassMode::DENOISED && !has_denoised_result) {
- pass = buffer_params.find_pass(pass->type);
- if (pass == nullptr) {
- /* Happens when denoised result pass is requested but is never written by the kernel. */
- return false;
- }
- }
-
- pass = buffer_params.get_actual_display_pass(pass);
-
- const float exposure = buffer_params.exposure;
- const int num_samples = path_trace_->get_num_render_tile_samples();
-
- PassAccessor::PassAccessInfo pass_access_info(*pass);
- pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher;
- pass_access_info.use_approximate_shadow_catcher_background =
- pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background;
-
- const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
- const PassAccessor::Destination destination(pixels, num_components);
-
- return path_trace_->get_render_tile_pixels(pass_accessor, destination);
-}
-
-bool Session::set_render_tile_pixels(const string &pass_name,
- int num_components,
- const float *pixels)
-{
- /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification
- * is happening while this function runs. */
-
- const BufferPass *pass = buffer_params_.find_pass(pass_name);
- if (!pass) {
- return false;
- }
-
- const float exposure = scene->film->get_exposure();
- const int num_samples = render_scheduler_.get_num_rendered_samples();
-
- const PassAccessor::PassAccessInfo pass_access_info(*pass);
- PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples);
- PassAccessor::Source source(pixels, num_components);
-
- return path_trace_->set_render_tile_pixels(pass_accessor, source);
-}
-
-/* --------------------------------------------------------------------
* Full-frame on-disk storage.
*/
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index 5623604bfe8..46c964bc98c 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -35,9 +35,10 @@ CCL_NAMESPACE_BEGIN
class BufferParams;
class Device;
class DeviceScene;
+class DisplayDriver;
+class OutputDriver;
class PathTrace;
class Progress;
-class GPUDisplay;
class RenderBuffers;
class Scene;
class SceneParams;
@@ -67,8 +68,6 @@ class SessionParams {
ShadingSystem shadingsystem;
- function<bool(const uchar *pixels, int width, int height, int channels)> write_render_cb;
-
SessionParams()
{
headless = false;
@@ -114,10 +113,6 @@ class Session {
Stats stats;
Profiler profiler;
- function<void(void)> write_render_tile_cb;
- function<void(void)> update_render_tile_cb;
- function<void(void)> read_render_tile_cb;
-
/* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after
* writing. Allows an engine integration to keep track of those files without worry about
* transferring the information when it needs to re-create session during rendering. */
@@ -143,7 +138,10 @@ class Session {
void set_samples(int samples);
void set_time_limit(double time_limit);
- void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
+ void set_output_driver(unique_ptr<OutputDriver> driver);
+ void set_display_driver(unique_ptr<DisplayDriver> driver);
+
+ double get_estimated_remaining_time() const;
void device_free();
@@ -154,24 +152,6 @@ class Session {
void collect_statistics(RenderStats *stats);
/* --------------------------------------------------------------------
- * Tile and tile pixels access.
- */
-
- bool has_multiple_render_tiles() const;
-
- /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. */
- int2 get_render_tile_size() const;
- int2 get_render_tile_offset() const;
-
- string_view get_render_tile_layer() const;
- string_view get_render_tile_view() const;
-
- bool copy_render_tile_from_device();
-
- bool get_render_tile_pixels(const string &pass_name, int num_components, float *pixels);
- bool set_render_tile_pixels(const string &pass_name, int num_components, const float *pixels);
-
- /* --------------------------------------------------------------------
* Full-frame on-disk storage.
*/
diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp
index 28910bffa7b..7e53a9d0911 100644
--- a/intern/cycles/render/tile.cpp
+++ b/intern/cycles/render/tile.cpp
@@ -307,8 +307,8 @@ static bool configure_image_spec_from_buffer(ImageSpec *image_spec,
DCHECK_GT(tile_size.x, 0);
DCHECK_GT(tile_size.y, 0);
- image_spec->tile_width = tile_size.x;
- image_spec->tile_height = tile_size.y;
+ image_spec->tile_width = min(TileManager::IMAGE_TILE_SIZE, tile_size.x);
+ image_spec->tile_height = min(TileManager::IMAGE_TILE_SIZE, tile_size.y);
}
return true;
@@ -335,6 +335,15 @@ TileManager::~TileManager()
{
}
+int TileManager::compute_render_tile_size(const int suggested_tile_size) const
+{
+ /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file
+ * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size
+ * because too big tile size leads to integer overflow inside OpenEXR. */
+ return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size :
+ align_up(suggested_tile_size, IMAGE_TILE_SIZE);
+}
+
void TileManager::reset_scheduling(const BufferParams &params, int2 tile_size)
{
VLOG(3) << "Using tile size of " << tile_size;
@@ -411,6 +420,11 @@ const Tile &TileManager::get_current_tile() const
return tile_state_.current_tile;
}
+const int2 TileManager::get_size() const
+{
+ return make_int2(buffer_params_.width, buffer_params_.height);
+}
+
bool TileManager::open_tile_output()
{
write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" +
@@ -427,7 +441,12 @@ bool TileManager::open_tile_output()
return false;
}
- write_state_.tile_out->open(write_state_.filename, write_state_.image_spec);
+ if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) {
+ LOG(ERROR) << "Error opening tile file: " << write_state_.tile_out->geterror();
+ write_state_.tile_out = nullptr;
+ return false;
+ }
+
write_state_.num_tiles_written = 0;
VLOG(3) << "Opened tile file " << write_state_.filename;
@@ -466,33 +485,29 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers)
const BufferParams &tile_params = tile_buffers.params;
- vector<float> pixel_storage;
const float *pixels = tile_buffers.buffer.data();
-
- /* Tiled writing expects pixels to contain data for an entire tile. Pad the render buffers with
- * empty pixels for tiles which are on the image boundary. */
- if (tile_params.width != tile_size_.x || tile_params.height != tile_size_.y) {
- const int64_t pass_stride = tile_params.pass_stride;
- const int64_t src_row_stride = tile_params.width * pass_stride;
-
- const int64_t dst_row_stride = tile_size_.x * pass_stride;
- pixel_storage.resize(dst_row_stride * tile_size_.y);
-
- const float *src = tile_buffers.buffer.data();
- float *dst = pixel_storage.data();
- pixels = dst;
-
- for (int y = 0; y < tile_params.height; ++y, src += src_row_stride, dst += dst_row_stride) {
- memcpy(dst, src, src_row_stride * sizeof(float));
- }
- }
-
const int tile_x = tile_params.full_x - buffer_params_.full_x;
const int tile_y = tile_params.full_y - buffer_params_.full_y;
VLOG(3) << "Write tile at " << tile_x << ", " << tile_y;
- if (!write_state_.tile_out->write_tile(tile_x, tile_y, 0, TypeDesc::FLOAT, pixels)) {
+
+ /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The
+ * write_tiles() method expects a contiguous image region that will be split into tiles
+ * internally. OpenEXR expects the size of this region to be a multiple of the tile size,
+ * however OpenImageIO automatically adds the required padding.
+ *
+ * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the
+ * image tile size, which happens in compute_render_tile_size. */
+ if (!write_state_.tile_out->write_tiles(tile_x,
+ tile_x + tile_params.width,
+ tile_y,
+ tile_y + tile_params.height,
+ 0,
+ 1,
+ TypeDesc::FLOAT,
+ pixels)) {
LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror();
+ return false;
}
++write_state_.num_tiles_written;
@@ -518,7 +533,14 @@ void TileManager::finish_write_tiles()
VLOG(3) << "Write dummy tile at " << tile.x << ", " << tile.y;
- write_state_.tile_out->write_tile(tile.x, tile.y, 0, TypeDesc::FLOAT, pixel_storage.data());
+ write_state_.tile_out->write_tiles(tile.x,
+ tile.x + tile.width,
+ tile.y,
+ tile.y + tile.height,
+ 0,
+ 1,
+ TypeDesc::FLOAT,
+ pixel_storage.data());
}
}
diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h
index 71b9e966278..08eaa4034f0 100644
--- a/intern/cycles/render/tile.h
+++ b/intern/cycles/render/tile.h
@@ -82,6 +82,7 @@ class TileManager {
bool done();
const Tile &get_current_tile() const;
+ const int2 get_size() const;
/* Write render buffer of a tile to a file on disk.
*
@@ -107,6 +108,12 @@ class TileManager {
RenderBuffers *buffers,
DenoiseParams *denoise_params);
+ /* Compute valid tile size compatible with image saving. */
+ int compute_render_tile_size(const int suggested_tile_size) const;
+
+ /* Tile size in the image file. */
+ static const int IMAGE_TILE_SIZE = 128;
+
protected:
/* Get tile configuration for its index.
* The tile index must be within [0, state_.tile_state_). */
diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/util_atomic.h
index de17efafcf2..faba411c769 100644
--- a/intern/cycles/util/util_atomic.h
+++ b/intern/cycles/util/util_atomic.h
@@ -34,7 +34,7 @@
#else /* __KERNEL_GPU__ */
-# ifdef __KERNEL_CUDA__
+# if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__)
# define atomic_add_and_fetch_float(p, x) (atomicAdd((float *)(p), (float)(x)) + (float)(x))
diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp
index 1d598725c84..2245668d02f 100644
--- a/intern/cycles/util/util_debug.cpp
+++ b/intern/cycles/util/util_debug.cpp
@@ -59,12 +59,23 @@ DebugFlags::CUDA::CUDA() : adaptive_compile(false)
reset();
}
+DebugFlags::HIP::HIP() : adaptive_compile(false)
+{
+ reset();
+}
+
void DebugFlags::CUDA::reset()
{
if (getenv("CYCLES_CUDA_ADAPTIVE_COMPILE") != NULL)
adaptive_compile = true;
}
+void DebugFlags::HIP::reset()
+{
+ if (getenv("CYCLES_HIP_ADAPTIVE_COMPILE") != NULL)
+ adaptive_compile = true;
+}
+
DebugFlags::OptiX::OptiX()
{
reset();
@@ -103,6 +114,10 @@ std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags)
os << "OptiX flags:\n"
<< " Debug : " << string_from_bool(debug_flags.optix.use_debug) << "\n";
+
+ os << "HIP flags:\n"
+ << " HIP streams : " << string_from_bool(debug_flags.hip.adaptive_compile) << "\n";
+
return os;
}
diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h
index 99e2723180c..81677201790 100644
--- a/intern/cycles/util/util_debug.h
+++ b/intern/cycles/util/util_debug.h
@@ -89,7 +89,18 @@ class DebugFlags {
void reset();
/* Whether adaptive feature based runtime compile is enabled or not.
- * Requires the CUDA Toolkit and only works on Linux atm. */
+ * Requires the CUDA Toolkit and only works on Linux at the moment. */
+ bool adaptive_compile;
+ };
+
+ /* Descriptor of HIP feature-set to be used. */
+ struct HIP {
+ HIP();
+
+ /* Reset flags to their defaults. */
+ void reset();
+
+ /* Whether adaptive feature based runtime compile is enabled or not.*/
bool adaptive_compile;
};
@@ -124,6 +135,9 @@ class DebugFlags {
/* Requested OptiX flags. */
OptiX optix;
+ /* Requested HIP flags. */
+ HIP hip;
+
private:
DebugFlags();
diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h
index d9edfec5da3..f36a492a1b0 100644
--- a/intern/cycles/util/util_half.h
+++ b/intern/cycles/util/util_half.h
@@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN
/* Half Floats */
/* CUDA has its own half data type, no need to define then */
-#ifndef __KERNEL_CUDA__
+#if !defined(__KERNEL_CUDA__) && !defined(__KERNEL_HIP__)
/* Implementing this as a class rather than a typedef so that the compiler can tell it apart from
* unsigned shorts. */
class half {
@@ -59,7 +59,7 @@ struct half4 {
half x, y, z, w;
};
-#ifdef __KERNEL_CUDA__
+#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__)
ccl_device_inline void float4_store_half(half *h, float4 f)
{
@@ -73,6 +73,7 @@ ccl_device_inline void float4_store_half(half *h, float4 f)
ccl_device_inline void float4_store_half(half *h, float4 f)
{
+
# ifndef __KERNEL_SSE2__
for (int i = 0; i < 4; i++) {
/* optimized float to half for pixels:
@@ -109,6 +110,8 @@ ccl_device_inline void float4_store_half(half *h, float4 f)
# endif
}
+# ifndef __KERNEL_HIP__
+
ccl_device_inline float half_to_float(half h)
{
float f;
@@ -117,6 +120,23 @@ ccl_device_inline float half_to_float(half h)
return f;
}
+# else
+
+ccl_device_inline float half_to_float(std::uint32_t a) noexcept
+{
+
+ std::uint32_t u = ((a << 13) + 0x70000000U) & 0x8fffe000U;
+
+ std::uint32_t v = __float_as_uint(__uint_as_float(u) *
+ __uint_as_float(0x77800000U) /*0x1.0p+112f*/) +
+ 0x38000000U;
+
+ u = (a & 0x7fff) != 0 ? v : u;
+
+ return __uint_as_float(u) * __uint_as_float(0x07800000U) /*0x1.0p-112f*/;
+}
+
+# endif /* __KERNEL_HIP__ */
ccl_device_inline float4 half4_to_float4(half4 h)
{
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index 6d728dde679..cb1e94c838c 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -26,6 +26,10 @@
# include <cmath>
#endif
+#ifdef __HIP__
+# include <hip/hip_vector_types.h>
+#endif
+
#include <float.h>
#include <math.h>
#include <stdio.h>
@@ -83,7 +87,8 @@ CCL_NAMESPACE_BEGIN
/* Scalar */
-#ifdef _WIN32
+#ifndef __HIP__
+# ifdef _WIN32
ccl_device_inline float fmaxf(float a, float b)
{
return (a > b) ? a : b;
@@ -93,7 +98,9 @@ ccl_device_inline float fminf(float a, float b)
{
return (a < b) ? a : b;
}
-#endif /* _WIN32 */
+
+# endif /* _WIN32 */
+#endif /* __HIP__ */
#ifndef __KERNEL_GPU__
using std::isfinite;
@@ -199,6 +206,7 @@ ccl_device_inline uint as_uint(float f)
return u.i;
}
+#ifndef __HIP__
ccl_device_inline int __float_as_int(float f)
{
union {
@@ -238,6 +246,7 @@ ccl_device_inline float __uint_as_float(uint i)
u.i = i;
return u.f;
}
+#endif
ccl_device_inline int4 __float4_as_int4(float4 f)
{
@@ -669,7 +678,7 @@ ccl_device float bits_to_01(uint bits)
ccl_device_inline uint count_leading_zeros(uint x)
{
-#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__)
+#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__)
return __clz(x);
#else
assert(x != 0);
@@ -685,7 +694,7 @@ ccl_device_inline uint count_leading_zeros(uint x)
ccl_device_inline uint count_trailing_zeros(uint x)
{
-#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__)
+#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__)
return (__ffs(x) - 1);
#else
assert(x != 0);
@@ -701,7 +710,7 @@ ccl_device_inline uint count_trailing_zeros(uint x)
ccl_device_inline uint find_first_set(uint x)
{
-#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__)
+#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__)
return __ffs(x);
#else
# ifdef _MSC_VER
diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/util_math_intersect.h
index fa3a541eea9..fd0c9124345 100644
--- a/intern/cycles/util/util_math_intersect.h
+++ b/intern/cycles/util/util_math_intersect.h
@@ -40,7 +40,7 @@ ccl_device bool ray_sphere_intersect(float3 ray_P,
/* Ray points away from sphere. */
return false;
}
- const float dsq = tsq - tp * tp; /* pythagoras */
+ const float dsq = tsq - tp * tp; /* Pythagoras. */
if (dsq > radiussq) {
/* Closest point on ray outside sphere. */
return false;
diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/util_progress.h
index dca8d3d0ab5..176ee11e1e9 100644
--- a/intern/cycles/util/util_progress.h
+++ b/intern/cycles/util/util_progress.h
@@ -100,7 +100,7 @@ class Progress {
cancel = true;
}
- bool get_cancel()
+ bool get_cancel() const
{
if (!cancel && cancel_cb)
cancel_cb();
@@ -108,7 +108,7 @@ class Progress {
return cancel;
}
- string get_cancel_message()
+ string get_cancel_message() const
{
thread_scoped_lock lock(progress_mutex);
return cancel_message;
@@ -130,12 +130,12 @@ class Progress {
cancel = true;
}
- bool get_error()
+ bool get_error() const
{
return error;
}
- string get_error_message()
+ string get_error_message() const
{
thread_scoped_lock lock(progress_mutex);
return error_message;
@@ -168,7 +168,7 @@ class Progress {
}
}
- void get_time(double &total_time_, double &render_time_)
+ void get_time(double &total_time_, double &render_time_) const
{
thread_scoped_lock lock(progress_mutex);
@@ -200,7 +200,7 @@ class Progress {
total_pixel_samples = total_pixel_samples_;
}
- float get_progress()
+ float get_progress() const
{
thread_scoped_lock lock(progress_mutex);
@@ -236,7 +236,7 @@ class Progress {
}
}
- int get_current_sample()
+ int get_current_sample() const
{
thread_scoped_lock lock(progress_mutex);
/* Note that the value here always belongs to the last tile that updated,
@@ -244,13 +244,13 @@ class Progress {
return current_tile_sample;
}
- int get_rendered_tiles()
+ int get_rendered_tiles() const
{
thread_scoped_lock lock(progress_mutex);
return rendered_tiles;
}
- int get_denoised_tiles()
+ int get_denoised_tiles() const
{
thread_scoped_lock lock(progress_mutex);
return denoised_tiles;
@@ -300,7 +300,7 @@ class Progress {
set_update();
}
- void get_status(string &status_, string &substatus_)
+ void get_status(string &status_, string &substatus_) const
{
thread_scoped_lock lock(progress_mutex);
@@ -330,8 +330,8 @@ class Progress {
}
protected:
- thread_mutex progress_mutex;
- thread_mutex update_mutex;
+ mutable thread_mutex progress_mutex;
+ mutable thread_mutex update_mutex;
function<void()> update_cb;
function<void()> cancel_cb;
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index 5f9bd808c8c..91f576ca304 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -40,7 +40,7 @@
* There are two coordinate systems:
*
* - The screen coordinate system. The origin of the screen is located in the
- * upper left corner of the screen.</li>
+ * upper left corner of the screen.
* - The client rectangle coordinate system. The client rectangle of a window
* is the area that is drawable by the application (excluding title bars etc.).
*/
diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
index 5b026eb1632..09b2e4dfe2b 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
+++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp
@@ -101,8 +101,7 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting(
uint8_t display, const GHOST_DisplaySetting &setting)
{
/*
- * Mode switching code ported from Quake 2 version 3.21 and bzflag version
- * 2.4.0:
+ * Mode switching code ported from Quake 2 version 3.21 and BZFLAG version 2.4.0:
* ftp://ftp.idsoftware.com/idstuff/source/q2source-3.21.zip
* See linux/gl_glx.c:GLimp_SetMode
* http://wiki.bzflag.org/BZFlag_Source
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index 35c7a7ef463..5370d4df857 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -374,8 +374,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure)
window->getClientBounds(bounds);
- /* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
- * Use offset of 8 in case the window is at screen bounds. */
+ /* Could also clamp to screen bounds wrap with a window outside the view will
+ * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index f44107ee000..482f20f5cd1 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -1100,8 +1100,8 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window->getClientBounds(bounds);
}
- /* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
- * Use inset in case the window is at screen bounds. */
+ /* Could also clamp to screen bounds wrap with a window outside the view will
+ * fail at the moment. Use inset in case the window is at screen bounds. */
bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 10ccb00cc15..86b4245ca67 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -973,8 +973,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure)
window->getClientBounds(bounds);
- /* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
- * Use offset of 8 in case the window is at screen bounds. */
+ /* Could also clamp to screen bounds wrap with a window outside the view will
+ * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum);
@@ -1528,13 +1528,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels);
}
- /* the (short) cast and the & 0xffff is bizarre and unexplained anywhere,
- * but I got garbage data without it. Found it in the xidump.c source --matt
+ /* NOTE(@broken): the (short) cast and the & 0xffff is bizarre and unexplained anywhere,
+ * but I got garbage data without it. Found it in the `xidump.c` source.
*
- * The '& 0xffff' just truncates the value to its two lowest bytes, this probably means
- * some drivers do not properly set the whole int value? Since we convert to float
- * afterward, I don't think we need to cast to short here, but do not have a device to
- * check this. --mont29
+ * NOTE(@mont29): The '& 0xffff' just truncates the value to its two lowest bytes,
+ * this probably means some drivers do not properly set the whole int value?
+ * Since we convert to float afterward,
+ * I don't think we need to cast to short here, but do not have a device to check this.
*/
if (AXIS_VALUE_GET(3, axis_value)) {
window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) /
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index de389951613..8b44403c598 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -1092,9 +1092,9 @@ GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order)
XWindowAttributes attr;
Atom atom;
- /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some
- * window managers ignore the former (e.g. kwin from kde) and others
- * don't implement the latter (e.g. fluxbox pre 0.9.9) */
+ /* We use both #XRaiseWindow and #_NET_ACTIVE_WINDOW, since some
+ * window managers ignore the former (e.g. KWIN from KDE) and others
+ * don't implement the latter (e.g. FLUXBOX before 0.9.9). */
XRaiseWindow(m_display, m_window);
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index 98a8553a3eb..bba72c907eb 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -89,7 +89,7 @@ typedef struct localListBase {
void *first, *last;
} localListBase;
-/* note: keep this struct aligned (e.g., irix/gcc) - Hos */
+/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */
typedef struct MemHead {
int tag1;
size_t len;
@@ -98,9 +98,8 @@ typedef struct MemHead {
const char *nextname;
int tag2;
short pad1;
- short alignment; /* if non-zero aligned alloc was used
- * and alignment is stored here.
- */
+ /* if non-zero aligned allocation was used and alignment is stored here. */
+ short alignment;
#ifdef DEBUG_MEMCOUNTER
int _count;
#endif
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
index b3fc021e1ee..4f4f332ff15 100644
--- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
+++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
@@ -553,7 +553,7 @@ void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords,
} // namespace
-// Note: Define as a class instead of typedcef to make it possible
+// Note: Define as a class instead of typedef to make it possible
// to have anonymous class in opensubdiv_evaluator_internal.h
class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer,
CpuVertexBuffer,
diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h
index e759c5f43b0..e78842036be 100644
--- a/intern/opensubdiv/opensubdiv_capi_type.h
+++ b/intern/opensubdiv/opensubdiv_capi_type.h
@@ -23,7 +23,7 @@
extern "C" {
#endif
-// Keep this a bitmask os it's possible to pass available
+// Keep this a bitmask so it's possible to pass available
// evaluators to Blender.
typedef enum eOpenSubdivEvaluator {
OPENSUBDIV_EVALUATOR_CPU = (1 << 0),
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 94c39b5832b9ef3b56ed94ce4011412e3d776eb
+Subproject 4833954c0ac85cc407e1d5a153aa11b1d1823ec
diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c
index 85532d01d5c..6753dc8563e 100644
--- a/release/datafiles/userdef/userdef_default_theme.c
+++ b/release/datafiles/userdef/userdef_default_theme.c
@@ -817,7 +817,7 @@ const bTheme U_theme_default = {
},
.shade2 = RGBA(0x7f707064),
.grid = RGBA(0x23232300),
- .wire = RGBA(0x808080ff),
+ .wire = RGBA(0x232323ff),
.select = RGBA(0xed5700ff),
.active = RGBA(0xffffffff),
.edge_select = RGBA(0xffffffff),
@@ -1175,6 +1175,35 @@ const bTheme U_theme_default = {
.color = RGBA(0x7a5441ff),
},
},
+ .strip_color = {
+ {
+ .color = RGBA(0xe2605bff),
+ },
+ {
+ .color = RGBA(0xf1a355ff),
+ },
+ {
+ .color = RGBA(0xf1dc55ff),
+ },
+ {
+ .color = RGBA(0x7bcc7bff),
+ },
+ {
+ .color = RGBA(0x5db6eaff),
+ },
+ {
+ .color = RGBA(0x8d59daff),
+ },
+ {
+ .color = RGBA(0xc673b8ff),
+ },
+ {
+ .color = RGBA(0x7a5441ff),
+ },
+ {
+ .color = RGBA(0x5f5f5fff),
+ },
+ },
};
/* clang-format on */
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject a85360cbdfbbee2bb46bcb93900f597a989bd33
+Subproject c64726810ba781d980921947ba819b1364689e5
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 42da56aa73726710107031787af5eea18679798
+Subproject 5a82baad9f986722104280e8354a4427d8e9eab
diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py
index 95184853f73..13fb87d386a 100644
--- a/release/scripts/modules/bl_i18n_utils/utils.py
+++ b/release/scripts/modules/bl_i18n_utils/utils.py
@@ -219,7 +219,7 @@ def enable_addons(addons=None, support=None, disable=False, check_only=False):
try:
import bpy
except ModuleNotFoundError:
- print("Could not import bpy, enable_addons must be run from whithin Blender.")
+ print("Could not import bpy, enable_addons must be run from within Blender.")
return
if addons is None:
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
index 2cd5dddefbc..c7ebdc1d5e1 100644
--- a/release/scripts/modules/bpy_extras/asset_utils.py
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -52,19 +52,12 @@ class AssetBrowserPanel:
bl_space_type = 'FILE_BROWSER'
@classmethod
- def poll(cls, context):
+ def asset_browser_panel_poll(cls, context):
return SpaceAssetInfo.is_asset_browser_poll(context)
-
-class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel):
- asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c
-
@classmethod
def poll(cls, context):
- return (
- SpaceAssetInfo.is_asset_browser_poll(context)
- and context.space_data.params.asset_category in cls.asset_categories
- )
+ return cls.asset_browser_panel_poll(context)
class AssetMetaDataPanel:
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 40f59307bec..797eb2627b3 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -39,8 +39,8 @@ if LANG is not None:
url_manual_prefix = url_manual_prefix.replace("manual/en", "manual/" + LANG)
url_manual_mapping = (
- ("bpy.types.cyclesworldsettings.sample_mbpy.types.cyclesworldsettings.sample_map_resolutionbpy.types.cyclesworldsettings.sample_map_resolutionap_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-mbpy-types-cyclesworldsettings-sample-map-resolutionbpy-types-cyclesworldsettings-sample-map-resolutionap-resolution"),
("bpy.types.movietrackingsettings.refine_intrinsics_tangential_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-tangential-distortion"),
+ ("bpy.types.spacesequesequencertimelineoverlaynceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequesequencertimelineoverlaynceeditor-show-strip-offset"),
("bpy.types.movietrackingsettings.refine_intrinsics_radial_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-radial-distortion"),
("bpy.types.fluiddomainsettings.sndparticle_potential_max_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-trappedair"),
("bpy.types.fluiddomainsettings.sndparticle_potential_min_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-trappedair"),
@@ -60,11 +60,13 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"),
("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"),
("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"),
+ ("bpy.types.cyclesrendersettings.preview_adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-min-samples"),
("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"),
("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"),
+ ("bpy.types.cyclesrendersettings.preview_denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-prefilter"),
("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"),
("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"),
- ("bpy.types.cyclesrendersettings.sample_all_lights_indirect*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-indirect"),
+ ("bpy.types.cyclesrendersettings.preview_adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-threshold"),
("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"),
("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"),
("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"),
@@ -76,14 +78,13 @@ url_manual_mapping = (
("bpy.types.toolsettings.annotation_stroke_placement_view3d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view3d"),
("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.sequencertimelineoverlay.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-waveform-display-type"),
("bpy.types.cyclesmaterialsettings.use_transparent_shadow*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-use-transparent-shadow"),
("bpy.types.cyclesobjectsettings.shadow_terminator_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-offset"),
("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"),
("bpy.types.cyclesrendersettings.debug_use_spatial_splits*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-spatial-splits"),
("bpy.types.cyclesrendersettings.light_sampling_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-light-sampling-threshold"),
- ("bpy.types.cyclesrendersettings.preview_start_resolution*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-preview-start-resolution"),
("bpy.types.cyclesrendersettings.rolling_shutter_duration*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-duration"),
- ("bpy.types.cyclesrendersettings.sample_all_lights_direct*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-all-lights-direct"),
("bpy.types.cyclesrendersettings.volume_preview_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-preview-step-rate"),
("bpy.types.fluiddomainsettings.sndparticle_update_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-update-radius"),
("bpy.types.fluiddomainsettings.use_collision_border_back*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-back"),
@@ -97,14 +98,15 @@ url_manual_mapping = (
("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.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"),
("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.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"),
("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"),
+ ("bpy.types.cyclesrendersettings.denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-input-passes"),
("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"),
("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"),
- ("bpy.types.cyclesrendersettings.use_progressive_refine*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-use-progressive-refine"),
("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/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"),
("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/view_layer/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"),
@@ -123,6 +125,7 @@ url_manual_mapping = (
("bpy.types.gpencillayer.use_annotation_onion_skinning*", "interface/annotate_tool.html#bpy-types-gpencillayer-use-annotation-onion-skinning"),
("bpy.types.greasepencil.use_adaptive_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-adaptive-curve-resolution"),
("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/view_layer/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"),
+ ("bpy.types.sequencertimelineoverlay.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-source"),
("bpy.types.toolsettings.use_gpencil_automerge_strokes*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-automerge-strokes"),
("bpy.types.toolsettings.use_proportional_edit_objects*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit-objects"),
("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"),
@@ -133,7 +136,7 @@ url_manual_mapping = (
("bpy.types.cyclesrendersettings.motion_blur_position*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-motion-blur-position"),
("bpy.types.cyclesrendersettings.rolling_shutter_type*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-type"),
("bpy.types.cyclesrendersettings.transmission_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-transmission-bounces"),
- ("bpy.types.cyclesrendersettings.transmission_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-transmission-samples"),
+ ("bpy.types.cyclesworldsettings.sample_map_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-map-resolution"),
("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"),
("bpy.types.fluiddomainsettings.gridlines_cell_filter*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-cell-filter"),
("bpy.types.fluiddomainsettings.gridlines_color_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-color-field"),
@@ -147,13 +150,13 @@ url_manual_mapping = (
("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"),
("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"),
("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"),
- ("bpy.types.spacesequenceeditor.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-waveform-display-type"),
("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"),
("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"),
("bpy.types.brush.elastic_deform_volume_preservation*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-volume-preservation"),
("bpy.types.brushgpencilsettings.fill_simplify_level*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-simplify-level"),
("bpy.types.brushgpencilsettings.use_jitter_pressure*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-jitter-pressure"),
("bpy.types.brushgpencilsettings.use_settings_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-random"),
+ ("bpy.types.cyclesrendersettings.denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-prefilter"),
("bpy.types.cyclesrendersettings.preview_dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-preview-dicing-rate"),
("bpy.types.cyclesrendersettings.sample_clamp_direct*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-direct"),
("bpy.types.cyclesworldsettings.volume_interpolation*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-interpolation"),
@@ -166,6 +169,7 @@ url_manual_mapping = (
("bpy.types.freestylelineset.select_external_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-external-contour"),
("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/view_layer/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"),
("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"),
+ ("bpy.types.sequencertimelineoverlay.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-name"),
("bpy.types.spacespreadsheet.geometry_component_type*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-geometry-component-type"),
("bpy.types.toolsettings.use_gpencil_weight_data_add*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-weight-data-add"),
("bpy.types.view3doverlay.texture_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-texture-paint-mode-opacity"),
@@ -180,10 +184,6 @@ url_manual_mapping = (
("bpy.types.cyclesrendersettings.adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-threshold"),
("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"),
("bpy.types.cyclesrendersettings.debug_use_hair_bvh*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-hair-bvh"),
- ("bpy.types.cyclesrendersettings.mesh_light_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-mesh-light-samples"),
- ("bpy.types.cyclesrendersettings.preview_aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-aa-samples"),
- ("bpy.types.cyclesrendersettings.subsurface_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-subsurface-samples"),
- ("bpy.types.cyclesrendersettings.use_square_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-square-samples"),
("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"),
("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"),
@@ -201,11 +201,12 @@ url_manual_mapping = (
("bpy.types.movietrackingsettings.use_tripod_solver*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-tripod-solver"),
("bpy.types.rendersettings.simplify_child_particles*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-child-particles"),
("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"),
+ ("bpy.types.sequencerpreviewoverlay.show_annotation*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-annotation"),
+ ("bpy.types.sequencerpreviewoverlay.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-safe-areas"),
("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"),
("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"),
("bpy.types.spaceoutliner.use_filter_case_sensitive*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-case-sensitive"),
("bpy.types.spaceoutliner.use_filter_object_content*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-content"),
- ("bpy.types.spacesequenceeditor.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-duration"),
("bpy.types.toolsettings.use_proportional_connected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-connected"),
("bpy.types.toolsettings.use_proportional_projected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-projected"),
("bpy.types.view3doverlay.vertex_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-vertex-paint-mode-opacity"),
@@ -284,13 +285,13 @@ 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.sequencerpreviewoverlay.show_metadata*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-metadata"),
+ ("bpy.types.sequencertimelineoverlay.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-fcurves"),
("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"),
("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*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-offset"),
- ("bpy.types.spacesequenceeditor.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-source"),
("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"),
("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"),
@@ -301,7 +302,6 @@ url_manual_mapping = (
("bpy.types.cyclesobjectsettings.use_camera_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-camera-cull"),
("bpy.types.cyclesobjectsettings.use_motion_blur*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-motion-blur"),
("bpy.types.cyclesrendersettings.diffuse_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-diffuse-bounces"),
- ("bpy.types.cyclesrendersettings.diffuse_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-diffuse-samples"),
("bpy.types.cyclesrendersettings.preview_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-samples"),
("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"),
("bpy.types.cyclesworldsettings.volume_step_size*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-step-size"),
@@ -340,9 +340,7 @@ url_manual_mapping = (
("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"),
("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"),
("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"),
- ("bpy.types.cyclesrendersettings.glossy_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-glossy-samples"),
("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"),
- ("bpy.types.cyclesrendersettings.volume_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-volume-samples"),
("bpy.types.cyclesworldsettings.sampling_method*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sampling-method"),
("bpy.types.cyclesworldsettings.volume_sampling*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-sampling"),
("bpy.types.editbone.bbone_handle_use_scale_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-end"),
@@ -370,16 +368,12 @@ url_manual_mapping = (
("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"),
("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"),
("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"),
- ("bpy.types.spacesequenceeditor.show_annotation*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-annotation"),
("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"),
- ("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"),
- ("bpy.types.spacesequenceeditor.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-name"),
("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"),
("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"),
("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"),
("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"),
("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"),
- ("bpy.types.worldlighting.use_ambient_occlusion*", "render/cycles/world_settings.html#bpy-types-worldlighting-use-ambient-occlusion"),
("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"),
("bpy.ops.object.multires_higher_levels_delete*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-higher-levels-delete"),
("bpy.ops.object.vertex_group_copy_to_selected*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-selected"),
@@ -460,9 +454,9 @@ url_manual_mapping = (
("bpy.types.sculpt.constant_detail_resolution*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-constant-detail-resolution"),
("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/view.html#bpy-types-spaceclipeditor-annotation-source"),
("bpy.types.spaceclipeditor.show_blue_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-blue-channel"),
+ ("bpy.types.spacefilebrowser.system_bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-bookmarks"),
("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"),
("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"),
- ("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"),
("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"),
("bpy.types.spacespreadsheetrowfilter.enabled*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-enabled"),
("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"),
@@ -484,10 +478,10 @@ url_manual_mapping = (
("bpy.types.cyclesrendersettings.blur_glossy*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-blur-glossy"),
("bpy.types.cyclesrendersettings.dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"),
("bpy.types.cyclesrendersettings.max_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-bounces"),
- ("bpy.types.cyclesrendersettings.progressive*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-progressive"),
("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"),
("bpy.types.editbone.bbone_custom_handle_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-end"),
("bpy.types.editbone.bbone_handle_type_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-start"),
+ ("bpy.types.fileselectparams.recursion_level*", "editors/file_browser.html#bpy-types-fileselectparams-recursion-level"),
("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"),
("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"),
("bpy.types.fluiddomainsettings.guide_parent*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-parent"),
@@ -516,7 +510,6 @@ url_manual_mapping = (
("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"),
("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"),
("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"),
- ("bpy.types.spacesequenceeditor.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-fcurves"),
("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"),
("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"),
("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"),
@@ -536,10 +529,8 @@ url_manual_mapping = (
("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"),
("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"),
("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"),
- ("bpy.types.cyclesrendersettings.aa_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-aa-samples"),
("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"),
- ("bpy.types.cyclesrendersettings.ao_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-ao-samples"),
- ("bpy.types.cyclesrendersettings.tile_order*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-tile-order"),
+ ("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"),
("bpy.types.cyclesvisibilitysettings.camera*", "render/cycles/world_settings.html#bpy-types-cyclesvisibilitysettings-camera"),
("bpy.types.cyclesworldsettings.max_bounces*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-max-bounces"),
("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"),
@@ -575,6 +566,8 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"),
("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"),
("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"),
+ ("bpy.types.spacefilebrowser.recent_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-recent-folders"),
+ ("bpy.types.spacefilebrowser.system_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-folders"),
("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"),
("bpy.types.spacesequenceeditor.use_proxies*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"),
("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"),
@@ -606,6 +599,7 @@ url_manual_mapping = (
("bpy.types.cyclescamerasettings.longitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-longitude"),
("bpy.types.editbone.bbone_handle_type_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-end"),
("bpy.types.editbone.use_endroll_as_inroll*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-endroll-as-inroll"),
+ ("bpy.types.fileselectparams.filter_search*", "editors/file_browser.html#bpy-types-fileselectparams-filter-search"),
("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"),
("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"),
("bpy.types.fluiddomainsettings.guide_beta*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-beta"),
@@ -674,6 +668,8 @@ url_manual_mapping = (
("bpy.types.cyclesrendersettings.caustics*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-caustics"),
("bpy.types.cyclesrendersettings.denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoiser"),
("bpy.types.editbone.use_inherit_rotation*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-inherit-rotation"),
+ ("bpy.types.fileselectparams.display_size*", "editors/file_browser.html#bpy-types-fileselectparams-display-size"),
+ ("bpy.types.fileselectparams.display_type*", "editors/file_browser.html#bpy-types-fileselectparams-display-type"),
("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"),
("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"),
("bpy.types.fluiddomainsettings.use_slice*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-use-slice"),
@@ -736,6 +732,8 @@ url_manual_mapping = (
("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"),
("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"),
("bpy.types.cyclesrendersettings.samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-samples"),
+ ("bpy.types.fileselectparams.show_hidden*", "editors/file_browser.html#bpy-types-fileselectparams-show-hidden"),
+ ("bpy.types.fileselectparams.sort_method*", "editors/file_browser.html#bpy-types-fileselectparams-sort-method"),
("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"),
("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"),
("bpy.types.freestylelinestyle.angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-max"),
@@ -752,6 +750,7 @@ url_manual_mapping = (
("bpy.types.object.use_empty_image_alpha*", "modeling/empties.html#bpy-types-object-use-empty-image-alpha"),
("bpy.types.rendersettings.frame_map_new*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-new"),
("bpy.types.rendersettings.frame_map_old*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-old"),
+ ("bpy.types.rendersettings.use_auto_tile*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-auto-tile"),
("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"),
("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"),
("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"),
@@ -797,6 +796,7 @@ url_manual_mapping = (
("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"),
("bpy.types.dopesheet.use_filter_invert*", "editors/graph_editor/channels.html#bpy-types-dopesheet-use-filter-invert"),
("bpy.types.editbone.use_local_location*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-local-location"),
+ ("bpy.types.fileselectparams.use_filter*", "editors/file_browser.html#bpy-types-fileselectparams-use-filter"),
("bpy.types.fluiddomainsettings.gravity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-gravity"),
("bpy.types.fluidflowsettings.flow_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-type"),
("bpy.types.fluidflowsettings.subframes*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-subframes"),
@@ -868,6 +868,7 @@ url_manual_mapping = (
("bpy.types.compositornodecolorbalance*", "compositing/types/color/color_balance.html#bpy-types-compositornodecolorbalance"),
("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"),
("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"),
+ ("bpy.types.fileselectparams.directory*", "editors/file_browser.html#bpy-types-fileselectparams-directory"),
("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"),
("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"),
("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"),
@@ -891,6 +892,7 @@ 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.spacefilebrowser.bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-bookmarks"),
("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"),
@@ -940,6 +942,7 @@ url_manual_mapping = (
("bpy.types.cyclesrendersettings.seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-seed"),
("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"),
("bpy.types.editbone.use_scale_easing*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-scale-easing"),
+ ("bpy.types.fileselectparams.filename*", "editors/file_browser.html#bpy-types-fileselectparams-filename"),
("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"),
("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"),
("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"),
@@ -1048,6 +1051,8 @@ url_manual_mapping = (
("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"),
("bpy.types.object.is_shadow_catcher*", "render/cycles/object_settings/object_data.html#bpy-types-object-is-shadow-catcher"),
("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"),
+ ("bpy.types.rendersettings.tile_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-size"),
+ ("bpy.types.sequencertimelineoverlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay"),
("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"),
("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"),
("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"),
@@ -1143,6 +1148,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.fps_base*", "render/output/properties/format.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.sequencerpreviewoverlay*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay"),
("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"),
("bpy.types.shadernodehuesaturation*", "render/shader_nodes/color/hue_saturation.html#bpy-types-shadernodehuesaturation"),
@@ -1153,7 +1159,7 @@ url_manual_mapping = (
("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"),
("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer/freestyle.html#bpy-types-viewlayer-use-freestyle"),
("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"),
- ("bpy.types.worldlighting.ao_factor*", "render/cycles/world_settings.html#bpy-types-worldlighting-ao-factor"),
+ ("bpy.types.worldlighting.ao_factor*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-ao-factor"),
("bpy.types.worldmistsettings.depth*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-depth"),
("bpy.types.worldmistsettings.start*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-start"),
("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"),
@@ -1255,11 +1261,11 @@ url_manual_mapping = (
("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"),
("bpy.types.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"),
("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"),
- ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/color/texture_mapping.html#bpy-types-texturegpencilmodifier"),
+ ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"),
("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"),
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
- ("bpy.types.worldlighting.distance*", "render/cycles/world_settings.html#bpy-types-worldlighting-distance"),
+ ("bpy.types.worldlighting.distance*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-distance"),
("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"),
("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"),
("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"),
@@ -1360,8 +1366,6 @@ url_manual_mapping = (
("bpy.types.object.visible_shadow*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-shadow"),
("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"),
- ("bpy.types.rendersettings.tile_y*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-y"),
("bpy.types.rigifyselectioncolors*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyselectioncolors"),
("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"),
("bpy.types.screen.show_statusbar*", "interface/window_system/topbar.html#bpy-types-screen-show-statusbar"),
@@ -1573,7 +1577,7 @@ url_manual_mapping = (
("bpy.types.stretchtoconstraint*", "animation/constraints/tracking/stretch_to.html#bpy-types-stretchtoconstraint"),
("bpy.types.texturenodecurvergb*", "editors/texture_node/types/color/rgb_curves.html#bpy-types-texturenodecurvergb"),
("bpy.types.texturenodevaltorgb*", "editors/texture_node/types/converter/rgb_to_bw.html#bpy-types-texturenodevaltorgb"),
- ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/deform/time_offset.html#bpy-types-timegpencilmodifier"),
+ ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/modify/time_offset.html#bpy-types-timegpencilmodifier"),
("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"),
("bpy.types.transformconstraint*", "animation/constraints/transform/transformation.html#bpy-types-transformconstraint"),
("bpy.types.triangulatemodifier*", "modeling/modifiers/generate/triangulate.html#bpy-types-triangulatemodifier"),
@@ -1652,6 +1656,7 @@ url_manual_mapping = (
("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"),
("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"),
("bpy.types.fcurve.array_index*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-array-index"),
+ ("bpy.types.fileselectidfilter*", "editors/file_browser.html#bpy-types-fileselectidfilter"),
("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"),
("bpy.types.freestylelinestyle*", "render/freestyle/view_layer/line_style/index.html#bpy-types-freestylelinestyle"),
("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"),
@@ -1820,6 +1825,7 @@ url_manual_mapping = (
("bpy.ops.clip.track_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-track-markers"),
("bpy.ops.curve.extrude_move*", "modeling/curves/editing/control_points.html#bpy-ops-curve-extrude-move"),
("bpy.ops.curve.make_segment*", "modeling/curves/editing/control_points.html#bpy-ops-curve-make-segment"),
+ ("bpy.ops.file.directory_new*", "editors/file_browser.html#bpy-ops-file-directory-new"),
("bpy.ops.graph.euler_filter*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-euler-filter"),
("bpy.ops.marker.camera_bind*", "animation/markers.html#bpy-ops-marker-camera-bind"),
("bpy.ops.mask.select_circle*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-circle"),
@@ -1874,6 +1880,7 @@ url_manual_mapping = (
("bpy.types.editbone.bbone_x*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-x"),
("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"),
("bpy.types.fcurve.data_path*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-data-path"),
+ ("bpy.types.fileselectparams*", "editors/file_browser.html#bpy-types-fileselectparams"),
("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"),
("bpy.types.freestylelineset*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset"),
("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"),
@@ -1904,6 +1911,7 @@ url_manual_mapping = (
("bpy.types.softbodymodifier*", "physics/soft_body/index.html#bpy-types-softbodymodifier"),
("bpy.types.softbodysettings*", "physics/soft_body/settings/index.html#bpy-types-softbodysettings"),
("bpy.types.solidifymodifier*", "modeling/modifiers/generate/solidify.html#bpy-types-solidifymodifier"),
+ ("bpy.types.spacefilebrowser*", "editors/file_browser.html#bpy-types-spacefilebrowser"),
("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"),
("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"),
("bpy.types.spacespreadsheet*", "editors/spreadsheet.html#bpy-types-spacespreadsheet"),
@@ -1923,6 +1931,7 @@ url_manual_mapping = (
("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"),
("bpy.ops.constraint.delete*", "animation/constraints/interface/header.html#bpy-ops-constraint-delete"),
("bpy.ops.curve.smooth_tilt*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-tilt"),
+ ("bpy.ops.file.reset_recent*", "editors/file_browser.html#bpy-ops-file-reset-recent"),
("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"),
("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"),
("bpy.ops.font.style_toggle*", "modeling/texts/editing.html#bpy-ops-font-style-toggle"),
@@ -2252,6 +2261,7 @@ url_manual_mapping = (
("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"),
("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"),
("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"),
+ ("bpy.ops.file.previous*", "editors/file_browser.html#bpy-ops-file-previous"),
("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"),
@@ -2300,6 +2310,8 @@ url_manual_mapping = (
("bpy.ops.curve.delete*", "modeling/curves/editing/curve.html#bpy-ops-curve-delete"),
("bpy.ops.curve.reveal*", "modeling/curves/editing/curve.html#bpy-ops-curve-reveal"),
("bpy.ops.curve.smooth*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth"),
+ ("bpy.ops.file.execute*", "editors/file_browser.html#bpy-ops-file-execute"),
+ ("bpy.ops.file.refresh*", "editors/file_browser.html#bpy-ops-file-refresh"),
("bpy.ops.fluid.preset*", "physics/fluid/type/domain/liquid/diffusion.html#bpy-ops-fluid-preset"),
("bpy.ops.gpencil.copy*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-copy"),
("bpy.ops.graph.delete*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-delete"),
@@ -2341,6 +2353,8 @@ url_manual_mapping = (
("bpy.types.vectorfont*", "modeling/texts/index.html#bpy-types-vectorfont"),
("bpy.ops.clip.reload*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-reload"),
("bpy.ops.curve.split*", "modeling/curves/editing/curve.html#bpy-ops-curve-split"),
+ ("bpy.ops.file.cancel*", "editors/file_browser.html#bpy-ops-file-cancel"),
+ ("bpy.ops.file.parent*", "editors/file_browser.html#bpy-ops-file-parent"),
("bpy.ops.graph.clean*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-clean"),
("bpy.ops.graph.paste*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-paste"),
("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"),
@@ -2420,6 +2434,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.file.next*", "editors/file_browser.html#bpy-ops-file-next"),
("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"),
@@ -2530,6 +2545,7 @@ url_manual_mapping = (
("bpy.ops.anim*", "animation/index.html#bpy-ops-anim"),
("bpy.ops.boid*", "physics/particles/emitter/physics/boids.html#bpy-ops-boid"),
("bpy.ops.clip*", "movie_clip/index.html#bpy-ops-clip"),
+ ("bpy.ops.file*", "editors/file_browser.html#bpy-ops-file"),
("bpy.ops.font*", "modeling/texts/index.html#bpy-ops-font"),
("bpy.ops.mask*", "movie_clip/masking/index.html#bpy-ops-mask"),
("bpy.ops.mesh*", "modeling/meshes/index.html#bpy-ops-mesh"),
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index 26a2f9ad89b..6d92c94a85c 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -21,9 +21,10 @@
import bpy
from mathutils import Vector
+from bpy.types import bpy_prop_array
from idprop.types import IDPropertyArray, IDPropertyGroup
-ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector)
+ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector, bpy_prop_array)
# Maximum length of an array property for which a multi-line
# edit field will be displayed in the Custom Properties panel.
@@ -136,7 +137,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True):
def assign_props(prop, val, key):
prop.data_path = context_member
- prop.property = key
+ prop.property_name = key
try:
prop.value = str(val)
diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py
index 15cc6097979..1852e150589 100644
--- a/release/scripts/presets/keyconfig/Blender.py
+++ b/release/scripts/presets/keyconfig/Blender.py
@@ -8,6 +8,7 @@ from bpy.props import (
DIRNAME, FILENAME = os.path.split(__file__)
IDNAME = os.path.splitext(FILENAME)[0]
+
def update_fn(_self, _context):
load()
@@ -54,12 +55,18 @@ class Prefs(bpy.types.KeyConfigPreferences):
default='PLAY',
update=update_fn,
)
- use_key_activate_tools: BoolProperty(
- name="Keys Activate Tools",
+ tool_key_mode: EnumProperty(
+ name="Tool Keys:",
description=(
- "Key shortcuts such as G, R, and S activate the tool instead of running it immediately"
+ "The method of keys to activate tools such as move, rotate & scale (G, R, S)"
),
- default=False,
+ items=(
+ ('IMMEDIATE', "Immediate",
+ "Activate actions immediately"),
+ ('TOOL', "Active Tool",
+ "Activate the tool for editors that support tools"),
+ ),
+ default='IMMEDIATE',
update=update_fn,
)
@@ -85,14 +92,28 @@ class Prefs(bpy.types.KeyConfigPreferences):
default=False,
update=update_fn,
)
+ # NOTE: expose `use_alt_tool` and `use_alt_cursor` as two options in the UI
+ # as the tool-tips and titles are different enough depending on RMB/LMB select.
use_alt_tool: BoolProperty(
name="Alt Tool Access",
description=(
- "Hold Alt to use the active tool when the gizmo would normally be required"
+ "Hold Alt to use the active tool when the gizmo would normally be required\n"
+ "Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used"
+ ),
+ default=False,
+ update=update_fn,
+ )
+ use_alt_cursor: BoolProperty(
+ name="Alt Cursor Access",
+ description=(
+ "Hold Alt-LMB to place the Cursor (instead of LMB), allows tools to activate on press instead of drag.\n"
+ "Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used"
),
default=False,
update=update_fn,
)
+ # end note.
+
use_select_all_toggle: BoolProperty(
name="Select All Toggles",
description=(
@@ -196,39 +217,63 @@ class Prefs(bpy.types.KeyConfigPreferences):
update=update_fn,
)
+ use_file_single_click: BoolProperty(
+ name="Open Folders on Single Click",
+ description=(
+ "Navigate into folders by clicking on them once instead of twice"
+ ),
+ default=False,
+ update=update_fn,
+ )
+
def draw(self, layout):
+ from bpy import context
+
layout.use_property_split = True
layout.use_property_decorate = False
+ prefs = context.preferences
+
is_select_left = (self.select_mouse == 'LEFT')
+ use_mouse_emulate_3_button = (
+ prefs.inputs.use_mouse_emulate_3_button and
+ prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
+ )
# General settings.
col = layout.column()
- col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True)
- col.row().prop(self, "spacebar_action", text="Spacebar Action", expand=True)
+ col.row().prop(self, "select_mouse", text="Select with Mouse Button:", expand=True)
+ col.row().prop(self, "spacebar_action", text="Spacebar Action:", expand=True)
if is_select_left:
- col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True)
+ col.row().prop(self, "gizmo_action", text="Activate Gizmo Event:", expand=True)
else:
- col.row().prop(self, "rmb_action", text="Right Mouse Select Action", expand=True)
+ col.row().prop(self, "rmb_action", text="Right Mouse Select Action:", expand=True)
- # Checkboxes sub-layout.
+ col.row().prop(self, "tool_key_mode", expand=True)
+
+ # Check-box sub-layout.
col = layout.column()
sub = col.column(align=True)
row = sub.row()
row.prop(self, "use_alt_click_leader")
+
+ rowsub = row.row()
if is_select_left:
- row.prop(self, "use_alt_tool")
+ rowsub.prop(self, "use_alt_tool")
+ else:
+ rowsub.prop(self, "use_alt_cursor")
+ rowsub.active = not use_mouse_emulate_3_button
+
row = sub.row()
row.prop(self, "use_select_all_toggle")
- row.prop(self, "use_key_activate_tools", text="Key Activates Tools")
# 3DView settings.
col = layout.column()
col.label(text="3D View")
- col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action", expand=True)
- col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action", expand=True)
- col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action", expand=True)
+ col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action:", expand=True)
+ col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action:", expand=True)
+ col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action:", expand=True)
# Checkboxes sub-layout.
col = layout.column()
@@ -237,6 +282,10 @@ class Prefs(bpy.types.KeyConfigPreferences):
sub.prop(self, "use_pie_click_drag")
sub.prop(self, "use_v3d_shade_ex_pie")
+ # File Browser settings.
+ col = layout.column()
+ col.label(text="File Browser")
+ col.row().prop(self, "use_file_single_click")
blender_default = bpy.utils.execfile(os.path.join(DIRNAME, "keymap_data", "blender_default.py"))
@@ -250,29 +299,33 @@ def load():
kc = context.window_manager.keyconfigs.new(IDNAME)
kc_prefs = kc.preferences
+ is_select_left = (kc_prefs.select_mouse == 'LEFT')
+ use_mouse_emulate_3_button = (
+ prefs.inputs.use_mouse_emulate_3_button and
+ prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
+ )
+
keyconfig_data = blender_default.generate_keymaps(
blender_default.Params(
select_mouse=kc_prefs.select_mouse,
- use_mouse_emulate_3_button=(
- prefs.inputs.use_mouse_emulate_3_button and
- prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
- ),
+ use_mouse_emulate_3_button=use_mouse_emulate_3_button,
spacebar_action=kc_prefs.spacebar_action,
- use_key_activate_tools=kc_prefs.use_key_activate_tools,
+ use_key_activate_tools=(kc_prefs.tool_key_mode == 'TOOL'),
v3d_tilde_action=kc_prefs.v3d_tilde_action,
use_v3d_mmb_pan=(kc_prefs.v3d_mmb_action == 'PAN'),
v3d_alt_mmb_drag_action=kc_prefs.v3d_alt_mmb_drag_action,
use_select_all_toggle=kc_prefs.use_select_all_toggle,
use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu,
use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie,
- use_gizmo_drag=(
- kc_prefs.select_mouse == 'LEFT' and
- kc_prefs.gizmo_action == 'DRAG'
+ use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'),
+ use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')),
+ use_alt_tool_or_cursor=(
+ (not use_mouse_emulate_3_button) and
+ (kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor)
),
- use_fallback_tool=(True if (kc_prefs.select_mouse == 'LEFT') else (kc_prefs.rmb_action == 'FALLBACK_TOOL')),
- use_alt_tool=(kc_prefs.use_alt_tool and kc_prefs.select_mouse == 'LEFT'),
use_alt_click_leader=kc_prefs.use_alt_click_leader,
use_pie_click_drag=kc_prefs.use_pie_click_drag,
+ use_file_single_click=kc_prefs.use_file_single_click,
),
)
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 5ecbe7715e3..35eb6490265 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -31,14 +31,14 @@ class Params:
"action_tweak",
"tool_mouse",
"tool_tweak",
+ "tool_maybe_tweak",
+ "tool_maybe_tweak_value",
"context_menu_event",
"cursor_set_event",
"cursor_tweak_event",
"use_mouse_emulate_3_button",
- # Experimental option.
- "pie_value",
- # User preferences.
+ # User preferences:
#
# Swap 'Space/Shift-Space'.
"spacebar_action",
@@ -66,14 +66,26 @@ class Params:
# Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching.
"v3d_alt_mmb_drag_action",
+ "use_file_single_click",
# Convenience variables:
# (derived from other settings).
#
# This case needs to be checked often,
- # convenience for: `params.use_fallback_tool if params.select_mouse == 'RIGHT' else False`.
+ # Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHT' else False)`.
"use_fallback_tool_rmb",
- # Convenience for: `'CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value`.
+ # Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`.
"select_mouse_value_fallback",
+ # Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')`
+ "pie_value",
+ # Shorthand for: `{"type": params.tool_tweak, "value": 'ANY'}`.
+ "tool_tweak_event",
+ # Shorthand for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`.
+ #
+ # NOTE: This is typically used for active tool key-map items however it should never
+ # be used for selection tools (the default box-select tool for example).
+ # Since this means with RMB select enabled in edit-mode for e.g.
+ # `Ctrl-LMB` would be caught by box-select instead of add/extrude.
+ "tool_maybe_tweak_event",
)
def __init__(
@@ -92,9 +104,10 @@ class Params:
use_v3d_tab_menu=False,
use_v3d_shade_ex_pie=False,
use_v3d_mmb_pan=False,
- use_alt_tool=False,
+ use_alt_tool_or_cursor=False,
use_alt_click_leader=False,
use_pie_click_drag=False,
+ use_file_single_click=False,
v3d_tilde_action='VIEW',
v3d_alt_mmb_drag_action='RELATIVE',
):
@@ -102,6 +115,9 @@ class Params:
self.apple = (platform == 'darwin')
self.legacy = legacy
+ if use_mouse_emulate_3_button:
+ assert(use_alt_tool_or_cursor is False)
+
if select_mouse == 'RIGHT':
# Right mouse select.
self.select_mouse = 'RIGHTMOUSE'
@@ -111,12 +127,23 @@ class Params:
self.action_tweak = 'EVT_TWEAK_L'
self.tool_mouse = 'LEFTMOUSE'
self.tool_tweak = 'EVT_TWEAK_L'
+ if use_alt_tool_or_cursor:
+ self.tool_maybe_tweak = 'LEFTMOUSE'
+ self.tool_maybe_tweak_value = 'PRESS'
+ else:
+ self.tool_maybe_tweak = 'EVT_TWEAK_L'
+ self.tool_maybe_tweak_value = 'ANY'
+
self.context_menu_event = {"type": 'W', "value": 'PRESS'}
- self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'}
+
+ # Use the "cursor" functionality for RMB select.
+ if use_alt_tool_or_cursor:
+ self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}
+ else:
+ self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'}
+
self.cursor_tweak_event = None
self.use_fallback_tool = use_fallback_tool
- self.use_fallback_tool_rmb = use_fallback_tool
- self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
self.tool_modifier = {}
else:
# Left mouse select uses Click event for selection. This is a little
@@ -129,6 +156,8 @@ class Params:
self.action_tweak = 'EVT_TWEAK_R'
self.tool_mouse = 'LEFTMOUSE'
self.tool_tweak = 'EVT_TWEAK_L'
+ self.tool_maybe_tweak = 'EVT_TWEAK_L'
+ self.tool_maybe_tweak_value = 'ANY'
if self.legacy:
self.context_menu_event = {"type": 'W', "value": 'PRESS'}
@@ -138,10 +167,9 @@ class Params:
self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True}
self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True}
self.use_fallback_tool = True
- self.use_fallback_tool_rmb = False
- self.select_mouse_value_fallback = self.select_mouse_value
- if use_alt_tool:
+ # Use the "tool" functionality for LMB select.
+ if use_alt_tool_or_cursor:
# Allow `Alt` to be pressed or not.
self.tool_modifier = {"alt": -1}
else:
@@ -149,7 +177,7 @@ class Params:
self.use_mouse_emulate_3_button = use_mouse_emulate_3_button
- # User preferences
+ # User preferences:
self.spacebar_action = spacebar_action
self.use_key_activate_tools = use_key_activate_tools
@@ -163,10 +191,15 @@ class Params:
self.use_alt_click_leader = use_alt_click_leader
self.use_pie_click_drag = use_pie_click_drag
- if not use_pie_click_drag:
- self.pie_value = 'PRESS'
- else:
- self.pie_value = 'CLICK_DRAG'
+
+ self.use_file_single_click = use_file_single_click
+
+ # Convenience variables:
+ self.use_fallback_tool_rmb = self.use_fallback_tool if self.select_mouse == 'RIGHT' else False
+ self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
+ self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS'
+ self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'}
+ self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value}
# ------------------------------------------------------------------------------
@@ -188,6 +221,13 @@ def _fallback_id(text, fallback):
return text
+def any_except(*args):
+ mod = {"ctrl": -1, "alt": -1, "shift": -1, "oskey": -1}
+ for arg in args:
+ del mod[arg]
+ return mod
+
+
# ------------------------------------------------------------------------------
# Keymap Item Wrappers
@@ -300,20 +340,23 @@ def _template_items_object_subdivision_set():
def _template_items_gizmo_tweak_value():
return [
- ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("gizmogroup.gizmo_tweak",
+ {"type": 'LEFTMOUSE', "value": 'PRESS', **any_except("alt")}, None),
]
def _template_items_gizmo_tweak_value_click_drag():
return [
- ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK', "any": True}, None),
- ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', "any": True}, None),
+ ("gizmogroup.gizmo_tweak",
+ {"type": 'LEFTMOUSE', "value": 'CLICK', **any_except("alt")}, None),
+ ("gizmogroup.gizmo_tweak",
+ {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None),
]
def _template_items_gizmo_tweak_value_drag():
return [
- ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', "any": True}, None),
+ ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None),
]
@@ -2112,16 +2155,20 @@ def km_file_browser_main(params):
{"items": items},
)
+ if not params.use_file_single_click:
+ items.extend([
+ ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("open", True), ("deselect_all", not params.legacy)]}),
+ ])
+
items.extend([
("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
# Both .execute and .select are needed here. The former only works if
# there's a file operator (i.e. not in regular editor mode) but is
# needed to load files. The latter makes selection work if there's no
# operator (i.e. in regular editor mode).
- ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
- {"properties": [("open", True), ("deselect_all", not params.legacy)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
- {"properties": [("open", False), ("deselect_all", not params.legacy)]}),
+ {"properties": [("open", params.use_file_single_click), ("deselect_all", not params.legacy)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
{"properties": [("extend", True), ("open", False)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
@@ -4365,7 +4412,6 @@ def km_object_mode(params):
("object.duplicates_make_real", {"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True}, None),
op_menu("VIEW3D_MT_make_single_user", {"type": 'U', "value": 'PRESS'}),
("object.convert", {"type": 'C', "value": 'PRESS', "alt": True}, None),
- ("object.proxy_make", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("object.make_local", {"type": 'L', "value": 'PRESS'}, None),
("object.data_transfer", {"type": 'T', "value": 'PRESS', "shift": True, "ctrl": True}, None),
])
@@ -5639,6 +5685,9 @@ def km_knife_tool_modal_map(_params):
("IGNORE_SNAP_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),
("IGNORE_SNAP_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
("IGNORE_SNAP_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
+ ("X_AXIS", {"type": 'X', "value": 'PRESS'}, None),
+ ("Y_AXIS", {"type": 'Y', "value": 'PRESS'}, None),
+ ("Z_AXIS", {"type": 'Z', "value": 'PRESS'}, None),
("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None),
("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None),
("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None),
@@ -6046,7 +6095,7 @@ def km_generic_tool_annotate_line(params):
"Generic Tool: Annotate Line",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.annotate", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.annotate", params.tool_maybe_tweak_event,
{"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}),
("gpencil.annotate", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
@@ -6096,7 +6145,8 @@ def km_image_editor_tool_uv_cursor(params):
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("uv.cursor_set", {"type": params.tool_mouse, "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'},
+ # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor.
+ ("transform.translate", params.tool_tweak_event,
{"properties": [("release_confirm", True), ("cursor_transform", True)]}),
]},
)
@@ -6121,8 +6171,8 @@ def km_image_editor_tool_uv_select_box(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_box",
- type=params.select_tweak if fallback else params.tool_tweak,
- value='ANY')),
+ # Don't use `tool_maybe_tweak_event`, see comment for this slot.
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_uv_select_for_fallback(params, fallback),
]},
)
@@ -6151,9 +6201,7 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_lasso",
- type=params.select_tweak if fallback else params.tool_tweak,
- value='ANY')
- ),
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_uv_select_for_fallback(params, fallback),
]},
)
@@ -6164,7 +6212,7 @@ def km_image_editor_tool_uv_rip_region(params):
"Image Editor Tool: Uv, Rip Region",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("uv.rip_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6194,7 +6242,7 @@ def km_image_editor_tool_uv_move(params):
"Image Editor Tool: Uv, Move",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.translate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.translate", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6205,7 +6253,7 @@ def km_image_editor_tool_uv_rotate(params):
"Image Editor Tool: Uv, Rotate",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.rotate", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6216,7 +6264,7 @@ def km_image_editor_tool_uv_scale(params):
"Image Editor Tool: Uv, Scale",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.resize", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6241,7 +6289,8 @@ def km_node_editor_tool_select_box(params, *, fallback):
{"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
- "node.select_box", type=params.tool_tweak, value='ANY',
+ "node.select_box",
+ type=params.tool_maybe_tweak, value=params.tool_maybe_tweak_value,
properties=[("tweak", True)],
)),
]},
@@ -6288,7 +6337,8 @@ def km_3d_view_tool_cursor(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("view3d.cursor3d", {"type": params.tool_mouse, "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'},
+ # Don't use `tool_maybe_tweak_event` since it conflicts with `PRESS` that places the cursor.
+ ("transform.translate", params.tool_tweak_event,
{"properties": [("release_confirm", True), ("cursor_transform", True)]}),
]},
)
@@ -6314,8 +6364,8 @@ def km_3d_view_tool_select_box(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_box",
- type=params.select_tweak if fallback else params.tool_tweak,
- value='ANY')),
+ # Don't use `tool_maybe_tweak_event`, see comment for this slot.
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_view3d_select_for_fallback(params, fallback),
]},
)
@@ -6345,8 +6395,7 @@ def km_3d_view_tool_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_lasso",
- type=params.select_tweak if fallback else params.tool_tweak,
- value='ANY')),
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_view3d_select_for_fallback(params, fallback),
]}
)
@@ -6357,8 +6406,7 @@ def km_3d_view_tool_transform(params):
"3D View Tool: Transform",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.from_gizmo",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("transform.from_gizmo", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -6368,8 +6416,7 @@ def km_3d_view_tool_move(params):
"3D View Tool: Move",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.translate",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.translate", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6380,8 +6427,7 @@ def km_3d_view_tool_rotate(params):
"3D View Tool: Rotate",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.rotate",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.rotate", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6392,14 +6438,14 @@ def km_3d_view_tool_scale(params):
"3D View Tool: Scale",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.resize",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.resize", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
def km_3d_view_tool_shear(params):
+ # Don't use 'tool_maybe_tweak_value' since we would loose tweak direction support.
return (
"3D View Tool: Shear",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
@@ -6424,7 +6470,7 @@ def km_3d_view_tool_measure(params):
"3D View Tool: Measure",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("view3d.ruler_add", {"type": params.tool_tweak, "value": 'ANY'}, None),
+ ("view3d.ruler_add", params.tool_maybe_tweak_event, None),
("view3d.ruler_remove", {"type": 'X', "value": 'PRESS'}, None),
("view3d.ruler_remove", {"type": 'DEL', "value": 'PRESS'}, None),
]},
@@ -6436,7 +6482,7 @@ def km_3d_view_tool_pose_breakdowner(params):
"3D View Tool: Pose, Breakdowner",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("pose.breakdown", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("pose.breakdown", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -6446,8 +6492,7 @@ def km_3d_view_tool_pose_push(params):
"3D View Tool: Pose, Push",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("pose.push",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("pose.push", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -6457,8 +6502,7 @@ def km_3d_view_tool_pose_relax(params):
"3D View Tool: Pose, Relax",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("pose.relax",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("pose.relax", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -6468,8 +6512,7 @@ def km_3d_view_tool_edit_armature_roll(params):
"3D View Tool: Edit Armature, Roll",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.transform",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True), ("mode", 'BONE_ROLL')]}),
]},
)
@@ -6480,7 +6523,7 @@ def km_3d_view_tool_edit_armature_bone_size(params):
"3D View Tool: Edit Armature, Bone Size",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True), ("mode", 'BONE_ENVELOPE')]}),
]},
)
@@ -6492,7 +6535,7 @@ def km_3d_view_tool_edit_armature_bone_envelope(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.bbone_resize", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.bbone_resize", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6503,7 +6546,7 @@ def km_3d_view_tool_edit_armature_extrude(params):
"3D View Tool: Edit Armature, Extrude",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("armature.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("armature.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6524,7 +6567,12 @@ def km_3d_view_tool_interactive_add(params):
"3D View Tool: Object, Add Primitive",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY', "any": True},
+ ("view3d.interactive_add",
+ {**params.tool_maybe_tweak_event,
+ # While "Alt" isn't an important shortcut to support,
+ # when the preferences to activate tools when "Alt" is held is used,
+ # it's illogical not to support holding "Alt", even though it is not required.
+ **({"any": True} if "alt" in params.tool_modifier else any_except("alt"))},
{"properties": [("wait_for_input", False)]}),
]},
)
@@ -6535,7 +6583,7 @@ def km_3d_view_tool_edit_mesh_extrude_region(params):
"3D View Tool: Edit Mesh, Extrude Region",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.extrude_context_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.extrude_context_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6546,7 +6594,7 @@ def km_3d_view_tool_edit_mesh_extrude_manifold(params):
"3D View Tool: Edit Mesh, Extrude Manifold",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.extrude_manifold", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [
("MESH_OT_extrude_region", [("use_dissolve_ortho_edges", True)]),
("TRANSFORM_OT_translate", [
@@ -6565,7 +6613,7 @@ def km_3d_view_tool_edit_mesh_extrude_along_normals(params):
"3D View Tool: Edit Mesh, Extrude Along Normals",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.extrude_region_shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.extrude_region_shrink_fatten", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}),
]},
)
@@ -6576,7 +6624,7 @@ def km_3d_view_tool_edit_mesh_extrude_individual(params):
"3D View Tool: Edit Mesh, Extrude Individual",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.extrude_faces_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.extrude_faces_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_shrink_fatten", [("release_confirm", True)])]}),
]},
)
@@ -6598,7 +6646,7 @@ def km_3d_view_tool_edit_mesh_inset_faces(params):
"3D View Tool: Edit Mesh, Inset Faces",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.inset", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.inset", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6609,7 +6657,7 @@ def km_3d_view_tool_edit_mesh_bevel(params):
"3D View Tool: Edit Mesh, Bevel",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.bevel", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.bevel", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6656,7 +6704,7 @@ def km_3d_view_tool_edit_mesh_bisect(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("mesh.bisect", {"type": params.tool_tweak, "value": 'ANY'}, None),
+ ("mesh.bisect", params.tool_maybe_tweak_event, None),
]},
)
@@ -6681,7 +6729,7 @@ def km_3d_view_tool_edit_mesh_spin(params):
"3D View Tool: Edit Mesh, Spin",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("mesh.spin", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -6691,7 +6739,7 @@ def km_3d_view_tool_edit_mesh_spin_duplicate(params):
"3D View Tool: Edit Mesh, Spin Duplicates",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.spin", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.spin", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("dupli", True)]}),
]},
)
@@ -6702,7 +6750,7 @@ def km_3d_view_tool_edit_mesh_smooth(params):
"3D View Tool: Edit Mesh, Smooth",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.vertices_smooth", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.vertices_smooth", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("wait_for_input", False)]}),
]},
)
@@ -6713,7 +6761,7 @@ def km_3d_view_tool_edit_mesh_randomize(params):
"3D View Tool: Edit Mesh, Randomize",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.vertex_random", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("wait_for_input", False)]}),
]},
)
@@ -6724,7 +6772,7 @@ def km_3d_view_tool_edit_mesh_edge_slide(params):
"3D View Tool: Edit Mesh, Edge Slide",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.edge_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.edge_slide", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6735,7 +6783,7 @@ def km_3d_view_tool_edit_mesh_vertex_slide(params):
"3D View Tool: Edit Mesh, Vertex Slide",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.vert_slide", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.vert_slide", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6746,7 +6794,7 @@ def km_3d_view_tool_edit_mesh_shrink_fatten(params):
"3D View Tool: Edit Mesh, Shrink/Fatten",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.shrink_fatten", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.shrink_fatten", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6757,7 +6805,7 @@ def km_3d_view_tool_edit_mesh_push_pull(params):
"3D View Tool: Edit Mesh, Push/Pull",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.push_pull", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.push_pull", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6768,7 +6816,7 @@ def km_3d_view_tool_edit_mesh_to_sphere(params):
"3D View Tool: Edit Mesh, To Sphere",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.tosphere", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6779,7 +6827,7 @@ def km_3d_view_tool_edit_mesh_rip_region(params):
"3D View Tool: Edit Mesh, Rip Region",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.rip_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.rip_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6790,7 +6838,7 @@ def km_3d_view_tool_edit_mesh_rip_edge(params):
"3D View Tool: Edit Mesh, Rip Edge",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("mesh.rip_edge_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("mesh.rip_edge_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6813,7 +6861,7 @@ def km_3d_view_tool_edit_curve_tilt(params):
"3D View Tool: Edit Curve, Tilt",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.tilt", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.tilt", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("release_confirm", True)]}),
]},
)
@@ -6824,7 +6872,7 @@ def km_3d_view_tool_edit_curve_radius(params):
"3D View Tool: Edit Curve, Radius",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.transform", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.transform", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("mode", 'CURVE_SHRINKFATTEN'), ("release_confirm", True)]}),
]},
)
@@ -6835,7 +6883,7 @@ def km_3d_view_tool_edit_curve_randomize(params):
"3D View Tool: Edit Curve, Randomize",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("transform.vertex_random", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("transform.vertex_random", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("wait_for_input", False)]}),
]},
)
@@ -6846,7 +6894,7 @@ def km_3d_view_tool_edit_curve_extrude(params):
"3D View Tool: Edit Curve, Extrude",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("curve.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ ("curve.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier},
{"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
]},
)
@@ -6868,9 +6916,9 @@ def km_3d_view_tool_sculpt_box_hide(params):
"3D View Tool: Sculpt, Box Hide",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("paint.hide_show", {"type": params.tool_tweak, "value": 'ANY'},
+ ("paint.hide_show", params.tool_maybe_tweak_event,
{"properties": [("action", 'HIDE')]}),
- ("paint.hide_show", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True},
+ ("paint.hide_show", {**params.tool_maybe_tweak_event, "ctrl": True},
{"properties": [("action", 'SHOW')]}),
("paint.hide_show", {"type": params.select_mouse, "value": params.select_mouse_value},
{"properties": [("action", 'SHOW'), ("area", 'ALL')]}),
@@ -6883,9 +6931,9 @@ def km_3d_view_tool_sculpt_box_mask(params):
"3D View Tool: Sculpt, Box Mask",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY'},
+ ("paint.mask_box_gesture", params.tool_maybe_tweak_event,
{"properties": [("value", 1.0)]}),
- ("paint.mask_box_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True},
+ ("paint.mask_box_gesture", {**params.tool_maybe_tweak_event, "ctrl": True},
{"properties": [("value", 0.0)]}),
]},
)
@@ -6896,9 +6944,9 @@ def km_3d_view_tool_sculpt_lasso_mask(params):
"3D View Tool: Sculpt, Lasso Mask",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("paint.mask_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'},
+ ("paint.mask_lasso_gesture", params.tool_maybe_tweak_event,
{"properties": [("value", 1.0)]}),
- ("paint.mask_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True},
+ ("paint.mask_lasso_gesture", {**params.tool_maybe_tweak_event, "ctrl": True},
{"properties": [("value", 0.0)]}),
]},
)
@@ -6909,8 +6957,7 @@ def km_3d_view_tool_sculpt_box_face_set(params):
"3D View Tool: Sculpt, Box Face Set",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.face_set_box_gesture", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.face_set_box_gesture", params.tool_maybe_tweak_event, None),
]},
)
@@ -6920,8 +6967,7 @@ def km_3d_view_tool_sculpt_lasso_face_set(params):
"3D View Tool: Sculpt, Lasso Face Set",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.face_set_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.face_set_lasso_gesture", params.tool_maybe_tweak_event, None),
]},
)
@@ -6931,8 +6977,7 @@ def km_3d_view_tool_sculpt_box_trim(params):
"3D View Tool: Sculpt, Box Trim",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.trim_box_gesture", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.trim_box_gesture", params.tool_maybe_tweak_event, None),
]},
)
@@ -6942,8 +6987,7 @@ def km_3d_view_tool_sculpt_lasso_trim(params):
"3D View Tool: Sculpt, Lasso Trim",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.trim_lasso_gesture", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.trim_lasso_gesture", params.tool_maybe_tweak_event, None),
]},
)
@@ -6953,9 +6997,9 @@ def km_3d_view_tool_sculpt_line_mask(params):
"3D View Tool: Sculpt, Line Mask",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY'},
+ ("paint.mask_line_gesture", params.tool_maybe_tweak_event,
{"properties": [("value", 1.0)]}),
- ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True},
+ ("paint.mask_line_gesture", {**params.tool_maybe_tweak_event, "ctrl": True},
{"properties": [("value", 0.0)]}),
]},
)
@@ -6966,8 +7010,7 @@ def km_3d_view_tool_sculpt_line_project(params):
"3D View Tool: Sculpt, Line Project",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.project_line_gesture", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.project_line_gesture", params.tool_maybe_tweak_event, None),
]},
)
@@ -6977,8 +7020,7 @@ def km_3d_view_tool_sculpt_mesh_filter(params):
"3D View Tool: Sculpt, Mesh Filter",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.mesh_filter", {"type": params.tool_tweak, "value": 'ANY'},
- None)
+ ("sculpt.mesh_filter", params.tool_maybe_tweak_event, None)
]},
)
@@ -6988,8 +7030,7 @@ def km_3d_view_tool_sculpt_cloth_filter(params):
"3D View Tool: Sculpt, Cloth Filter",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.cloth_filter", {"type": params.tool_tweak, "value": 'ANY'},
- None)
+ ("sculpt.cloth_filter", params.tool_maybe_tweak_event, None)
]},
)
@@ -6999,8 +7040,7 @@ def km_3d_view_tool_sculpt_color_filter(params):
"3D View Tool: Sculpt, Color Filter",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'},
- None)
+ ("sculpt.color_filter", params.tool_maybe_tweak_event, None)
]},
)
@@ -7010,10 +7050,8 @@ def km_3d_view_tool_sculpt_mask_by_color(params):
"3D View Tool: Sculpt, Mask by Color",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'},
- None),
- ("sculpt.mask_by_color", {"type": params.tool_tweak, "value": 'ANY'},
- None),
+ ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'}, None),
+ ("sculpt.mask_by_color", params.tool_tweak_event, None),
]},
)
@@ -7023,8 +7061,7 @@ def km_3d_view_tool_sculpt_face_set_edit(params):
"3D View Tool: Sculpt, Face Set Edit",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.face_set_edit", {"type": params.tool_mouse, "value": 'PRESS'},
- None),
+ ("sculpt.face_set_edit", {"type": params.tool_mouse, "value": 'PRESS'}, None),
]},
)
@@ -7054,7 +7091,7 @@ def km_3d_view_tool_paint_weight_gradient(params):
"3D View Tool: Paint Weight, Gradient",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("paint.weight_gradient", {"type": params.tool_tweak, "value": 'ANY'}, None),
+ ("paint.weight_gradient", params.tool_maybe_tweak_event, None),
]},
)
@@ -7064,7 +7101,7 @@ def km_3d_view_tool_paint_gpencil_line(params):
"3D View Tool: Paint Gpencil, Line",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_line", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_line", params.tool_maybe_tweak_event,
{"properties": [("wait_for_input", False)]}),
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("wait_for_input", False)]}),
@@ -7081,7 +7118,7 @@ def km_3d_view_tool_paint_gpencil_polyline(params):
"3D View Tool: Paint Gpencil, Polyline",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_polyline", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_polyline", params.tool_maybe_tweak_event,
{"properties": [("wait_for_input", False)]}),
("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("wait_for_input", False)]}),
@@ -7096,7 +7133,7 @@ def km_3d_view_tool_paint_gpencil_box(params):
"3D View Tool: Paint Gpencil, Box",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_box", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_box", params.tool_maybe_tweak_event,
{"properties": [("wait_for_input", False)]}),
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("wait_for_input", False)]}),
@@ -7113,7 +7150,7 @@ def km_3d_view_tool_paint_gpencil_circle(params):
"3D View Tool: Paint Gpencil, Circle",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_circle", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_circle", params.tool_maybe_tweak_event,
{"properties": [("wait_for_input", False)]}),
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("wait_for_input", False)]}),
@@ -7130,7 +7167,7 @@ def km_3d_view_tool_paint_gpencil_arc(params):
"3D View Tool: Paint Gpencil, Arc",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_curve", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_curve", params.tool_maybe_tweak_event,
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
@@ -7147,7 +7184,7 @@ def km_3d_view_tool_paint_gpencil_curve(params):
"3D View Tool: Paint Gpencil, Curve",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.primitive_curve", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.primitive_curve", params.tool_maybe_tweak_event,
{"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
# Lasso select
("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
@@ -7187,7 +7224,7 @@ def km_3d_view_tool_paint_gpencil_interpolate(params):
"3D View Tool: Paint Gpencil, Interpolate",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.interpolate", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7212,7 +7249,9 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
- "gpencil.select_box", type=params.select_tweak if fallback else params.tool_tweak, value='ANY')),
+ "gpencil.select_box",
+ # Don't use `tool_maybe_tweak_event`, see comment for this slot.
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_view3d_gpencil_select_for_fallback(params, fallback),
]},
)
@@ -7242,8 +7281,7 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"gpencil.select_lasso",
- type=params.select_tweak if fallback else params.tool_tweak,
- value='ANY')),
+ **({"type": params.select_tweak, "value": 'ANY'} if fallback else params.tool_tweak_event))),
*_template_view3d_gpencil_select_for_fallback(params, fallback),
]}
)
@@ -7254,7 +7292,7 @@ def km_3d_view_tool_edit_gpencil_extrude(params):
"3D View Tool: Edit Gpencil, Extrude",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.extrude_move", {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, None),
+ ("gpencil.extrude_move", {**params.tool_maybe_tweak_event, **params.tool_modifier}, None),
]},
)
@@ -7265,7 +7303,7 @@ def km_3d_view_tool_edit_gpencil_radius(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("transform.transform", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.transform", params.tool_maybe_tweak_event,
{"properties": [("mode", 'GPENCIL_SHRINKFATTEN'), ("release_confirm", True)]}),
]},
)
@@ -7277,7 +7315,7 @@ def km_3d_view_tool_edit_gpencil_bend(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("transform.bend", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.bend", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7289,7 +7327,7 @@ def km_3d_view_tool_edit_gpencil_shear(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("transform.shear", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.shear", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7301,7 +7339,7 @@ def km_3d_view_tool_edit_gpencil_to_sphere(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("transform.tosphere", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.tosphere", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7313,7 +7351,7 @@ def km_3d_view_tool_edit_gpencil_transform_fill(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
# No need for `tool_modifier` since this takes all input.
- ("gpencil.transform_fill", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.transform_fill", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7324,7 +7362,7 @@ def km_3d_view_tool_edit_gpencil_interpolate(params):
"3D View Tool: Edit Gpencil, Interpolate",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'},
+ ("gpencil.interpolate", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7342,7 +7380,7 @@ def km_3d_view_tool_sculpt_gpencil_select_box(params):
return (
"3D View Tool: Sculpt Gpencil, Select Box",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
- {"items": _template_items_tool_select_actions("gpencil.select_box", type=params.tool_tweak, value='ANY')},
+ {"items": _template_items_tool_select_actions("gpencil.select_box", **params.tool_tweak_event)},
)
@@ -7361,7 +7399,7 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params):
return (
"3D View Tool: Sculpt Gpencil, Select Lasso",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
- {"items": _template_items_tool_select_actions("gpencil.select_lasso", type=params.tool_tweak, value='ANY')},
+ {"items": _template_items_tool_select_actions("gpencil.select_lasso", **params.tool_tweak_event)},
)
@@ -7383,8 +7421,9 @@ def km_sequencer_editor_tool_select_box(params, *, fallback):
_fallback_id("Sequencer Tool: Select Box", fallback),
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
+ # Don't use `tool_maybe_tweak_event`, see comment for this slot.
*_template_items_tool_select_actions_simple(
- "sequencer.select_box", type=params.tool_tweak, value='ANY',
+ "sequencer.select_box", **params.tool_tweak_event,
properties=[("tweak", params.select_mouse == 'LEFTMOUSE')],
),
# RMB select can already set the frame, match the tweak tool.
@@ -7425,7 +7464,7 @@ def km_sequencer_editor_tool_move(params):
"Sequencer Tool: Move",
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.translate", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7436,7 +7475,7 @@ def km_sequencer_editor_tool_rotate(params):
"Sequencer Tool: Rotate",
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.rotate", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
@@ -7447,7 +7486,7 @@ def km_sequencer_editor_tool_scale(params):
"Sequencer Tool: Scale",
{"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
{"items": [
- ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'},
+ ("transform.resize", params.tool_maybe_tweak_event,
{"properties": [("release_confirm", True)]}),
]},
)
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 6a24f072ed0..886abae3602 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -3876,6 +3876,9 @@ def km_knife_tool_modal_map(_params):
("IGNORE_SNAP_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None),
("IGNORE_SNAP_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None),
("IGNORE_SNAP_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None),
+ ("X_AXIS", {"type": 'X', "value": 'PRESS'}, None),
+ ("Y_AXIS", {"type": 'Y', "value": 'PRESS'}, None),
+ ("Z_AXIS", {"type": 'Z', "value": 'PRESS'}, None),
("ANGLE_SNAP_TOGGLE", {"type": 'A', "value": 'PRESS'}, None),
("CYCLE_ANGLE_SNAP_EDGE", {"type": 'R', "value": 'PRESS'}, None),
("CUT_THROUGH_TOGGLE", {"type": 'C', "value": 'PRESS'}, None),
diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
index c8328f5ee42..f8b504b2e34 100644
--- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
+++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
@@ -54,11 +54,26 @@ def update_factory_startup_grease_pencils():
gpd.onion_keyframe_type = 'ALL'
+def update_factory_startup_theme():
+ # To prevent saving over the current theme Preferences,
+ # store the current state of use_preferences_save to use later.
+ preferences = bpy.context.preferences
+ save_preferences_state = preferences.use_preferences_save
+
+ # Turn use_preferences_save off and set header background alpha.
+ preferences.use_preferences_save = False
+ preferences.themes['Default'].view_3d.space.header[3] = 0.8
+
+ # Restore the original use_preferences_save status.
+ preferences.use_preferences_save = save_preferences_state
+
+
@persistent
def load_handler(_):
update_factory_startup_screens()
update_factory_startup_scenes()
update_factory_startup_grease_pencils()
+ update_factory_startup_theme()
def register():
diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py
index b241e537c38..de46e5c85fb 100644
--- a/release/scripts/startup/bl_operators/assets.py
+++ b/release/scripts/startup/bl_operators/assets.py
@@ -27,17 +27,28 @@ from bpy_extras.asset_utils import (
)
-class ASSET_OT_tag_add(Operator):
+class AssetBrowserMetadataOperator:
+ @classmethod
+ def poll(cls, context):
+ if not SpaceAssetInfo.is_asset_browser_poll(context) or not context.asset_file_handle:
+ return False
+
+ if not context.asset_file_handle.local_id:
+ Operator.poll_message_set(
+ "Asset metadata from external asset libraries can't be "
+ "edited, only assets stored in the current file can"
+ )
+ return False
+ return True
+
+
+class ASSET_OT_tag_add(AssetBrowserMetadataOperator, Operator):
"""Add a new keyword tag to the active asset"""
bl_idname = "asset.tag_add"
bl_label = "Add Asset Tag"
bl_options = {'REGISTER', 'UNDO'}
- @classmethod
- def poll(cls, context):
- return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context)
-
def execute(self, context):
active_asset = SpaceAssetInfo.get_active_asset(context)
active_asset.tags.new("Unnamed Tag")
@@ -45,7 +56,7 @@ class ASSET_OT_tag_add(Operator):
return {'FINISHED'}
-class ASSET_OT_tag_remove(Operator):
+class ASSET_OT_tag_remove(AssetBrowserMetadataOperator, Operator):
"""Remove an existing keyword tag from the active asset"""
bl_idname = "asset.tag_remove"
@@ -54,21 +65,20 @@ class ASSET_OT_tag_remove(Operator):
@classmethod
def poll(cls, context):
- if not SpaceAssetInfo.is_asset_browser_poll(context):
+ if not super().poll(context):
return False
- active_asset = SpaceAssetInfo.get_active_asset(context)
- if not active_asset:
- return False
-
- return active_asset.active_tag in range(len(active_asset.tags))
+ active_asset_file = context.asset_file_handle
+ asset_metadata = active_asset_file.asset_data
+ return asset_metadata.active_tag in range(len(asset_metadata.tags))
def execute(self, context):
- active_asset = SpaceAssetInfo.get_active_asset(context)
- tag = active_asset.tags[active_asset.active_tag]
+ active_asset_file = context.asset_file_handle
+ asset_metadata = active_asset_file.asset_data
+ tag = asset_metadata.tags[asset_metadata.active_tag]
- active_asset.tags.remove(tag)
- active_asset.active_tag -= 1
+ asset_metadata.tags.remove(tag)
+ asset_metadata.active_tag -= 1
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index ebf80ca9ee4..6bf45cc5a15 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -23,6 +23,7 @@ import bpy
from bpy.types import (
Menu,
Operator,
+ bpy_prop_array,
)
from bpy.props import (
BoolProperty,
@@ -31,6 +32,8 @@ from bpy.props import (
FloatProperty,
IntProperty,
StringProperty,
+ IntVectorProperty,
+ FloatVectorProperty,
)
from bpy.app.translations import pgettext_iface as iface_
@@ -1266,48 +1269,20 @@ rna_path = StringProperty(
options={'HIDDEN'},
)
-rna_value = StringProperty(
- name="Property Value",
- description="Property value edit",
- maxlen=1024,
-)
-
-rna_default = StringProperty(
- name="Default Value",
- description="Default value of the property. Important for NLA mixing",
- maxlen=1024,
-)
-
-rna_custom_property = StringProperty(
+rna_custom_property_name = StringProperty(
name="Property Name",
description="Property name edit",
# Match `MAX_IDPROP_NAME - 1` in Blender's source.
maxlen=63,
)
-rna_min = FloatProperty(
- name="Min",
- description="Minimum value of the property",
- default=-10000.0,
- precision=3,
-)
-
-rna_max = FloatProperty(
- name="Max",
- description="Maximum value of the property",
- default=10000.0,
- precision=3,
-)
-
-rna_use_soft_limits = BoolProperty(
- name="Use Soft Limits",
- description="Limits the Property Value slider to a range, values outside the range must be inputted numerically",
-)
-
-rna_is_overridable_library = BoolProperty(
- name="Is Library Overridable",
- description="Allow the property to be overridden when the data-block is linked",
- default=False,
+rna_custom_property_type_items = (
+ ('FLOAT', "Float", "A single floating-point value"),
+ ('FLOAT_ARRAY', "Float Array", "An array of floating-point values"),
+ ('INT', "Integer", "A single integer"),
+ ('INT_ARRAY', "Integer Array", "An array of integers"),
+ ('STRING', "String", "A string value"),
+ ('PYTHON', "Python", "Edit a python value directly, for unsupported property types"),
)
# Most useful entries of rna_enum_property_subtype_items for number arrays:
@@ -1319,160 +1294,333 @@ rna_vector_subtype_items = (
('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"),
)
-
class WM_OT_properties_edit(Operator):
- """Edit the attributes of the property"""
+ """Change a custom property's type, or adjust how it is displayed in the interface"""
bl_idname = "wm.properties_edit"
bl_label = "Edit Property"
# register only because invoke_props_popup requires.
bl_options = {'REGISTER', 'INTERNAL'}
+ # Common settings used for all property types. Generally, separate properties are used for each
+ # type to improve the experience when choosing UI data values.
+
data_path: rna_path
- property: rna_custom_property
- value: rna_value
- default: rna_default
- min: rna_min
- max: rna_max
- use_soft_limits: rna_use_soft_limits
- is_overridable_library: rna_is_overridable_library
- soft_min: rna_min
- soft_max: rna_max
+ property_name: rna_custom_property_name
+ property_type: EnumProperty(
+ name="Type",
+ items=lambda self, _context: WM_OT_properties_edit.type_items,
+ )
+ is_overridable_library: BoolProperty(
+ name="Is Library Overridable",
+ description="Allow the property to be overridden when the data-block is linked",
+ default=False,
+ )
description: StringProperty(
- name="Tooltip",
+ name="Description",
+ )
+
+ # Shared for integer and string properties.
+
+ use_soft_limits: BoolProperty(
+ name="Use Soft Limits",
+ description="Limits the Property Value slider to a range, values outside the range must be inputted numerically",
+ )
+ array_length: IntProperty(
+ name="Array Length",
+ default=3,
+ min=1,
+ max=32, # 32 is the maximum size for RNA array properties.
+ )
+
+ # Integer properties.
+
+ # This property stores values for both array and non-array properties.
+ default_int: IntVectorProperty(
+ name="Default Value",
+ size=32,
+ )
+ min_int: IntProperty(
+ name="Min",
+ default=-10000,
+ )
+ max_int: IntProperty(
+ name="Max",
+ default=10000,
+ )
+ soft_min_int: IntProperty(
+ name="Soft Min",
+ default=-10000,
+ )
+ soft_max_int: IntProperty(
+ name="Soft Max",
+ default=10000,
+ )
+ step_int: IntProperty(
+ name="Step",
+ min=1,
+ default=1,
+ )
+
+ # Float properties.
+
+ # This property stores values for both array and non-array properties.
+ default_float: FloatVectorProperty(
+ name="Default Value",
+ size=32,
+ )
+ min_float: FloatProperty(
+ name="Min",
+ default=-10000.0,
+ )
+ max_float: FloatProperty(
+ name="Max",
+ default=-10000.0,
+ )
+ soft_min_float: FloatProperty(
+ name="Soft Min",
+ default=-10000.0,
+ )
+ soft_max_float: FloatProperty(
+ name="Soft Max",
+ default=-10000.0,
+ )
+ precision: IntProperty(
+ name="Precision",
+ default=3,
+ min=0,
+ max=8,
+ )
+ step_float: FloatProperty(
+ name="Step",
+ default=0.1,
+ min=0.001,
)
subtype: EnumProperty(
name="Subtype",
items=lambda self, _context: WM_OT_properties_edit.subtype_items,
)
- subtype_items = rna_vector_subtype_items
-
- def _init_subtype(self, prop_type, is_array, subtype):
- subtype = subtype or 'NONE'
- subtype_items = rna_vector_subtype_items
+ # String properties.
- # Add a temporary enum entry to preserve unknown subtypes
- if not any(subtype == item[0] for item in subtype_items):
- subtype_items += ((subtype, subtype, ""),)
+ default_string: StringProperty(
+ name="Default Value",
+ maxlen=1024,
+ )
- WM_OT_properties_edit.subtype_items = subtype_items
- self.subtype = subtype
+ # Store the value converted to a string as a fallback for otherwise unsupported types.
+ eval_string: StringProperty(
+ name="Value",
+ description="Python value for unsupported custom property types"
+ )
- def _cmp_props_get(self):
- # Changing these properties will refresh the UI
- return {
- "use_soft_limits": self.use_soft_limits,
- "soft_range": (self.soft_min, self.soft_max),
- "hard_range": (self.min, self.max),
- }
+ type_items = rna_custom_property_type_items
+ subtype_items = rna_vector_subtype_items
- def get_value_eval(self):
- failed = False
- try:
- value_eval = eval(self.value)
- # assert else None -> None, not "None", see T33431.
- assert(type(value_eval) in {str, float, int, bool, tuple, list})
- except:
- failed = True
- value_eval = self.value
+ # Helper method to avoid repetative code to retrieve a single value from sequences and non-sequences.
+ @staticmethod
+ def _convert_new_value_single(old_value, new_type):
+ if hasattr(old_value, "__len__"):
+ return new_type(old_value[0])
+ return new_type(old_value)
- return value_eval, failed
+ # Helper method to create a list of a given value and type, using a sequence or non-sequence old value.
+ @staticmethod
+ def _convert_new_value_array(old_value, new_type, new_len):
+ if hasattr(old_value, "__len__"):
+ new_array = [new_type()] * new_len
+ for i in range(min(len(old_value), new_len)):
+ new_array[i] = new_type(old_value[i])
+ return new_array
+ return [new_type(old_value)] * new_len
+
+ # Convert an old property for a string, avoiding unhelpful string representations for custom list types.
+ @staticmethod
+ def _convert_old_property_to_string(item, name):
+ # The IDProperty group view API currently doesn't have a "lookup" method.
+ for key, value in item.items():
+ if key == name:
+ old_value = value
+ break
- def get_default_eval(self):
- failed = False
- try:
- default_eval = eval(self.default)
- # assert else None -> None, not "None", see T33431.
- assert(type(default_eval) in {str, float, int, bool, tuple, list})
- except:
- failed = True
- default_eval = self.default
+ # In order to get a better string conversion, convert the property to a builtin sequence type first.
+ to_dict = getattr(old_value, "to_dict", None)
+ to_list = getattr(old_value, "to_list", None)
+ if to_dict:
+ old_value = to_dict()
+ elif to_list:
+ old_value = to_list()
- return default_eval, failed
+ return str(old_value)
- def execute(self, context):
+ # Retrieve the current type of the custom property on the RNA struct. Some properties like group properties
+ # can be created in the UI, but editing their meta-data isn't supported. In that case, return 'PYTHON'.
+ def _get_property_type(self, item, property_name):
from rna_prop_ui import (
- rna_idprop_ui_prop_update,
rna_idprop_value_item_type,
)
- data_path = self.data_path
- prop = self.property
- prop_escape = bpy.utils.escape_identifier(prop)
-
- prop_old = getattr(self, "_last_prop", [None])[0]
+ prop_value = item[property_name]
- if prop_old is None:
- self.report({'ERROR'}, "Direct execution not supported")
- return {'CANCELLED'}
-
- value_eval, value_failed = self.get_value_eval()
- default_eval, default_failed = self.get_default_eval()
-
- # First remove
- item = eval("context.%s" % data_path)
-
- if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
- self.report({'ERROR'}, "Cannot edit properties from override data")
- return {'CANCELLED'}
-
- prop_type_old = type(item[prop_old])
+ prop_type, is_array = rna_idprop_value_item_type(prop_value)
+ if prop_type == int:
+ if is_array:
+ return 'INT_ARRAY'
+ return 'INT'
+ elif prop_type == float:
+ if is_array:
+ return 'FLOAT_ARRAY'
+ return 'FLOAT'
+ elif prop_type == str:
+ if is_array:
+ return 'PYTHON'
+ return 'STRING'
- # Deleting the property will also remove the UI data.
- del item[prop_old]
+ return 'PYTHON'
- # Reassign
- item[prop] = value_eval
- item.property_overridable_library_set('["%s"]' % prop_escape, self.is_overridable_library)
- rna_idprop_ui_prop_update(item, prop)
+ def _init_subtype(self, subtype):
+ subtype = subtype or 'NONE'
+ subtype_items = rna_vector_subtype_items
- self._last_prop[:] = [prop]
+ # Add a temporary enum entry to preserve unknown subtypes
+ if not any(subtype == item[0] for item in subtype_items):
+ subtype_items += ((subtype, subtype, ""),)
- prop_value = item[prop]
- prop_type_new = type(prop_value)
- prop_type, is_array = rna_idprop_value_item_type(prop_value)
+ WM_OT_properties_edit.subtype_items = subtype_items
+ self.subtype = subtype
- if prop_type == int:
- ui_data = item.id_properties_ui(prop)
- if type(default_eval) == str:
- self.report({'WARNING'}, "Could not evaluate number from default value")
- default_eval = None
- elif hasattr(default_eval, "__len__"):
- default_eval = [int(round(value)) for value in default_eval]
+ # Fill the operator's properties with the UI data properties from the existing custom property.
+ # Note that if the UI data doesn't exist yet, the access will create it and use those default values.
+ def _fill_old_ui_data(self, item, name):
+ ui_data = item.id_properties_ui(name)
+ rna_data = ui_data.as_dict()
+
+ if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}:
+ self.min_float = rna_data["min"]
+ self.max_float = rna_data["max"]
+ self.soft_min_float = rna_data["soft_min"]
+ self.soft_max_float = rna_data["soft_max"]
+ self.precision = rna_data["precision"]
+ self.step_float = rna_data["step"]
+ self.subtype = rna_data["subtype"]
+ self.use_soft_limits = (
+ self.min_float != self.soft_min_float or
+ self.max_float != self.soft_max_float
+ )
+ default = self._convert_new_value_array(rna_data["default"], float, 32)
+ self.default_float = default if isinstance(default, list) else [default] * 32
+ elif self.property_type in {'INT', 'INT_ARRAY'}:
+ self.min_int = rna_data["min"]
+ self.max_int = rna_data["max"]
+ self.soft_min_int = rna_data["soft_min"]
+ self.soft_max_int = rna_data["soft_max"]
+ self.step_int = rna_data["step"]
+ self.use_soft_limits = (
+ self.min_int != self.soft_min_int or
+ self.max_int != self.soft_max_int
+ )
+ self.default_int = self._convert_new_value_array(rna_data["default"], int, 32)
+ elif self.property_type == 'STRING':
+ self.default_string = rna_data["default"]
+
+ if self.property_type in { 'FLOAT_ARRAY', 'INT_ARRAY'}:
+ self.array_length = len(item[name])
+
+ # The dictionary does not contain the description if it was empty.
+ self.description = rna_data.get("description", "")
+
+ self._init_subtype(self.subtype)
+ escaped_name = bpy.utils.escape_identifier(name)
+ self.is_overridable_library = bool(item.is_property_overridable_library('["%s"]' % escaped_name))
+
+ # When the operator chooses a different type than the original property,
+ # attempt to convert the old value to the new type for continuity and speed.
+ def _get_converted_value(self, item, name_old, prop_type_new):
+ if prop_type_new == 'INT':
+ return self._convert_new_value_single(item[name_old], int)
+
+ if prop_type_new == 'FLOAT':
+ return self._convert_new_value_single(item[name_old], float)
+
+ if prop_type_new == 'INT_ARRAY':
+ prop_type_old = self._get_property_type(item, name_old)
+ if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY'}:
+ return self._convert_new_value_array(item[name_old], int, self.array_length)
+
+ if prop_type_new == 'FLOAT_ARRAY':
+ prop_type_old = self._get_property_type(item, name_old)
+ if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}:
+ return self._convert_new_value_array(item[name_old], float, self.array_length)
+
+ if prop_type_new == 'STRING':
+ return self._convert_old_property_to_string(item, name_old)
+
+ # If all else fails, create an empty string property. That should avoid errors later on anyway.
+ return ""
+
+ # Any time the target type is changed in the dialog, it's helpful to convert the UI data values
+ # to the new type as well, when possible, currently this only applies for floats and ints.
+ def _convert_old_ui_data_to_new_type(self, prop_type_old, prop_type_new):
+ if prop_type_new in {'INT', 'INT_ARRAY'} and prop_type_old in {'FLOAT', 'FLOAT_ARRAY'}:
+ self.min_int = int(self.min_float)
+ self.max_int = int(self.max_float)
+ self.soft_min_int = int(self.soft_min_float)
+ self.soft_max_int = int(self.soft_max_float)
+ self.default_int = self._convert_new_value_array(self.default_float, int, 32)
+ elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'} and prop_type_old in {'INT', 'INT_ARRAY'}:
+ self.min_float = float(self.min_int)
+ self.max_float = float(self.max_int)
+ self.soft_min_float = float(self.soft_min_int)
+ self.soft_max_float = float(self.soft_max_int)
+ self.default_float = self._convert_new_value_array(self.default_int, float, 32)
+ # Don't convert between string and float/int defaults here, it's not expected like the other conversions.
+
+ # Fill the property's UI data with the values chosen in the operator.
+ def _create_ui_data_for_new_prop(self, item, name, prop_type_new):
+ if prop_type_new in {'INT', 'INT_ARRAY'}:
+ ui_data = item.id_properties_ui(name)
ui_data.update(
- min=int(round(self.min)),
- max=int(round(self.max)),
- soft_min=int(round(self.soft_min)),
- soft_max=int(round(self.soft_max)),
- default=default_eval,
- subtype=self.subtype,
- description=self.description
+ min=self.min_int,
+ max=self.max_int,
+ soft_min=self.soft_min_int if self.use_soft_limits else self.min_int,
+ soft_max=self.soft_max_int if self.use_soft_limits else self.min_int,
+ step=self.step_int,
+ default=self.default_int[0] if prop_type_new == 'INT' else self.default_int[:self.array_length],
+ description=self.description,
)
- elif prop_type == float:
- ui_data = item.id_properties_ui(prop)
- if type(default_eval) == str:
- self.report({'WARNING'}, "Could not evaluate number from default value")
- default_eval = None
+ elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'}:
+ ui_data = item.id_properties_ui(name)
ui_data.update(
- min=self.min,
- max=self.max,
- soft_min=self.soft_min,
- soft_max=self.soft_max,
- default=default_eval,
+ min=self.min_float,
+ max=self.max_float,
+ soft_min=self.soft_min_float if self.use_soft_limits else self.min_float,
+ soft_max=self.soft_max_float if self.use_soft_limits else self.max_float,
+ step=self.step_float,
+ precision=self.precision,
+ default=self.default_float[0] if prop_type_new == 'FLOAT' else self.default_float[:self.array_length],
+ description=self.description,
subtype=self.subtype,
- description=self.description
)
- elif prop_type == str and not is_array and not default_failed: # String arrays do not support UI data.
- ui_data = item.id_properties_ui(prop)
+ elif prop_type_new == 'STRING':
+ ui_data = item.id_properties_ui(name)
ui_data.update(
- default=self.default,
- subtype=self.subtype,
- description=self.description
+ default=self.default_string,
+ description=self.description,
)
+ escaped_name = bpy.utils.escape_identifier(name)
+ item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library)
+
+ def _update_blender_for_prop_change(self, context, item, name, prop_type_old, prop_type_new):
+ from rna_prop_ui import (
+ rna_idprop_ui_prop_update,
+ )
+
+ rna_idprop_ui_prop_update(item, name)
+
# If we have changed the type of the property, update its potential anim curves!
if prop_type_old != prop_type_new:
- data_path = '["%s"]' % prop_escape
+ escaped_name = bpy.utils.escape_identifier(name)
+ data_path = '["%s"]' % escaped_name
done = set()
def _update(fcurves):
@@ -1498,149 +1646,196 @@ class WM_OT_properties_edit(Operator):
for nt in adt.nla_tracks:
_update_strips(nt.strips)
- # Otherwise existing buttons which reference freed
- # memory may crash Blender T26510.
- # context.area.tag_redraw()
+ # Otherwise existing buttons which reference freed memory may crash Blender (T26510).
for win in context.window_manager.windows:
for area in win.screen.areas:
area.tag_redraw()
- return {'FINISHED'}
+ def execute(self, context):
+ name_old = getattr(self, "_old_prop_name", [None])[0]
+ if name_old is None:
+ self.report({'ERROR'}, "Direct execution not supported")
+ return {'CANCELLED'}
- def invoke(self, context, _event):
- from rna_prop_ui import (
- rna_idprop_value_to_python,
- rna_idprop_value_item_type
- )
+ data_path = self.data_path
+ name = self.property_name
- prop = self.property
- prop_escape = bpy.utils.escape_identifier(prop)
+ item = eval("context.%s" % data_path)
+ if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
+ self.report({'ERROR'}, "Cannot edit properties from override data")
+ return {'CANCELLED'}
- data_path = self.data_path
+ prop_type_old = self._get_property_type(item, name_old)
+ prop_type_new = self.property_type
+ self._old_prop_name[:] = [name]
+
+ if prop_type_new == 'PYTHON':
+ try:
+ new_value = eval(self.eval_string)
+ except Exception as ex:
+ self.report({'WARNING'}, "Python evaluation failed: " + str(ex))
+ return {'CANCELLED'}
+ try:
+ item[name] = new_value
+ except Exception as ex:
+ self.report({'ERROR'}, "Failed to assign value: " + str(ex))
+ return {'CANCELLED'}
+ if name_old != name:
+ del item[name_old]
+ else:
+ new_value = self._get_converted_value(item, name_old, prop_type_new)
+ del item[name_old]
+ item[name] = new_value
+
+ self._create_ui_data_for_new_prop(item, name, prop_type_new)
+ self._update_blender_for_prop_change(context, item, name, prop_type_old, prop_type_new)
+
+ return {'FINISHED'}
+
+ def invoke(self, context, _event):
+ data_path = self.data_path
if not data_path:
self.report({'ERROR'}, "Data path not set")
return {'CANCELLED'}
- self._last_prop = [prop]
+ name = self.property_name
- item = eval("context.%s" % data_path)
+ self._old_prop_name = [name]
+ self.last_property_type = self.property_type
+ item = eval("context.%s" % data_path)
if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
- self.report({'ERROR'}, "Cannot edit properties from override data")
+ self.report({'ERROR'}, "Properties from override data can not be edited")
return {'CANCELLED'}
- # retrieve overridable static
- is_overridable = item.is_property_overridable_library('["%s"]' % prop_escape)
- self.is_overridable_library = bool(is_overridable)
-
- # default default value
- value, value_failed = self.get_value_eval()
- prop_type, is_array = rna_idprop_value_item_type(value)
- if prop_type in {int, float}:
- self.default = str(prop_type(0))
- else:
- self.default = ""
-
- # setup defaults
- if prop_type in {int, float}:
- ui_data = item.id_properties_ui(prop)
- rna_data = ui_data.as_dict()
- self.subtype = rna_data["subtype"]
- self.min = rna_data["min"]
- self.max = rna_data["max"]
- self.soft_min = rna_data["soft_min"]
- self.soft_max = rna_data["soft_max"]
- self.use_soft_limits = (
- self.min != self.soft_min or
- self.max != self.soft_max
- )
- self.default = str(rna_data["default"])
- self.description = rna_data.get("description", "")
- elif prop_type == str and not is_array and not value_failed: # String arrays do not support UI data.
- ui_data = item.id_properties_ui(prop)
- rna_data = ui_data.as_dict()
- self.subtype = rna_data["subtype"]
- self.default = str(rna_data["default"])
- self.description = rna_data.get("description", "")
- else:
- self.min = self.soft_min = 0
- self.max = self.soft_max = 1
- self.use_soft_limits = False
- self.description = ""
+ # Set operator's property type with the type of the existing property, to display the right settings.
+ old_type = self._get_property_type(item, name)
+ self.property_type = old_type
- self._init_subtype(prop_type, is_array, self.subtype)
+ # So that the operator can do something for unsupported properties, change the property into
+ # a string, just for editing in the dialog. When the operator executes, it will be converted back
+ # into a python value. Always do this conversion, in case the Python property edit type is selected.
+ self.eval_string = self._convert_old_property_to_string(item, name)
- # store for comparison
- self._cmp_props = self._cmp_props_get()
+ if old_type != 'PYTHON':
+ self._fill_old_ui_data(item, name)
wm = context.window_manager
return wm.invoke_props_dialog(self)
- def check(self, _context):
- cmp_props = self._cmp_props_get()
+ def check(self, context):
changed = False
- if self._cmp_props != cmp_props:
- if cmp_props["use_soft_limits"]:
- if cmp_props["soft_range"] != self._cmp_props["soft_range"]:
- self.min = min(self.min, self.soft_min)
- self.max = max(self.max, self.soft_max)
+
+ # In order to convert UI data between types for type changes before the operator has actually executed,
+ # compare against the type the last time the check method was called (the last time a value was edited).
+ if self.property_type != self.last_property_type:
+ self._convert_old_ui_data_to_new_type(self.last_property_type, self.property_type)
+ changed = True
+
+ # Make sure that min is less than max, soft range is inside hard range, etc.
+ if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}:
+ if self.min_float > self.max_float:
+ self.min_float, self.max_float = self.max_float, self.min_float
+ changed = True
+ if self.soft_min_float > self.soft_max_float:
+ self.soft_min_float, self.soft_max_float = self.soft_max_float, self.soft_min_float
+ changed = True
+ if self.use_soft_limits:
+ if self.soft_max_float > self.max_float:
+ self.soft_max_float = self.max_float
changed = True
- if cmp_props["hard_range"] != self._cmp_props["hard_range"]:
- self.soft_min = max(self.min, self.soft_min)
- self.soft_max = min(self.max, self.soft_max)
+ if self.soft_min_float < self.min_float:
+ self.soft_min_float = self.min_float
changed = True
- else:
- if cmp_props["soft_range"] != cmp_props["hard_range"]:
- self.soft_min = self.min
- self.soft_max = self.max
+ elif self.property_type in {'INT', 'INT_ARRAY'}:
+ if self.min_int > self.max_int:
+ self.min_int, self.max_int = self.max_int, self.min_int
+ changed = True
+ if self.soft_min_int > self.soft_max_int:
+ self.soft_min_int, self.soft_max_int = self.soft_max_int, self.soft_min_int
+ changed = True
+ if self.use_soft_limits:
+ if self.soft_max_int > self.max_int:
+ self.soft_max_int = self.max_int
+ changed = True
+ if self.soft_min_int < self.min_int:
+ self.soft_min_int = self.min_int
changed = True
- changed |= (cmp_props["use_soft_limits"] != self._cmp_props["use_soft_limits"])
-
- if changed:
- cmp_props = self._cmp_props_get()
-
- self._cmp_props = cmp_props
+ self.last_property_type = self.property_type
return changed
def draw(self, _context):
- from rna_prop_ui import (
- rna_idprop_value_item_type,
- )
-
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
- layout.prop(self, "property")
- layout.prop(self, "value")
+ layout.prop(self, "property_type")
+ layout.prop(self, "property_name")
- value, value_failed = self.get_value_eval()
- proptype, is_array = rna_idprop_value_item_type(value)
+ if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}:
+ if self.property_type == 'FLOAT_ARRAY':
+ layout.prop(self, "array_length")
+ col = layout.column(align=True)
+ col.prop(self, "default_float", index=0, text="Default")
+ for i in range(1, self.array_length):
+ col.prop(self, "default_float", index=i, text=" ")
+ else:
+ layout.prop(self, "default_float", index=0)
+
+ col = layout.column(align=True)
+ col.prop(self, "min_float")
+ col.prop(self, "max_float")
+
+ col = layout.column()
+ col.prop(self, "is_overridable_library")
+ col.prop(self, "use_soft_limits")
+
+ col = layout.column(align=True)
+ col.enabled = self.use_soft_limits
+ col.prop(self, "soft_min_float", text="Soft Min")
+ col.prop(self, "soft_max_float", text="Max")
+
+ layout.prop(self, "step_float")
+ layout.prop(self, "precision")
+
+ # Subtype is only supported for float properties currently.
+ if self.property_type != 'FLOAT':
+ layout.prop(self, "subtype")
+ elif self.property_type in {'INT', 'INT_ARRAY'}:
+ if self.property_type == 'INT_ARRAY':
+ layout.prop(self, "array_length")
+ col = layout.column(align=True)
+ col.prop(self, "default_int", index=0, text="Default")
+ for i in range(1, self.array_length):
+ col.prop(self, "default_int", index=i, text=" ")
+ else:
+ layout.prop(self, "default_int", index=0)
- row = layout.row()
- row.enabled = proptype in {int, float, str}
- row.prop(self, "default")
+ col = layout.column(align=True)
+ col.prop(self, "min_int")
+ col.prop(self, "max_int")
- col = layout.column(align=True)
- col.prop(self, "min")
- col.prop(self, "max")
+ col = layout.column()
+ col.prop(self, "is_overridable_library")
+ col.prop(self, "use_soft_limits")
- col = layout.column()
- col.prop(self, "is_overridable_library")
- col.prop(self, "use_soft_limits")
+ col = layout.column(align=True)
+ col.enabled = self.use_soft_limits
+ col.prop(self, "soft_min_int", text="Soft Min")
+ col.prop(self, "soft_max_int", text="Max")
- col = layout.column(align=True)
- col.enabled = self.use_soft_limits
- col.prop(self, "soft_min", text="Soft Min")
- col.prop(self, "soft_max", text="Max")
- layout.prop(self, "description")
+ layout.prop(self, "step_int")
+ elif self.property_type == 'STRING':
+ layout.prop(self, "default_string")
- if is_array and proptype == float:
- layout.prop(self, "subtype")
+ if self.property_type == 'PYTHON':
+ layout.prop(self, "eval_string")
+ else:
+ layout.prop(self, "description")
class WM_OT_properties_add(Operator):
@@ -1706,7 +1901,7 @@ class WM_OT_properties_remove(Operator):
bl_options = {'UNDO', 'INTERNAL'}
data_path: rna_path
- property: rna_custom_property
+ property_name: rna_custom_property_name
def execute(self, context):
from rna_prop_ui import (
@@ -1719,9 +1914,9 @@ class WM_OT_properties_remove(Operator):
self.report({'ERROR'}, "Cannot remove properties from override data")
return {'CANCELLED'}
- prop = self.property
- rna_idprop_ui_prop_update(item, prop)
- del item[prop]
+ name = self.property_name
+ rna_idprop_ui_prop_update(item, name)
+ del item[name]
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index d9ad094ac4f..ba5ecd1efde 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -639,7 +639,6 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
add_attributes(mesh.attributes)
add_attributes(mesh.uv_layers)
- add_attributes(mesh.vertex_colors)
add_attributes(ob.vertex_groups)
colliding_names = [name for name, layers in attributes_by_name.items() if len(layers) >= 2]
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index e52136fc416..5dd8c69f3d5 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -648,30 +648,6 @@ class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu):
layout.operator("file.select_box")
-class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel):
- bl_label = "Asset Navigation"
- bl_region_type = 'TOOLS'
- bl_options = {'HIDE_HEADER'}
-
- @classmethod
- def poll(cls, context):
- return (
- asset_utils.AssetBrowserPanel.poll(context) and
- context.preferences.experimental.use_extended_asset_browser
- )
-
- def draw(self, context):
- layout = self.layout
-
- space_file = context.space_data
-
- col = layout.column()
-
- col.scale_x = 1.3
- col.scale_y = 1.3
- col.prop(space_file.params, "asset_category", expand=True)
-
-
class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
bl_region_type = 'TOOL_PROPS'
bl_label = "Asset Metadata"
@@ -691,10 +667,23 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
if asset_file_handle.local_id:
# If the active file is an ID, use its name directly so renaming is possible from right here.
layout.prop(asset_file_handle.local_id, "name", text="")
+
+ col = layout.column(align=True)
+ col.label(text="Asset Catalog:")
+ col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID")
+ col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name")
+
row = layout.row()
row.label(text="Source: Current File")
else:
layout.prop(asset_file_handle, "name", text="")
+
+ col = layout.column(align=True)
+ col.enabled = False
+ col.label(text="Asset Catalog:")
+ col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID")
+ col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name")
+
col = layout.column(align=True) # Just to reduce margin.
col.label(text="Source:")
row = col.row()
@@ -773,9 +762,10 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu):
layout.separator()
- sub = layout.row()
+ sub = layout.column()
sub.operator_context = 'EXEC_DEFAULT'
- sub.operator("asset.clear", text="Clear Asset")
+ sub.operator("asset.clear", text="Clear Asset").set_fake_user = False
+ sub.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True
layout.separator()
@@ -807,7 +797,6 @@ classes = (
ASSETBROWSER_MT_editor_menus,
ASSETBROWSER_MT_view,
ASSETBROWSER_MT_select,
- ASSETBROWSER_PT_navigation_bar,
ASSETBROWSER_PT_metadata,
ASSETBROWSER_PT_metadata_preview,
ASSETBROWSER_PT_metadata_details,
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 3ee668888f3..6a769b1aecc 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -598,16 +598,10 @@ class IMAGE_HT_tool_header(Header):
def draw(self, context):
layout = self.layout
- layout.template_header()
-
self.draw_tool_settings(context)
layout.separator_spacer()
- IMAGE_HT_header.draw_xform_template(layout, context)
-
- layout.separator_spacer()
-
self.draw_mode_settings(context)
def draw_tool_settings(self, context):
@@ -762,8 +756,7 @@ class IMAGE_HT_header(Header):
show_uvedit = sima.show_uvedit
show_maskedit = sima.show_maskedit
- if not show_region_tool_header:
- layout.template_header()
+ layout.template_header()
if sima.mode != 'UV':
layout.prop(sima, "ui_mode", text="")
@@ -784,8 +777,7 @@ class IMAGE_HT_header(Header):
layout.separator_spacer()
- if not show_region_tool_header:
- IMAGE_HT_header.draw_xform_template(layout, context)
+ IMAGE_HT_header.draw_xform_template(layout, context)
layout.template_ID(sima, "image", new="image.new", open="image.open")
@@ -934,6 +926,10 @@ class IMAGE_PT_snapping(Panel):
row = col.row(align=True)
row.prop(tool_settings, "snap_target", expand=True)
+ col.separator()
+ if 'INCREMENT' in tool_settings.snap_uv_element:
+ col.prop(tool_settings, "use_snap_uv_grid_absolute")
+
col.label(text="Affect")
row = col.row(align=True)
row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True)
@@ -1467,6 +1463,33 @@ class IMAGE_PT_udim_grid(Panel):
col = layout.column()
col.prop(uvedit, "tile_grid_shape", text="Grid Shape")
+class IMAGE_PT_custom_grid(Panel):
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'UI'
+ bl_category = "View"
+ bl_label = "Custom Grid"
+
+ @classmethod
+ def poll(cls, context):
+ sima = context.space_data
+ return sima.show_uvedit
+
+ def draw_header(self, context):
+ sima = context.space_data
+ uvedit = sima.uv_editor
+ self.layout.prop(uvedit, "use_custom_grid", text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ sima = context.space_data
+ uvedit = sima.uv_editor
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ col = layout.column()
+ col.prop(uvedit, "custom_grid_subdivisions", text="Subdivisions")
class IMAGE_PT_overlay(Panel):
bl_space_type = 'IMAGE_EDITOR'
@@ -1652,6 +1675,7 @@ classes = (
IMAGE_PT_uv_cursor,
IMAGE_PT_annotation,
IMAGE_PT_udim_grid,
+ IMAGE_PT_custom_grid,
IMAGE_PT_overlay,
IMAGE_PT_overlay_uv_edit,
IMAGE_PT_overlay_uv_edit_geometry,
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index 5f36009901a..f806fc345d1 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -754,6 +754,11 @@ class NodeTreeInterfacePanel:
# Display descriptions only for Geometry Nodes, since it's only used in the modifier panel.
if tree.type == 'GEOMETRY':
layout.prop(active_socket, "description")
+ field_socket_prefixes = {
+ "NodeSocketInt", "NodeSocketColor", "NodeSocketVector", "NodeSocketBool", "NodeSocketFloat"}
+ is_field_type = any(active_socket.bl_socket_idname.startswith(prefix) for prefix in field_socket_prefixes)
+ if in_out == "OUT" and is_field_type:
+ layout.prop(active_socket, "attribute_domain")
active_socket.draw(context, layout)
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index febd064147f..3d18160d90f 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -323,7 +323,8 @@ class OUTLINER_MT_asset(Menu):
space = context.space_data
layout.operator("asset.mark")
- layout.operator("asset.clear")
+ layout.operator("asset.clear", text="Clear Asset").set_fake_user = False
+ layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True
class OUTLINER_PT_filter(Panel):
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 543164f25fc..197e3efebda 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -46,44 +46,85 @@ def selected_sequences_len(context):
def draw_color_balance(layout, color_balance):
+ layout.prop(color_balance, "correction_method")
+
layout.use_property_split = False
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- col = flow.column()
-
- box = col.box()
- split = box.split(factor=0.35)
- col = split.column(align=True)
- col.label(text="Lift:")
- col.separator()
- col.separator()
- col.prop(color_balance, "lift", text="")
- col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
- split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
-
- col = flow.column()
-
- box = col.box()
- split = box.split(factor=0.35)
- col = split.column(align=True)
- col.label(text="Gamma:")
- col.separator()
- col.separator()
- col.prop(color_balance, "gamma", text="")
- col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
- split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
-
- col = flow.column()
-
- box = col.box()
- split = box.split(factor=0.35)
- col = split.column(align=True)
- col.label(text="Gain:")
- col.separator()
- col.separator()
- col.prop(color_balance, "gain", text="")
- col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
- split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
+
+ if color_balance.correction_method == 'LIFT_GAMMA_GAIN':
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Lift:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "lift", text="")
+ col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
+
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Gamma:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "gamma", text="")
+ col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
+
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Gain:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "gain", text="")
+ col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
+
+ elif color_balance.correction_method == 'OFFSET_POWER_SLOPE':
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Offset:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "offset", text="")
+ col.prop(color_balance, "invert_offset", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "offset", value_slider=True, cubic=True)
+
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Power:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "power", text="")
+ col.prop(color_balance, "invert_power", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "power", value_slider=True, cubic=True)
+
+ col = flow.column()
+
+ box = col.box()
+ split = box.split(factor=0.35)
+ col = split.column(align=True)
+ col.label(text="Slope:")
+ col.separator()
+ col.separator()
+ col.prop(color_balance, "slope", text="")
+ col.prop(color_balance, "invert_slope", text="Invert", icon='ARROW_LEFTRIGHT')
+ split.template_color_picker(color_balance, "slope", value_slider=True, cubic=True)
class SEQUENCER_PT_active_tool(ToolActivePanelHelper, Panel):
@@ -99,8 +140,6 @@ class SEQUENCER_HT_tool_header(Header):
def draw(self, context):
layout = self.layout
- layout.template_header()
-
self.draw_tool_settings(context)
# TODO: options popover.
@@ -132,8 +171,7 @@ class SEQUENCER_HT_header(Header):
show_region_tool_header = st.show_region_tool_header
- if not show_region_tool_header:
- layout.template_header()
+ layout.template_header()
layout.prop(st, "view_type", text="")
@@ -151,6 +189,12 @@ class SEQUENCER_HT_header(Header):
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
row = layout.row(align=True)
row.prop(sequencer_tool_settings, "overlap_mode", text="")
+
+ if st.view_type == 'SEQUENCER_PREVIEW':
+ row = layout.row(align=True)
+ row.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True)
+
+ if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
row = layout.row(align=True)
row.prop(tool_settings, "use_snap_sequencer", text="")
sub = row.row(align=True)
@@ -242,6 +286,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.prop(overlay_settings, "show_strip_name", text="Name")
layout.prop(overlay_settings, "show_strip_source", text="Source")
layout.prop(overlay_settings, "show_strip_duration", text="Duration")
+ layout.prop(overlay_settings, "show_strip_tag_color", text="Color Tags")
layout.separator()
@@ -375,9 +420,9 @@ class SEQUENCER_MT_view(Menu):
layout.operator("view2d.zoom_border", text="Zoom")
layout.menu("SEQUENCER_MT_preview_zoom")
- if st.display_mode == 'IMAGE':
- layout.prop(st, "use_zoom_to_fit")
- elif st.display_mode == 'WAVEFORM':
+ layout.prop(st, "use_zoom_to_fit")
+
+ if st.display_mode == 'WAVEFORM':
layout.separator()
layout.prop(st, "show_separate_color", text="Show Separate Color Channels")
@@ -870,6 +915,9 @@ class SEQUENCER_MT_strip(Menu):
layout.operator("sequencer.meta_toggle", text="Toggle Meta")
layout.separator()
+ layout.menu("SEQUENCER_MT_color_tag_picker")
+
+ layout.separator()
layout.menu("SEQUENCER_MT_strip_lock_mute")
layout.separator()
@@ -966,6 +1014,9 @@ class SEQUENCER_MT_context_menu(Menu):
layout.operator("sequencer.meta_toggle", text="Toggle Meta")
layout.separator()
+ layout.menu("SEQUENCER_MT_color_tag_picker")
+
+ layout.separator()
layout.menu("SEQUENCER_MT_strip_lock_mute")
@@ -997,6 +1048,41 @@ class SequencerButtonsPanel_Output:
return cls.has_preview(context)
+class SEQUENCER_PT_color_tag_picker(Panel):
+ bl_label = "Color Tag"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+ bl_category = "Strip"
+ bl_options = {'HIDE_HEADER', 'INSTANCED'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_sequence_strip is not None
+
+ def draw(self, context):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.operator("sequencer.strip_color_tag_set", icon="X").color = 'NONE'
+ for i in range(1, 10):
+ icon = 'SEQUENCE_COLOR_%02d' % i
+ row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i
+
+
+class SEQUENCER_MT_color_tag_picker(Menu):
+ bl_label = "Set Color Tag"
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_sequence_strip is not None
+
+ def draw(self, context):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.operator_enum("sequencer.strip_color_tag_set", "color", icon_only=True)
+
+
class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
@@ -1040,9 +1126,20 @@ class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel):
else:
icon_header = 'SEQ_SEQUENCER'
- row = layout.row()
+ row = layout.row(align=True)
+ row.use_property_decorate = False
row.label(text="", icon=icon_header)
+ row.separator()
row.prop(strip, "name", text="")
+
+ sub = row.row(align=True)
+ if strip.color_tag == 'NONE':
+ sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon='COLOR')
+ else:
+ icon = 'SEQUENCE_' + strip.color_tag
+ sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon=icon)
+
+ row.separator()
row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False)
@@ -2328,8 +2425,11 @@ classes = (
SEQUENCER_MT_strip_transform,
SEQUENCER_MT_strip_input,
SEQUENCER_MT_strip_lock_mute,
+ SEQUENCER_MT_color_tag_picker,
SEQUENCER_MT_context_menu,
+ SEQUENCER_PT_color_tag_picker,
+
SEQUENCER_PT_active_tool,
SEQUENCER_PT_strip,
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py
index 98e29d3baba..4a598d0aa63 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_common.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py
@@ -190,7 +190,7 @@ class ToolActivePanelHelper:
ToolSelectPanelHelper.draw_active_tool_header(
context,
layout.column(),
- show_tool_name=True,
+ show_tool_icon=True,
tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type),
)
@@ -766,7 +766,7 @@ class ToolSelectPanelHelper:
def draw_active_tool_header(
context, layout,
*,
- show_tool_name=False,
+ show_tool_icon=False,
tool_key=None,
):
if tool_key is None:
@@ -783,9 +783,12 @@ class ToolSelectPanelHelper:
return None
# Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
# Add some spacing since the icon is currently assuming regular small icon size.
- layout.label(text=" " + item.label if show_tool_name else " ", icon_value=icon_value)
- if show_tool_name:
+ if show_tool_icon:
+ layout.label(text=" " + item.label, icon_value=icon_value)
layout.separator()
+ else:
+ layout.label(text=item.label)
+
draw_settings = item.draw_settings
if draw_settings is not None:
draw_settings(context, layout, tool)
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index a4a51cb9910..5970d6fdf2b 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -497,18 +497,14 @@ class _defs_view3d_add:
props = tool.operator_properties("view3d.interactive_add")
if not extra:
row = layout.row()
- row.scale_x = 0.8
row.label(text="Depth:")
row = layout.row()
- row.scale_x = 0.9
row.prop(props, "plane_depth", text="")
row = layout.row()
- row.scale_x = 0.8
row.label(text="Orientation:")
row = layout.row()
row.prop(props, "plane_orientation", text="")
row = layout.row()
- row.scale_x = 0.8
row.prop(props, "snap_target")
region_is_header = bpy.context.region.type == 'TOOL_HEADER'
@@ -1691,6 +1687,7 @@ class _defs_weight_paint:
props = tool.operator_properties("paint.weight_gradient")
layout.prop(props, "type", expand=True)
+ layout.popover("VIEW3D_PT_tools_weight_gradient")
return dict(
idname="builtin.gradient",
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 0093110d326..7a8b6d42cad 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -1073,6 +1073,25 @@ class USERPREF_PT_theme_collection_colors(ThemePanel, CenterAlignMixIn, Panel):
flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False)
+class USERPREF_PT_theme_strip_colors(ThemePanel, CenterAlignMixIn, Panel):
+ bl_label = "Strip Colors"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _context):
+ layout = self.layout
+
+ layout.label(icon='SEQ_STRIP_DUPLICATE')
+
+ def draw_centered(self, context, layout):
+ theme = context.preferences.themes[0]
+
+ layout.use_property_split = True
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ for i, ui in enumerate(theme.strip_color, 1):
+ flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False)
+
+
# Base class for dynamically defined theme-space panels.
# This is not registered.
class PreferenceThemeSpacePanel:
@@ -2251,7 +2270,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")),
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
- ({"property": "use_geometry_nodes_fields"}, "T91274"),
),
)
@@ -2283,7 +2301,9 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
context, (
({"property": "use_undo_legacy"}, "T60695"),
({"property": "override_auto_resync"}, "T83811"),
+ ({"property": "proxy_to_override_auto_conversion"}, "T91671"),
({"property": "use_cycles_debug"}, None),
+ ({"property": "use_geometry_nodes_legacy"}, "T91274"),
),
)
@@ -2347,6 +2367,7 @@ classes = (
USERPREF_PT_theme_text_style,
USERPREF_PT_theme_bone_color_sets,
USERPREF_PT_theme_collection_colors,
+ USERPREF_PT_theme_strip_colors,
USERPREF_PT_file_paths_data,
USERPREF_PT_file_paths_render,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 3879f7de250..281c57b282f 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -46,16 +46,10 @@ class VIEW3D_HT_tool_header(Header):
def draw(self, context):
layout = self.layout
- layout.row(align=True).template_header()
-
self.draw_tool_settings(context)
layout.separator_spacer()
- VIEW3D_HT_header.draw_xform_template(layout, context)
-
- layout.separator_spacer()
-
self.draw_mode_settings(context)
def draw_tool_settings(self, context):
@@ -604,10 +598,8 @@ class VIEW3D_HT_header(Header):
tool_settings = context.tool_settings
view = context.space_data
shading = view.shading
- show_region_tool_header = view.show_region_tool_header
- if not show_region_tool_header:
- layout.row(align=True).template_header()
+ layout.row(align=True).template_header()
row = layout.row(align=True)
obj = context.active_object
@@ -754,7 +746,7 @@ class VIEW3D_HT_header(Header):
)
layout.separator_spacer()
- elif not show_region_tool_header:
+ else:
# Transform settings depending on tool header visibility
VIEW3D_HT_header.draw_xform_template(layout, context)
@@ -2234,8 +2226,6 @@ class VIEW3D_MT_object_relations(Menu):
def draw(self, _context):
layout = self.layout
- layout.operator("object.proxy_make", text="Make Proxy...")
-
layout.operator("object.make_override_library", text="Make Library Override...")
layout.operator("object.convert_proxy_to_override")
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 16b5ed33f3f..acc3d933b85 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -688,6 +688,39 @@ class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, Smooth
bl_options = {'DEFAULT_CLOSED'}
+class VIEW3D_PT_tools_weight_gradient(Panel, View3DPaintPanel):
+ bl_context = ".weightpaint" # dot on purpose (access from topbar)
+ bl_label = "Falloff"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ settings = context.tool_settings.weight_paint
+ brush = settings.brush
+ return brush is not None
+
+ def draw(self, context):
+ layout = self.layout
+ settings = context.tool_settings.weight_paint
+ brush = settings.brush
+
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(brush, "curve_preset", text="")
+
+ if brush.curve_preset == 'CUSTOM':
+ layout.template_curve_mapping(brush, "curve", brush=True)
+
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
+ row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
+ row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
+ row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
+ row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
+ row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
+
+
# TODO, move to space_view3d.py
class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel):
bl_context = ".paint_common" # dot on purpose (access from topbar)
@@ -2219,6 +2252,7 @@ classes = (
VIEW3D_PT_tools_brush_falloff_frontface,
VIEW3D_PT_tools_brush_falloff_normal,
VIEW3D_PT_tools_brush_display,
+ VIEW3D_PT_tools_weight_gradient,
VIEW3D_PT_sculpt_dyntopo,
VIEW3D_PT_sculpt_voxel_remesh,
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 9ad162da7dc..37d5c5997ad 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -180,11 +180,8 @@ def object_eevee_cycles_shader_nodes_poll(context):
eevee_cycles_shader_nodes_poll(context))
-def geometry_nodes_fields_poll(context):
- return context.preferences.experimental.use_geometry_nodes_fields
-
-def geometry_nodes_fields_legacy_poll(context):
- return not context.preferences.experimental.use_geometry_nodes_fields
+def geometry_nodes_legacy_poll(context):
+ return context.preferences.experimental.use_geometry_nodes_legacy
# All standard node categories currently used in nodes.
@@ -282,6 +279,7 @@ shader_node_categories = [
]),
ShaderNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[
NodeItem("ShaderNodeMapRange"),
+ NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeValToRGB"),
@@ -483,27 +481,27 @@ texture_node_categories = [
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
- NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll),
-
- NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeAttributeStatistic", poll=geometry_nodes_fields_poll),
+ NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_legacy_poll),
+
+ NodeItem("GeometryNodeAttributeCapture"),
+ NodeItem("GeometryNodeAttributeStatistic"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
@@ -513,24 +511,29 @@ geometry_node_categories = [
NodeItem("ShaderNodeCombineRGB"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
- NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_fields_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveToPoints", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyCurveEndpoints", poll=geometry_nodes_legacy_poll),
NodeItem("GeometryNodeCurveToMesh"),
NodeItem("GeometryNodeCurveResample"),
- NodeItem("GeometryNodeCurveToPoints"),
- NodeItem("GeometryNodeCurveEndpoints"),
NodeItem("GeometryNodeCurveFill"),
NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveLength"),
- NodeItem("GeometryNodeCurveParameter", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeInputTangent", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeCurveSample", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeCurveFillet", poll=geometry_nodes_fields_poll),
+ NodeItem("GeometryNodeCurveSplineType"),
+ NodeItem("GeometryNodeSplineLength"),
+ NodeItem("GeometryNodeCurveSubdivide"),
+ NodeItem("GeometryNodeCurveParameter"),
+ NodeItem("GeometryNodeCurveSetHandles"),
+ NodeItem("GeometryNodeInputTangent"),
+ NodeItem("GeometryNodeCurveSample"),
+ NodeItem("GeometryNodeCurveFillet"),
+ NodeItem("GeometryNodeCurveReverse"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
@@ -542,44 +545,48 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurvePrimitiveBezierSegment"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
- NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_fields_legacy_poll),
+ NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeProximity"),
NodeItem("GeometryNodeBoundBox"),
NodeItem("GeometryNodeConvexHull"),
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeJoinGeometry"),
NodeItem("GeometryNodeSeparateComponents"),
- NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeRealizeInstances", poll=geometry_nodes_fields_poll),
+ NodeItem("GeometryNodeSetPosition"),
+ NodeItem("GeometryNodeRealizeInstances"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
+ NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_legacy_poll),
+
NodeItem("GeometryNodeObjectInfo"),
NodeItem("GeometryNodeCollectionInfo"),
- NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
- NodeItem("GeometryNodeInputPosition", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeInputIndex", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeInputNormal", poll=geometry_nodes_fields_poll),
+ NodeItem("GeometryNodeInputPosition"),
+ NodeItem("GeometryNodeInputIndex"),
+ NodeItem("GeometryNodeInputNormal"),
]),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
- NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll),
+ NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_legacy_poll),
- NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll),
- NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll),
+ NodeItem("GeometryNodeMaterialAssign"),
+ NodeItem("GeometryNodeMaterialSelection"),
NodeItem("GeometryNodeMaterialReplace"),
]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
+ NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll),
+
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
- NodeItem("GeometryNodeEdgeSplit"),
- NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeMeshSubdivide"),
+ NodeItem("GeometryNodePointsToVertices"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -592,31 +599,39 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
- NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_fields_legacy_poll),
- NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_fields_legacy_poll),
+ NodeItem("GeometryNodeMeshToPoints"),
+ NodeItem("GeometryNodeInstanceOnPoints"),
+ NodeItem("GeometryNodeDistributePointsOnFaces"),
+ NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll),
+ NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll),
]),
GeometryNodeCategory("GEO_TEXT", "Text", items=[
NodeItem("FunctionNodeStringLength"),
NodeItem("FunctionNodeStringSubstring"),
NodeItem("FunctionNodeValueToString"),
NodeItem("GeometryNodeStringJoin"),
+ NodeItem("FunctionNodeInputSpecialCharacters"),
+ NodeItem("GeometryNodeStringToCurves"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
+ NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
+ NodeItem("FunctionNodeRotateEuler"),
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
+ NodeItem("FunctionNodeRandomValue"),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
- NodeItem("ShaderNodeTexNoise", poll=geometry_nodes_fields_poll),
+ NodeItem("ShaderNodeTexNoise"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
@@ -629,7 +644,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeViewer"),
]),
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
- NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_fields_legacy_poll),
+ NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll),
NodeItem("GeometryNodeVolumeToMesh"),
]),
diff --git a/source/blender/blendthumb/src/Dll.cpp b/source/blender/blendthumb/src/Dll.cpp
index 6516540034e..7f10777f884 100644
--- a/source/blender/blendthumb/src/Dll.cpp
+++ b/source/blender/blendthumb/src/Dll.cpp
@@ -14,11 +14,17 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+/** \file
+ * \ingroup blendthumb
+ *
+ * Thumbnail from Blend file extraction for MS-Windows (DLL).
+ */
+
#include <new>
#include <objbase.h>
-#include <shlobj.h> // For SHChangeNotify
+#include <shlobj.h> /* For #SHChangeNotify */
#include <shlwapi.h>
-#include <thumbcache.h> // For IThumbnailProvider.
+#include <thumbcache.h> /* For IThumbnailProvider */
extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv);
@@ -33,16 +39,16 @@ struct CLASS_OBJECT_INIT {
PFNCREATEINSTANCE pfnCreate;
};
-// add classes supported by this module here
+/* Add classes supported by this module here. */
const CLASS_OBJECT_INIT c_rgClassObjectInit[] = {
{&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}};
long g_cRefModule = 0;
-// Handle the DLL's module
-HINSTANCE g_hInst = NULL;
+/** Handle the DLL's module */
+HINSTANCE g_hInst = nullptr;
-// Standard DLL functions
+/** Standard DLL functions. */
STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
{
if (dwReason == DLL_PROCESS_ATTACH) {
@@ -54,7 +60,7 @@ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
STDAPI DllCanUnloadNow()
{
- // Only allow the DLL to be unloaded after all outstanding references have been released
+ /* Only allow the DLL to be unloaded after all outstanding references have been released. */
return (g_cRefModule == 0) ? S_OK : S_FALSE;
}
@@ -76,7 +82,7 @@ class CClassFactory : public IClassFactory {
REFIID riid,
void **ppv)
{
- *ppv = NULL;
+ *ppv = nullptr;
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
for (size_t i = 0; i < cClassObjectInits; i++) {
if (clsid == *pClassObjectInits[i].pClsid) {
@@ -87,7 +93,8 @@ class CClassFactory : public IClassFactory {
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
- break; // match found
+ /* Match found. */
+ break;
}
}
return hr;
@@ -98,7 +105,7 @@ class CClassFactory : public IClassFactory {
DllAddRef();
}
- // IUnknown
+ /** #IUnknown */
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}};
@@ -119,7 +126,7 @@ class CClassFactory : public IClassFactory {
return cRef;
}
- // IClassFactory
+ /** #IClassFactory */
IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv);
@@ -152,33 +159,37 @@ STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv)
clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv);
}
-// A struct to hold the information required for a registry entry
-
+/**
+ * A struct to hold the information required for a registry entry.
+ */
struct REGISTRY_ENTRY {
HKEY hkeyRoot;
PCWSTR pszKeyName;
PCWSTR pszValueName;
DWORD dwValueType;
- PCWSTR pszData; // These two fields could/should have been a union, but C++
- DWORD dwData; // only lets you initialize the first field in a union.
+ /** These two fields could/should have been a union, but C++ */
+ PCWSTR pszData;
+ /** Only lets you initialize the first field in a union. */
+ DWORD dwData;
};
-// Creates a registry key (if needed) and sets the default value of the key
-
+/**
+ * Creates a registry key (if needed) and sets the default value of the key.
+ */
HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
{
HKEY hKey;
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot,
pRegistryEntry->pszKeyName,
0,
- NULL,
+ nullptr,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
- NULL,
+ nullptr,
&hKey,
- NULL));
+ nullptr));
if (SUCCEEDED(hr)) {
- // All this just to support REG_DWORD...
+ /* All this just to support #REG_DWORD. */
DWORD size;
DWORD data;
BYTE *lpData = (LPBYTE)pRegistryEntry->pszData;
@@ -202,9 +213,9 @@ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
return hr;
}
-//
-// Registers this COM server
-//
+/**
+ * Registers this COM server.
+ */
STDAPI DllRegisterServer()
{
HRESULT hr;
@@ -216,15 +227,15 @@ STDAPI DllRegisterServer()
}
else {
const REGISTRY_ENTRY rgRegistryEntries[] = {
- // RootKey KeyName ValueName ValueType Data
+ /* `RootKey KeyName ValueName ValueType Data` */
{HKEY_CURRENT_USER,
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
- NULL,
+ nullptr,
REG_SZ,
SZ_BLENDTHUMBHANDLER},
{HKEY_CURRENT_USER,
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32",
- NULL,
+ nullptr,
REG_SZ,
szModuleName},
{HKEY_CURRENT_USER,
@@ -237,10 +248,10 @@ STDAPI DllRegisterServer()
L"Treatment",
REG_DWORD,
0,
- 0}, // doesn't appear to do anything...
+ 0}, /* This doesn't appear to do anything. */
{HKEY_CURRENT_USER,
L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}",
- NULL,
+ nullptr,
REG_SZ,
SZ_CLSID_BLENDTHUMBHANDLER},
};
@@ -251,17 +262,17 @@ STDAPI DllRegisterServer()
}
}
if (SUCCEEDED(hr)) {
- // This tells the shell to invalidate the thumbnail cache. This is important because any
- // .blend files viewed before registering this handler would otherwise show cached blank
- // thumbnails.
- SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+ /* This tells the shell to invalidate the thumbnail cache.
+ * This is important because any `.blend` files viewed before registering this handler
+ * would otherwise show cached blank thumbnails. */
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
}
return hr;
}
-//
-// Unregisters this COM server
-//
+/**
+ * Unregisters this COM server
+ */
STDAPI DllUnregisterServer()
{
HRESULT hr = S_OK;
@@ -270,11 +281,11 @@ STDAPI DllUnregisterServer()
L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"};
- // Delete the registry entries
+ /* Delete the registry entries. */
for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) {
hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i]));
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
- // If the registry entry has already been deleted, say S_OK.
+ /* If the registry entry has already been deleted, say S_OK. */
hr = S_OK;
}
}
diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h
index 50eb2859279..42eea41b7a7 100644
--- a/source/blender/blenkernel/BKE_asset.h
+++ b/source/blender/blenkernel/BKE_asset.h
@@ -22,6 +22,8 @@
#include "BLI_utildefines.h"
+#include "DNA_asset_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -46,6 +48,12 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *
const char *name);
void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag);
+/** Clean up the catalog ID (white-spaces removed, length reduced, etc.) and assign it. */
+void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data);
+void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data,
+ bUUID catalog_id,
+ const char *catalog_simple_name);
+
void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref);
struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh
new file mode 100644
index 00000000000..8afc4fe2ad2
--- /dev/null
+++ b/source/blender/blenkernel/BKE_asset_catalog.hh
@@ -0,0 +1,360 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#ifndef __cplusplus
+# error This is a C++ header. The C interface is yet to be implemented/designed.
+#endif
+
+#include "BLI_function_ref.hh"
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_uuid.h"
+#include "BLI_vector.hh"
+
+#include "BKE_asset_catalog_path.hh"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+namespace blender::bke {
+
+using CatalogID = bUUID;
+using CatalogPathComponent = std::string;
+/* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not
+ * available on the minimum macOS target version. */
+using CatalogFilePath = std::string;
+
+class AssetCatalog;
+class AssetCatalogDefinitionFile;
+class AssetCatalogTree;
+class AssetCatalogFilter;
+
+/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single
+ * directory hierarchy). */
+class AssetCatalogService {
+ public:
+ static const CatalogFilePath DEFAULT_CATALOG_FILENAME;
+
+ public:
+ AssetCatalogService() = default;
+ explicit AssetCatalogService(const CatalogFilePath &asset_library_root);
+
+ /** Load asset catalog definitions from the files found in the asset library. */
+ void load_from_disk();
+ /** Load asset catalog definitions from the given file or directory. */
+ void load_from_disk(const CatalogFilePath &file_or_directory_path);
+
+ /**
+ * Write the catalog definitions to disk in response to the blend file being saved.
+ *
+ * The location where the catalogs are saved is variable, and depends on the location of the
+ * blend file. The first matching rule wins:
+ *
+ * - Already loaded a CDF from disk?
+ * -> Always write to that file.
+ * - The directory containing the blend file has a blender_assets.cats.txt file?
+ * -> Merge with & write to that file.
+ * - The directory containing the blend file is part of an asset library, as per
+ * the user's preferences?
+ * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt
+ * - Create a new file blender_assets.cats.txt next to the blend file.
+ *
+ * Return true on success, which either means there were no in-memory categories to save,
+ * or the save was successful. */
+ bool write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path);
+
+ /**
+ * Merge on-disk changes into the in-memory asset catalogs.
+ * This should be called before writing the asset catalogs to disk.
+ *
+ * - New on-disk catalogs are loaded into memory.
+ * - Already-known on-disk catalogs are ignored (so will be overwritten with our in-memory
+ * data). This includes in-memory marked-as-deleted catalogs.
+ */
+ void merge_from_disk_before_writing();
+
+ /** Return catalog with the given ID. Return nullptr if not found. */
+ AssetCatalog *find_catalog(CatalogID catalog_id) const;
+
+ /** Return first catalog with the given path. Return nullptr if not found. This is not an
+ * efficient call as it's just a linear search over the catalogs. */
+ AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const;
+
+ /**
+ * Create a filter object that can be used to determine whether an asset belongs to the given
+ * catalog, or any of the catalogs in the sub-tree rooted at the given catalog.
+ *
+ * \see #AssetCatalogFilter
+ */
+ AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const;
+
+ /** Create a catalog with some sensible auto-generated catalog ID.
+ * The catalog will be saved to the default catalog file.*/
+ AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path);
+
+ /**
+ * Soft-delete the catalog, ensuring it actually gets deleted when the catalog definition file is
+ * written. */
+ void delete_catalog(CatalogID catalog_id);
+
+ /**
+ * Update the catalog path, also updating the catalog path of all sub-catalogs.
+ */
+ void update_catalog_path(CatalogID catalog_id, const AssetCatalogPath &new_catalog_path);
+
+ AssetCatalogTree *get_catalog_tree();
+
+ /** Return true only if there are no catalogs known. */
+ bool is_empty() const;
+
+ protected:
+ /* These pointers are owned by this AssetCatalogService. */
+ Map<CatalogID, std::unique_ptr<AssetCatalog>> catalogs_;
+ Map<CatalogID, std::unique_ptr<AssetCatalog>> deleted_catalogs_;
+ std::unique_ptr<AssetCatalogDefinitionFile> catalog_definition_file_;
+ std::unique_ptr<AssetCatalogTree> catalog_tree_ = std::make_unique<AssetCatalogTree>();
+ CatalogFilePath asset_library_root_;
+
+ void load_directory_recursive(const CatalogFilePath &directory_path);
+ void load_single_file(const CatalogFilePath &catalog_definition_file_path);
+
+ std::unique_ptr<AssetCatalogDefinitionFile> parse_catalog_file(
+ const CatalogFilePath &catalog_definition_file_path);
+
+ /**
+ * Construct an in-memory catalog definition file (CDF) from the currently known catalogs.
+ * This object can then be processed further before saving to disk. */
+ std::unique_ptr<AssetCatalogDefinitionFile> construct_cdf_in_memory(
+ const CatalogFilePath &file_path);
+
+ /**
+ * Find a suitable path to write a CDF to.
+ *
+ * This depends on the location of the blend file, and on whether a CDF already exists next to it
+ * or whether the blend file is saved inside an asset library.
+ */
+ static CatalogFilePath find_suitable_cdf_path_for_writing(
+ const CatalogFilePath &blend_file_path);
+
+ std::unique_ptr<AssetCatalogTree> read_into_tree();
+ void rebuild_tree();
+
+ /**
+ * For every catalog, ensure that its parent path also has a known catalog.
+ */
+ void create_missing_catalogs();
+};
+
+/**
+ * Representation of a catalog path in the #AssetCatalogTree.
+ */
+class AssetCatalogTreeItem {
+ friend class AssetCatalogTree;
+
+ public:
+ /** Container for child items. Uses a #std::map to keep items ordered by their name (i.e. their
+ * last catalog component). */
+ using ChildMap = std::map<std::string, AssetCatalogTreeItem>;
+ using ItemIterFn = FunctionRef<void(AssetCatalogTreeItem &)>;
+
+ AssetCatalogTreeItem(StringRef name,
+ CatalogID catalog_id,
+ const AssetCatalogTreeItem *parent = nullptr);
+
+ CatalogID get_catalog_id() const;
+ StringRef get_name() const;
+ /** Return the full catalog path, defined as the name of this catalog prefixed by the full
+ * catalog path of its parent and a separator. */
+ AssetCatalogPath catalog_path() const;
+ int count_parents() const;
+ bool has_children() const;
+
+ /** Iterate over children calling \a callback for each of them, but do not recurse into their
+ * children. */
+ void foreach_child(const ItemIterFn callback);
+
+ protected:
+ /** Child tree items, ordered by their names. */
+ ChildMap children_;
+ /** The user visible name of this component. */
+ CatalogPathComponent name_;
+ CatalogID catalog_id_;
+
+ /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to
+ * build a path). */
+ const AssetCatalogTreeItem *parent_ = nullptr;
+
+ private:
+ static void foreach_item_recursive(ChildMap &children_, ItemIterFn callback);
+};
+
+/**
+ * A representation of the catalog paths as tree structure. Each component of the catalog tree is
+ * represented by an #AssetCatalogTreeItem. The last path component of an item is used as its name,
+ * which may also be shown to the user.
+ * An item can not have multiple children with the same name. That means the name uniquely
+ * identifies an item within its parent.
+ *
+ * There is no single root tree element, the #AssetCatalogTree instance itself represents the root.
+ */
+class AssetCatalogTree {
+ using ChildMap = AssetCatalogTreeItem::ChildMap;
+ using ItemIterFn = AssetCatalogTreeItem::ItemIterFn;
+
+ public:
+ /** Ensure an item representing \a path is in the tree, adding it if necessary. */
+ void insert_item(const AssetCatalog &catalog);
+
+ void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback);
+ /** Iterate over root items calling \a callback for each of them, but do not recurse into their
+ * children. */
+ void foreach_root_item(const ItemIterFn callback);
+
+ protected:
+ /** Child tree items, ordered by their names. */
+ ChildMap root_items_;
+};
+
+/** Keeps track of which catalogs are defined in a certain file on disk.
+ * Only contains non-owning pointers to the #AssetCatalog instances, so ensure the lifetime of this
+ * class is shorter than that of the #`AssetCatalog`s themselves. */
+class AssetCatalogDefinitionFile {
+ public:
+ /* For now this is the only version of the catalog definition files that is supported.
+ * Later versioning code may be added to handle older files. */
+ const static int SUPPORTED_VERSION;
+ const static std::string VERSION_MARKER;
+ const static std::string HEADER;
+
+ CatalogFilePath file_path;
+
+ AssetCatalogDefinitionFile() = default;
+
+ /**
+ * Write the catalog definitions to the same file they were read from.
+ * Return true when the file was written correctly, false when there was a problem.
+ */
+ bool write_to_disk() const;
+ /**
+ * Write the catalog definitions to an arbitrary file path.
+ *
+ * Any existing file is backed up to "filename~". Any previously existing backup is overwritten.
+ *
+ * Return true when the file was written correctly, false when there was a problem.
+ */
+ bool write_to_disk(const CatalogFilePath &dest_file_path) const;
+
+ bool contains(CatalogID catalog_id) const;
+ /* Add a new catalog. Undefined behavior if a catalog with the same ID was already added. */
+ void add_new(AssetCatalog *catalog);
+
+ using AssetCatalogParsedFn = FunctionRef<bool(std::unique_ptr<AssetCatalog>)>;
+ void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path,
+ AssetCatalogParsedFn callback);
+
+ protected:
+ /* Catalogs stored in this file. They are mapped by ID to make it possible to query whether a
+ * catalog is already known, without having to find the corresponding `AssetCatalog*`. */
+ Map<CatalogID, AssetCatalog *> catalogs_;
+
+ bool parse_version_line(StringRef line);
+ std::unique_ptr<AssetCatalog> parse_catalog_line(StringRef line);
+
+ /**
+ * Write the catalog definitions to the given file path.
+ * Return true when the file was written correctly, false when there was a problem.
+ */
+ bool write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const;
+ bool ensure_directory_exists(const CatalogFilePath directory_path) const;
+};
+
+/** Asset Catalog definition, containing a symbolic ID and a path that points to a node in the
+ * catalog hierarchy. */
+class AssetCatalog {
+ public:
+ AssetCatalog() = default;
+ AssetCatalog(CatalogID catalog_id, const AssetCatalogPath &path, const std::string &simple_name);
+
+ CatalogID catalog_id;
+ AssetCatalogPath path;
+ /**
+ * Simple, human-readable name for the asset catalog. This is stored on assets alongside the
+ * catalog ID; the catalog ID is a UUID that is not human-readable,
+ * so to avoid complete data-loss when the catalog definition file gets lost,
+ * we also store a human-readable simple name for the catalog. */
+ std::string simple_name;
+
+ struct Flags {
+ /* Treat this catalog as deleted. Keeping deleted catalogs around is necessary to support
+ * merging of on-disk changes with in-memory changes. */
+ bool is_deleted = false;
+ } flags;
+
+ /**
+ * Create a new Catalog with the given path, auto-generating a sensible catalog simple-name.
+ *
+ * NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned
+ * `AssetCatalog`'s path differ from the given one.
+ */
+ static std::unique_ptr<AssetCatalog> from_path(const AssetCatalogPath &path);
+
+ protected:
+ /** Generate a sensible catalog ID for the given path. */
+ static std::string sensible_simple_name_for_path(const AssetCatalogPath &path);
+};
+
+/** Comparator for asset catalogs, ordering by (path, UUID). */
+struct AssetCatalogPathCmp {
+ bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const
+ {
+ if (lhs->path == rhs->path) {
+ return lhs->catalog_id < rhs->catalog_id;
+ }
+ return lhs->path < rhs->path;
+ }
+};
+
+/**
+ * Set that stores catalogs ordered by (path, UUID).
+ * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */
+using AssetCatalogOrderedSet = std::set<const AssetCatalog *, AssetCatalogPathCmp>;
+
+/**
+ * Filter that can determine whether an asset should be visible or not, based on its catalog ID.
+ *
+ * \see AssetCatalogService::create_filter()
+ */
+class AssetCatalogFilter {
+ public:
+ bool contains(CatalogID asset_catalog_id) const;
+
+ protected:
+ friend AssetCatalogService;
+ const Set<CatalogID> matching_catalog_ids;
+
+ explicit AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids);
+};
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh
new file mode 100644
index 00000000000..328625b1bfa
--- /dev/null
+++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh
@@ -0,0 +1,143 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#ifndef __cplusplus
+# error This is a C++ header.
+#endif
+
+#include "BLI_function_ref.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_sys_types.h"
+
+#include <string>
+
+namespace blender::bke {
+
+/**
+ * Location of an Asset Catalog in the catalog tree, denoted by slash-separated path components.
+ *
+ * Each path component is a string that is not allowed to have slashes or colons. The latter is to
+ * make things easy to save in the colon-delimited Catalog Definition File format.
+ *
+ * The path of a catalog determines where in the catalog hierarchy the catalog is shown. Examples
+ * are "Characters/Ellie/Poses/Hand" or "Kit_bash/City/Skyscrapers". The path looks like a
+ * file-system path, with a few differences:
+ *
+ * - Only slashes are used as path component separators.
+ * - All paths are absolute, so there is no need for a leading slash.
+ *
+ * See https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Catalogs
+ *
+ * Paths are stored as byte sequences, and assumed to be UTF-8.
+ */
+class AssetCatalogPath {
+ friend std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append);
+
+ private:
+ /**
+ * The path itself, such as "Agents/Secret/327".
+ */
+ std::string path_;
+
+ public:
+ static const char SEPARATOR;
+
+ AssetCatalogPath() = delete;
+ AssetCatalogPath(StringRef path);
+ AssetCatalogPath(const std::string &path);
+ AssetCatalogPath(const char *path);
+ AssetCatalogPath(const AssetCatalogPath &other_path) = default;
+ AssetCatalogPath(AssetCatalogPath &&other_path) noexcept;
+ ~AssetCatalogPath() = default;
+
+ uint64_t hash() const;
+ uint64_t length() const; /* Length of the path in bytes. */
+
+ /** C-string representation of the path. */
+ const char *c_str() const;
+ const std::string &str() const;
+
+ /* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor.
+ * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */
+ bool operator==(const AssetCatalogPath &other_path) const;
+ bool operator!=(const AssetCatalogPath &other_path) const;
+ bool operator<(const AssetCatalogPath &other_path) const;
+ AssetCatalogPath &operator=(const AssetCatalogPath &other_path) = default;
+ AssetCatalogPath &operator=(AssetCatalogPath &&other_path) = default;
+
+ /** Concatenate two paths, returning the new path. */
+ AssetCatalogPath operator/(const AssetCatalogPath &path_to_append) const;
+
+ /* False when the path is empty, true otherwise. */
+ operator bool() const;
+
+ /**
+ * Clean up the path. This ensures:
+ * - Every path component is stripped of its leading/trailing spaces.
+ * - Empty components (caused by double slashes or leading/trailing slashes) are removed.
+ * - Invalid characters are replaced with valid ones.
+ */
+ [[nodiscard]] AssetCatalogPath cleanup() const;
+
+ /**
+ * \return true only if the given path is a parent of this catalog's path.
+ * When this catalog's path is equal to the given path, return true as well.
+ * In other words, this defines a weak subset.
+ *
+ * True: "some/path/there" is contained in "some/path" and "some".
+ * False: "path/there" is not contained in "some/path/there".
+ *
+ * Note that non-cleaned-up paths (so for example starting or ending with a
+ * slash) are not supported, and result in undefined behavior.
+ */
+ bool is_contained_in(const AssetCatalogPath &other_path) const;
+
+ /**
+ * \return the parent path, or an empty path if there is no parent.
+ */
+ AssetCatalogPath parent() const;
+
+ /**
+ * Change the initial part of the path from `from_path` to `to_path`.
+ * If this path does not start with `from_path`, return an empty path as result.
+ *
+ * Example:
+ *
+ * AssetCatalogPath path("some/path/to/some/catalog");
+ * path.rebase("some/path", "new/base") -> "new/base/to/some/catalog"
+ */
+ AssetCatalogPath rebase(const AssetCatalogPath &from_path,
+ const AssetCatalogPath &to_path) const;
+
+ /** Call the callback function for each path component, in left-to-right order. */
+ using ComponentIteratorFn = FunctionRef<void(StringRef component_name, bool is_last_component)>;
+ void iterate_components(ComponentIteratorFn callback) const;
+
+ protected:
+ /** Strip leading/trailing spaces and replace disallowed characters. */
+ static std::string cleanup_component(StringRef component_name);
+};
+
+/** Output the path as string. */
+std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append);
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h
new file mode 100644
index 00000000000..3ddfbb1415e
--- /dev/null
+++ b/source/blender/blenkernel/BKE_asset_library.h
@@ -0,0 +1,74 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+struct Main;
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Forward declaration, defined in intern/asset_library.hh */
+typedef struct AssetLibrary AssetLibrary;
+
+/** TODO(@sybren): properly have a think/discussion about the API for this. */
+struct AssetLibrary *BKE_asset_library_load(const char *library_path);
+void BKE_asset_library_free(struct AssetLibrary *asset_library);
+
+/**
+ * Try to find an appropriate location for an asset library root from a file or directory path.
+ * Does not check if \a input_path exists.
+ *
+ * The design is made to find an appropriate asset library path from a .blend file path, but
+ * technically works with any file or directory as \a input_path.
+ * Design is:
+ * * If \a input_path lies within a known asset library path (i.e. an asset library registered in
+ * the Preferences), return the asset library path.
+ * * Otherwise, if \a input_path has a parent path, return the parent path (e.g. to use the
+ * directory a .blend file is in as asset library root).
+ * * If \a input_path is empty or doesn't have a parent path (e.g. because a .blend wasn't saved
+ * yet), there is no suitable path. The caller has to decide how to handle this case.
+ *
+ * \param r_library_path: The returned asset library path with a trailing slash, or an empty string
+ * if no suitable path is found. Assumed to be a buffer of at least
+ * #FILE_MAXDIR bytes.
+ *
+ * \return True if the function could find a valid, that is, a non-empty path to return in \a
+ * r_library_path.
+ */
+bool BKE_asset_library_find_suitable_root_path_from_path(
+ const char *input_path, char r_library_path[768 /* FILE_MAXDIR */]);
+/**
+ * Uses the current location on disk of the file represented by \a bmain as input to
+ * #BKE_asset_library_find_suitable_root_path_from_path(). Refer to it for a design
+ * description.
+ *
+ * \return True if the function could find a valid, that is, a non-empty path to return in \a
+ * r_library_path. If \a bmain wasn't saved into a file yet, the return value will be
+ * false.
+ */
+bool BKE_asset_library_find_suitable_root_path_from_main(
+ const struct Main *bmain, char r_library_path[768 /* FILE_MAXDIR */]);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh
new file mode 100644
index 00000000000..1dc02f7aa9b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_asset_library.hh
@@ -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.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#ifndef __cplusplus
+# error This is a C++-only header file. Use BKE_asset_library.h instead.
+#endif
+
+#include "BKE_asset_library.h"
+
+#include "BKE_asset_catalog.hh"
+#include "BKE_callbacks.h"
+
+#include <memory>
+
+namespace blender::bke {
+
+struct AssetLibrary {
+ std::unique_ptr<AssetCatalogService> catalog_service;
+
+ void load(StringRefNull library_root_directory);
+
+ void on_save_handler_register();
+ void on_save_handler_unregister();
+
+ void on_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers);
+
+ private:
+ bCallbackFuncStore on_save_callback_store_;
+};
+
+} // namespace blender::bke
+
+blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service(
+ const ::AssetLibrary *library);
+blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library);
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index cf54e7efa0d..da3de2f08bd 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -101,6 +101,8 @@ class AttributeIDRef {
BLI_assert(this->is_anonymous());
return *anonymous_id_;
}
+
+ friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
};
} // namespace blender::bke
@@ -120,6 +122,11 @@ struct AttributeMetaData {
}
};
+struct AttributeKind {
+ AttributeDomain domain;
+ CustomDataType data_type;
+};
+
/**
* Base class for the attribute initializer types described below.
*/
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 1f25106404a..31ce1b124e9 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,13 +39,13 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 25
+#define BLENDER_FILE_SUBVERSION 32
/* 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
* was written with too new a version. */
#define BLENDER_FILE_MIN_VERSION 300
-#define BLENDER_FILE_MIN_SUBVERSION 11
+#define BLENDER_FILE_MIN_SUBVERSION 26
/** User readable version string. */
const char *BKE_blender_version_string(void);
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h
index ef2a0ed34a0..7c518f33c89 100644
--- a/source/blender/blenkernel/BKE_callbacks.h
+++ b/source/blender/blenkernel/BKE_callbacks.h
@@ -130,6 +130,7 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain,
struct Depsgraph *depsgraph,
eCbEvent evt);
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt);
+void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt);
void BKE_callback_global_init(void);
void BKE_callback_global_finalize(void);
diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h
index ec2262d4f60..109947cece4 100644
--- a/source/blender/blenkernel/BKE_colortools.h
+++ b/source/blender/blenkernel/BKE_colortools.h
@@ -97,6 +97,7 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap,
float vecout[3],
const float vecin[3]);
bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
+void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
/* non-const, these modify the curve */
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index db5663fcc94..8fb596a8096 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -87,11 +87,6 @@ void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
const bool for_render);
-void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph,
- const struct Scene *scene,
- struct Object *ob,
- struct ListBase *dispbase,
- struct Mesh **r_final);
void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_font.h
index 522d3843bb2..827ae1b6a0f 100644
--- a/source/blender/blenkernel/BKE_font.h
+++ b/source/blender/blenkernel/BKE_font.h
@@ -85,6 +85,15 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
struct CharTrans **r_chartransdata);
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
bool BKE_vfont_to_curve(struct Object *ob, int mode);
+void BKE_vfont_build_char(struct Curve *cu,
+ struct ListBase *nubase,
+ unsigned int character,
+ struct CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize);
int BKE_vfont_select_get(struct Object *ob, int *r_start, int *r_end);
void BKE_vfont_select_clamp(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 317d513dae6..f182fb527e1 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -25,6 +25,7 @@
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
+#include "BLI_function_ref.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
@@ -280,6 +281,8 @@ struct GeometrySet {
return this->remove(Component::static_type);
}
+ void keep_only(const blender::Span<GeometryComponentType> component_types);
+
void add(const GeometryComponent &component);
blender::Vector<const GeometryComponent *> get_components_for_read() const;
@@ -293,6 +296,25 @@ struct GeometrySet {
bool owns_direct_data() const;
void ensure_owns_direct_data();
+ using AttributeForeachCallback =
+ blender::FunctionRef<void(const blender::bke::AttributeIDRef &attribute_id,
+ const AttributeMetaData &meta_data,
+ const GeometryComponent &component)>;
+
+ void attribute_foreach(blender::Span<GeometryComponentType> component_types,
+ bool include_instances,
+ AttributeForeachCallback callback) const;
+
+ void gather_attributes_for_propagation(
+ blender::Span<GeometryComponentType> component_types,
+ GeometryComponentType dst_component_type,
+ bool include_instances,
+ blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const;
+
+ using ForeachSubGeometryCallback = blender::FunctionRef<void(GeometrySet &geometry_set)>;
+
+ void modify_geometry_sets(ForeachSubGeometryCallback callback);
+
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
@@ -307,6 +329,8 @@ struct GeometrySet {
bool has_instances() const;
bool has_volume() const;
bool has_curve() const;
+ bool has_realized_data() const;
+ bool is_empty() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
@@ -481,11 +505,38 @@ class InstanceReference {
{
}
- InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_)
+ InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_)
+ {
+ if (other.geometry_set_) {
+ geometry_set_ = std::make_unique<GeometrySet>(*other.geometry_set_);
+ }
+ }
+
+ InstanceReference(InstanceReference &&other)
+ : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_))
+ {
+ other.type_ = Type::None;
+ other.data_ = nullptr;
+ }
+
+ InstanceReference &operator=(const InstanceReference &other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+ this->~InstanceReference();
+ new (this) InstanceReference(other);
+ return *this;
+ }
+
+ InstanceReference &operator=(InstanceReference &&other)
{
- if (src.type_ == Type::GeometrySet) {
- geometry_set_ = std::make_unique<GeometrySet>(*src.geometry_set_);
+ if (this == &other) {
+ return *this;
}
+ this->~InstanceReference();
+ new (this) InstanceReference(std::move(other));
+ return *this;
}
Type type() const
@@ -579,6 +630,7 @@ class InstancesComponent : public GeometryComponent {
void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
blender::Span<InstanceReference> references() const;
+ void remove_unused_references();
void ensure_geometry_instances();
GeometrySet &geometry_set_from_reference(const int reference_index);
@@ -597,6 +649,9 @@ class InstancesComponent : public GeometryComponent {
int attribute_domain_size(const AttributeDomain domain) const final;
+ void foreach_referenced_geometry(
+ blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const;
+
bool is_empty() const final;
bool owns_direct_data() const override;
@@ -667,6 +722,13 @@ class AttributeFieldInput : public fn::FieldInput {
{
}
+ template<typename T> static fn::Field<T> Create(std::string name)
+ {
+ const CPPType &type = CPPType::get<T>();
+ auto field_input = std::make_shared<AttributeFieldInput>(std::move(name), type);
+ return fn::Field<T>{field_input};
+ }
+
StringRefNull attribute_name() const
{
return name_;
@@ -696,6 +758,14 @@ class AnonymousAttributeFieldInput : public fn::FieldInput {
{
}
+ template<typename T> static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id)
+ {
+ const CPPType &type = CPPType::get<T>();
+ auto field_input = std::make_shared<AnonymousAttributeFieldInput>(std::move(anonymous_id),
+ type);
+ return fn::Field<T>{field_input};
+ }
+
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh
index 44a0ee30c4c..653450c7d8e 100644
--- a/source/blender/blenkernel/BKE_geometry_set_instances.hh
+++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh
@@ -39,21 +39,12 @@ struct GeometryInstanceGroup {
Vector<float4x4> transforms;
};
-void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
- const AttributeForeachCallback callback,
- const int limit);
-
void geometry_set_gather_instances(const GeometrySet &geometry_set,
Vector<GeometryInstanceGroup> &r_instance_groups);
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set);
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
-struct AttributeKind {
- CustomDataType data_type;
- AttributeDomain domain;
-};
-
/**
* Add information about all the attributes on every component of the type. The resulting info
* will contain the highest complexity data type and the highest priority domain among every
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 36f57209e33..d2a8ec2e332 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -133,6 +133,9 @@ enum {
LIB_ID_COPY_SHAPEKEY = 1 << 26,
/** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */
LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27,
+ /** EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we
+ duplicate scene/collections, or objects. */
+ LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28,
/* *** Helper 'defines' gathering most common flag sets. *** */
/** Shapekeys are not real ID's, more like local data to geometry IDs... */
@@ -261,7 +264,8 @@ struct ID *BKE_id_copy_ex(struct Main *bmain,
const int flag);
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
struct ID *id,
- const uint duplicate_flags);
+ const uint duplicate_flags,
+ const int copy_flags);
void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b);
void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index c6658ff424a..b94a1b33606 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -84,6 +84,8 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Object *ob_proxy);
+void BKE_lib_override_library_main_proxy_convert(struct Main *bmain,
+ struct BlendFileReadReport *reports);
bool BKE_lib_override_library_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -171,6 +173,8 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain);
void BKE_lib_override_library_update(struct Main *bmain, struct ID *local);
void BKE_lib_override_library_main_update(struct Main *bmain);
+bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id);
+
/* Storage (.blend file writing) part. */
/* For now, we just use a temp main list. */
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 69e2d52e1dd..b1eaf7207fa 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -90,7 +90,7 @@ void BKE_object_material_array_assign(struct Main *bmain,
short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma);
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob);
bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob);
-bool BKE_object_material_slot_used(struct ID *id, short actcol);
+bool BKE_object_material_slot_used(struct Object *object, short actcol);
struct Material *BKE_gpencil_material(struct Object *ob, short act);
struct MaterialGPencilStyle *BKE_gpencil_material_settings(struct Object *ob, short act);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index b0a8fee1178..f44d35c62a3 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -651,9 +651,8 @@ extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me);
/* Inlines */
-/* Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h,
- * but I don't want to force every user of BKE_mesh.h to also include that file.
- * ~~ Sybren */
+/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h,
+ * but I don't want to force every user of BKE_mesh.h to also include that file. */
BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
const int *index_mp_to_orig,
const int i)
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 21ca65baf00..a81a620ab12 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -417,7 +417,7 @@ typedef struct bNodeTreeType {
void (*local_sync)(struct bNodeTree *localtree, struct bNodeTree *ntree);
void (*local_merge)(struct Main *bmain, struct bNodeTree *localtree, struct bNodeTree *ntree);
- /* Tree update. Overrides nodetype->updatetreefunc! */
+ /* Tree update. Overrides `nodetype->updatetreefunc` ! */
void (*update)(struct bNodeTree *ntree);
bool (*validate_link)(struct bNodeTree *ntree, struct bNodeLink *link);
@@ -443,7 +443,7 @@ void ntreeTypeFreeLink(const struct bNodeTreeType *nt);
bool ntreeIsRegistered(struct bNodeTree *ntree);
struct GHashIterator *ntreeTypeGetIterator(void);
-/* helper macros for iterating over tree types */
+/* Helper macros for iterating over tree types. */
#define NODE_TREE_TYPES_BEGIN(ntype) \
{ \
GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \
@@ -480,7 +480,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type);
bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup);
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree);
void ntreeUpdateAllNew(struct Main *main);
-void ntreeUpdateAllUsers(struct Main *main, struct ID *id);
+void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag);
void ntreeGetDependencyList(struct bNodeTree *ntree,
struct bNode ***r_deplist,
@@ -548,7 +548,7 @@ void nodeUnregisterType(struct bNodeType *ntype);
bool nodeTypeUndefined(struct bNode *node);
struct GHashIterator *nodeTypeGetIterator(void);
-/* helper macros for iterating over node types */
+/* Helper macros for iterating over node types. */
#define NODE_TYPES_BEGIN(ntype) \
{ \
GHashIterator *__node_type_iter__ = nodeTypeGetIterator(); \
@@ -574,7 +574,7 @@ const char *nodeStaticSocketType(int type, int subtype);
const char *nodeStaticSocketInterfaceType(int type, int subtype);
const char *nodeStaticSocketLabel(int type, int subtype);
-/* helper macros for iterating over node types */
+/* Helper macros for iterating over node types. */
#define NODE_SOCKET_TYPES_BEGIN(stype) \
{ \
GHashIterator *__node_socket_type_iter__ = nodeSocketTypeGetIterator(); \
@@ -746,7 +746,8 @@ int BKE_node_clipboard_get_type(void);
/* Node Instance Hash */
typedef struct bNodeInstanceHash {
- GHash *ghash; /* XXX should be made a direct member, GHash allocation needs to support it */
+ /** XXX should be made a direct member, #GHash allocation needs to support it */
+ GHash *ghash;
} bNodeInstanceHash;
typedef void (*bNodeInstanceValueFP)(void *value);
@@ -1102,6 +1103,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
#define SH_NODE_VERTEX_COLOR 706
#define SH_NODE_OUTPUT_AOV 707
#define SH_NODE_VECTOR_ROTATE 708
+#define SH_NODE_CURVE_FLOAT 709
/* custom defines options for Material node */
// #define SH_NODE_MAT_DIFF 1
@@ -1346,7 +1348,7 @@ 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
+/* Update the runtime layer names with the crypto-matte layer names of the references
* render layer or image. */
void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
@@ -1411,12 +1413,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
* \{ */
#define GEO_NODE_TRIANGULATE 1000
-#define GEO_NODE_EDGE_SPLIT 1001
+#define GEO_NODE_LEGACY_EDGE_SPLIT 1001
#define GEO_NODE_TRANSFORM 1002
#define GEO_NODE_BOOLEAN 1003
#define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004
#define GEO_NODE_LEGACY_POINT_INSTANCE 1005
-#define GEO_NODE_SUBDIVISION_SURFACE 1006
+#define GEO_NODE_LEGACY_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008
#define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009
@@ -1451,14 +1453,14 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
#define GEO_NODE_MESH_PRIMITIVE_GRID 1039
#define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040
-#define GEO_NODE_LECAGY_ATTRIBUTE_CLAMP 1041
+#define GEO_NODE_LEGACY_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
#define GEO_NODE_SWITCH 1043
#define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044
#define GEO_NODE_CURVE_TO_MESH 1045
#define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046
#define GEO_NODE_CURVE_RESAMPLE 1047
-#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
+#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE 1048
#define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049
#define GEO_NODE_INPUT_MATERIAL 1050
#define GEO_NODE_MATERIAL_REPLACE 1051
@@ -1467,7 +1469,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_LENGTH 1054
#define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055
#define GEO_NODE_CONVEX_HULL 1056
-#define GEO_NODE_CURVE_TO_POINTS 1057
+#define GEO_NODE_LEGACY_CURVE_TO_POINTS 1057
#define GEO_NODE_LEGACY_CURVE_REVERSE 1058
#define GEO_NODE_SEPARATE_COMPONENTS 1059
#define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060
@@ -1479,7 +1481,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PRIMITIVE_CIRCLE 1066
#define GEO_NODE_VIEWER 1067
#define GEO_NODE_CURVE_PRIMITIVE_LINE 1068
-#define GEO_NODE_CURVE_ENDPOINTS 1069
+#define GEO_NODE_LEGACY_CURVE_ENDPOINTS 1069
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
#define GEO_NODE_CURVE_TRIM 1071
#define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072
@@ -1500,7 +1502,17 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_STRING_JOIN 1087
#define GEO_NODE_CURVE_PARAMETER 1088
#define GEO_NODE_CURVE_FILLET 1089
-
+#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090
+#define GEO_NODE_STRING_TO_CURVES 1091
+#define GEO_NODE_INSTANCE_ON_POINTS 1092
+#define GEO_NODE_MESH_TO_POINTS 1093
+#define GEO_NODE_POINTS_TO_VERTICES 1094
+#define GEO_NODE_CURVE_REVERSE 1095
+#define GEO_NODE_PROXIMITY 1096
+#define GEO_NODE_CURVE_SUBDIVIDE 1097
+#define GEO_NODE_INPUT_SPLINE_LENGTH 1098
+#define GEO_NODE_CURVE_SPLINE_TYPE 1099
+#define GEO_NODE_CURVE_SET_HANDLES 1100
/** \} */
/* -------------------------------------------------------------------- */
@@ -1509,13 +1521,16 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_BOOLEAN_MATH 1200
#define FN_NODE_FLOAT_COMPARE 1202
-#define FN_NODE_RANDOM_FLOAT 1206
+#define FN_NODE_LEGACY_RANDOM_FLOAT 1206
#define FN_NODE_INPUT_VECTOR 1207
#define FN_NODE_INPUT_STRING 1208
#define FN_NODE_FLOAT_TO_INT 1209
#define FN_NODE_VALUE_TO_STRING 1210
#define FN_NODE_STRING_LENGTH 1211
#define FN_NODE_STRING_SUBSTRING 1212
+#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213
+#define FN_NODE_RANDOM_VALUE 1214
+#define FN_NODE_ROTATE_EULER 1215
/** \} */
diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h
index 04a41d425bb..bd887c1ea0d 100644
--- a/source/blender/blenkernel/BKE_preferences.h
+++ b/source/blender/blenkernel/BKE_preferences.h
@@ -29,22 +29,32 @@ extern "C" {
struct UserDef;
struct bUserAssetLibrary;
-void BKE_preferences_asset_library_free(struct bUserAssetLibrary *library) ATTR_NONNULL();
-
struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef,
const char *name,
const char *path) ATTR_NONNULL(1);
+void BKE_preferences_asset_library_remove(struct UserDef *userdef,
+ struct bUserAssetLibrary *library) ATTR_NONNULL();
+
void BKE_preferences_asset_library_name_set(struct UserDef *userdef,
struct bUserAssetLibrary *library,
const char *name) ATTR_NONNULL();
-void BKE_preferences_asset_library_remove(struct UserDef *userdef,
- struct bUserAssetLibrary *library) ATTR_NONNULL();
-
struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(
const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(
const struct UserDef *userdef, const char *name) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+
+/**
+ * Return the bUserAssetLibrary that contains the given file/directory path. The given path can be
+ * the library's top-level directory, or any path inside that directory.
+ *
+ * When more than one asset libraries match, the first matching one is returned (no smartness when
+ * there nested asset libraries).
+ *
+ * Return NULL when no such asset library is found. */
+struct bUserAssetLibrary *BKE_preferences_asset_library_containing_path(
+ const struct UserDef *userdef, const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+
int BKE_preferences_asset_library_get_index(const struct UserDef *userdef,
const struct bUserAssetLibrary *library)
ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 541ff19c1cd..97e0d8415a5 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -316,6 +316,9 @@ class BezierSpline final : public Spline {
void translate(const blender::float3 &translation) override;
void transform(const blender::float4x4 &matrix) override;
+ void set_handle_position_right(const int index, const blender::float3 &value);
+ void set_handle_position_left(const int index, const blender::float3 &value);
+
bool point_is_sharp(const int index) const;
void mark_cache_invalid() final;
diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h
index 3d47c8d3bc5..2fb27fad30d 100644
--- a/source/blender/blenkernel/BKE_subdiv.h
+++ b/source/blender/blenkernel/BKE_subdiv.h
@@ -56,7 +56,7 @@ typedef enum eSubdivFVarLinearInterpolation {
} eSubdivFVarLinearInterpolation;
typedef struct SubdivSettings {
- /* Simple subdivision corresponds to "Simple" option in the interface. When its enabled the
+ /* Simple subdivision corresponds to "Simple" option in the interface. When it's enabled the
* subdivided mesh is not "smoothed": new vertices are added uniformly on the existing surface.
*
* On an OpenSubdiv implementation level this translates to a subdivision scheme:
diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.h b/source/blender/blenkernel/BKE_subdiv_foreach.h
index a351b9a3d11..3f74299455d 100644
--- a/source/blender/blenkernel/BKE_subdiv_foreach.h
+++ b/source/blender/blenkernel/BKE_subdiv_foreach.h
@@ -127,7 +127,7 @@ typedef struct SubdivForeachContext {
SubdivForeachVertexFromEdgeCb vertex_edge;
/* Called exactly once, always corresponds to a single ptex face. */
SubdivForeachVertexInnerCb vertex_inner;
- /* Called once for each loose vertex. One loose coarse vertexcorresponds
+ /* Called once for each loose vertex. One loose coarse vertex corresponds
* to a single subdivision vertex.
*/
SubdivForeachLooseCb vertex_loose;
@@ -144,7 +144,7 @@ typedef struct SubdivForeachContext {
SubdivForeachPolygonCb poly;
/* User-defined pointer, to allow callbacks know something about context the
- * traversal is happening for,
+ * traversal is happening for.
*/
void *user_data;
@@ -163,7 +163,7 @@ typedef struct SubdivForeachContext {
* indices (for vertices, edges, loops, polygons) in the same way as subdivision
* modifier will do for a dense mesh.
*
- * Returns truth if the whole topology was traversed, without any early exits.
+ * Returns true if the whole topology was traversed, without any early exits.
*
* TODO(sergey): Need to either get rid of subdiv or of coarse_mesh.
* The main point here is to be able to get base level topology, which can be
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index d9333996632..5fe0d54c2cf 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -18,7 +18,7 @@
/** \file
* \ingroup bke
- * \brief Volume datablock.
+ * \brief Volume data-block.
*/
#ifdef __cplusplus
extern "C" {
@@ -37,7 +37,7 @@ struct VolumeGridVector;
void BKE_volumes_init(void);
-/* Datablock Management */
+/* Data-block Management */
void BKE_volume_init_grids(struct Volume *volume);
void *BKE_volume_add(struct Main *bmain, const char *name);
@@ -122,13 +122,13 @@ void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4
/* Volume Editing
*
- * These are intended for modifiers to use on evaluated datablocks.
+ * These are intended for modifiers to use on evaluated data-blocks.
*
- * new_for_eval creates a volume datablock with no grids or file path, but
+ * new_for_eval creates a volume data-block with no grids or file path, but
* preserves other settings such as viewport display options.
*
- * copy_for_eval creates a volume datablock preserving everything except the
- * file path. Grids are shared with the source datablock, not copied. */
+ * copy_for_eval creates a volume data-block preserving everything except the
+ * file path. Grids are shared with the source data-block, not copied. */
struct Volume *BKE_volume_new_for_eval(const struct Volume *volume_src);
struct Volume *BKE_volume_copy_for_eval(struct Volume *volume_src, bool reference);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index de7864ef36a..fb7fdd1ac21 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -84,6 +84,9 @@ set(SRC
intern/armature_selection.cc
intern/armature_update.c
intern/asset.cc
+ intern/asset_catalog.cc
+ intern/asset_catalog_path.cc
+ intern/asset_library.cc
intern/attribute.c
intern/attribute_access.cc
intern/attribute_math.cc
@@ -303,6 +306,10 @@ set(SRC
BKE_armature.h
BKE_armature.hh
BKE_asset.h
+ BKE_asset_catalog.hh
+ BKE_asset_catalog_path.hh
+ BKE_asset_library.h
+ BKE_asset_library.hh
BKE_attribute.h
BKE_attribute_access.hh
BKE_attribute_math.hh
@@ -783,6 +790,10 @@ if(WITH_GTESTS)
set(TEST_SRC
intern/action_test.cc
intern/armature_test.cc
+ intern/asset_catalog_test.cc
+ intern/asset_catalog_path_test.cc
+ intern/asset_library_test.cc
+ intern/asset_test.cc
intern/cryptomatte_test.cc
intern/fcurve_test.cc
intern/lattice_deform_test.cc
diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc
index f74018b20c5..ae9ded3c754 100644
--- a/source/blender/blenkernel/intern/asset.cc
+++ b/source/blender/blenkernel/intern/asset.cc
@@ -26,8 +26,10 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_string_ref.hh"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
+#include "BLI_uuid.h"
#include "BKE_asset.h"
#include "BKE_icons.h"
@@ -37,6 +39,8 @@
#include "MEM_guardedalloc.h"
+using namespace blender;
+
AssetMetaData *BKE_asset_metadata_create(void)
{
AssetMetaData *asset_data = (AssetMetaData *)MEM_callocN(sizeof(*asset_data), __func__);
@@ -115,6 +119,27 @@ void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref
memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref));
}
+void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data)
+{
+ asset_data->catalog_id = BLI_uuid_nil();
+ asset_data->catalog_simple_name[0] = '\0';
+}
+
+void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data,
+ const ::bUUID catalog_id,
+ const char *catalog_simple_name)
+{
+ asset_data->catalog_id = catalog_id;
+
+ constexpr size_t max_simple_name_length = sizeof(asset_data->catalog_simple_name);
+
+ /* The substr() call is necessary to make copy() copy the first N characters (instead of refusing
+ * to copy and producing an empty string). */
+ StringRef trimmed_id =
+ StringRef(catalog_simple_name).trim().substr(0, max_simple_name_length - 1);
+ trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length);
+}
+
/* Queries -------------------------------------------- */
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
new file mode 100644
index 00000000000..2c7cf28d60d
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -0,0 +1,791 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.h"
+#include "BKE_preferences.h"
+
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "BLI_string_ref.hh"
+
+#include "DNA_userdef_types.h"
+
+/* For S_ISREG() and S_ISDIR() on Windows. */
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#endif
+
+#include <fstream>
+#include <set>
+
+namespace blender::bke {
+
+const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt";
+
+/* For now this is the only version of the catalog definition files that is supported.
+ * Later versioning code may be added to handle older files. */
+const int AssetCatalogDefinitionFile::SUPPORTED_VERSION = 1;
+/* String that's matched in the catalog definition file to know that the line is the version
+ * declaration. It has to start with a space to ensure it won't match any hypothetical future field
+ * that starts with "VERSION". */
+const std::string AssetCatalogDefinitionFile::VERSION_MARKER = "VERSION ";
+
+const std::string AssetCatalogDefinitionFile::HEADER =
+ "# This is an Asset Catalog Definition file for Blender.\n"
+ "#\n"
+ "# Empty lines and lines starting with `#` will be ignored.\n"
+ "# The first non-ignored line should be the version indicator.\n"
+ "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n";
+
+AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root)
+ : asset_library_root_(asset_library_root)
+{
+}
+
+bool AssetCatalogService::is_empty() const
+{
+ return catalogs_.is_empty();
+}
+
+AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const
+{
+ const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
+ if (catalog_uptr_ptr == nullptr) {
+ return nullptr;
+ }
+ return catalog_uptr_ptr->get();
+}
+
+AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const
+{
+ for (const auto &catalog : catalogs_.values()) {
+ if (catalog->path == path) {
+ return catalog.get();
+ }
+ }
+
+ return nullptr;
+}
+
+AssetCatalogFilter AssetCatalogService::create_catalog_filter(
+ const CatalogID active_catalog_id) const
+{
+ Set<CatalogID> matching_catalog_ids;
+ matching_catalog_ids.add(active_catalog_id);
+
+ const AssetCatalog *active_catalog = find_catalog(active_catalog_id);
+ if (!active_catalog) {
+ /* If the UUID is unknown (i.e. not mapped to an actual Catalog), it is impossible to determine
+ * its children. The filter can still work on the given UUID. */
+ return AssetCatalogFilter(std::move(matching_catalog_ids));
+ }
+
+ /* This cannot just iterate over tree items to get all the required data, because tree items only
+ * represent single UUIDs. It could be used to get the main UUIDs of the children, though, and
+ * then only do an exact match on the path (instead of the more complex `is_contained_in()`
+ * call). Without an extra indexed-by-path acceleration structure, this is still going to require
+ * a linear search, though. */
+ for (const auto &catalog_uptr : this->catalogs_.values()) {
+ if (catalog_uptr->path.is_contained_in(active_catalog->path)) {
+ matching_catalog_ids.add(catalog_uptr->catalog_id);
+ }
+ }
+
+ return AssetCatalogFilter(std::move(matching_catalog_ids));
+}
+
+void AssetCatalogService::delete_catalog(CatalogID catalog_id)
+{
+ std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
+ if (catalog_uptr_ptr == nullptr) {
+ /* Catalog cannot be found, which is fine. */
+ return;
+ }
+
+ /* Mark the catalog as deleted. */
+ AssetCatalog *catalog = catalog_uptr_ptr->get();
+ catalog->flags.is_deleted = true;
+
+ /* Move ownership from this->catalogs_ to this->deleted_catalogs_. */
+ this->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr));
+
+ /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */
+ this->catalogs_.remove(catalog_id);
+
+ this->rebuild_tree();
+}
+
+void AssetCatalogService::update_catalog_path(CatalogID catalog_id,
+ const AssetCatalogPath &new_catalog_path)
+{
+ AssetCatalog *renamed_cat = this->find_catalog(catalog_id);
+ const AssetCatalogPath old_cat_path = renamed_cat->path;
+
+ for (auto &catalog_uptr : catalogs_.values()) {
+ AssetCatalog *cat = catalog_uptr.get();
+
+ const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path);
+ if (!new_path) {
+ continue;
+ }
+ cat->path = new_path;
+ }
+
+ this->rebuild_tree();
+}
+
+AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path)
+{
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path(catalog_path);
+
+ /* So we can std::move(catalog) and still use the non-owning pointer: */
+ AssetCatalog *const catalog_ptr = catalog.get();
+
+ /* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse
+ * catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs.
+ */
+ BLI_assert_msg(!catalogs_.contains(catalog->catalog_id), "duplicate catalog ID not supported");
+ catalogs_.add_new(catalog->catalog_id, std::move(catalog));
+
+ if (catalog_definition_file_) {
+ /* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet,
+ * it's enough to have the catalog known to the service as it'll be saved to a new file. */
+ catalog_definition_file_->add_new(catalog_ptr);
+ }
+
+ BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist.");
+ catalog_tree_->insert_item(*catalog_ptr);
+
+ return catalog_ptr;
+}
+
+static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root)
+{
+ char file_path[PATH_MAX];
+ BLI_join_dirfile(file_path,
+ sizeof(file_path),
+ asset_library_root.data(),
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME.data());
+ return file_path;
+}
+
+void AssetCatalogService::load_from_disk()
+{
+ load_from_disk(asset_library_root_);
+}
+
+void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_directory_path)
+{
+ BLI_stat_t status;
+ if (BLI_stat(file_or_directory_path.data(), &status) == -1) {
+ // TODO(@sybren): throw an appropriate exception.
+ return;
+ }
+
+ if (S_ISREG(status.st_mode)) {
+ load_single_file(file_or_directory_path);
+ }
+ else if (S_ISDIR(status.st_mode)) {
+ load_directory_recursive(file_or_directory_path);
+ }
+ else {
+ // TODO(@sybren): throw an appropriate exception.
+ }
+
+ /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */
+
+ rebuild_tree();
+}
+
+void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path)
+{
+ // TODO(@sybren): implement proper multi-file support. For now, just load
+ // the default file if it is there.
+ CatalogFilePath file_path = asset_definition_default_file_path_from_dir(directory_path);
+
+ if (!BLI_exists(file_path.data())) {
+ /* No file to be loaded is perfectly fine. */
+ return;
+ }
+
+ this->load_single_file(file_path);
+}
+
+void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path)
+{
+ /* TODO(@sybren): check that #catalog_definition_file_path is contained in #asset_library_root_,
+ * otherwise some assumptions may fail. */
+ std::unique_ptr<AssetCatalogDefinitionFile> cdf = parse_catalog_file(
+ catalog_definition_file_path);
+
+ BLI_assert_msg(!this->catalog_definition_file_,
+ "Only loading of a single catalog definition file is supported.");
+ this->catalog_definition_file_ = std::move(cdf);
+}
+
+std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_file(
+ const CatalogFilePath &catalog_definition_file_path)
+{
+ auto cdf = std::make_unique<AssetCatalogDefinitionFile>();
+ cdf->file_path = catalog_definition_file_path;
+
+ auto catalog_parsed_callback = [this, catalog_definition_file_path](
+ std::unique_ptr<AssetCatalog> catalog) {
+ if (this->catalogs_.contains(catalog->catalog_id)) {
+ // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet.
+ std::cerr << catalog_definition_file_path << ": multiple definitions of catalog "
+ << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl;
+ /* Don't store 'catalog'; unique_ptr will free its memory. */
+ return false;
+ }
+
+ /* The AssetCatalog pointer is now owned by the AssetCatalogService. */
+ this->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
+ return true;
+ };
+
+ cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback);
+
+ return cdf;
+}
+
+void AssetCatalogService::merge_from_disk_before_writing()
+{
+ /* TODO(Sybren): expand to support multiple CDFs. */
+
+ if (!catalog_definition_file_ || catalog_definition_file_->file_path.empty() ||
+ !BLI_is_file(catalog_definition_file_->file_path.c_str())) {
+ return;
+ }
+
+ auto catalog_parsed_callback = [this](std::unique_ptr<AssetCatalog> catalog) {
+ const bUUID catalog_id = catalog->catalog_id;
+
+ /* The following two conditions could be or'ed together. Keeping them separated helps when
+ * adding debug prints, breakpoints, etc. */
+ if (this->catalogs_.contains(catalog_id)) {
+ /* This catalog was already seen, so just ignore it. */
+ return false;
+ }
+ if (this->deleted_catalogs_.contains(catalog_id)) {
+ /* This catalog was already seen and subsequently deleted, so just ignore it. */
+ return false;
+ }
+
+ /* This is a new catalog, so let's keep it around. */
+ this->catalogs_.add_new(catalog_id, std::move(catalog));
+ return true;
+ };
+
+ catalog_definition_file_->parse_catalog_file(catalog_definition_file_->file_path,
+ catalog_parsed_callback);
+}
+
+bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path)
+{
+ /* TODO(Sybren): expand to support multiple CDFs. */
+
+ /* - Already loaded a CDF from disk? -> Always write to that file. */
+ if (this->catalog_definition_file_) {
+ merge_from_disk_before_writing();
+ return catalog_definition_file_->write_to_disk();
+ }
+
+ if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) {
+ /* Avoid saving anything, when there is nothing to save. */
+ return true; /* Writing nothing when there is nothing to write is still a success. */
+ }
+
+ const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path);
+ this->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write);
+ merge_from_disk_before_writing();
+ return catalog_definition_file_->write_to_disk();
+}
+
+CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
+ const CatalogFilePath &blend_file_path)
+{
+ BLI_assert_msg(!blend_file_path.empty(),
+ "A non-empty .blend file path is required to be able to determine where the "
+ "catalog definition file should be put");
+
+ /* Determine the default CDF path in the same directory of the blend file. */
+ char blend_dir_path[PATH_MAX];
+ BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path));
+ const CatalogFilePath cdf_path_next_to_blend = asset_definition_default_file_path_from_dir(
+ blend_dir_path);
+
+ if (BLI_exists(cdf_path_next_to_blend.c_str())) {
+ /* - The directory containing the blend file has a blender_assets.cats.txt file?
+ * -> Merge with & write to that file. */
+ return cdf_path_next_to_blend;
+ }
+
+ /* - There's no definition file next to the .blend file.
+ * -> Ask the asset library API for an appropriate location. */
+ char suitable_root_path[PATH_MAX];
+ BKE_asset_library_find_suitable_root_path_from_path(blend_file_path.c_str(), suitable_root_path);
+ char asset_lib_cdf_path[PATH_MAX];
+ BLI_path_join(asset_lib_cdf_path,
+ sizeof(asset_lib_cdf_path),
+ suitable_root_path,
+ DEFAULT_CATALOG_FILENAME.c_str(),
+ NULL);
+
+ return asset_lib_cdf_path;
+}
+
+std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::construct_cdf_in_memory(
+ const CatalogFilePath &file_path)
+{
+ auto cdf = std::make_unique<AssetCatalogDefinitionFile>();
+ cdf->file_path = file_path;
+
+ for (auto &catalog : catalogs_.values()) {
+ cdf->add_new(catalog.get());
+ }
+
+ return cdf;
+}
+
+std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree()
+{
+ auto tree = std::make_unique<AssetCatalogTree>();
+
+ /* Go through the catalogs, insert each path component into the tree where needed. */
+ for (auto &catalog : catalogs_.values()) {
+ tree->insert_item(*catalog);
+ }
+
+ return tree;
+}
+
+void AssetCatalogService::rebuild_tree()
+{
+ create_missing_catalogs();
+ this->catalog_tree_ = read_into_tree();
+}
+
+void AssetCatalogService::create_missing_catalogs()
+{
+ /* Construct an ordered set of paths to check, so that parents are ordered before children. */
+ std::set<AssetCatalogPath> paths_to_check;
+ for (auto &catalog : catalogs_.values()) {
+ paths_to_check.insert(catalog->path);
+ }
+
+ std::set<AssetCatalogPath> seen_paths;
+ /* The empty parent should never be created, so always be considered "seen". */
+ seen_paths.insert(AssetCatalogPath(""));
+
+ /* Find and create missing direct parents (so ignoring parents-of-parents). */
+ while (!paths_to_check.empty()) {
+ /* Pop the first path of the queue. */
+ const AssetCatalogPath path = *paths_to_check.begin();
+ paths_to_check.erase(paths_to_check.begin());
+
+ if (seen_paths.find(path) != seen_paths.end()) {
+ /* This path has been seen already, so it can be ignored. */
+ continue;
+ }
+ seen_paths.insert(path);
+
+ const AssetCatalogPath parent_path = path.parent();
+ if (seen_paths.find(parent_path) != seen_paths.end()) {
+ /* The parent exists, continue to the next path. */
+ continue;
+ }
+
+ /* The parent doesn't exist, so create it and queue it up for checking its parent. */
+ create_catalog(parent_path);
+ paths_to_check.insert(parent_path);
+ }
+
+ /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */
+}
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
+ CatalogID catalog_id,
+ const AssetCatalogTreeItem *parent)
+ : name_(name), catalog_id_(catalog_id), parent_(parent)
+{
+}
+
+CatalogID AssetCatalogTreeItem::get_catalog_id() const
+{
+ return catalog_id_;
+}
+
+StringRef AssetCatalogTreeItem::get_name() const
+{
+ return name_;
+}
+
+AssetCatalogPath AssetCatalogTreeItem::catalog_path() const
+{
+ AssetCatalogPath current_path = name_;
+ for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
+ current_path = AssetCatalogPath(parent->name_) / current_path;
+ }
+ return current_path;
+}
+
+int AssetCatalogTreeItem::count_parents() const
+{
+ int i = 0;
+ for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
+ i++;
+ }
+ return i;
+}
+
+bool AssetCatalogTreeItem::has_children() const
+{
+ return !children_.empty();
+}
+
+/* ---------------------------------------------------------------------- */
+
+void AssetCatalogTree::insert_item(const AssetCatalog &catalog)
+{
+ const AssetCatalogTreeItem *parent = nullptr;
+ /* The children for the currently iterated component, where the following component should be
+ * added to (if not there yet). */
+ AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_;
+
+ BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'),
+ "Malformed catalog path; should not start with a separator");
+
+ const CatalogID nil_id{};
+
+ catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) {
+ /* Insert new tree element - if no matching one is there yet! */
+ auto [key_and_item, was_inserted] = current_item_children->emplace(
+ component_name,
+ AssetCatalogTreeItem(
+ component_name, is_last_component ? catalog.catalog_id : nil_id, parent));
+ AssetCatalogTreeItem &item = key_and_item->second;
+
+ /* If full path of this catalog already exists as parent path of a previously read catalog,
+ * we can ensure this tree item's UUID is set here. */
+ if (is_last_component && BLI_uuid_is_nil(item.catalog_id_)) {
+ item.catalog_id_ = catalog.catalog_id;
+ }
+
+ /* Walk further into the path (no matter if a new item was created or not). */
+ parent = &item;
+ current_item_children = &item.children_;
+ });
+}
+
+void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback)
+{
+ AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback);
+}
+
+void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children,
+ const ItemIterFn callback)
+{
+ for (auto &[key, item] : children) {
+ callback(item);
+ foreach_item_recursive(item.children_, callback);
+ }
+}
+
+void AssetCatalogTree::foreach_root_item(const ItemIterFn callback)
+{
+ for (auto &[key, item] : root_items_) {
+ callback(item);
+ }
+}
+
+void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback)
+{
+ for (auto &[key, item] : children_) {
+ callback(item);
+ }
+}
+
+AssetCatalogTree *AssetCatalogService::get_catalog_tree()
+{
+ return catalog_tree_.get();
+}
+
+bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const
+{
+ return catalogs_.contains(catalog_id);
+}
+
+void AssetCatalogDefinitionFile::add_new(AssetCatalog *catalog)
+{
+ catalogs_.add_new(catalog->catalog_id, catalog);
+}
+
+void AssetCatalogDefinitionFile::parse_catalog_file(
+ const CatalogFilePath &catalog_definition_file_path,
+ AssetCatalogParsedFn catalog_loaded_callback)
+{
+ std::fstream infile(catalog_definition_file_path);
+
+ bool seen_version_number = false;
+ std::string line;
+ while (std::getline(infile, line)) {
+ const StringRef trimmed_line = StringRef(line).trim();
+ if (trimmed_line.is_empty() || trimmed_line[0] == '#') {
+ continue;
+ }
+
+ if (!seen_version_number) {
+ /* The very first non-ignored line should be the version declaration. */
+ const bool is_valid_version = this->parse_version_line(trimmed_line);
+ if (!is_valid_version) {
+ std::cerr << catalog_definition_file_path
+ << ": first line should be version declaration; ignoring file." << std::endl;
+ break;
+ }
+ seen_version_number = true;
+ continue;
+ }
+
+ std::unique_ptr<AssetCatalog> catalog = this->parse_catalog_line(trimmed_line);
+ if (!catalog) {
+ continue;
+ }
+
+ AssetCatalog *non_owning_ptr = catalog.get();
+ const bool keep_catalog = catalog_loaded_callback(std::move(catalog));
+ if (!keep_catalog) {
+ continue;
+ }
+
+ if (this->contains(non_owning_ptr->catalog_id)) {
+ std::cerr << catalog_definition_file_path << ": multiple definitions of catalog "
+ << non_owning_ptr->catalog_id << " in the same file, using first occurrence."
+ << std::endl;
+ /* Don't store 'catalog'; unique_ptr will free its memory. */
+ continue;
+ }
+
+ /* The AssetDefinitionFile should include this catalog when writing it back to disk. */
+ this->add_new(non_owning_ptr);
+ }
+}
+
+bool AssetCatalogDefinitionFile::parse_version_line(const StringRef line)
+{
+ if (!line.startswith(VERSION_MARKER)) {
+ return false;
+ }
+
+ const std::string version_string = line.substr(VERSION_MARKER.length());
+ const int file_version = std::atoi(version_string.c_str());
+
+ /* No versioning, just a blunt check whether it's the right one. */
+ return file_version == SUPPORTED_VERSION;
+}
+
+std::unique_ptr<AssetCatalog> AssetCatalogDefinitionFile::parse_catalog_line(const StringRef line)
+{
+ const char delim = ':';
+ const int64_t first_delim = line.find_first_of(delim);
+ if (first_delim == StringRef::not_found) {
+ std::cerr << "Invalid catalog line in " << this->file_path << ": " << line << std::endl;
+ return std::unique_ptr<AssetCatalog>(nullptr);
+ }
+
+ /* Parse the catalog ID. */
+ const std::string id_as_string = line.substr(0, first_delim).trim();
+ bUUID catalog_id;
+ const bool uuid_parsed_ok = BLI_uuid_parse_string(&catalog_id, id_as_string.c_str());
+ if (!uuid_parsed_ok) {
+ std::cerr << "Invalid UUID in " << this->file_path << ": " << line << std::endl;
+ return std::unique_ptr<AssetCatalog>(nullptr);
+ }
+
+ /* Parse the path and simple name. */
+ const StringRef path_and_simple_name = line.substr(first_delim + 1);
+ const int64_t second_delim = path_and_simple_name.find_first_of(delim);
+
+ std::string path_in_file;
+ std::string simple_name;
+ if (second_delim == 0) {
+ /* Delimiter as first character means there is no path. These lines are to be ignored. */
+ return std::unique_ptr<AssetCatalog>(nullptr);
+ }
+
+ if (second_delim == StringRef::not_found) {
+ /* No delimiter means no simple name, just treat it as all "path". */
+ path_in_file = path_and_simple_name;
+ simple_name = "";
+ }
+ else {
+ path_in_file = path_and_simple_name.substr(0, second_delim);
+ simple_name = path_and_simple_name.substr(second_delim + 1).trim();
+ }
+
+ AssetCatalogPath catalog_path = path_in_file;
+ return std::make_unique<AssetCatalog>(catalog_id, catalog_path.cleanup(), simple_name);
+}
+
+bool AssetCatalogDefinitionFile::write_to_disk() const
+{
+ BLI_assert_msg(!this->file_path.empty(), "Writing to CDF requires its file path to be known");
+ return this->write_to_disk(this->file_path);
+}
+
+bool AssetCatalogDefinitionFile::write_to_disk(const CatalogFilePath &dest_file_path) const
+{
+ const CatalogFilePath writable_path = dest_file_path + ".writing";
+ const CatalogFilePath backup_path = dest_file_path + "~";
+
+ if (!this->write_to_disk_unsafe(writable_path)) {
+ /* TODO: communicate what went wrong. */
+ return false;
+ }
+ if (BLI_exists(dest_file_path.c_str())) {
+ if (BLI_rename(dest_file_path.c_str(), backup_path.c_str())) {
+ /* TODO: communicate what went wrong. */
+ return false;
+ }
+ }
+ if (BLI_rename(writable_path.c_str(), dest_file_path.c_str())) {
+ /* TODO: communicate what went wrong. */
+ return false;
+ }
+
+ return true;
+}
+
+bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const
+{
+ char directory[PATH_MAX];
+ BLI_split_dir_part(dest_file_path.c_str(), directory, sizeof(directory));
+ if (!ensure_directory_exists(directory)) {
+ /* TODO(Sybren): pass errors to the UI somehow. */
+ return false;
+ }
+
+ std::ofstream output(dest_file_path);
+
+ // TODO(@sybren): remember the line ending style that was originally read, then use that to write
+ // the file again.
+
+ // Write the header.
+ output << HEADER;
+ output << "" << std::endl;
+ output << VERSION_MARKER << SUPPORTED_VERSION << std::endl;
+ output << "" << std::endl;
+
+ // Write the catalogs, ordered by path (primary) and UUID (secondary).
+ AssetCatalogOrderedSet catalogs_by_path;
+ for (const AssetCatalog *catalog : catalogs_.values()) {
+ if (catalog->flags.is_deleted) {
+ continue;
+ }
+ catalogs_by_path.insert(catalog);
+ }
+
+ for (const AssetCatalog *catalog : catalogs_by_path) {
+ output << catalog->catalog_id << ":" << catalog->path << ":" << catalog->simple_name
+ << std::endl;
+ }
+ output.close();
+ return !output.bad();
+}
+
+bool AssetCatalogDefinitionFile::ensure_directory_exists(
+ const CatalogFilePath directory_path) const
+{
+ /* TODO(@sybren): design a way to get such errors presented to users (or ensure that they never
+ * occur). */
+ if (directory_path.empty()) {
+ std::cerr
+ << "AssetCatalogService: no asset library root configured, unable to ensure it exists."
+ << std::endl;
+ return false;
+ }
+
+ if (BLI_exists(directory_path.data())) {
+ if (!BLI_is_dir(directory_path.data())) {
+ std::cerr << "AssetCatalogService: " << directory_path
+ << " exists but is not a directory, this is not a supported situation."
+ << std::endl;
+ return false;
+ }
+
+ /* Root directory exists, work is done. */
+ return true;
+ }
+
+ /* Ensure the root directory exists. */
+ std::error_code err_code;
+ if (!BLI_dir_create_recursive(directory_path.data())) {
+ std::cerr << "AssetCatalogService: error creating directory " << directory_path << ": "
+ << err_code << std::endl;
+ return false;
+ }
+
+ /* Root directory has been created, work is done. */
+ return true;
+}
+
+AssetCatalog::AssetCatalog(const CatalogID catalog_id,
+ const AssetCatalogPath &path,
+ const std::string &simple_name)
+ : catalog_id(catalog_id), path(path), simple_name(simple_name)
+{
+}
+
+std::unique_ptr<AssetCatalog> AssetCatalog::from_path(const AssetCatalogPath &path)
+{
+ const AssetCatalogPath clean_path = path.cleanup();
+ const CatalogID cat_id = BLI_uuid_generate_random();
+ const std::string simple_name = sensible_simple_name_for_path(clean_path);
+ auto catalog = std::make_unique<AssetCatalog>(cat_id, clean_path, simple_name);
+ return catalog;
+}
+
+std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &path)
+{
+ std::string name = path.str();
+ std::replace(name.begin(), name.end(), AssetCatalogPath::SEPARATOR, '-');
+ if (name.length() < MAX_NAME - 1) {
+ return name;
+ }
+
+ /* Trim off the start of the path, as that's the most generic part and thus contains the least
+ * information. */
+ return "..." + name.substr(name.length() - 60);
+}
+
+AssetCatalogFilter::AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids)
+ : matching_catalog_ids(std::move(matching_catalog_ids))
+{
+}
+
+bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const
+{
+ return matching_catalog_ids.contains(asset_catalog_id);
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc
new file mode 100644
index 00000000000..85b8969cb8c
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_catalog_path.cc
@@ -0,0 +1,228 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_asset_catalog_path.hh"
+
+#include "BLI_path_util.h"
+
+namespace blender::bke {
+
+const char AssetCatalogPath::SEPARATOR = '/';
+
+AssetCatalogPath::AssetCatalogPath(const std::string &path) : path_(path)
+{
+}
+
+AssetCatalogPath::AssetCatalogPath(StringRef path) : path_(path)
+{
+}
+
+AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path)
+{
+}
+
+AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept
+ : path_(std::move(other_path.path_))
+{
+}
+
+uint64_t AssetCatalogPath::hash() const
+{
+ std::hash<std::string> hasher{};
+ return hasher(this->path_);
+}
+
+uint64_t AssetCatalogPath::length() const
+{
+ return this->path_.length();
+}
+
+const char *AssetCatalogPath::c_str() const
+{
+ return this->path_.c_str();
+}
+
+const std::string &AssetCatalogPath::str() const
+{
+ return this->path_;
+}
+
+/* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor.
+ * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */
+bool AssetCatalogPath::operator==(const AssetCatalogPath &other_path) const
+{
+ return this->path_ == other_path.path_;
+}
+
+bool AssetCatalogPath::operator!=(const AssetCatalogPath &other_path) const
+{
+ return !(*this == other_path);
+}
+
+bool AssetCatalogPath::operator<(const AssetCatalogPath &other_path) const
+{
+ return this->path_ < other_path.path_;
+}
+
+AssetCatalogPath AssetCatalogPath::operator/(const AssetCatalogPath &path_to_append) const
+{
+ /* `"" / "path"` or `"path" / ""` should just result in `"path"` */
+ if (!*this) {
+ return path_to_append;
+ }
+ if (!path_to_append) {
+ return *this;
+ }
+
+ std::stringstream new_path;
+ new_path << this->path_ << SEPARATOR << path_to_append.path_;
+ return AssetCatalogPath(new_path.str());
+}
+
+AssetCatalogPath::operator bool() const
+{
+ return !this->path_.empty();
+}
+
+std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append)
+{
+ stream << path_to_append.path_;
+ return stream;
+}
+
+AssetCatalogPath AssetCatalogPath::cleanup() const
+{
+ std::stringstream clean_components;
+ bool first_component_seen = false;
+
+ this->iterate_components([&clean_components, &first_component_seen](StringRef component_name,
+ bool /*is_last_component*/) {
+ const std::string clean_component = cleanup_component(component_name);
+
+ if (clean_component.empty()) {
+ /* These are caused by leading, trailing, or double slashes. */
+ return;
+ }
+
+ /* If a previous path component has been streamed already, we need a path separator. This
+ * cannot use the `is_last_component` boolean, because the last component might be skipped due
+ * to the condition above. */
+ if (first_component_seen) {
+ clean_components << SEPARATOR;
+ }
+ first_component_seen = true;
+
+ clean_components << clean_component;
+ });
+
+ return AssetCatalogPath(clean_components.str());
+}
+
+std::string AssetCatalogPath::cleanup_component(StringRef component)
+{
+ std::string cleaned = component.trim();
+ /* Replace colons with something else, as those are used in the CDF file as delimiter. */
+ std::replace(cleaned.begin(), cleaned.end(), ':', '-');
+ return cleaned;
+}
+
+bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const
+{
+ if (!other_path) {
+ /* The empty path contains all other paths. */
+ return true;
+ }
+
+ if (this->path_ == other_path.path_) {
+ /* Weak is-in relation: equal paths contain each other. */
+ return true;
+ }
+
+ /* To be a child path of 'other_path', our path must be at least a separator and another
+ * character longer. */
+ if (this->length() < other_path.length() + 2) {
+ return false;
+ }
+
+ /* Create StringRef to be able to use .startswith(). */
+ const StringRef this_path(this->path_);
+ const bool prefix_ok = this_path.startswith(other_path.path_);
+ const char next_char = this_path[other_path.length()];
+ return prefix_ok && next_char == SEPARATOR;
+}
+
+AssetCatalogPath AssetCatalogPath::parent() const
+{
+ if (!*this) {
+ return AssetCatalogPath("");
+ }
+ std::string::size_type last_sep_index = this->path_.rfind(SEPARATOR);
+ if (last_sep_index == std::string::npos) {
+ return AssetCatalogPath("");
+ }
+ return AssetCatalogPath(this->path_.substr(0, last_sep_index));
+}
+
+void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const
+{
+ const char *next_slash_ptr;
+
+ for (const char *path_component = this->path_.data(); path_component && path_component[0];
+ /* Jump to one after the next slash if there is any. */
+ path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
+ next_slash_ptr = BLI_path_slash_find(path_component);
+
+ const bool is_last_component = next_slash_ptr == nullptr;
+ /* Note that this won't be null terminated. */
+ const StringRef component_name = is_last_component ?
+ path_component :
+ StringRef(path_component,
+ next_slash_ptr - path_component);
+
+ callback(component_name, is_last_component);
+ }
+}
+
+AssetCatalogPath AssetCatalogPath::rebase(const AssetCatalogPath &from_path,
+ const AssetCatalogPath &to_path) const
+{
+ if (!from_path) {
+ if (!to_path) {
+ return AssetCatalogPath("");
+ }
+ return to_path / *this;
+ }
+
+ if (!this->is_contained_in(from_path)) {
+ return AssetCatalogPath("");
+ }
+
+ if (*this == from_path) {
+ /* Early return, because otherwise the length+1 below is going to cause problems. */
+ return to_path;
+ }
+
+ /* When from_path = "test", we need to skip "test/" to get the rest of the path, hence the +1. */
+ const StringRef suffix = StringRef(this->path_).substr(from_path.length() + 1);
+ const AssetCatalogPath path_suffix(suffix);
+ return to_path / path_suffix;
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
new file mode 100644
index 00000000000..af15cbf405a
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
@@ -0,0 +1,251 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation
+ * All rights reserved.
+ */
+
+#include "BKE_asset_catalog_path.hh"
+
+#include "BLI_set.hh"
+#include "BLI_vector.hh"
+
+#include <set>
+#include <sstream>
+
+#include "testing/testing.h"
+
+namespace blender::bke::tests {
+
+TEST(AssetCatalogPathTest, construction)
+{
+ AssetCatalogPath from_char_literal("the/path");
+
+ const std::string str_const = "the/path";
+ AssetCatalogPath from_string_constant(str_const);
+
+ std::string str_variable = "the/path";
+ AssetCatalogPath from_string_variable(str_variable);
+
+ std::string long_string = "this is a long/string/with/a/path in the middle";
+ StringRef long_string_ref(long_string);
+ StringRef middle_bit = long_string_ref.substr(10, 23);
+ AssetCatalogPath from_string_ref(middle_bit);
+ EXPECT_EQ(from_string_ref, "long/string/with/a/path");
+}
+
+TEST(AssetCatalogPathTest, length)
+{
+ const AssetCatalogPath one("1");
+ EXPECT_EQ(1, one.length());
+
+ const AssetCatalogPath empty("");
+ EXPECT_EQ(0, empty.length());
+
+ const AssetCatalogPath utf8("some/родитель");
+ EXPECT_EQ(21, utf8.length()) << "13 characters should be 21 bytes.";
+}
+
+TEST(AssetCatalogPathTest, comparison_operators)
+{
+ const AssetCatalogPath empty("");
+ const AssetCatalogPath the_path("the/path");
+ const AssetCatalogPath the_path_child("the/path/child");
+ const AssetCatalogPath unrelated_path("unrelated/path");
+ const AssetCatalogPath other_instance_same_path("the/path");
+
+ EXPECT_LT(empty, the_path);
+ EXPECT_LT(the_path, the_path_child);
+ EXPECT_LT(the_path, unrelated_path);
+
+ EXPECT_EQ(empty, empty) << "Identical empty instances should compare equal.";
+ EXPECT_EQ(empty, "") << "Comparison to empty string should be possible.";
+ EXPECT_EQ(the_path, the_path) << "Identical non-empty instances should compare equal.";
+ EXPECT_EQ(the_path, "the/path") << "Comparison to string should be possible.";
+ EXPECT_EQ(the_path, other_instance_same_path)
+ << "Different instances with equal path should compare equal.";
+
+ EXPECT_NE(the_path, the_path_child);
+ EXPECT_NE(the_path, unrelated_path);
+ EXPECT_NE(the_path, empty);
+
+ EXPECT_FALSE(empty);
+ EXPECT_TRUE(the_path);
+}
+
+TEST(AssetCatalogPathTest, move_semantics)
+{
+ AssetCatalogPath source_path("source/path");
+ EXPECT_TRUE(source_path);
+
+ AssetCatalogPath dest_path = std::move(source_path);
+ EXPECT_FALSE(source_path);
+ EXPECT_TRUE(dest_path);
+}
+
+TEST(AssetCatalogPathTest, concatenation)
+{
+ AssetCatalogPath some_parent("some/родитель");
+ AssetCatalogPath child = some_parent / "ребенок";
+
+ EXPECT_EQ(some_parent, "some/родитель")
+ << "Appending a child path should not modify the parent.";
+ EXPECT_EQ(child, "some/родитель/ребенок");
+
+ AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук";
+ EXPECT_EQ(appended_compound_path, "some/родитель/ребенок/внук");
+
+ AssetCatalogPath empty("");
+ AssetCatalogPath child_of_the_void = empty / "child";
+ EXPECT_EQ(child_of_the_void, "child")
+ << "Appending to an empty path should not create an initial slash.";
+
+ AssetCatalogPath parent_of_the_void = some_parent / empty;
+ EXPECT_EQ(parent_of_the_void, "some/родитель")
+ << "Prepending to an empty path should not create a trailing slash.";
+
+ std::string subpath = "child";
+ AssetCatalogPath concatenated_with_string = some_parent / subpath;
+ EXPECT_EQ(concatenated_with_string, "some/родитель/child");
+}
+
+TEST(AssetCatalogPathTest, hashable)
+{
+ AssetCatalogPath path("heyyyyy");
+
+ std::set<AssetCatalogPath> path_std_set;
+ path_std_set.insert(path);
+
+ blender::Set<AssetCatalogPath> path_blender_set;
+ path_blender_set.add(path);
+}
+
+TEST(AssetCatalogPathTest, stream_operator)
+{
+ AssetCatalogPath path("путь/в/Пермь");
+ std::stringstream sstream;
+ sstream << path;
+ EXPECT_EQ("путь/в/Пермь", sstream.str());
+}
+
+TEST(AssetCatalogPathTest, is_contained_in)
+{
+ const AssetCatalogPath catpath("simple/path/child");
+ EXPECT_FALSE(catpath.is_contained_in("unrelated"));
+ EXPECT_FALSE(catpath.is_contained_in("sim"));
+ EXPECT_FALSE(catpath.is_contained_in("simple/pathx"));
+ EXPECT_FALSE(catpath.is_contained_in("simple/path/c"));
+ EXPECT_FALSE(catpath.is_contained_in("simple/path/child/grandchild"));
+ EXPECT_FALSE(catpath.is_contained_in("simple/path/"))
+ << "Non-normalized paths are not expected to work.";
+
+ EXPECT_TRUE(catpath.is_contained_in(""));
+ EXPECT_TRUE(catpath.is_contained_in("simple"));
+ EXPECT_TRUE(catpath.is_contained_in("simple/path"));
+
+ /* Test with some UTF8 non-ASCII characters. */
+ AssetCatalogPath some_parent("some/родитель");
+ AssetCatalogPath child = some_parent / "ребенок";
+
+ EXPECT_TRUE(child.is_contained_in(some_parent));
+ EXPECT_TRUE(child.is_contained_in("some"));
+
+ AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук";
+ EXPECT_TRUE(appended_compound_path.is_contained_in(some_parent));
+ EXPECT_TRUE(appended_compound_path.is_contained_in(child));
+
+ /* Test "going up" directory-style. */
+ AssetCatalogPath child_with_dotdot = some_parent / "../../other/hierarchy/part";
+ EXPECT_TRUE(child_with_dotdot.is_contained_in(some_parent))
+ << "dotdot path components should have no meaning";
+}
+
+TEST(AssetCatalogPathTest, cleanup)
+{
+ AssetCatalogPath ugly_path("/ some / родитель / ");
+ AssetCatalogPath clean_path = ugly_path.cleanup();
+
+ EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path)
+ << "cleanup should not modify the path instance itself";
+
+ EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path);
+
+ AssetCatalogPath double_slashed("some//родитель");
+ EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup());
+
+ AssetCatalogPath with_colons("some/key:subkey=value/path");
+ EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup());
+}
+
+TEST(AssetCatalogPathTest, iterate_components)
+{
+ AssetCatalogPath path("путь/в/Пермь");
+ Vector<std::pair<std::string, bool>> seen_components;
+
+ path.iterate_components([&seen_components](StringRef component_name, bool is_last_component) {
+ std::pair<std::string, bool> parameter_pair = std::make_pair<std::string, bool>(
+ component_name, bool(is_last_component));
+ seen_components.append(parameter_pair);
+ });
+
+ ASSERT_EQ(3, seen_components.size());
+
+ EXPECT_EQ("путь", seen_components[0].first);
+ EXPECT_EQ("в", seen_components[1].first);
+ EXPECT_EQ("Пермь", seen_components[2].first);
+
+ EXPECT_FALSE(seen_components[0].second);
+ EXPECT_FALSE(seen_components[1].second);
+ EXPECT_TRUE(seen_components[2].second);
+}
+
+TEST(AssetCatalogPathTest, rebase)
+{
+ AssetCatalogPath path("some/path/to/some/catalog");
+ EXPECT_EQ(path.rebase("some/path", "new/base"), "new/base/to/some/catalog");
+ EXPECT_EQ(path.rebase("", "new/base"), "new/base/some/path/to/some/catalog");
+
+ EXPECT_EQ(path.rebase("some/path/to/some/catalog", "some/path/to/some/catalog"),
+ "some/path/to/some/catalog")
+ << "Rebasing to itself should not change the path.";
+
+ EXPECT_EQ(path.rebase("path/to", "new/base"), "")
+ << "Non-matching base path should return empty string to indicate 'NO'.";
+
+ /* Empty strings should be handled without crashing or other nasty side-effects. */
+ AssetCatalogPath empty("");
+ EXPECT_EQ(empty.rebase("path/to", "new/base"), "");
+ EXPECT_EQ(empty.rebase("", "new/base"), "new/base");
+ EXPECT_EQ(empty.rebase("", ""), "");
+}
+
+TEST(AssetCatalogPathTest, parent)
+{
+ const AssetCatalogPath ascii_path("path/with/missing/parents");
+ EXPECT_EQ(ascii_path.parent(), "path/with/missing");
+
+ const AssetCatalogPath path("путь/в/Пермь/долог/и/далек");
+ EXPECT_EQ(path.parent(), "путь/в/Пермь/долог/и");
+ EXPECT_EQ(path.parent().parent(), "путь/в/Пермь/долог");
+ EXPECT_EQ(path.parent().parent().parent(), "путь/в/Пермь");
+
+ const AssetCatalogPath one_level("one");
+ EXPECT_EQ(one_level.parent(), "");
+
+ const AssetCatalogPath empty("");
+ EXPECT_EQ(empty.parent(), "");
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
new file mode 100644
index 00000000000..fb471a8ee7b
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -0,0 +1,965 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation
+ * All rights reserved.
+ */
+
+#include "BKE_appdir.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_preferences.h"
+
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+
+#include "DNA_userdef_types.h"
+
+#include "testing/testing.h"
+
+namespace blender::bke::tests {
+
+/* UUIDs from lib/tests/asset_library/blender_assets.cats.txt */
+const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71");
+const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78");
+const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46");
+const bUUID UUID_POSES_ELLIE_TRAILING_SLASH("3376b94b-a28d-4d05-86c1-bf30b937130d");
+const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed");
+const bUUID UUID_POSES_RUZENA_HAND("81811c31-1a88-4bd7-bb34-c6fc2607a12e");
+const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348");
+const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3");
+
+/* UUIDs from lib/tests/asset_library/modified_assets.cats.txt */
+const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34");
+
+/* Subclass that adds accessors such that protected fields can be used in tests. */
+class TestableAssetCatalogService : public AssetCatalogService {
+ public:
+ TestableAssetCatalogService() = default;
+
+ explicit TestableAssetCatalogService(const CatalogFilePath &asset_library_root)
+ : AssetCatalogService(asset_library_root)
+ {
+ }
+
+ AssetCatalogDefinitionFile *get_catalog_definition_file()
+ {
+ return catalog_definition_file_.get();
+ }
+
+ void create_missing_catalogs()
+ {
+ AssetCatalogService::create_missing_catalogs();
+ }
+
+ int64_t count_catalogs_with_path(const CatalogFilePath &path)
+ {
+ int64_t count = 0;
+ for (auto &catalog_uptr : catalogs_.values()) {
+ if (catalog_uptr->path == path) {
+ count++;
+ }
+ }
+ return count;
+ }
+};
+
+class AssetCatalogTest : public testing::Test {
+ protected:
+ CatalogFilePath asset_library_root_;
+ CatalogFilePath temp_library_path_;
+
+ void SetUp() override
+ {
+ const std::string test_files_dir = blender::tests::flags_test_asset_dir();
+ if (test_files_dir.empty()) {
+ FAIL();
+ }
+
+ asset_library_root_ = test_files_dir + "/" + "asset_library";
+ temp_library_path_ = "";
+ }
+
+ /* Register a temporary path, which will be removed at the end of the test.
+ * The returned path ends in a slash. */
+ CatalogFilePath use_temp_path()
+ {
+ BKE_tempdir_init("");
+ const CatalogFilePath tempdir = BKE_tempdir_session();
+ temp_library_path_ = tempdir + "test-temporary-path/";
+ return temp_library_path_;
+ }
+
+ CatalogFilePath create_temp_path()
+ {
+ CatalogFilePath path = use_temp_path();
+ BLI_dir_create_recursive(path.c_str());
+ return path;
+ }
+
+ struct CatalogPathInfo {
+ StringRef name;
+ int parent_count;
+ };
+
+ void assert_expected_item(const CatalogPathInfo &expected_path,
+ const AssetCatalogTreeItem &actual_item)
+ {
+ char expected_filename[FILE_MAXFILE];
+ /* Is the catalog name as expected? "character", "Ellie", ... */
+ BLI_split_file_part(expected_path.name.data(), expected_filename, sizeof(expected_filename));
+ EXPECT_EQ(expected_filename, actual_item.get_name());
+ /* Does the computed number of parents match? */
+ EXPECT_EQ(expected_path.parent_count, actual_item.count_parents());
+ EXPECT_EQ(expected_path.name, actual_item.catalog_path().str());
+ }
+
+ /**
+ * Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if
+ * the items map exactly to \a expected_paths.
+ */
+ void assert_expected_tree_items(AssetCatalogTree *tree,
+ const std::vector<CatalogPathInfo> &expected_paths)
+ {
+ int i = 0;
+ tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) {
+ ASSERT_LT(i, expected_paths.size())
+ << "More catalogs in tree than expected; did not expect " << actual_item.catalog_path();
+ assert_expected_item(expected_paths[i], actual_item);
+ i++;
+ });
+ }
+
+ /**
+ * Iterate over the root items of \a tree and check if the items map exactly to \a
+ * expected_paths. Similar to #assert_expected_tree_items() but calls
+ * #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item().
+ */
+ void assert_expected_tree_root_items(AssetCatalogTree *tree,
+ const std::vector<CatalogPathInfo> &expected_paths)
+ {
+ int i = 0;
+ tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) {
+ ASSERT_LT(i, expected_paths.size())
+ << "More catalogs in tree root than expected; did not expect "
+ << actual_item.catalog_path();
+ assert_expected_item(expected_paths[i], actual_item);
+ i++;
+ });
+ }
+
+ /**
+ * Iterate over the child items of \a parent_item and check if the items map exactly to \a
+ * expected_paths. Similar to #assert_expected_tree_items() but calls
+ * #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item().
+ */
+ void assert_expected_tree_item_child_items(AssetCatalogTreeItem *parent_item,
+ const std::vector<CatalogPathInfo> &expected_paths)
+ {
+ int i = 0;
+ parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) {
+ ASSERT_LT(i, expected_paths.size())
+ << "More catalogs in tree item than expected; did not expect "
+ << actual_item.catalog_path();
+ assert_expected_item(expected_paths[i], actual_item);
+ i++;
+ });
+ }
+
+ void TearDown() override
+ {
+ if (!temp_library_path_.empty()) {
+ BLI_delete(temp_library_path_.c_str(), true, true);
+ temp_library_path_ = "";
+ }
+ }
+};
+
+TEST_F(AssetCatalogTest, load_single_file)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt");
+
+ /* Test getting a non-existent catalog ID. */
+ EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random()));
+
+ /* Test getting an invalid catalog (without path definition). */
+ AssetCatalog *cat_without_path = service.find_catalog(UUID_ID_WITHOUT_PATH);
+ ASSERT_EQ(nullptr, cat_without_path);
+
+ /* Test getting a regular catalog. */
+ AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE);
+ ASSERT_NE(nullptr, poses_ellie);
+ EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id);
+ EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str());
+ EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name);
+
+ /* Test white-space stripping and support in the path. */
+ AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE);
+ ASSERT_NE(nullptr, poses_whitespace);
+ EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id);
+ EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path.str());
+ EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name);
+
+ /* Test getting a UTF-8 catalog ID. */
+ AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA);
+ ASSERT_NE(nullptr, poses_ruzena);
+ EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id);
+ EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path.str());
+ EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name);
+}
+
+TEST_F(AssetCatalogTest, insert_item_into_tree)
+{
+ {
+ AssetCatalogTree tree;
+ std::unique_ptr<AssetCatalog> catalog_empty_path = AssetCatalog::from_path("");
+ tree.insert_item(*catalog_empty_path);
+
+ assert_expected_tree_items(&tree, {});
+ }
+
+ {
+ AssetCatalogTree tree;
+
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item");
+ tree.insert_item(*catalog);
+ assert_expected_tree_items(&tree, {{"item", 0}});
+
+ /* Insert child after parent already exists. */
+ std::unique_ptr<AssetCatalog> child_catalog = AssetCatalog::from_path("item/child");
+ tree.insert_item(*catalog);
+ assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}});
+
+ std::vector<CatalogPathInfo> expected_paths;
+
+ /* Test inserting multi-component sub-path. */
+ std::unique_ptr<AssetCatalog> grandgrandchild_catalog = AssetCatalog::from_path(
+ "item/child/grandchild/grandgrandchild");
+ tree.insert_item(*catalog);
+ expected_paths = {{"item", 0},
+ {"item/child", 1},
+ {"item/child/grandchild", 2},
+ {"item/child/grandchild/grandgrandchild", 3}};
+ assert_expected_tree_items(&tree, expected_paths);
+
+ std::unique_ptr<AssetCatalog> root_level_catalog = AssetCatalog::from_path("root level");
+ tree.insert_item(*catalog);
+ expected_paths = {{"item", 0},
+ {"item/child", 1},
+ {"item/child/grandchild", 2},
+ {"item/child/grandchild/grandgrandchild", 3},
+ {"root level", 0}};
+ assert_expected_tree_items(&tree, expected_paths);
+ }
+
+ {
+ AssetCatalogTree tree;
+
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item/child");
+ tree.insert_item(*catalog);
+ assert_expected_tree_items(&tree, {{"item", 0}, {"item/child", 1}});
+ }
+
+ {
+ AssetCatalogTree tree;
+
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("white space");
+ tree.insert_item(*catalog);
+ assert_expected_tree_items(&tree, {{"white space", 0}});
+ }
+
+ {
+ AssetCatalogTree tree;
+
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("/item/white space");
+ tree.insert_item(*catalog);
+ assert_expected_tree_items(&tree, {{"item", 0}, {"item/white space", 1}});
+ }
+
+ {
+ AssetCatalogTree tree;
+
+ std::unique_ptr<AssetCatalog> catalog_unicode_path = AssetCatalog::from_path("Ružena");
+ tree.insert_item(*catalog_unicode_path);
+ assert_expected_tree_items(&tree, {{"Ružena", 0}});
+
+ catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena");
+ tree.insert_item(*catalog_unicode_path);
+ assert_expected_tree_items(&tree, {{"Ružena", 0}, {"Ružena/Ružena", 1}});
+ }
+}
+
+TEST_F(AssetCatalogTest, load_single_file_into_tree)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt");
+
+ /* Contains not only paths from the CDF but also the missing parents (implicitly defined
+ * catalogs). */
+ std::vector<CatalogPathInfo> expected_paths{
+ {"character", 0},
+ {"character/Ellie", 1},
+ {"character/Ellie/poselib", 2},
+ {"character/Ellie/poselib/tailslash", 3},
+ {"character/Ellie/poselib/white space", 3},
+ {"character/Ružena", 1},
+ {"character/Ružena/poselib", 2},
+ {"character/Ružena/poselib/face", 3},
+ {"character/Ružena/poselib/hand", 3},
+ {"path", 0}, /* Implicit. */
+ {"path/without", 1}, /* Implicit. */
+ {"path/without/simplename", 2}, /* From CDF. */
+ };
+
+ AssetCatalogTree *tree = service.get_catalog_tree();
+ assert_expected_tree_items(tree, expected_paths);
+}
+
+TEST_F(AssetCatalogTest, foreach_in_tree)
+{
+ {
+ AssetCatalogTree tree{};
+ const std::vector<CatalogPathInfo> no_catalogs{};
+
+ assert_expected_tree_items(&tree, no_catalogs);
+ assert_expected_tree_root_items(&tree, no_catalogs);
+ /* Need a root item to check child items. */
+ std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("something");
+ tree.insert_item(*catalog);
+ tree.foreach_root_item([&no_catalogs, this](AssetCatalogTreeItem &item) {
+ assert_expected_tree_item_child_items(&item, no_catalogs);
+ });
+ }
+
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt");
+
+ std::vector<CatalogPathInfo> expected_root_items{{"character", 0}, {"path", 0}};
+ AssetCatalogTree *tree = service.get_catalog_tree();
+ assert_expected_tree_root_items(tree, expected_root_items);
+
+ /* Test if the direct children of the root item are what's expected. */
+ std::vector<std::vector<CatalogPathInfo>> expected_root_child_items = {
+ /* Children of the "character" root item. */
+ {{"character/Ellie", 1}, {"character/Ružena", 1}},
+ /* Children of the "path" root item. */
+ {{"path/without", 1}},
+ };
+ int i = 0;
+ tree->foreach_root_item([&expected_root_child_items, &i, this](AssetCatalogTreeItem &item) {
+ assert_expected_tree_item_child_items(&item, expected_root_child_items[i]);
+ i++;
+ });
+}
+
+TEST_F(AssetCatalogTest, find_catalog_by_path)
+{
+ TestableAssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ AssetCatalog *catalog;
+
+ EXPECT_EQ(nullptr, service.find_catalog_by_path(""));
+ catalog = service.find_catalog_by_path("character/Ellie/poselib/white space");
+ EXPECT_NE(nullptr, catalog);
+ EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, catalog->catalog_id);
+ catalog = service.find_catalog_by_path("character/Ružena/poselib");
+ EXPECT_NE(nullptr, catalog);
+ EXPECT_EQ(UUID_POSES_RUZENA, catalog->catalog_id);
+
+ /* "character/Ellie/poselib" is used by two catalogs. Check if it's using the first one. */
+ catalog = service.find_catalog_by_path("character/Ellie/poselib");
+ EXPECT_NE(nullptr, catalog);
+ EXPECT_EQ(UUID_POSES_ELLIE, catalog->catalog_id);
+ EXPECT_NE(UUID_POSES_ELLIE_TRAILING_SLASH, catalog->catalog_id);
+}
+
+TEST_F(AssetCatalogTest, write_single_file)
+{
+ TestableAssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ const CatalogFilePath save_to_path = use_temp_path() +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ cdf->write_to_disk(save_to_path);
+
+ AssetCatalogService loaded_service(save_to_path);
+ loaded_service.load_from_disk();
+
+ /* Test that the expected catalogs are there. */
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
+
+ /* Test that the invalid catalog definition wasn't copied. */
+ EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_ID_WITHOUT_PATH));
+
+ /* TODO(@sybren): test ordering of catalogs in the file. */
+}
+
+TEST_F(AssetCatalogTest, no_writing_empty_files)
+{
+ const CatalogFilePath temp_lib_root = create_temp_path();
+ AssetCatalogService service(temp_lib_root);
+ service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend");
+
+ const CatalogFilePath default_cdf_path = temp_lib_root +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ EXPECT_FALSE(BLI_exists(default_cdf_path.c_str()));
+}
+
+/* Already loaded a CDF, saving to some unrelated directory. */
+TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf)
+{
+ const CatalogFilePath top_level_dir = create_temp_path(); /* Has trailing slash. */
+
+ /* Create a copy of the CDF in SVN, so we can safely write to it. */
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath cdf_dirname = top_level_dir + "other_dir/";
+ const CatalogFilePath cdf_filename = cdf_dirname + AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ ASSERT_TRUE(BLI_dir_create_recursive(cdf_dirname.c_str()));
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_filename.c_str()))
+ << "Unable to copy " << original_cdf_file << " to " << cdf_filename;
+
+ /* Load the CDF, add a catalog, and trigger a write. This should write to the loaded CDF. */
+ TestableAssetCatalogService service(cdf_filename);
+ service.load_from_disk();
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
+ EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path);
+
+ /* Test that the CDF was created in the expected location. */
+ const CatalogFilePath backup_filename = cdf_filename + "~";
+ EXPECT_TRUE(BLI_exists(cdf_filename.c_str()));
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the on-disk CDF contains the expected catalogs. */
+ AssetCatalogService loaded_service(cdf_filename);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id))
+ << "Expected to see the newly-created catalog.";
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE))
+ << "Expected to see the already-existing catalog.";
+}
+
+/* Create some catalogs in memory, save to directory that doesn't contain anything else. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory)
+{
+ const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */
+
+ TestableAssetCatalogService service;
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ const CatalogFilePath blendfilename = target_dir + "some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
+
+ /* Test that the CDF was created in the expected location. */
+ const CatalogFilePath expected_cdf_path = target_dir +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ EXPECT_TRUE(BLI_exists(expected_cdf_path.c_str()));
+
+ /* Test that the in-memory CDF has been created, and contains the expected catalog. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_TRUE(cdf->contains(cat->catalog_id));
+
+ /* Test that the on-disk CDF contains the expected catalog. */
+ AssetCatalogService loaded_service(expected_cdf_path);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+}
+
+/* Create some catalogs in memory, save to directory that contains a default CDF. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge)
+{
+ const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath writable_cdf_file = target_dir +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
+
+ /* Create the catalog service without loading the already-existing CDF. */
+ TestableAssetCatalogService service;
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ /* Mock that the blend file is written to a subdirectory of the asset library. */
+ const CatalogFilePath blendfilename = target_dir + "some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
+
+ /* Test that the CDF still exists in the expected location. */
+ const CatalogFilePath backup_filename = writable_cdf_file + "~";
+ EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str()));
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the in-memory CDF has the expected file path. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_EQ(writable_cdf_file, cdf->file_path);
+
+ /* Test that the in-memory catalogs have been merged with the on-disk one. */
+ AssetCatalogService loaded_service(writable_cdf_file);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+}
+
+/* Create some catalogs in memory, save to subdirectory of a registered asset library. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib)
+{
+ const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/";
+ CatalogFilePath writable_cdf_file = registered_asset_lib +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ BLI_path_slash_native(writable_cdf_file.data());
+
+ /* Set up a temporary asset library for testing. */
+ bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add(
+ &U, "Test", registered_asset_lib.c_str());
+ ASSERT_NE(nullptr, asset_lib_pref);
+ ASSERT_TRUE(BLI_dir_create_recursive(registered_asset_lib.c_str()));
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
+
+ /* Create the catalog service without loading the already-existing CDF. */
+ TestableAssetCatalogService service;
+ const CatalogFilePath blenddirname = registered_asset_lib + "subdirectory/";
+ const CatalogFilePath blendfilename = blenddirname + "some_file.blend";
+ ASSERT_TRUE(BLI_dir_create_recursive(blenddirname.c_str()));
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ /* Mock that the blend file is written to the directory already containing a CDF. */
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename));
+
+ /* Test that the CDF still exists in the expected location. */
+ EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str()));
+ const CatalogFilePath backup_filename = writable_cdf_file + "~";
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the in-memory CDF has the expected file path. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ BLI_path_slash_native(cdf->file_path.data());
+ EXPECT_EQ(writable_cdf_file, cdf->file_path);
+
+ /* Test that the in-memory catalogs have been merged with the on-disk one. */
+ AssetCatalogService loaded_service(writable_cdf_file);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+
+ BKE_preferences_asset_library_remove(&U, asset_lib_pref);
+}
+
+TEST_F(AssetCatalogTest, create_first_catalog_from_scratch)
+{
+ /* Even from scratch a root directory should be known. */
+ const CatalogFilePath temp_lib_root = use_temp_path();
+ AssetCatalogService service;
+
+ /* Just creating the service should NOT create the path. */
+ EXPECT_FALSE(BLI_exists(temp_lib_root.c_str()));
+
+ AssetCatalog *cat = service.create_catalog("some/catalog/path");
+ ASSERT_NE(nullptr, cat);
+ EXPECT_EQ(cat->path, "some/catalog/path");
+ EXPECT_EQ(cat->simple_name, "some-catalog-path");
+
+ /* Creating a new catalog should not save anything to disk yet. */
+ EXPECT_FALSE(BLI_exists(temp_lib_root.c_str()));
+
+ /* Writing to disk should create the directory + the default file. */
+ service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend");
+ EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str()));
+
+ const CatalogFilePath definition_file_path = temp_lib_root + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ EXPECT_TRUE(BLI_is_file(definition_file_path.c_str()));
+
+ AssetCatalogService loaded_service(temp_lib_root);
+ loaded_service.load_from_disk();
+
+ /* Test that the expected catalog is there. */
+ AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id);
+ ASSERT_NE(nullptr, written_cat);
+ EXPECT_EQ(written_cat->catalog_id, cat->catalog_id);
+ EXPECT_EQ(written_cat->path, cat->path.str());
+}
+
+TEST_F(AssetCatalogTest, create_catalog_after_loading_file)
+{
+ const CatalogFilePath temp_lib_root = create_temp_path();
+
+ /* Copy the asset catalog definition files to a separate location, so that we can test without
+ * overwriting the test file in SVN. */
+ const CatalogFilePath default_catalog_path = asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ const CatalogFilePath writable_catalog_path = temp_lib_root +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ ASSERT_EQ(0, BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str()));
+ EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str()));
+ EXPECT_TRUE(BLI_is_file(writable_catalog_path.c_str()));
+
+ TestableAssetCatalogService service(temp_lib_root);
+ service.load_from_disk();
+ EXPECT_EQ(writable_catalog_path, service.get_catalog_definition_file()->file_path);
+ EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE)) << "expected catalogs to be loaded";
+
+ /* This should create a new catalog but not write to disk. */
+ const AssetCatalog *new_catalog = service.create_catalog("new/catalog");
+ const bUUID new_catalog_id = new_catalog->catalog_id;
+
+ /* Reload the on-disk catalog file. */
+ TestableAssetCatalogService loaded_service(temp_lib_root);
+ loaded_service.load_from_disk();
+ EXPECT_EQ(writable_catalog_path, loaded_service.get_catalog_definition_file()->file_path);
+
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE))
+ << "expected pre-existing catalogs to be kept in the file";
+ EXPECT_EQ(nullptr, loaded_service.find_catalog(new_catalog_id))
+ << "expecting newly added catalog to not yet be saved to " << temp_lib_root;
+
+ /* Write and reload the catalog file. */
+ service.write_to_disk_on_blendfile_save(temp_lib_root + "phony.blend");
+ AssetCatalogService reloaded_service(temp_lib_root);
+ reloaded_service.load_from_disk();
+ EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE))
+ << "expected pre-existing catalogs to be kept in the file";
+ EXPECT_NE(nullptr, reloaded_service.find_catalog(new_catalog_id))
+ << "expecting newly added catalog to exist in the file";
+}
+
+TEST_F(AssetCatalogTest, create_catalog_path_cleanup)
+{
+ AssetCatalogService service;
+ AssetCatalog *cat = service.create_catalog(" /some/path / ");
+
+ EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id));
+ EXPECT_EQ("some/path", cat->path.str());
+ EXPECT_EQ("some-path", cat->simple_name);
+}
+
+TEST_F(AssetCatalogTest, create_catalog_simple_name)
+{
+ AssetCatalogService service;
+ AssetCatalog *cat = service.create_catalog(
+ "production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands");
+
+ EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id));
+ EXPECT_EQ("production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands",
+ cat->path.str());
+ EXPECT_EQ("...ht-Characters-Victora-Pose Library-Approved-Body Parts-Hands", cat->simple_name);
+}
+
+TEST_F(AssetCatalogTest, delete_catalog_leaf)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt");
+
+ /* Delete a leaf catalog, i.e. one that is not a parent of another catalog.
+ * This keeps this particular test easy. */
+ service.delete_catalog(UUID_POSES_RUZENA_HAND);
+ EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND));
+
+ /* Contains not only paths from the CDF but also the missing parents (implicitly defined
+ * catalogs). This is why a leaf catalog was deleted. */
+ std::vector<CatalogPathInfo> expected_paths{
+ {"character", 0},
+ {"character/Ellie", 1},
+ {"character/Ellie/poselib", 2},
+ {"character/Ellie/poselib/tailslash", 3},
+ {"character/Ellie/poselib/white space", 3},
+ {"character/Ružena", 1},
+ {"character/Ružena/poselib", 2},
+ {"character/Ružena/poselib/face", 3},
+ // {"character/Ružena/poselib/hand", 3}, /* This is the deleted one. */
+ {"path", 0},
+ {"path/without", 1},
+ {"path/without/simplename", 2},
+ };
+
+ AssetCatalogTree *tree = service.get_catalog_tree();
+ assert_expected_tree_items(tree, expected_paths);
+}
+
+TEST_F(AssetCatalogTest, delete_catalog_write_to_disk)
+{
+ TestableAssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ service.delete_catalog(UUID_POSES_ELLIE);
+
+ const CatalogFilePath save_to_path = use_temp_path();
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ cdf->write_to_disk(save_to_path + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ AssetCatalogService loaded_service(save_to_path);
+ loaded_service.load_from_disk();
+
+ /* Test that the expected catalogs are there, except the deleted one. */
+ EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
+}
+
+TEST_F(AssetCatalogTest, update_catalog_path)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA);
+ const AssetCatalogPath orig_path = orig_cat->path;
+
+ service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena");
+
+ EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path))
+ << "The original (pre-rename) path should not be associated with a catalog any more.";
+
+ const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA);
+ ASSERT_NE(nullptr, renamed_cat);
+ ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog.";
+ EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name)
+ << "Changing the path should not change the simple name.";
+ EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id)
+ << "Changing the path should not change the catalog ID.";
+
+ EXPECT_EQ("charlib/Ružena", renamed_cat->path.str())
+ << "Changing the path should change the path. Surprise.";
+
+ EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str())
+ << "Changing the path should update children.";
+ EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str())
+ << "Changing the path should update children.";
+}
+
+TEST_F(AssetCatalogTest, merge_catalog_files)
+{
+ const CatalogFilePath cdf_dir = create_temp_path();
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt";
+ const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt";
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str()));
+
+ /* Load the unmodified, original CDF. */
+ TestableAssetCatalogService service(asset_library_root_);
+ service.load_from_disk(cdf_dir);
+
+ /* Copy a modified file, to mimic a situation where someone changed the
+ * CDF after we loaded it. */
+ ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str()));
+
+ /* Overwrite the modified file. This should merge the on-disk file with our catalogs. */
+ service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend");
+
+ AssetCatalogService loaded_service(cdf_dir);
+ loaded_service.load_from_disk();
+
+ /* Test that the expected catalogs are there. */
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); /* New in the modified file. */
+
+ /* When there are overlaps, the in-memory (i.e. last-saved) paths should win. */
+ const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE);
+ EXPECT_EQ("character/Ružena/poselib/face", ruzena_face->path.str());
+}
+
+TEST_F(AssetCatalogTest, backups)
+{
+ const CatalogFilePath cdf_dir = create_temp_path();
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt";
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
+
+ /* Read a CDF, modify, and write it. */
+ AssetCatalogService service(cdf_dir);
+ service.load_from_disk();
+ service.delete_catalog(UUID_POSES_ELLIE);
+ service.write_to_disk_on_blendfile_save(cdf_dir + "phony.blend");
+
+ const CatalogFilePath backup_path = writable_cdf_file + "~";
+ ASSERT_TRUE(BLI_is_file(backup_path.c_str()));
+
+ AssetCatalogService loaded_service;
+ loaded_service.load_from_disk(backup_path);
+
+ /* Test that the expected catalogs are there, including the deleted one.
+ * This is the backup, after all. */
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
+}
+
+TEST_F(AssetCatalogTest, order_by_path)
+{
+ const bUUID cat2_uuid("22222222-b847-44d9-bdca-ff04db1c24f5");
+ const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); /* Sorts earlier than above. */
+ const AssetCatalog cat1(BLI_uuid_generate_random(), "simple/path/child", "");
+ const AssetCatalog cat2(cat2_uuid, "simple/path", "");
+ const AssetCatalog cat3(BLI_uuid_generate_random(), "complex/path/...or/is/it?", "");
+ const AssetCatalog cat4(
+ cat4_uuid, "simple/path", "different ID, same path"); /* should be kept */
+ const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); /* disappears */
+
+ AssetCatalogOrderedSet by_path;
+ by_path.insert(&cat1);
+ by_path.insert(&cat2);
+ by_path.insert(&cat3);
+ by_path.insert(&cat4);
+ by_path.insert(&cat5);
+
+ AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin();
+
+ EXPECT_EQ(1, by_path.count(&cat1));
+ EXPECT_EQ(1, by_path.count(&cat2));
+ EXPECT_EQ(1, by_path.count(&cat3));
+ EXPECT_EQ(1, by_path.count(&cat4));
+ ASSERT_EQ(4, by_path.size()) << "Expecting cat5 to not be stored in the set, as it duplicates "
+ "an already-existing path + UUID";
+
+ EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); /* complex/path */
+ EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 111.. ID */
+ EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 222.. ID */
+ EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); /* simple/path/child */
+
+ if (set_iter != by_path.end()) {
+ const AssetCatalog *next_cat = *set_iter;
+ FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":"
+ << next_cat->path;
+ }
+}
+
+TEST_F(AssetCatalogTest, create_missing_catalogs)
+{
+ TestableAssetCatalogService new_service;
+ new_service.create_catalog("path/with/missing/parents");
+
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path("path/with/missing"))
+ << "Missing parents should not be immediately created.";
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) << "Empty path should never be valid";
+
+ new_service.create_missing_catalogs();
+
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with/missing"));
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with"));
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path"));
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path(""))
+ << "Empty path should never be valid, even when after missing catalogs";
+}
+
+TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading)
+{
+ TestableAssetCatalogService loaded_service(asset_library_root_);
+ loaded_service.load_from_disk();
+
+ const AssetCatalog *cat_char = loaded_service.find_catalog_by_path("character");
+ const AssetCatalog *cat_ellie = loaded_service.find_catalog_by_path("character/Ellie");
+ const AssetCatalog *cat_ruzena = loaded_service.find_catalog_by_path("character/Ružena");
+ ASSERT_NE(nullptr, cat_char) << "Missing parents should be created immediately after loading.";
+ ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading.";
+ ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading.";
+
+ AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF.";
+ EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF.";
+ EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id))
+ << "Missing parents should be saved to a CDF.";
+
+ /* Check that each missing parent is only created once. The CDF contains multiple paths that
+ * could trigger the creation of missing parents, so this test makes sense. */
+ EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character"));
+ EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ellie"));
+ EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena"));
+}
+
+TEST_F(AssetCatalogTest, create_catalog_filter)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk();
+
+ /* Alias for the same catalog as the main one. */
+ AssetCatalog *alias_ruzena = service.create_catalog("character/Ružena/poselib");
+ /* Alias for a sub-catalog. */
+ AssetCatalog *alias_ruzena_hand = service.create_catalog("character/Ružena/poselib/hand");
+
+ AssetCatalogFilter filter = service.create_catalog_filter(UUID_POSES_RUZENA);
+
+ /* Positive test for loaded-from-disk catalogs. */
+ EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA))
+ << "Main catalog should be included in the filter.";
+ EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_HAND))
+ << "Sub-catalog should be included in the filter.";
+ EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_FACE))
+ << "Sub-catalog should be included in the filter.";
+
+ /* Positive test for newly-created catalogs. */
+ EXPECT_TRUE(filter.contains(alias_ruzena->catalog_id))
+ << "Alias of main catalog should be included in the filter.";
+ EXPECT_TRUE(filter.contains(alias_ruzena_hand->catalog_id))
+ << "Alias of sub-catalog should be included in the filter.";
+
+ /* Negative test for unrelated catalogs. */
+ EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
+ EXPECT_FALSE(filter.contains(UUID_ID_WITHOUT_PATH));
+ EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
+ EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_WHITESPACE));
+ EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_TRAILING_SLASH));
+ EXPECT_FALSE(filter.contains(UUID_WITHOUT_SIMPLENAME));
+}
+
+TEST_F(AssetCatalogTest, create_catalog_filter_for_unknown_uuid)
+{
+ AssetCatalogService service;
+ const bUUID unknown_uuid = BLI_uuid_generate_random();
+
+ AssetCatalogFilter filter = service.create_catalog_filter(unknown_uuid);
+ EXPECT_TRUE(filter.contains(unknown_uuid));
+
+ EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
+ EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
+}
+
+TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets)
+{
+ AssetCatalogService service;
+
+ AssetCatalogFilter filter = service.create_catalog_filter(BLI_uuid_nil());
+ EXPECT_TRUE(filter.contains(BLI_uuid_nil()));
+ EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc
new file mode 100644
index 00000000000..27e66ee5725
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_library.cc
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_asset_library.hh"
+#include "BKE_callbacks.h"
+#include "BKE_main.h"
+#include "BKE_preferences.h"
+
+#include "BLI_path_util.h"
+
+#include "DNA_userdef_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include <memory>
+
+/**
+ * Loading an asset library at this point only means loading the catalogs. Later on this should
+ * invoke reading of asset representations too.
+ */
+struct AssetLibrary *BKE_asset_library_load(const char *library_path)
+{
+ blender::bke::AssetLibrary *lib = new blender::bke::AssetLibrary();
+ lib->on_save_handler_register();
+ lib->load(library_path);
+ return reinterpret_cast<struct AssetLibrary *>(lib);
+}
+
+void BKE_asset_library_free(struct AssetLibrary *asset_library)
+{
+ blender::bke::AssetLibrary *lib = reinterpret_cast<blender::bke::AssetLibrary *>(asset_library);
+ lib->on_save_handler_unregister();
+ delete lib;
+}
+
+bool BKE_asset_library_find_suitable_root_path_from_path(const char *input_path,
+ char *r_library_path)
+{
+ if (bUserAssetLibrary *preferences_lib = BKE_preferences_asset_library_containing_path(
+ &U, input_path)) {
+ BLI_strncpy(r_library_path, preferences_lib->path, FILE_MAXDIR);
+ return true;
+ }
+
+ BLI_split_dir_part(input_path, r_library_path, FILE_MAXDIR);
+ return r_library_path[0] != '\0';
+}
+
+bool BKE_asset_library_find_suitable_root_path_from_main(const Main *bmain, char *r_library_path)
+{
+ return BKE_asset_library_find_suitable_root_path_from_path(bmain->name, r_library_path);
+}
+
+blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service(
+ const ::AssetLibrary *library_c)
+{
+ if (library_c == nullptr) {
+ return nullptr;
+ }
+
+ const blender::bke::AssetLibrary &library = reinterpret_cast<const blender::bke::AssetLibrary &>(
+ *library_c);
+ return library.catalog_service.get();
+}
+
+blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library)
+{
+ blender::bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(
+ library);
+ if (catalog_service == nullptr) {
+ return nullptr;
+ }
+
+ return catalog_service->get_catalog_tree();
+}
+
+namespace blender::bke {
+
+void AssetLibrary::load(StringRefNull library_root_directory)
+{
+ auto catalog_service = std::make_unique<AssetCatalogService>(library_root_directory);
+ catalog_service->load_from_disk();
+ this->catalog_service = std::move(catalog_service);
+}
+
+namespace {
+void asset_library_on_save_post(struct Main *main,
+ struct PointerRNA **pointers,
+ const int num_pointers,
+ void *arg)
+{
+ AssetLibrary *asset_lib = static_cast<AssetLibrary *>(arg);
+ asset_lib->on_save_post(main, pointers, num_pointers);
+}
+} // namespace
+
+void AssetLibrary::on_save_handler_register()
+{
+ /* The callback system doesn't own `on_save_callback_store_`. */
+ on_save_callback_store_.alloc = false;
+
+ on_save_callback_store_.func = asset_library_on_save_post;
+ on_save_callback_store_.arg = this;
+
+ BKE_callback_add(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST);
+}
+
+void AssetLibrary::on_save_handler_unregister()
+{
+ BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST);
+}
+
+void AssetLibrary::on_save_post(struct Main *main,
+ struct PointerRNA ** /*pointers*/,
+ const int /*num_pointers*/)
+{
+ if (this->catalog_service == nullptr) {
+ return;
+ }
+
+ this->catalog_service->write_to_disk_on_blendfile_save(main->name);
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc
new file mode 100644
index 00000000000..30ac4dc6ad8
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_library_test.cc
@@ -0,0 +1,82 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation
+ * All rights reserved.
+ */
+
+#include "BKE_appdir.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+
+#include "testing/testing.h"
+
+namespace blender::bke::tests {
+
+TEST(AssetLibraryTest, load_and_free_c_functions)
+{
+ const std::string test_files_dir = blender::tests::flags_test_asset_dir();
+ if (test_files_dir.empty()) {
+ FAIL();
+ }
+
+ /* Load the asset library. */
+ const std::string library_path = test_files_dir + "/" + "asset_library";
+ ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data());
+ ASSERT_NE(nullptr, library_c_ptr);
+
+ /* Check that it can be cast to the C++ type and has a Catalog Service. */
+ blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast<blender::bke::AssetLibrary *>(
+ library_c_ptr);
+ AssetCatalogService *service = library_cpp_ptr->catalog_service.get();
+ ASSERT_NE(nullptr, service);
+
+ /* Check that the catalogs defined in the library are actually loaded. This just tests one single
+ * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for
+ * the asset catalog service tests. */
+ const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78");
+ AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie);
+ ASSERT_NE(nullptr, poses_ellie) << "unable to find POSES_ELLIE catalog";
+ EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str());
+
+ BKE_asset_library_free(library_c_ptr);
+}
+
+TEST(AssetLibraryTest, load_nonexistent_directory)
+{
+ const std::string test_files_dir = blender::tests::flags_test_asset_dir();
+ if (test_files_dir.empty()) {
+ FAIL();
+ }
+
+ /* Load the asset library. */
+ const std::string library_path = test_files_dir + "/" +
+ "asset_library/this/subdir/does/not/exist";
+ ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data());
+ ASSERT_NE(nullptr, library_c_ptr);
+
+ /* Check that it can be cast to the C++ type and has a Catalog Service. */
+ blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast<blender::bke::AssetLibrary *>(
+ library_c_ptr);
+ AssetCatalogService *service = library_cpp_ptr->catalog_service.get();
+ ASSERT_NE(nullptr, service);
+
+ /* Check that the catalog service doesn't have any catalogs. */
+ EXPECT_TRUE(service->is_empty());
+
+ BKE_asset_library_free(library_c_ptr);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/asset_test.cc b/source/blender/blenkernel/intern/asset_test.cc
new file mode 100644
index 00000000000..77b98a8ac0a
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset_test.cc
@@ -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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation
+ * All rights reserved.
+ */
+
+#include "BKE_asset.h"
+
+#include "BLI_uuid.h"
+
+#include "DNA_asset_types.h"
+
+#include "testing/testing.h"
+
+namespace blender::bke::tests {
+
+TEST(AssetMetadataTest, set_catalog_id)
+{
+ AssetMetaData meta;
+ const bUUID uuid = BLI_uuid_generate_random();
+
+ /* Test trivial values. */
+ BKE_asset_metadata_catalog_id_clear(&meta);
+ EXPECT_TRUE(BLI_uuid_is_nil(meta.catalog_id));
+ EXPECT_STREQ("", meta.catalog_simple_name);
+
+ /* Test simple situation where the given short name is used as-is. */
+ BKE_asset_metadata_catalog_id_set(&meta, uuid, "simple");
+ EXPECT_TRUE(BLI_uuid_equal(uuid, meta.catalog_id));
+ EXPECT_STREQ("simple", meta.catalog_simple_name);
+
+ /* Test white-space trimming. */
+ BKE_asset_metadata_catalog_id_set(&meta, uuid, " Govoriš angleško? ");
+ EXPECT_STREQ("Govoriš angleško?", meta.catalog_simple_name);
+
+ /* Test length trimming to 63 chars + terminating zero. */
+ constexpr char len66[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20";
+ constexpr char len63[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1";
+ BKE_asset_metadata_catalog_id_set(&meta, uuid, len66);
+ EXPECT_STREQ(len63, meta.catalog_simple_name);
+
+ /* Test length trimming happens after white-space trimming. */
+ constexpr char len68[] =
+ " \
+ 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 ";
+ BKE_asset_metadata_catalog_id_set(&meta, uuid, len68);
+ EXPECT_STREQ(len63, meta.catalog_simple_name);
+
+ /* Test length trimming to 63 bytes, and not 63 characters. ✓ in UTF-8 is three bytes long. */
+ constexpr char with_utf8[] =
+ "00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20";
+ BKE_asset_metadata_catalog_id_set(&meta, uuid, with_utf8);
+ EXPECT_STREQ("00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d",
+ meta.catalog_simple_name);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 8c4f87be91f..c2837b522c4 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -55,6 +55,21 @@ using blender::fn::GVArray_For_SingleValue;
namespace blender::bke {
+std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
+{
+ if (attribute_id.is_named()) {
+ stream << attribute_id.name();
+ }
+ else if (attribute_id.is_anonymous()) {
+ const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
+ stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
+ }
+ else {
+ stream << "<none>";
+ }
+ return stream;
+}
+
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
{
switch (type) {
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 1c5d8804280..6957f9b5a69 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -344,6 +344,13 @@ static void setup_app_data(bContext *C,
do_versions_ipos_to_animato(bmain);
}
+ /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */
+ /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a
+ * version bump and check here. */
+ if (!USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) {
+ BKE_lib_override_library_main_proxy_convert(bmain, reports);
+ }
+
bmain->recovered = 0;
/* startup.blend or recovered startup */
diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c
index 11ee9492b44..87d5961b12e 100644
--- a/source/blender/blenkernel/intern/callbacks.c
+++ b/source/blender/blenkernel/intern/callbacks.c
@@ -80,6 +80,15 @@ void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
BLI_addtail(lb, funcstore);
}
+void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt)
+{
+ ListBase *lb = &callback_slots[evt];
+ BLI_remlink(lb, funcstore);
+ if (funcstore->alloc) {
+ MEM_freeN(funcstore);
+ }
+}
+
void BKE_callback_global_init(void)
{
/* do nothing */
@@ -95,10 +104,7 @@ void BKE_callback_global_finalize(void)
bCallbackFuncStore *funcstore_next;
for (funcstore = lb->first; funcstore; funcstore = funcstore_next) {
funcstore_next = funcstore->next;
- BLI_remlink(lb, funcstore);
- if (funcstore->alloc) {
- MEM_freeN(funcstore);
- }
+ BKE_callback_remove(funcstore, evt);
}
}
}
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 2d172f23428..8e50b9e9534 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -597,7 +597,7 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
else if (collection_old->id.newid == NULL) {
collection_new = (Collection *)BKE_id_copy_for_duplicate(
- bmain, (ID *)collection_old, duplicate_flags);
+ bmain, (ID *)collection_old, duplicate_flags, LIB_ID_COPY_DEFAULT);
if (collection_new == collection_old) {
return collection_new;
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index f2c2e552a9f..62b817487fc 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -1212,6 +1212,20 @@ void BKE_curvemapping_init(CurveMapping *cumap)
}
}
+void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size)
+{
+ int a;
+
+ *size = CM_TABLE + 1;
+ *array = MEM_callocN(sizeof(float) * (*size) * 4, "CurveMapping");
+
+ for (a = 0; a < *size; a++) {
+ if (cumap->cm[0].table) {
+ (*array)[a * 4 + 0] = cumap->cm[0].table[a].y;
+ }
+ }
+}
+
void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size)
{
int a;
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index b9b15eba6a4..b2b03d28483 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -3499,7 +3499,7 @@ static void stretchto_new_data(void *cdata)
bStretchToConstraint *data = (bStretchToConstraint *)cdata;
data->volmode = 0;
- data->plane = 0;
+ data->plane = SWING_Y;
data->orglength = 0.0;
data->bulge = 1.0;
data->bulge_max = 1.0f;
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index b0d196b2bb0..0dcfea78ca5 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -404,6 +404,7 @@ void BKE_curve_init(Curve *cu, const short curve_type)
}
else if (cu->type == OB_SURF) {
cu->flag |= CU_3D;
+ cu->resolu = 4;
cu->resolv = 4;
}
cu->bevel_profile = NULL;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index ad2d5d267d5..3bb02e1856b 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -1856,6 +1856,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
NULL,
NULL,
NULL},
+ /* 51: CD_HAIRLENGTH */
+ {sizeof(float), "float", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -1912,6 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
+ "CDHairLength",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index e756daa1156..0776f3b9a68 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -261,7 +261,7 @@ bool BKE_displist_surfindex_get(
return true;
}
-/* ****************** make displists ********************* */
+/* ****************** Make #DispList ********************* */
#ifdef __INTEL_COMPILER
/* ICC with the optimization -02 causes crashes. */
# pragma intel optimization_level 1
@@ -1540,23 +1540,6 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
boundbox_displist_object(ob);
}
-void BKE_displist_make_curveTypes_forRender(
- Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *r_dispbase, Mesh **r_final)
-{
- if (ob->runtime.curve_cache == nullptr) {
- ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__);
- }
-
- if (ob->type == OB_SURF) {
- evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final);
- }
- else {
- GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- *r_final = mesh_component.release();
- }
-}
-
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
{
bool doit = false;
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index d75b3259148..9083c507160 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -317,7 +317,7 @@ static bool setError(DynamicPaintCanvasSettings *canvas, const char *string)
static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface)
{
if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) {
- return 0; /* not supported atm */
+ return 0; /* Not supported at the moment. */
}
if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
const Mesh *canvas_mesh = dynamicPaint_canvas_mesh_get(surface->canvas);
@@ -1231,7 +1231,7 @@ void dynamicPaint_Modifier_copy(const struct DynamicPaintModifierData *pmd,
/* copy existing surfaces */
for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
DynamicPaintSurface *t_surface = dynamicPaint_createNewSurface(tpmd->canvas, NULL);
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* TODO(sergey): Consider passing some tips to the surface
* creation to avoid this allocate-and-free cache behavior. */
BKE_ptcache_free_list(&t_surface->ptcaches);
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 1324b37f39c..e272b71acb8 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -5094,7 +5094,7 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
/* pointcache options */
BKE_ptcache_free_list(&(tfds->ptcaches[0]));
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* Share the cache with the original object's modifier. */
tfmd->modifier.flag |= eModifierFlag_SharedCaches;
tfds->point_cache[0] = fds->point_cache[0];
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index aa13f86523a..0e159418724 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -34,6 +34,7 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_math_base_safe.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
@@ -490,15 +491,15 @@ static void build_underline(Curve *cu,
mul_v2_fl(bp[3].vec, font_size);
}
-static void buildchar(Curve *cu,
- ListBase *nubase,
- unsigned int character,
- CharInfo *info,
- float ofsx,
- float ofsy,
- float rot,
- int charidx,
- const float fsize)
+void BKE_vfont_build_char(Curve *cu,
+ ListBase *nubase,
+ unsigned int character,
+ CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize)
{
VFontData *vfd = vfont_get_data(which_vfont(cu, info));
if (!vfd) {
@@ -794,8 +795,8 @@ static bool vfont_to_curve(Object *ob,
bool ok = false;
const float font_size = cu->fsize * iter_data->scale_to_fit;
const bool word_wrap = iter_data->word_wrap;
- const float xof_scale = cu->xof / font_size;
- const float yof_scale = cu->yof / font_size;
+ const float xof_scale = safe_divide(cu->xof, font_size);
+ const float yof_scale = safe_divide(cu->yof, font_size);
int last_line = -1;
/* Length of the text disregarding \n breaks. */
float current_line_length = 0.0f;
@@ -889,7 +890,7 @@ static bool vfont_to_curve(Object *ob,
linedist = cu->linedist;
curbox = 0;
- textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / font_size);
+ textbox_scale(&tb_scale, &cu->tb[curbox], safe_divide(1.0f, font_size));
use_textbox = (tb_scale.w != 0.0f);
xof = MARGIN_X_MIN;
@@ -1525,7 +1526,7 @@ static bool vfont_to_curve(Object *ob,
}
/* We do not want to see any character for \n or \r */
if (cha != '\n') {
- buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
+ BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
}
if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) {
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 7d0537178ef..73c628d3f0f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -535,6 +535,9 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
* array implementations try to make it workable in common situations.
* \{ */
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
template<typename T>
static void point_attribute_materialize(Span<Span<T>> data,
Span<int> offsets,
@@ -546,7 +549,15 @@ 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];
- r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
+
+ Span<T> src = data[spline_index];
+ MutableSpan<T> dst = r_span.slice(offset, next_offset - offset);
+ if (src.is_empty()) {
+ dst.fill(T());
+ }
+ else {
+ dst.copy_from(src);
+ }
}
}
else {
@@ -557,11 +568,20 @@ static void point_attribute_materialize(Span<Span<T>> data,
}
const int index_in_spline = dst_index - offsets[spline_index];
- r_span[dst_index] = data[spline_index][index_in_spline];
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ r_span[dst_index] = T();
+ }
+ else {
+ r_span[dst_index] = src[index_in_spline];
+ }
}
}
}
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
template<typename T>
static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
Span<int> offsets,
@@ -574,7 +594,14 @@ static void point_attribute_materialize_to_uninitialized(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];
- uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ uninitialized_fill_n(dst + offset, next_offset - offset, T());
+ }
+ else {
+ uninitialized_copy_n(src.data(), next_offset - offset, dst + offset);
+ }
}
}
else {
@@ -585,7 +612,13 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
const int index_in_spline = dst_index - offsets[spline_index];
- new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ new (dst + dst_index) T();
+ }
+ else {
+ new (dst + dst_index) T(src[index_in_spline]);
+ }
}
}
}
@@ -769,6 +802,169 @@ class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
}
};
+class VArray_For_BezierHandle final : public VArray<float3> {
+ private:
+ Span<SplinePtr> splines_;
+ Array<int> offsets_;
+ bool is_right_;
+
+ public:
+ VArray_For_BezierHandle(Span<SplinePtr> splines, Array<int> offsets, const bool is_right)
+ : VArray<float3>(offsets.last()),
+ splines_(std::move(splines)),
+ offsets_(std::move(offsets)),
+ is_right_(is_right)
+ {
+ }
+
+ static float3 get_internal(const int64_t index,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right)
+ {
+ const PointIndices indices = lookup_point_indices(offsets, index);
+ const Spline &spline = *splines[indices.spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
+ return is_right ? bezier_spline.handle_positions_right()[indices.point_index] :
+ bezier_spline.handle_positions_left()[indices.point_index];
+ }
+ return float3(0);
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ return get_internal(index, splines_, offsets_, is_right_);
+ }
+
+ /**
+ * Utility so we can pass handle positions to the materialize functions above.
+ *
+ * \note This relies on the ability of the materialize implementations to
+ * handle empty spans, since only Bezier splines have handles.
+ */
+ static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right)
+ {
+ Array<Span<float3>> spans(splines.size());
+ for (const int i : spans.index_range()) {
+ if (splines[i]->type() == Spline::Type::Bezier) {
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
+ spans[i] = is_right ? bezier_spline.handle_positions_right() :
+ bezier_spline.handle_positions_left();
+ }
+ else {
+ spans[i] = {};
+ }
+ }
+ return spans;
+ }
+
+ static void materialize_internal(const IndexMask mask,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right,
+ MutableSpan<float3> r_span)
+ {
+ Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+ point_attribute_materialize(spans.as_span(), offsets, mask, r_span);
+ }
+
+ static void materialize_to_uninitialized_internal(const IndexMask mask,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right,
+ MutableSpan<float3> r_span)
+ {
+ Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span);
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+};
+
+class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+ bool is_right_;
+
+ public:
+ VMutableArray_For_BezierHandles(MutableSpan<SplinePtr> splines,
+ Array<int> offsets,
+ const bool is_right)
+ : VMutableArray<float3>(offsets.last()),
+ splines_(splines),
+ offsets_(std::move(offsets)),
+ is_right_(is_right)
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_);
+ }
+
+ void set_impl(const int64_t index, float3 value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ Spline &spline = *splines_[indices.spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+ if (is_right_) {
+ bezier_spline.set_handle_position_right(indices.point_index, value);
+ }
+ else {
+ bezier_spline.set_handle_position_left(indices.point_index, value);
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ const int offset = offsets_[spline_index];
+
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+ if (is_right_) {
+ for (const int i : IndexRange(bezier_spline.size())) {
+ bezier_spline.set_handle_position_right(i, src[offset + i]);
+ }
+ }
+ else {
+ for (const int i : IndexRange(bezier_spline.size())) {
+ bezier_spline.set_handle_position_left(i, src[offset + i]);
+ }
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ VArray_For_BezierHandle::materialize_to_uninitialized_internal(
+ mask, splines_, offsets_, is_right_, r_span);
+ }
+};
+
/**
* Provider for any builtin control point attribute that doesn't need
* special handling like access to other arrays in the spline.
@@ -906,6 +1102,78 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
}
};
+class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
+ private:
+ bool is_right_;
+
+ public:
+ BezierHandleAttributeProvider(const bool is_right)
+ : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable),
+ is_right_(is_right)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ return {};
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<float3, VArray_For_BezierHandle>>(
+ offsets.last(), curve->splines(), std::move(offsets), is_right_);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ return {};
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
+ offsets.last(), curve->splines(), std::move(offsets), is_right_);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ return curve->has_spline_with_type(Spline::Type::Bezier) &&
+ component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1196,6 +1464,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
spline_custom_data_access);
static PositionAttributeProvider position;
+ static BezierHandleAttributeProvider handles_start(false);
+ static BezierHandleAttributeProvider handles_end(true);
static BuiltinPointAttributeProvider<float> radius(
"radius",
@@ -1213,8 +1483,9 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static DynamicPointAttributeProvider point_custom_data;
- return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
- {&spline_custom_data, &point_custom_data});
+ return ComponentAttributeProviders(
+ {&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index 9479d012cb8..4204d62e1a7 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -14,11 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <mutex>
+
#include "BLI_float4x4.hh"
#include "BLI_map.hh"
#include "BLI_rand.hh"
#include "BLI_set.hh"
#include "BLI_span.hh"
+#include "BLI_task.hh"
#include "BLI_vector.hh"
#include "DNA_collection_types.h"
@@ -122,44 +125,14 @@ blender::Span<int> InstancesComponent::instance_ids() const
}
/**
- * If references have a collection or object type, convert them into geometry instances. This
- * will join geometry components from nested instances if necessary. After that, the geometry
- * sets can be edited.
- */
-void InstancesComponent::ensure_geometry_instances()
-{
- VectorSet<InstanceReference> new_references;
- new_references.reserve(references_.size());
- for (const InstanceReference &reference : references_) {
- if (reference.type() == InstanceReference::Type::Object) {
- GeometrySet geometry_set;
- InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- const int handle = instances.add_reference(reference.object());
- instances.add_instance(handle, float4x4::identity());
- new_references.add_new(geometry_set);
- }
- else if (reference.type() == InstanceReference::Type::Collection) {
- GeometrySet geometry_set;
- InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- const int handle = instances.add_reference(reference.collection());
- instances.add_instance(handle, float4x4::identity());
- new_references.add_new(geometry_set);
- }
- else {
- new_references.add_new(reference);
- }
- }
- references_ = std::move(new_references);
-}
-
-/**
* With write access to the instances component, the data in the instanced geometry sets can be
* changed. This is a function on the component rather than each reference to ensure `const`
* correctness for that reason.
*/
GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index)
{
- /* If this assert fails, it means #ensure_geometry_instances must be called first. */
+ /* If this assert fails, it means #ensure_geometry_instances must be called first or that the
+ * reference can't be converted to a geometry set. */
BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet);
/* The const cast is okay because the instance's hash in the set
@@ -182,6 +155,86 @@ blender::Span<InstanceReference> InstancesComponent::references() const
return references_;
}
+void InstancesComponent::remove_unused_references()
+{
+ using namespace blender;
+ using namespace blender::bke;
+
+ const int tot_instances = this->instances_amount();
+ const int tot_references_before = references_.size();
+
+ if (tot_instances == 0) {
+ /* If there are no instances, no reference is needed. */
+ references_.clear();
+ return;
+ }
+ if (tot_references_before == 1) {
+ /* There is only one reference and at least one instance. So the only existing reference is
+ * used. Nothing to do here. */
+ return;
+ }
+
+ Array<bool> usage_by_handle(tot_references_before, false);
+ std::mutex mutex;
+
+ /* Loop over all instances to see which references are used. */
+ threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) {
+ /* Use local counter to avoid lock contention. */
+ Array<bool> local_usage_by_handle(tot_references_before, false);
+
+ for (const int i : range) {
+ const int handle = instance_reference_handles_[i];
+ BLI_assert(handle >= 0 && handle < tot_references_before);
+ local_usage_by_handle[handle] = true;
+ }
+
+ std::lock_guard lock{mutex};
+ for (const int i : IndexRange(tot_references_before)) {
+ usage_by_handle[i] |= local_usage_by_handle[i];
+ }
+ });
+
+ if (!usage_by_handle.as_span().contains(false)) {
+ /* All references are used. */
+ return;
+ }
+
+ /* Create new references and a mapping for the handles. */
+ Vector<int> handle_mapping;
+ VectorSet<InstanceReference> new_references;
+ int next_new_handle = 0;
+ bool handles_have_to_be_updated = false;
+ for (const int old_handle : IndexRange(tot_references_before)) {
+ if (!usage_by_handle[old_handle]) {
+ /* Add some dummy value. It won't be read again. */
+ handle_mapping.append(-1);
+ }
+ else {
+ const InstanceReference &reference = references_[old_handle];
+ handle_mapping.append(next_new_handle);
+ new_references.add_new(reference);
+ if (old_handle != next_new_handle) {
+ handles_have_to_be_updated = true;
+ }
+ next_new_handle++;
+ }
+ }
+ references_ = new_references;
+
+ if (!handles_have_to_be_updated) {
+ /* All remaining handles are the same as before, so they don't have to be updated. This happens
+ * when unused handles are only at the end. */
+ return;
+ }
+
+ /* Update handles of instances. */
+ threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) {
+ for (const int i : range) {
+ instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]];
+ }
+ });
+}
+
int InstancesComponent::instances_amount() const
{
return instance_transforms_.size();
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index e717d289894..0aac6ae3adf 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_map.hh"
+#include "BLI_task.hh"
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
@@ -151,6 +152,19 @@ void GeometrySet::remove(const GeometryComponentType component_type)
components_.remove(component_type);
}
+/**
+ * Remove all geometry components with types that are not in the provided list.
+ */
+void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component_types)
+{
+ for (auto it = components_.keys().begin(); it != components_.keys().end(); ++it) {
+ const GeometryComponentType type = *it;
+ if (!component_types.contains(type)) {
+ components_.remove(it);
+ }
+ }
+}
+
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_.contains(component.type()));
@@ -291,6 +305,29 @@ bool GeometrySet::has_curve() const
return component != nullptr && component->has_curve();
}
+/* Returns true when the geometry set has any data that is not an instance. */
+bool GeometrySet::has_realized_data() const
+{
+ if (components_.is_empty()) {
+ return false;
+ }
+ if (components_.size() > 1) {
+ return true;
+ }
+ /* Check if the only component is an #InstancesComponent. */
+ return this->get_component_for_read<InstancesComponent>() == nullptr;
+}
+
+/* Return true if the geometry set has any component that isn't empty. */
+bool GeometrySet::is_empty() const
+{
+ if (components_.is_empty()) {
+ return true;
+ }
+ return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() ||
+ this->has_instances());
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -375,6 +412,108 @@ CurveEval *GeometrySet::get_curve_for_write()
return component.get_for_write();
}
+void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_types,
+ const bool include_instances,
+ const AttributeForeachCallback callback) const
+{
+ using namespace blender;
+ using namespace blender::bke;
+ for (const GeometryComponentType component_type : component_types) {
+ if (!this->has(component_type)) {
+ continue;
+ }
+ const GeometryComponent &component = *this->get_component_for_read(component_type);
+ component.attribute_foreach(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ callback(attribute_id, meta_data, component);
+ return true;
+ });
+ }
+ if (include_instances && this->has_instances()) {
+ const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>();
+ instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) {
+ instance_geometry_set.attribute_foreach(component_types, include_instances, callback);
+ });
+ }
+}
+
+void GeometrySet::gather_attributes_for_propagation(
+ const Span<GeometryComponentType> component_types,
+ const GeometryComponentType dst_component_type,
+ bool include_instances,
+ blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const
+{
+ using namespace blender;
+ using namespace blender::bke;
+ /* Only needed right now to check if an attribute is built-in on this component type.
+ * TODO: Get rid of the dummy component. */
+ const GeometryComponent *dummy_component = GeometryComponent::create(dst_component_type);
+ this->attribute_foreach(
+ component_types,
+ include_instances,
+ [&](const AttributeIDRef &attribute_id,
+ const AttributeMetaData &meta_data,
+ const GeometryComponent &component) {
+ if (component.attribute_is_builtin(attribute_id)) {
+ if (!dummy_component->attribute_is_builtin(attribute_id)) {
+ /* Don't propagate built-in attributes that are not built-in on the destination
+ * component. */
+ return;
+ }
+ }
+ if (attribute_id.is_anonymous()) {
+ if (!BKE_anonymous_attribute_id_has_strong_references(&attribute_id.anonymous_id())) {
+ /* Don't propagate anonymous attributes that are not used anymore. */
+ return;
+ }
+ }
+ auto add_info = [&](AttributeKind *attribute_kind) {
+ attribute_kind->domain = meta_data.domain;
+ attribute_kind->data_type = meta_data.data_type;
+ };
+ auto modify_info = [&](AttributeKind *attribute_kind) {
+ attribute_kind->domain = bke::attribute_domain_highest_priority(
+ {attribute_kind->domain, meta_data.domain});
+ attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
+ {attribute_kind->data_type, meta_data.data_type});
+ };
+ r_attributes.add_or_modify(attribute_id, add_info, modify_info);
+ });
+ delete dummy_component;
+}
+
+static void gather_mutable_geometry_sets(GeometrySet &geometry_set,
+ Vector<GeometrySet *> &r_geometry_sets)
+{
+ r_geometry_sets.append(&geometry_set);
+ if (!geometry_set.has_instances()) {
+ return;
+ }
+ /* In the future this can be improved by deduplicating instance references across different
+ * instances. */
+ InstancesComponent &instances_component =
+ geometry_set.get_component_for_write<InstancesComponent>();
+ instances_component.ensure_geometry_instances();
+ for (const int handle : instances_component.references().index_range()) {
+ if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) {
+ GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle);
+ gather_mutable_geometry_sets(instance_geometry, r_geometry_sets);
+ }
+ }
+}
+
+/**
+ * Modify every (recursive) instance separately. This is often more efficient than realizing all
+ * instances just to change the same thing on all of them.
+ */
+void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback)
+{
+ Vector<GeometrySet *> geometry_sets;
+ gather_mutable_geometry_sets(*this, geometry_sets);
+ blender::threading::parallel_for_each(
+ geometry_sets, [&](GeometrySet *geometry_set) { callback(*geometry_set); });
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 9dca2c2907e..77348c3d22c 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -14,6 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_collection.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -23,6 +24,7 @@
#include "BKE_spline.hh"
#include "DNA_collection_types.h"
+#include "DNA_layer_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -187,134 +189,6 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set,
geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups);
}
-static bool collection_instance_attribute_foreach(const Collection &collection,
- const AttributeForeachCallback callback,
- const int limit,
- int &count);
-
-static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
- const AttributeForeachCallback callback,
- const int limit,
- int &count);
-
-static bool object_instance_attribute_foreach(const Object &object,
- const AttributeForeachCallback callback,
- const int limit,
- int &count)
-{
- GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
- if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) {
- return false;
- }
-
- if (object.type == OB_EMPTY) {
- const Collection *collection_instance = object.instance_collection;
- if (collection_instance != nullptr) {
- if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) {
- return false;
- }
- }
- }
- return true;
-}
-
-static bool collection_instance_attribute_foreach(const Collection &collection,
- const AttributeForeachCallback callback,
- const int limit,
- int &count)
-{
- LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
- BLI_assert(collection_object->ob != nullptr);
- const Object &object = *collection_object->ob;
- if (!object_instance_attribute_foreach(object, callback, limit, count)) {
- return false;
- }
- }
- LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
- BLI_assert(collection_child->collection != nullptr);
- const Collection &collection = *collection_child->collection;
- if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
- return false;
- }
- }
- return true;
-}
-
-/**
- * \return True if the recursive iteration should continue, false if the limit is reached or the
- * callback has returned false indicating it should stop.
- */
-static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
- const AttributeForeachCallback callback,
- const int limit,
- int &count)
-{
- for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
- if (!component->attribute_foreach(callback)) {
- return false;
- }
- }
-
- /* Now that this geometry set is visited, increase the count and check with the limit. */
- if (limit > 0 && count++ > limit) {
- return false;
- }
-
- const InstancesComponent *instances_component =
- geometry_set.get_component_for_read<InstancesComponent>();
- if (instances_component == nullptr) {
- return true;
- }
-
- for (const InstanceReference &reference : instances_component->references()) {
- switch (reference.type()) {
- case InstanceReference::Type::Object: {
- const Object &object = reference.object();
- if (!object_instance_attribute_foreach(object, callback, limit, count)) {
- return false;
- }
- break;
- }
- case InstanceReference::Type::Collection: {
- const Collection &collection = reference.collection();
- if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
- return false;
- }
- break;
- }
- case InstanceReference::Type::GeometrySet: {
- const GeometrySet &geometry_set = reference.geometry_set();
- if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) {
- return false;
- }
- break;
- }
- case InstanceReference::Type::None: {
- break;
- }
- }
- }
-
- return true;
-}
-
-/**
- * Call the callback on all of this geometry set's components, including geometry sets from
- * instances and recursive instances. This is necessary to access available attributes without
- * making all of the set's geometry real.
- *
- * \param limit: The total number of geometry sets to visit before returning early. This is used
- * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor
- * of performance at the cost of visiting every unique attribute.
- */
-void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
- const AttributeForeachCallback callback,
- const int limit)
-{
- int count = 0;
- instances_attribute_foreach_recursive(geometry_set, callback, limit, count);
-}
-
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
@@ -700,7 +574,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G
geometry_set_gather_instances_attribute_info(
set_groups,
{GEO_COMPONENT_TYPE_CURVE},
- {"position", "radius", "tilt", "cyclic", "resolution"},
+ {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"},
attributes);
join_attributes(set_groups,
{GEO_COMPONENT_TYPE_CURVE},
@@ -745,3 +619,91 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
}
} // namespace blender::bke
+
+void InstancesComponent::foreach_referenced_geometry(
+ blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const
+{
+ using namespace blender::bke;
+ for (const InstanceReference &reference : references_) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ const Object &object = reference.object();
+ const GeometrySet object_geometry_set = object_get_geometry_set_for_read(object);
+ callback(object_geometry_set);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
+ const GeometrySet object_geometry_set = object_get_geometry_set_for_read(*object);
+ callback(object_geometry_set);
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::GeometrySet: {
+ const GeometrySet &instance_geometry_set = reference.geometry_set();
+ callback(instance_geometry_set);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * If references have a collection or object type, convert them into geometry instances
+ * recursively. After that, the geometry sets can be edited. There may still be instances of other
+ * types of they can't be converted to geometry sets.
+ */
+void InstancesComponent::ensure_geometry_instances()
+{
+ using namespace blender;
+ using namespace blender::bke;
+ VectorSet<InstanceReference> new_references;
+ new_references.reserve(references_.size());
+ for (const InstanceReference &reference : references_) {
+ switch (reference.type()) {
+ case InstanceReference::Type::None:
+ case InstanceReference::Type::GeometrySet: {
+ /* Those references can stay as their were. */
+ new_references.add_new(reference);
+ break;
+ }
+ case InstanceReference::Type::Object: {
+ /* Create a new reference that contains the geometry set of the object. We may want to
+ * treat e.g. lamps and similar object types separately here. */
+ const Object &object = reference.object();
+ GeometrySet object_geometry_set = object_get_geometry_set_for_read(object);
+ if (object_geometry_set.has_instances()) {
+ InstancesComponent &component =
+ object_geometry_set.get_component_for_write<InstancesComponent>();
+ component.ensure_geometry_instances();
+ }
+ new_references.add_new(std::move(object_geometry_set));
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ /* Create a new reference that contains a geometry set that contains all objects from the
+ * collection as instances. */
+ GeometrySet collection_geometry_set;
+ InstancesComponent &component =
+ collection_geometry_set.get_component_for_write<InstancesComponent>();
+ Collection &collection = reference.collection();
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
+ const int handle = component.add_reference(*object);
+ component.add_instance(handle, object->obmat);
+ float4x4 &transform = component.instance_transforms().last();
+ sub_v3_v3(transform.values[3], collection.instance_offset);
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ component.ensure_geometry_instances();
+ new_references.add_new(std::move(collection_geometry_set));
+ break;
+ }
+ }
+ }
+ references_ = std::move(new_references);
+}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 82a44afbbb1..ed84694a919 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -319,7 +319,7 @@ IDTypeInfo IDType_ID_GD = {
.name = "GPencil",
.name_plural = "grease_pencils",
.translation_context = BLT_I18NCONTEXT_ID_GPENCIL,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = NULL,
.copy_data = greasepencil_copy_data,
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 0752424df71..3819c0699f4 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -543,7 +543,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
int actcol = ob_gp->actcol;
for (int slot = 1; slot <= ob_gp->totcol; slot++) {
- while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp->data, slot)) {
+ while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp, slot)) {
ob_gp->actcol = slot;
BKE_object_material_slot_remove(bmain, ob_gp);
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index 976b26a1f3a..debdf44b0bb 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -738,8 +738,8 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x);
/* In general curvature = 1/radius. For the case without the
- * weights introduced by #segment_influence, the calculation is
- * curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */
+ * weights introduced by #segment_influence, the calculation is:
+ * `curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length` */
float curvature = normalize_v3(total_angle) / overshoot_length;
/* Compensate for the weights powf(added_len, segment_influence). */
curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence);
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 6be03bffb3c..a6164340477 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -65,7 +65,7 @@
static CLG_LogRef LOG = {"bke.gpencil_modifier"};
static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL};
#if 0
-/* Note that GPencil actually does not support these atm, but might do in the future. */
+/* Note that GPencil actually does not support these at the moment, but might do in the future. */
static GpencilVirtualModifierData virtualModifierCommonData;
#endif
@@ -129,7 +129,8 @@ GpencilModifierData *BKE_gpencil_modifiers_get_virtual_modifierlist(
GpencilModifierData *md = ob->greasepencil_modifiers.first;
#if 0
- /* Note that GPencil actually does not support these atm, but might do in the future. */
+ /* Note that GPencil actually does not support these at the moment,
+ * but might do in the future. */
*virtualModifierData = virtualModifierCommonData;
if (ob->parent) {
if (ob->parent->type == OB_ARMATURE && ob->partype == PARSKEL) {
@@ -328,8 +329,9 @@ void BKE_gpencil_modifier_init(void)
gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */
#if 0
- /* Note that GPencil actually does not support these atm, but might do in the future. */
- /* Initialize global cmmon storage used for virtual modifier list */
+ /* Note that GPencil actually does not support these at the moment,
+ * but might do in the future. */
+ /* Initialize global common storage used for virtual modifier list. */
GpencilModifierData *md;
md = BKE_gpencil_modifier_new(eGpencilModifierType_Armature);
virtualModifierCommonData.amd = *((ArmatureGpencilModifierData *)md);
@@ -518,7 +520,7 @@ static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData),
* Copy grease pencil modifier data.
* \param md: Source modifier data
* \param target: Target modifier data
- * \parm flag: Flags
+ * \param flag: Flags
*/
void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md,
GpencilModifierData *target,
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index 9b72a2d1a72..26a1240080f 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -2013,7 +2013,8 @@ static void nlastrips_to_animdata(ID *id, ListBase *strips)
}
}
- /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */
+ /* Try to add this strip to the current NLA-Track
+ * (i.e. the 'last' one on the stack at the moment). */
if (BKE_nlatrack_add_strip(nlt, strip, false) == 0) {
/* trying to add to the current failed (no space),
* so add a new track to the stack, and add to that...
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 44fc86877a7..c09fcf0715e 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -1904,7 +1904,7 @@ KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force
return kb;
}
-/* only the active keyblock */
+/* Only the active key-block. */
KeyBlock *BKE_keyblock_from_object(Object *ob)
{
Key *key = BKE_key_from_object(ob);
@@ -2247,7 +2247,7 @@ void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me)
* Computes normals (vertices, polygons and/or loops ones) of given mesh for given shape key.
*
* \param kb: the KeyBlock to use to compute normals.
- * \param mesh: the Mesh to apply keyblock to.
+ * \param mesh: the Mesh to apply key-block to.
* \param r_vertnors: if non-NULL, an array of vectors, same length as number of vertices.
* \param r_polynors: if non-NULL, an array of vectors, same length as number of polygons.
* \param r_loopnors: if non-NULL, an array of vectors, same length as number of loops.
@@ -2345,7 +2345,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve
return;
}
- /* Copy coords to keyblock */
+ /* Copy coords to key-block. */
if (ELEM(ob->type, OB_MESH, OB_LATTICE)) {
for (a = 0; a < tot; a++, fp += 3, co++) {
copy_v3_v3(fp, *co);
@@ -2405,7 +2405,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v
kb->data = MEM_mallocN(tot * elemsize, __func__);
- /* Copy coords to keyblock */
+ /* Copy coords to key-block. */
BKE_keyblock_update_from_vertcos(ob, kb, vertCos);
}
@@ -2594,7 +2594,7 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
}
/**
- * Check if given keyblock (as index) is used as basis by others in given key.
+ * Check if given key-block (as index) is used as basis by others in given key.
*/
bool BKE_keyblock_is_basis(Key *key, const int index)
{
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 18824e73ee5..3b2d2c5d2c3 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -674,7 +674,10 @@ ID *BKE_id_copy(Main *bmain, const ID *id)
* Invokes the appropriate copy method for the block and returns the result in
* newid, unless test. Returns true if the block can be copied.
*/
-ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplicate_flags)
+ID *BKE_id_copy_for_duplicate(Main *bmain,
+ ID *id,
+ const eDupli_ID_Flags duplicate_flags,
+ const int copy_flags)
{
if (id == NULL) {
return id;
@@ -685,7 +688,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica
return id;
}
- ID *id_new = BKE_id_copy(bmain, id);
+ ID *id_new = BKE_id_copy_ex(bmain, id, NULL, copy_flags);
/* Copying add one user by default, need to get rid of that one. */
id_us_min(id_new);
ID_NEW_SET(id, id_new);
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index c60a9104144..68675e5fc91 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -1000,9 +1000,92 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
+ /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created
+ * overrides.
+ * While this might not be 100% the desired behavior, it is likely to be the case most of the
+ * time. Ref: T91711. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (!ID_IS_LINKED(id_iter)) {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
+static void lib_override_library_proxy_convert_do(Main *bmain,
+ Scene *scene,
+ Object *ob_proxy,
+ BlendFileReadReport *reports)
+{
+ Object *ob_proxy_group = ob_proxy->proxy_group;
+ const bool is_override_instancing_object = ob_proxy_group != NULL;
+
+ const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
+
+ if (success) {
+ CLOG_INFO(&LOG,
+ 4,
+ "Proxy object '%s' successfuly converted to library overrides",
+ ob_proxy->id.name);
+ /* Remove the instance empty from this scene, the items now have an overridden collection
+ * instead. */
+ if (is_override_instancing_object) {
+ BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
+ }
+ reports->count.proxies_to_lib_overrides_success++;
+ }
+}
+
+/**
+ * Convert all proxy objects into library overrides.
+ *
+ * \note Only affects local proxies, linked ones are not affected.
+ *
+ * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in
+ * which case \a scene's master collection children hierarchy is used instead).
+ */
+void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
+{
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy_group == NULL) {
+ continue;
+ }
+
+ lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ }
+ FOREACH_SCENE_OBJECT_END;
+
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy == NULL) {
+ continue;
+ }
+
+ lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ }
+ FOREACH_SCENE_OBJECT_END;
+ }
+
+ LISTBASE_FOREACH (Object *, object, &bmain->objects) {
+ if (ID_IS_LINKED(object)) {
+ if (object->proxy != NULL) {
+ CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name);
+ reports->count.linked_proxies++;
+ }
+ continue;
+ }
+
+ if (object->proxy_group != NULL || object->proxy != NULL) {
+ CLOG_WARN(
+ &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name);
+ reports->count.proxies_to_lib_overrides_failures++;
+ }
+ }
+}
+
/**
* Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked
* data, from an existing override hierarchy.
@@ -2889,6 +2972,31 @@ void BKE_lib_override_library_main_update(Main *bmain)
G_MAIN = orig_gmain;
}
+/** In case an ID is used by another liboverride ID, user may not be allowed to delete it. */
+bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id)
+{
+ if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) {
+ return true;
+ }
+
+ /* The only strong known case currently are objects used by override collections. */
+ /* TODO: There are most likely other cases... This may need to be addressed in a better way at
+ * some point. */
+ if (GS(id->name) != ID_OB) {
+ return true;
+ }
+ Object *ob = (Object *)id;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (!ID_IS_OVERRIDE_LIBRARY(collection)) {
+ continue;
+ }
+ if (BKE_collection_has_object(collection, ob)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/**
* Storage (how to store overriding data into `.blend` files).
*
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 250b8d4d515..48396c5e6d9 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -345,7 +345,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id)
{
/* Update all group nodes using a node group. */
- ntreeUpdateAllUsers(bmain, new_id);
+ ntreeUpdateAllUsers(bmain, new_id, 0);
}
/**
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 26dcadcc77b..9c3291edbcc 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -485,6 +485,7 @@ void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapp
const bool already_exist_in_mapping = BLI_ghash_ensure_p(
library_weak_reference_mapping, key, &id_p);
BLI_assert(!already_exist_in_mapping);
+ UNUSED_VARS_NDEBUG(already_exist_in_mapping);
BLI_strncpy(new_id->library_weak_reference->library_filepath,
library_filepath,
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 5f53d5e1ae8..fa3fbd457d1 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -46,6 +46,7 @@
#include "DNA_meta_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_volume_types.h"
@@ -73,6 +74,7 @@
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
@@ -462,21 +464,33 @@ static void material_data_index_remove_id(ID *id, short index)
}
}
-bool BKE_object_material_slot_used(ID *id, short actcol)
+bool BKE_object_material_slot_used(Object *object, short actcol)
{
- /* ensure we don't try get materials from non-obdata */
- BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name)));
+ if (!BKE_object_supports_material_slots(object)) {
+ return false;
+ }
- switch (GS(id->name)) {
+ LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
+ if (psys->part->omat == actcol) {
+ return true;
+ }
+ }
+
+ ID *ob_data = object->data;
+ if (ob_data == NULL || !OB_DATA_SUPPORT_ID(GS(ob_data->name))) {
+ return false;
+ }
+
+ switch (GS(ob_data->name)) {
case ID_ME:
- return BKE_mesh_material_index_used((Mesh *)id, actcol - 1);
+ return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1);
case ID_CU:
- return BKE_curve_material_index_used((Curve *)id, actcol - 1);
+ return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1);
case ID_MB:
- /* meta-elems don't have materials atm */
+ /* Meta-elements don't support materials at the moment. */
return false;
case ID_GD:
- return BKE_gpencil_material_index_used((bGPdata *)id, actcol - 1);
+ return BKE_gpencil_material_index_used((bGPdata *)ob_data, actcol - 1);
default:
return false;
}
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 9dd583b4c6b..a2590171abd 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -454,7 +454,7 @@ static void make_face(PROCESS *process, int i1, int i2, int i3, int i4)
cur = process->indices[process->curindex++];
- /* displists now support array drawing, we treat tri's as fake quad */
+ /* #DispList supports array drawing, treat tri's as fake quad. */
cur[0] = i1;
cur[1] = i2;
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index 467f7d4543e..59cdb6a2b27 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -41,6 +41,7 @@
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
@@ -51,6 +52,7 @@
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
+#include "BKE_spline.hh"
/* these 2 are only used by conversion functions */
#include "BKE_curve.h"
/* -- */
@@ -58,6 +60,8 @@
/* -- */
#include "BKE_pointcloud.h"
+#include "BKE_curve_to_mesh.hh"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -237,7 +241,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
int p1, p2, p3, p4, *index;
const bool conv_polys = (
- /* 2d polys are filled with DL_INDEX3 displists */
+ /* 2D polys are filled with #DispList.type == #DL_INDEX3. */
(CU_DO_2DFILL(cu) == false) ||
/* surf polys are never filled */
BKE_curve_type_get(cu) == OB_SURF);
@@ -573,90 +577,6 @@ Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
return BKE_mesh_new_nomain_from_curve_displist(ob, &disp);
}
-static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char *obdata_name)
-{
- if (ob->runtime.data_eval && GS(((ID *)ob->runtime.data_eval)->name) != ID_ME) {
- return;
- }
-
- Mesh *me_eval = (Mesh *)ob->runtime.data_eval;
- Mesh *me;
- MVert *allvert = nullptr;
- MEdge *alledge = nullptr;
- MLoop *allloop = nullptr;
- MLoopUV *alluv = nullptr;
- MPoly *allpoly = nullptr;
- int totvert, totedge, totloop, totpoly;
-
- Curve *cu = (Curve *)ob->data;
-
- if (me_eval == nullptr) {
- if (mesh_nurbs_displist_to_mdata(cu,
- dispbase,
- &allvert,
- &totvert,
- &alledge,
- &totedge,
- &allloop,
- &allpoly,
- &alluv,
- &totloop,
- &totpoly) != 0) {
- /* Error initializing */
- return;
- }
-
- /* make mesh */
- me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
-
- me->totvert = totvert;
- me->totedge = totedge;
- me->totloop = totloop;
- me->totpoly = totpoly;
-
- me->mvert = (MVert *)CustomData_add_layer(
- &me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert);
- me->medge = (MEdge *)CustomData_add_layer(
- &me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge);
- me->mloop = (MLoop *)CustomData_add_layer(
- &me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop);
- me->mpoly = (MPoly *)CustomData_add_layer(
- &me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly);
-
- if (alluv) {
- const char *uvname = "UVMap";
- me->mloopuv = (MLoopUV *)CustomData_add_layer_named(
- &me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname);
- }
-
- BKE_mesh_calc_normals(me);
- }
- else {
- me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
-
- ob->runtime.data_eval = nullptr;
- BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true);
- }
-
- me->totcol = cu->totcol;
- me->mat = cu->mat;
-
- mesh_copy_texture_space_from_curve_type(cu, me);
-
- cu->mat = nullptr;
- cu->totcol = 0;
-
- /* Do not decrement ob->data usercount here,
- * it's done at end of func with BKE_id_free_us() call. */
- ob->data = me;
- ob->type = OB_MESH;
-
- /* For temporary objects in BKE_mesh_new_from_object don't remap
- * the entire scene with associated depsgraph updates, which are
- * problematic for renderers exporting data. */
- BKE_id_free(nullptr, cu);
-}
-
struct EdgeLink {
struct EdgeLink *next, *prev;
void *edge;
@@ -948,54 +868,32 @@ void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
BKE_object_free_derived_caches(ob);
}
-/* Create a temporary object to be used for nurbs-to-mesh conversion.
- *
- * This is more complex that it should be because #mesh_from_nurbs_displist will do more than
- * simply conversion and will attempt to take over ownership of evaluated result and will also
- * modify the input object. */
-static Object *object_for_curve_to_mesh_create(Object *object)
+/* Create a temporary object to be used for nurbs-to-mesh conversion. */
+static Object *object_for_curve_to_mesh_create(const Object *object)
{
- Curve *curve = (Curve *)object->data;
+ const Curve *curve = (const Curve *)object->data;
- /* Create object itself. */
+ /* Create a temporary object which can be evaluated and modified by generic
+ * curve evaluation (hence the #LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */
Object *temp_object = (Object *)BKE_id_copy_ex(
- nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE);
/* Remove all modifiers, since we don't want them to be applied. */
BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT);
- /* Copy relevant evaluated fields of curve cache.
- *
- * Note that there are extra fields in there like bevel and path, but those are not needed during
- * conversion, so they are not copied to save unnecessary allocations. */
- if (temp_object->runtime.curve_cache == nullptr) {
- temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
- "CurveCache for curve types");
- }
-
- if (object->runtime.curve_cache != nullptr) {
- BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
- }
-
- /* Constructive modifiers will use mesh to store result. */
- if (object->runtime.data_eval != nullptr) {
- BKE_id_copy_ex(
- nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE);
- }
-
- /* Need to create copy of curve itself as well, it will be freed by underlying conversion
- * functions.
- *
- * NOTE: Copies the data, but not the shapekeys. */
- BKE_id_copy_ex(
- nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
+ /* Need to create copy of curve itself as well, since it will be changed by the curve evaluation
+ * process. NOTE: Copies the data, but not the shape-keys. */
+ temp_object->data = BKE_id_copy_ex(nullptr,
+ (const ID *)object->data,
+ nullptr,
+ LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE);
Curve *temp_curve = (Curve *)temp_object->data;
/* Make sure texture space is calculated for a copy of curve, it will be used for the final
* result. */
BKE_curve_texspace_calc(temp_curve);
- /* Temporarily set edit so we get updates from edit mode, but also because for text datablocks
+ /* Temporarily set edit so we get updates from edit mode, but also because for text data-blocks
* copying it while in edit mode gives invalid data structures. */
temp_curve->editfont = curve->editfont;
temp_curve->editnurb = curve->editnurb;
@@ -1006,23 +904,10 @@ static Object *object_for_curve_to_mesh_create(Object *object)
/**
* Populate `object->runtime.curve_cache` which is then used to create the mesh.
*/
-static void curve_to_mesh_eval_ensure(Object *object)
+static void curve_to_mesh_eval_ensure(Object &object)
{
- Curve *curve = (Curve *)object->data;
- Curve remapped_curve = *curve;
- Object remapped_object = *object;
- BKE_object_runtime_reset(&remapped_object);
-
- remapped_object.data = &remapped_curve;
-
- if (object->runtime.curve_cache == nullptr) {
- object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
- "CurveCache for Curve");
- }
-
- /* Temporarily share the curve-cache with the temporary object, owned by `object`. */
- remapped_object.runtime.curve_cache = object->runtime.curve_cache;
-
+ BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU);
+ Curve &curve = *static_cast<Curve *>(object.data);
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@@ -1031,83 +916,97 @@ static void curve_to_mesh_eval_ensure(Object *object)
* So we create temporary copy of the object which will use same data as the original bevel, but
* will have no modifiers. */
Object bevel_object = {{nullptr}};
- if (remapped_curve.bevobj != nullptr) {
- bevel_object = *remapped_curve.bevobj;
+ if (curve.bevobj != nullptr) {
+ bevel_object = *curve.bevobj;
BLI_listbase_clear(&bevel_object.modifiers);
BKE_object_runtime_reset(&bevel_object);
- remapped_curve.bevobj = &bevel_object;
+ curve.bevobj = &bevel_object;
}
/* Same thing for taper. */
Object taper_object = {{nullptr}};
- if (remapped_curve.taperobj != nullptr) {
- taper_object = *remapped_curve.taperobj;
+ if (curve.taperobj != nullptr) {
+ taper_object = *curve.taperobj;
BLI_listbase_clear(&taper_object.modifiers);
BKE_object_runtime_reset(&taper_object);
- remapped_curve.taperobj = &taper_object;
+ curve.taperobj = &taper_object;
}
/* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since
* they are only used for modifier stack, which we have explicitly disabled for all objects.
*
* TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a
- * bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also
- * Mesh From Curve operator.
+ * bit of internal functions (#BKE_mesh_nomain_to_mesh) and also Mesh From Curve operator.
* Brecht says hold off with that. */
- Mesh *mesh_eval = nullptr;
- BKE_displist_make_curveTypes_forRender(
- nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
+ BKE_displist_make_curveTypes(nullptr, nullptr, &object, true);
- /* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
- * real issue currently, code here is broken in more than one way, fix(es) will be done
- * separately. */
- if (mesh_eval != nullptr) {
- BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
- }
-
- /* Owned by `object` & needed by the caller to create the mesh. */
- remapped_object.runtime.curve_cache = nullptr;
-
- BKE_object_runtime_free_data(&remapped_object);
- BKE_object_runtime_free_data(&taper_object);
+ BKE_object_runtime_free_data(&bevel_object);
BKE_object_runtime_free_data(&taper_object);
}
-static Mesh *mesh_new_from_curve_type_object(Object *object)
+/* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */
+static const Mesh *get_evaluated_mesh_from_object(const Object *object)
{
- Curve *curve = (Curve *)object->data;
- Object *temp_object = object_for_curve_to_mesh_create(object);
- Curve *temp_curve = (Curve *)temp_object->data;
+ const Mesh *mesh = BKE_object_get_evaluated_mesh(object);
+ if (mesh) {
+ return mesh;
+ }
+ GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval;
+ if (geometry_set_eval) {
+ return geometry_set_eval->get_mesh_for_read();
+ }
+ return nullptr;
+}
- /* When input object is an original one, we don't have evaluated curve cache yet, so need to
- * create it in the temporary object. */
- if (!DEG_is_evaluated_object(object)) {
- curve_to_mesh_eval_ensure(temp_object);
+static const CurveEval *get_evaluated_curve_from_object(const Object *object)
+{
+ GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval;
+ if (geometry_set_eval) {
+ return geometry_set_eval->get_curve_for_read();
}
+ return nullptr;
+}
- /* Reset pointers before conversion. */
- temp_curve->editfont = nullptr;
- temp_curve->editnurb = nullptr;
+static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_object)
+{
+ const Mesh *mesh = get_evaluated_mesh_from_object(evaluated_object);
+ if (mesh) {
+ return BKE_mesh_copy_for_eval(mesh, false);
+ }
+ const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object);
+ if (curve) {
+ return blender::bke::curve_to_wire_mesh(*curve);
+ }
+ return nullptr;
+}
- /* Convert to mesh. */
- mesh_from_nurbs_displist(
- temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2);
+static Mesh *mesh_new_from_curve_type_object(const Object *object)
+{
+ /* If the object is evaluated, it should either have an evaluated mesh or curve data already.
+ * The mesh can be duplicated, or the curve converted to wire mesh edges. */
+ if (DEG_is_evaluated_object(object)) {
+ return mesh_new_from_evaluated_curve_type_object(object);
+ }
- /* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't
- * the curve did not have any segments or otherwise would have generated an empty mesh. */
- if (temp_object->type != OB_MESH) {
- BKE_id_free(nullptr, temp_object->data);
- BKE_id_free(nullptr, temp_object);
- return nullptr;
+ /* Otherwise, create a temporary "fake" evaluated object and try again. This might have
+ * different results, since in order to avoid having adverse affects to other original objects,
+ * modifiers are cleared. An alternative would be to create a temporary depsgraph only for this
+ * object and its dependencies. */
+ Object *temp_object = object_for_curve_to_mesh_create(object);
+ ID *temp_data = static_cast<ID *>(temp_object->data);
+ curve_to_mesh_eval_ensure(*temp_object);
+
+ /* If evaluating the curve replaced object data with different data, free the original data. */
+ if (temp_data != temp_object->data) {
+ BKE_id_free(nullptr, temp_data);
}
- Mesh *mesh_result = (Mesh *)temp_object->data;
+ Mesh *mesh = mesh_new_from_evaluated_curve_type_object(temp_object);
+ BKE_id_free(nullptr, temp_object->data);
BKE_id_free(nullptr, temp_object);
- /* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */
-
- return mesh_result;
+ return mesh;
}
static Mesh *mesh_new_from_mball_object(Object *object)
@@ -1290,7 +1189,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
return mesh_in_bmain;
}
- /* Make sure mesh only points original datablocks, also increase users of materials and other
+ /* Make sure mesh only points original data-blocks, also increase users of materials and other
* possibly referenced data-blocks.
*
* Going to original data-blocks is required to have bmain in a consistent state, where
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index b55b02c7bf2..6f6cf12f023 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -100,7 +100,7 @@ void BKE_modifier_init(void)
/* Initialize modifier types */
modifier_type_init(modifier_types); /* MOD_utils.c */
- /* Initialize global cmmon storage used for virtual modifier list */
+ /* Initialize global common storage used for virtual modifier list. */
md = BKE_modifier_new(eModifierType_Armature);
virtualModifierCommonData.amd = *((ArmatureModifierData *)md);
BKE_modifier_free(md);
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 4ce2ae3c11f..487e925df79 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -1484,7 +1484,7 @@ void BKE_nlastrip_recalculate_bounds(NlaStrip *strip)
}
/* Is the given NLA-strip the first one to occur for the given AnimData block */
-// TODO: make this an api method if necessary, but need to add prefix first
+/* TODO: make this an api method if necessary, but need to add prefix first */
static bool nlastrip_is_first(AnimData *adt, NlaStrip *strip)
{
NlaTrack *nlt;
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index d56a7bf8fb4..73060caa2f8 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -52,9 +52,12 @@
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_path_util.h"
+#include "BLI_set.hh"
+#include "BLI_stack.hh"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
+#include "BLI_vector_set.hh"
#include "BLT_translation.h"
@@ -80,6 +83,7 @@
#include "NOD_function.h"
#include "NOD_geometry.h"
#include "NOD_node_declaration.hh"
+#include "NOD_node_tree_ref.hh"
#include "NOD_shader.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -93,6 +97,21 @@
#define NODE_DEFAULT_MAX_WIDTH 700
+using blender::Array;
+using blender::MutableSpan;
+using blender::Set;
+using blender::Span;
+using blender::Stack;
+using blender::Vector;
+using blender::VectorSet;
+using blender::nodes::FieldInferencingInterface;
+using blender::nodes::InputSocketFieldType;
+using blender::nodes::NodeDeclaration;
+using blender::nodes::OutputFieldDependency;
+using blender::nodes::OutputSocketFieldType;
+using blender::nodes::SocketDeclaration;
+using namespace blender::nodes::node_tree_ref_types;
+
/* Fallback types for undefined tree, nodes, sockets */
static bNodeTreeType NodeTreeTypeUndefined;
bNodeType NodeTypeUndefined;
@@ -110,6 +129,10 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
struct bNode *node,
const bool mute);
+static FieldInferencingInterface *node_field_inferencing_interface_copy(
+ const FieldInferencingInterface &field_inferencing_interface);
+static void node_field_inferencing_interface_free(
+ const FieldInferencingInterface *field_inferencing_interface);
static void ntree_init_data(ID *id)
{
@@ -220,6 +243,11 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* node tree will generate its own interface type */
ntree_dst->interface_type = nullptr;
+
+ if (ntree_src->field_inferencing_interface) {
+ ntree_dst->field_inferencing_interface = node_field_inferencing_interface_copy(
+ *ntree_src->field_inferencing_interface);
+ }
}
static void ntree_free_data(ID *id)
@@ -265,6 +293,8 @@ static void ntree_free_data(ID *id)
MEM_freeN(sock);
}
+ node_field_inferencing_interface_free(ntree->field_inferencing_interface);
+
/* free preview hash */
if (ntree->previews) {
BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free);
@@ -508,7 +538,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) &&
- ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
+ ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
else if ((ntree->type == NTREE_GEOMETRY) &&
@@ -647,6 +677,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
ntree->progress = nullptr;
ntree->execdata = nullptr;
+ ntree->field_inferencing_interface = nullptr;
+
BLO_read_data_address(reader, &ntree->adt);
BKE_animdata_blend_read_data(reader, ntree->adt);
@@ -682,6 +714,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
switch (node->type) {
case SH_NODE_CURVE_VEC:
case SH_NODE_CURVE_RGB:
+ case SH_NODE_CURVE_FLOAT:
case CMP_NODE_TIME:
case CMP_NODE_CURVE_VEC:
case CMP_NODE_CURVE_RGB:
@@ -792,6 +825,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
/* TODO: should be dealt by new generic cache handling of IDs... */
ntree->previews = nullptr;
+ if (ntree->type == NTREE_GEOMETRY) {
+ /* Update field referencing for the geometry nodes modifier. */
+ ntree->update |= NTREE_UPDATE_FIELD_INFERENCING;
+ }
+
/* type verification is in lib-link */
}
@@ -1092,7 +1130,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr);
/* XXX Warning: context can be nullptr in case nodes are added in do_versions.
- * Delayed init is not supported for nodes with context-based initfunc_api atm.
+ * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment.
*/
BLI_assert(C != nullptr);
ntype->initfunc_api(C, &ptr);
@@ -4425,7 +4463,510 @@ void ntreeUpdateAllNew(Main *main)
FOREACH_NODETREE_END;
}
-void ntreeUpdateAllUsers(Main *main, ID *id)
+static FieldInferencingInterface *node_field_inferencing_interface_copy(
+ const FieldInferencingInterface &field_inferencing_interface)
+{
+ return new FieldInferencingInterface(field_inferencing_interface);
+}
+
+static void node_field_inferencing_interface_free(
+ const FieldInferencingInterface *field_inferencing_interface)
+{
+ delete field_inferencing_interface;
+}
+
+namespace blender::bke::node_field_inferencing {
+
+static bool is_field_socket_type(eNodeSocketDatatype type)
+{
+ return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
+}
+
+static bool is_field_socket_type(const SocketRef &socket)
+{
+ return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type);
+}
+
+static bool update_field_inferencing(bNodeTree &btree);
+
+static InputSocketFieldType get_interface_input_field_type(const NodeRef &node,
+ const InputSocketRef &socket)
+{
+ if (!is_field_socket_type(socket)) {
+ return InputSocketFieldType::None;
+ }
+ if (node.is_reroute_node()) {
+ return InputSocketFieldType::IsSupported;
+ }
+ if (node.is_group_output_node()) {
+ /* Outputs always support fields when the data type is correct. */
+ return InputSocketFieldType::IsSupported;
+ }
+ if (node.is_undefined()) {
+ return InputSocketFieldType::None;
+ }
+
+ const NodeDeclaration *node_decl = node.declaration();
+
+ /* Node declarations should be implemented for nodes involved here. */
+ BLI_assert(node_decl != nullptr);
+
+ /* Get the field type from the declaration. */
+ const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()];
+ const InputSocketFieldType field_type = socket_decl.input_field_type();
+ if (field_type == InputSocketFieldType::Implicit) {
+ return field_type;
+ }
+ if (node_decl->is_function_node()) {
+ /* In a function node, every socket supports fields. */
+ return InputSocketFieldType::IsSupported;
+ }
+ return field_type;
+}
+
+static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node,
+ const OutputSocketRef &socket)
+{
+ if (!is_field_socket_type(socket)) {
+ /* Non-field sockets always output data. */
+ return OutputFieldDependency::ForDataSource();
+ }
+ if (node.is_reroute_node()) {
+ /* The reroute just forwards what is passed in. */
+ return OutputFieldDependency::ForDependentField();
+ }
+ if (node.is_group_input_node()) {
+ /* Input nodes get special treatment in #determine_group_input_states. */
+ return OutputFieldDependency::ForDependentField();
+ }
+ if (node.is_undefined()) {
+ return OutputFieldDependency::ForDataSource();
+ }
+
+ const NodeDeclaration *node_decl = node.declaration();
+
+ /* Node declarations should be implemented for nodes involved here. */
+ BLI_assert(node_decl != nullptr);
+
+ if (node_decl->is_function_node()) {
+ /* In a generic function node, all outputs depend on all inputs. */
+ return OutputFieldDependency::ForDependentField();
+ }
+
+ /* Use the socket declaration. */
+ const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()];
+ return socket_decl.output_field_dependency();
+}
+
+/**
+ * Retrieves information about how the node interacts with fields.
+ * In the future, this information can be stored in the node declaration. This would allow this
+ * function to return a reference, making it more efficient.
+ */
+static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node)
+{
+ /* Node groups already reference all required information, so just return that. */
+ if (node.is_group_node()) {
+ bNodeTree *group = (bNodeTree *)node.bnode()->id;
+ if (group == nullptr) {
+ return FieldInferencingInterface();
+ }
+ if (group->field_inferencing_interface == nullptr) {
+ /* Update group recursively. */
+ update_field_inferencing(*group);
+ }
+ return *group->field_inferencing_interface;
+ }
+
+ FieldInferencingInterface inferencing_interface;
+ for (const InputSocketRef *input_socket : node.inputs()) {
+ inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket));
+ }
+
+ for (const OutputSocketRef *output_socket : node.outputs()) {
+ inferencing_interface.outputs.append(
+ get_interface_output_field_dependency(node, *output_socket));
+ }
+ return inferencing_interface;
+}
+
+/**
+ * This struct contains information for every socket. The values are propagated through the
+ * network.
+ */
+struct SocketFieldState {
+ /* This socket is currently a single value. It could become a field though. */
+ bool is_single = true;
+ /* This socket is required to be a single value. It must not be a field. */
+ bool requires_single = false;
+ /* This socket starts a new field. */
+ bool is_field_source = false;
+};
+
+static Vector<const InputSocketRef *> gather_input_socket_dependencies(
+ const OutputFieldDependency &field_dependency, const NodeRef &node)
+{
+ const OutputSocketFieldType type = field_dependency.field_type();
+ Vector<const InputSocketRef *> input_sockets;
+ switch (type) {
+ case OutputSocketFieldType::FieldSource:
+ case OutputSocketFieldType::None: {
+ break;
+ }
+ case OutputSocketFieldType::DependentField: {
+ /* This output depends on all inputs. */
+ input_sockets.extend(node.inputs());
+ break;
+ }
+ case OutputSocketFieldType::PartiallyDependent: {
+ /* This output depends only on a few inputs. */
+ for (const int i : field_dependency.linked_input_indices()) {
+ input_sockets.append(&node.input(i));
+ }
+ break;
+ }
+ }
+ return input_sockets;
+}
+
+/**
+ * Check what the group output socket depends on. Potentially traverses the node tree
+ * to figure out if it is always a field or if it depends on any group inputs.
+ */
+static OutputFieldDependency find_group_output_dependencies(
+ const InputSocketRef &group_output_socket,
+ const Span<SocketFieldState> field_state_by_socket_id)
+{
+ if (!is_field_socket_type(group_output_socket)) {
+ return OutputFieldDependency::ForDataSource();
+ }
+
+ /* Use a Set here instead of an array indexed by socket id, because we my only need to look at
+ * very few sockets. */
+ Set<const InputSocketRef *> handled_sockets;
+ Stack<const InputSocketRef *> sockets_to_check;
+
+ handled_sockets.add(&group_output_socket);
+ sockets_to_check.push(&group_output_socket);
+
+ /* Keeps track of group input indices that are (indirectly) connected to the output. */
+ Vector<int> linked_input_indices;
+
+ while (!sockets_to_check.is_empty()) {
+ const InputSocketRef *input_socket = sockets_to_check.pop();
+
+ for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
+ const NodeRef &origin_node = origin_socket->node();
+ const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()];
+
+ if (origin_state.is_field_source) {
+ if (origin_node.is_group_input_node()) {
+ /* Found a group input that the group output depends on. */
+ linked_input_indices.append_non_duplicates(origin_socket->index());
+ }
+ else {
+ /* Found a field source that is not the group input. So the output is always a field. */
+ return OutputFieldDependency::ForFieldSource();
+ }
+ }
+ else if (!origin_state.is_single) {
+ const FieldInferencingInterface inferencing_interface =
+ get_node_field_inferencing_interface(origin_node);
+ const OutputFieldDependency &field_dependency =
+ inferencing_interface.outputs[origin_socket->index()];
+
+ /* Propagate search further to the left. */
+ for (const InputSocketRef *origin_input_socket :
+ gather_input_socket_dependencies(field_dependency, origin_node)) {
+ if (!field_state_by_socket_id[origin_input_socket->id()].is_single) {
+ if (handled_sockets.add(origin_input_socket)) {
+ sockets_to_check.push(origin_input_socket);
+ }
+ }
+ }
+ }
+ }
+ }
+ return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices));
+}
+
+static void propagate_data_requirements_from_right_to_left(
+ const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
+{
+ const Vector<const NodeRef *> sorted_nodes = tree.toposort(
+ NodeTreeRef::ToposortDirection::RightToLeft);
+
+ for (const NodeRef *node : sorted_nodes) {
+ const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface(
+ *node);
+
+ for (const OutputSocketRef *output_socket : node->outputs()) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+
+ const OutputFieldDependency &field_dependency =
+ inferencing_interface.outputs[output_socket->index()];
+
+ if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) {
+ continue;
+ }
+ if (field_dependency.field_type() == OutputSocketFieldType::None) {
+ state.requires_single = true;
+ continue;
+ }
+
+ /* The output is required to be a single value when it is connected to any input that does
+ * not support fields. */
+ for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) {
+ state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single;
+ }
+
+ if (state.requires_single) {
+ bool any_input_is_field_implicitly = false;
+ const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies(
+ field_dependency, *node);
+ for (const InputSocketRef *input_socket : connected_inputs) {
+ if (inferencing_interface.inputs[input_socket->index()] ==
+ InputSocketFieldType::Implicit) {
+ if (!input_socket->is_logically_linked()) {
+ any_input_is_field_implicitly = true;
+ break;
+ }
+ }
+ }
+ if (any_input_is_field_implicitly) {
+ /* This output isn't a single value actually. */
+ state.requires_single = false;
+ }
+ else {
+ /* If the output is required to be a single value, the connected inputs in the same node
+ * must not be fields as well. */
+ for (const InputSocketRef *input_socket : connected_inputs) {
+ field_state_by_socket_id[input_socket->id()].requires_single = true;
+ }
+ }
+ }
+ }
+
+ /* Some inputs do not require fields independent of what the outputs are connected to. */
+ for (const InputSocketRef *input_socket : node->inputs()) {
+ SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
+ if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
+ state.requires_single = true;
+ }
+ }
+ }
+}
+
+static void determine_group_input_states(
+ const NodeTreeRef &tree,
+ FieldInferencingInterface &new_inferencing_interface,
+ const MutableSpan<SocketFieldState> field_state_by_socket_id)
+{
+ {
+ /* Non-field inputs never support fields. */
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) {
+ if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) {
+ new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
+ }
+ }
+ }
+ /* Check if group inputs are required to be single values, because they are (indirectly)
+ * connected to some socket that does not support fields. */
+ for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
+ for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ if (state.requires_single) {
+ new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None;
+ }
+ }
+ }
+ /* If an input does not support fields, this should be reflected in all Group Input nodes. */
+ for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
+ for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] !=
+ InputSocketFieldType::None;
+ if (supports_field) {
+ state.is_single = false;
+ state.is_field_source = true;
+ }
+ else {
+ state.requires_single = true;
+ }
+ }
+ SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()];
+ dummy_socket_state.requires_single = true;
+ }
+}
+
+static void propagate_field_status_from_left_to_right(
+ const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
+{
+ Vector<const NodeRef *> sorted_nodes = tree.toposort(
+ NodeTreeRef::ToposortDirection::LeftToRight);
+
+ for (const NodeRef *node : sorted_nodes) {
+ if (node->is_group_input_node()) {
+ continue;
+ }
+
+ const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface(
+ *node);
+
+ /* Update field state of input sockets, also taking into account linked origin sockets. */
+ for (const InputSocketRef *input_socket : node->inputs()) {
+ SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
+ if (state.requires_single) {
+ state.is_single = true;
+ continue;
+ }
+ state.is_single = true;
+ if (input_socket->logically_linked_sockets().is_empty()) {
+ if (inferencing_interface.inputs[input_socket->index()] ==
+ InputSocketFieldType::Implicit) {
+ state.is_single = false;
+ }
+ }
+ else {
+ for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
+ if (!field_state_by_socket_id[origin_socket->id()].is_single) {
+ state.is_single = false;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Update field state of output sockets, also taking into account input sockets. */
+ for (const OutputSocketRef *output_socket : node->outputs()) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ const OutputFieldDependency &field_dependency =
+ inferencing_interface.outputs[output_socket->index()];
+
+ switch (field_dependency.field_type()) {
+ case OutputSocketFieldType::None: {
+ state.is_single = true;
+ break;
+ }
+ case OutputSocketFieldType::FieldSource: {
+ state.is_single = false;
+ state.is_field_source = true;
+ break;
+ }
+ case OutputSocketFieldType::PartiallyDependent:
+ case OutputSocketFieldType::DependentField: {
+ for (const InputSocketRef *input_socket :
+ gather_input_socket_dependencies(field_dependency, *node)) {
+ if (!field_state_by_socket_id[input_socket->id()].is_single) {
+ state.is_single = false;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void determine_group_output_states(const NodeTreeRef &tree,
+ FieldInferencingInterface &new_inferencing_interface,
+ const Span<SocketFieldState> field_state_by_socket_id)
+{
+ for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) {
+ /* Ignore inactive group output nodes. */
+ if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) {
+ continue;
+ }
+ /* Determine dependencies of all group outputs. */
+ for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) {
+ OutputFieldDependency field_dependency = find_group_output_dependencies(
+ *group_output_socket, field_state_by_socket_id);
+ new_inferencing_interface.outputs[group_output_socket->index()] = std::move(
+ field_dependency);
+ }
+ break;
+ }
+}
+
+static void update_socket_shapes(const NodeTreeRef &tree,
+ const Span<SocketFieldState> field_state_by_socket_id)
+{
+ const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
+ const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT;
+ const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
+
+ for (const InputSocketRef *socket : tree.input_sockets()) {
+ bNodeSocket *bsocket = socket->bsocket();
+ const SocketFieldState &state = field_state_by_socket_id[socket->id()];
+ if (state.requires_single) {
+ bsocket->display_shape = requires_data_shape;
+ }
+ else if (state.is_single) {
+ bsocket->display_shape = data_but_can_be_field_shape;
+ }
+ else {
+ bsocket->display_shape = is_field_shape;
+ }
+ }
+ for (const OutputSocketRef *socket : tree.output_sockets()) {
+ bNodeSocket *bsocket = socket->bsocket();
+ const SocketFieldState &state = field_state_by_socket_id[socket->id()];
+ if (state.requires_single) {
+ bsocket->display_shape = requires_data_shape;
+ }
+ else if (state.is_single) {
+ bsocket->display_shape = data_but_can_be_field_shape;
+ }
+ else {
+ bsocket->display_shape = is_field_shape;
+ }
+ }
+}
+
+static bool update_field_inferencing(bNodeTree &btree)
+{
+ using namespace blender::nodes;
+ if (btree.type != NTREE_GEOMETRY) {
+ return false;
+ }
+
+ /* Create new inferencing interface for this node group. */
+ FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface();
+ new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs),
+ InputSocketFieldType::IsSupported);
+ new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs),
+ OutputFieldDependency::ForDataSource());
+
+ /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */
+ const NodeTreeRef tree{&btree};
+
+ /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
+ Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size());
+
+ propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id);
+ determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id);
+ propagate_field_status_from_left_to_right(tree, field_state_by_socket_id);
+ determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id);
+ update_socket_shapes(tree, field_state_by_socket_id);
+
+ /* Update the previous group interface. */
+ const bool group_interface_changed = btree.field_inferencing_interface == nullptr ||
+ *btree.field_inferencing_interface !=
+ *new_inferencing_interface;
+ delete btree.field_inferencing_interface;
+ btree.field_inferencing_interface = new_inferencing_interface;
+
+ return group_interface_changed;
+}
+
+} // namespace blender::bke::node_field_inferencing
+
+/**
+ * \param tree_update_flag: #eNodeTreeUpdate enum.
+ */
+void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag)
{
if (id == nullptr) {
return;
@@ -4446,7 +4987,8 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
}
if (need_update) {
- ntreeUpdateTree(nullptr, ntree);
+ ntree->update |= tree_update_flag;
+ ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree);
}
}
FOREACH_NODETREE_END;
@@ -4508,8 +5050,18 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
ntreeInterfaceTypeUpdate(ntree);
}
+ int tree_user_update_flag = 0;
+
+ if (ntree->update & NTREE_UPDATE) {
+ /* If the field interface of this node tree has changed, all node trees using
+ * this group will need to recalculate their interface as well. */
+ if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) {
+ tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING;
+ }
+ }
+
if (bmain) {
- ntreeUpdateAllUsers(bmain, &ntree->id);
+ ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -5023,6 +5575,7 @@ static void registerShaderNodes()
register_node_type_sh_shadertorgb();
register_node_type_sh_normal();
register_node_type_sh_mapping();
+ register_node_type_sh_curve_float();
register_node_type_sh_curve_vec();
register_node_type_sh_curve_rgb();
register_node_type_sh_map_range();
@@ -5156,10 +5709,17 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
+ register_node_type_geo_legacy_curve_set_handles();
+ register_node_type_geo_legacy_attribute_proximity();
+ register_node_type_geo_legacy_attribute_randomize();
register_node_type_geo_legacy_material_assign();
register_node_type_geo_legacy_select_by_material();
+ register_node_type_geo_legacy_curve_spline_type();
+ register_node_type_geo_legacy_curve_reverse();
+ register_node_type_geo_legacy_curve_subdivide();
register_node_type_geo_align_rotation_to_vector();
+ register_node_type_geo_attribute_capture();
register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
@@ -5167,12 +5727,9 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
- register_node_type_geo_attribute_capture();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
- register_node_type_geo_attribute_proximity();
- register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_remove();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_statistic();
@@ -5183,9 +5740,9 @@ static void registerGeometryNodes()
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
register_node_type_geo_convex_hull();
- register_node_type_geo_curve_sample();
register_node_type_geo_curve_endpoints();
register_node_type_geo_curve_fill();
+ register_node_type_geo_curve_fillet();
register_node_type_geo_curve_length();
register_node_type_geo_curve_parameter();
register_node_type_geo_curve_primitive_bezier_segment();
@@ -5197,24 +5754,28 @@ static void registerGeometryNodes()
register_node_type_geo_curve_primitive_star();
register_node_type_geo_curve_resample();
register_node_type_geo_curve_reverse();
+ register_node_type_geo_curve_sample();
register_node_type_geo_curve_set_handles();
register_node_type_geo_curve_spline_type();
register_node_type_geo_curve_subdivide();
- register_node_type_geo_curve_fillet();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_to_points();
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
+ register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_edge_split();
register_node_type_geo_input_index();
register_node_type_geo_input_material();
register_node_type_geo_input_normal();
register_node_type_geo_input_position();
register_node_type_geo_input_tangent();
+ register_node_type_geo_input_spline_length();
+ register_node_type_geo_instance_on_points();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
register_node_type_geo_material_assign();
register_node_type_geo_material_replace();
+ register_node_type_geo_material_selection();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
@@ -5225,6 +5786,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
+ register_node_type_geo_mesh_to_points();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -5232,15 +5794,17 @@ static void registerGeometryNodes()
register_node_type_geo_point_scale();
register_node_type_geo_point_separate();
register_node_type_geo_point_translate();
+ register_node_type_geo_points_to_vertices();
register_node_type_geo_points_to_volume();
+ register_node_type_geo_proximity();
register_node_type_geo_raycast();
register_node_type_geo_realize_instances();
register_node_type_geo_sample_texture();
register_node_type_geo_select_by_handle_type();
- register_node_type_geo_string_join();
- register_node_type_geo_material_selection();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
+ register_node_type_geo_string_join();
+ register_node_type_geo_string_to_curves();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
@@ -5251,12 +5815,16 @@ static void registerGeometryNodes()
static void registerFunctionNodes()
{
+ register_node_type_fn_legacy_random_float();
+
register_node_type_fn_boolean_math();
register_node_type_fn_float_compare();
register_node_type_fn_float_to_int();
+ register_node_type_fn_input_special_characters();
register_node_type_fn_input_string();
register_node_type_fn_input_vector();
- register_node_type_fn_random_float();
+ register_node_type_fn_random_value();
+ register_node_type_fn_rotate_euler();
register_node_type_fn_string_length();
register_node_type_fn_string_substring();
register_node_type_fn_value_to_string();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 465ec9dc665..ec39c5b45c4 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2634,10 +2634,16 @@ Object *BKE_object_duplicate(Main *bmain,
{
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0;
+ int copy_flags = LIB_ID_COPY_DEFAULT;
if (!is_subprocess) {
BKE_main_id_newptr_and_tag_clear(bmain);
}
+ else {
+ /* In case copying object is a sub-process of collection (or scene) copying, do not try to
+ * re-assign RB objects to existing RBW collections. */
+ copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING;
+ }
if (is_root_id) {
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
@@ -2649,24 +2655,22 @@ Object *BKE_object_duplicate(Main *bmain,
Material ***matarar;
- Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag);
+ Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag, copy_flags);
/* 0 == full linked. */
if (dupflag == 0) {
return obn;
}
- BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag);
-
if (dupflag & USER_DUP_MAT) {
for (int i = 0; i < obn->totcol; i++) {
- BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag);
+ BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag, copy_flags);
}
}
if (dupflag & USER_DUP_PSYS) {
ParticleSystem *psys;
for (psys = obn->particlesystem.first; psys; psys = psys->next) {
- BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag);
+ BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag, copy_flags);
}
}
@@ -2677,77 +2681,77 @@ Object *BKE_object_duplicate(Main *bmain,
switch (obn->type) {
case OB_MESH:
if (dupflag & USER_DUP_MESH) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_CURVE:
if (dupflag & USER_DUP_CURVE) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_SURF:
if (dupflag & USER_DUP_SURF) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_FONT:
if (dupflag & USER_DUP_FONT) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_MBALL:
if (dupflag & USER_DUP_MBALL) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_LAMP:
if (dupflag & USER_DUP_LAMP) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_ARMATURE:
if (dupflag & USER_DUP_ARM) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_LATTICE:
if (dupflag != 0) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_CAMERA:
if (dupflag != 0) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_LIGHTPROBE:
if (dupflag & USER_DUP_LIGHTPROBE) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_SPEAKER:
if (dupflag != 0) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_GPENCIL:
if (dupflag & USER_DUP_GPENCIL) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_HAIR:
if (dupflag & USER_DUP_HAIR) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_POINTCLOUD:
if (dupflag & USER_DUP_POINTCLOUD) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
case OB_VOLUME:
if (dupflag & USER_DUP_VOLUME) {
- id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
}
@@ -2758,7 +2762,7 @@ Object *BKE_object_duplicate(Main *bmain,
matarar = BKE_object_material_array_p(obn);
if (matarar) {
for (int i = 0; i < obn->totcol; i++) {
- BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag);
+ BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag, copy_flags);
}
}
}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 50b0fb1c9f5..7b2a1af7086 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -4619,11 +4619,11 @@ void psys_get_particle_on_path(ParticleSimulationData *sim,
pind.cache = cached ? psys->pointcache : NULL;
pind.epoint = NULL;
pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE);
- /* pind.dm disabled in editmode means we don't get effectors taken into
- * account when subdividing for instance */
+ /* `pind.dm` disabled in edit-mode means we don't get effectors taken into
+ * account when subdividing for instance. */
pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ?
NULL :
- psys->hair_out_mesh; /* XXX Sybren EEK */
+ psys->hair_out_mesh; /* XXX(@sybren) EEK. */
init_particle_interpolation(sim->ob, psys, pa, &pind);
do_particle_interpolation(psys, p, pa, t, &pind, state);
diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c
index 8dcf6de164a..0b8e8d7c311 100644
--- a/source/blender/blenkernel/intern/preferences.c
+++ b/source/blender/blenkernel/intern/preferences.c
@@ -61,6 +61,15 @@ bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef,
return library;
}
+/**
+ * Unlink and free a library preference member.
+ * \note Free's \a library itself.
+ */
+void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library)
+{
+ BLI_freelinkN(&userdef->asset_libraries, library);
+}
+
void BKE_preferences_asset_library_name_set(UserDef *userdef,
bUserAssetLibrary *library,
const char *name)
@@ -74,15 +83,6 @@ void BKE_preferences_asset_library_name_set(UserDef *userdef,
sizeof(library->name));
}
-/**
- * Unlink and free a library preference member.
- * \note Free's \a library itself.
- */
-void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library)
-{
- BLI_freelinkN(&userdef->asset_libraries, library);
-}
-
bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(const UserDef *userdef, int index)
{
return BLI_findlink(&userdef->asset_libraries, index);
@@ -94,6 +94,17 @@ bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(const UserDef *u
return BLI_findstring(&userdef->asset_libraries, name, offsetof(bUserAssetLibrary, name));
}
+bUserAssetLibrary *BKE_preferences_asset_library_containing_path(const UserDef *userdef,
+ const char *path)
+{
+ LISTBASE_FOREACH (bUserAssetLibrary *, asset_lib_pref, &userdef->asset_libraries) {
+ if (BLI_path_contains(asset_lib_pref->path, path)) {
+ return asset_lib_pref;
+ }
+ }
+ return NULL;
+}
+
int BKE_preferences_asset_library_get_index(const UserDef *userdef,
const bUserAssetLibrary *library)
{
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 328c54fc21b..1ea659b2d41 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -302,7 +302,7 @@ void BKE_rigidbody_object_copy(Main *bmain, Object *ob_dst, const Object *ob_src
ob_dst->rigidbody_object = rigidbody_copy_object(ob_src, flag);
ob_dst->rigidbody_constraint = rigidbody_copy_constraint(ob_src, flag);
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if ((flag & (LIB_ID_CREATE_NO_MAIN | LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING)) != 0) {
return;
}
@@ -1211,8 +1211,8 @@ RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw, const int flag)
id_us_plus((ID *)rbw_copy->constraints);
}
- if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
- /* This is a regular copy, and not a CoW copy for depsgraph evaluation */
+ if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0) {
+ /* This is a regular copy, and not a CoW copy for depsgraph evaluation. */
rbw_copy->shared = MEM_callocN(sizeof(*rbw_copy->shared), "RigidBodyWorld_Shared");
BKE_ptcache_copy_list(&rbw_copy->shared->ptcaches, &rbw->shared->ptcaches, LIB_ID_COPY_CACHES);
rbw_copy->shared->pointcache = rbw_copy->shared->ptcaches.first;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 6b5c94a2786..a9a8cd93b1d 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -63,6 +63,8 @@
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLO_readfile.h"
+
#include "BLT_translation.h"
#include "BKE_action.h"
@@ -993,8 +995,13 @@ static void link_recurs_seq(BlendDataReader *reader, ListBase *lb)
{
BLO_read_list(reader, lb);
- LISTBASE_FOREACH (Sequence *, seq, lb) {
- if (seq->seqbase.first) {
+ LISTBASE_FOREACH_MUTABLE (Sequence *, seq, lb) {
+ /* Sanity check. */
+ if (!SEQ_valid_strip_channel(seq)) {
+ BLI_freelinkN(lb, seq);
+ BLO_read_data_reports(reader)->count.vse_strips_skipped++;
+ }
+ else if (seq->seqbase.first) {
link_recurs_seq(reader, &seq->seqbase);
}
}
@@ -1794,6 +1801,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
/* Scene duplication is always root of duplication currently. */
const bool is_subprocess = false;
const bool is_root_id = true;
+ const int copy_flags = LIB_ID_COPY_DEFAULT;
if (!is_subprocess) {
BKE_main_id_newptr_and_tag_clear(bmain);
@@ -1809,21 +1817,40 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
/* Copy Freestyle LineStyle datablocks. */
LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) {
LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) {
- BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags);
+ BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags, copy_flags);
}
}
/* Full copy of world (included animations) */
- BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags);
+ BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags, copy_flags);
/* Full copy of GreasePencil. */
- BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags);
+ BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags, copy_flags);
/* Deep-duplicate collections and objects (using preferences' settings for which sub-data to
* duplicate along the object itself). */
BKE_collection_duplicate(
bmain, NULL, sce_copy->master_collection, duplicate_flags, LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ /* Rigid body world collections may not be instantiated as scene's collections, ensure they
+ * also get properly duplicated. */
+ if (sce_copy->rigidbody_world != NULL) {
+ if (sce_copy->rigidbody_world->group != NULL) {
+ BKE_collection_duplicate(bmain,
+ NULL,
+ sce_copy->rigidbody_world->group,
+ duplicate_flags,
+ LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ }
+ if (sce_copy->rigidbody_world->constraints != NULL) {
+ BKE_collection_duplicate(bmain,
+ NULL,
+ sce_copy->rigidbody_world->constraints,
+ duplicate_flags,
+ LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ }
+ }
+
if (!is_subprocess) {
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */
BKE_libblock_relink_to_newid(&sce_copy->id);
@@ -2465,7 +2492,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
// DEG_debug_graph_relations_validate(depsgraph, bmain, scene);
/* Flush editing data if needed. */
prepare_mesh_for_viewport_render(bmain, view_layer);
- /* Update all objects: drivers, matrices, displists, etc. flags set
+ /* Update all objects: drivers, matrices, #DispList, etc. flags set
* by depsgraph or manual, no layer check here, gets correct flushed. */
DEG_evaluate_on_refresh(depsgraph);
/* Update sound system. */
@@ -2541,7 +2568,7 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle
BKE_image_editors_update_frame(bmain, scene->r.cfra);
BKE_sound_set_cfra(scene->r.cfra);
DEG_graph_relations_update(depsgraph);
- /* Update all objects: drivers, matrices, displists, etc. flags set
+ /* Update all objects: drivers, matrices, #DispList, etc. flags set
* by depgraph or manual, no layer check here, gets correct flushed.
*
* NOTE: Only update for new frame on first iteration. Second iteration is for ensuring user
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index fbc781f5eb9..b7eb9d31b23 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -2295,7 +2295,7 @@ static void softbody_calc_forces(
sb_sfesf_threads_run(depsgraph, scene, ob, timenow, sb->totspring, NULL);
}
- /* after spring scan because it uses Effoctors too */
+ /* After spring scan because it uses effectors too. */
ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights, false);
if (do_deflector) {
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index b36d7a21669..f719a1cfda2 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -289,6 +289,56 @@ void BezierSpline::transform(const blender::float4x4 &matrix)
this->mark_cache_invalid();
}
+static void set_handle_position(const float3 &position,
+ const BezierSpline::HandleType type,
+ const BezierSpline::HandleType type_other,
+ const float3 &new_value,
+ float3 &handle,
+ float3 &handle_other)
+{
+ /* Don't bother when the handle positions are calculated automatically anyway. */
+ if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) {
+ return;
+ }
+
+ handle = new_value;
+ if (type_other == BezierSpline::HandleType::Align) {
+ /* Keep track of the old length of the opposite handle. */
+ const float length = float3::distance(handle_other, position);
+ /* Set the other handle to directly opposite from the current handle. */
+ const float3 dir = (handle - position).normalized();
+ handle_other = position - dir * length;
+ }
+}
+
+/**
+ * Set positions for the right handle of the control point, ensuring that
+ * aligned handles stay aligned. Has no effect for auto and vector type handles.
+ */
+void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
+{
+ set_handle_position(positions_[index],
+ handle_types_right_[index],
+ handle_types_left_[index],
+ value,
+ handle_positions_right_[index],
+ handle_positions_left_[index]);
+}
+
+/**
+ * Set positions for the left handle of the control point, ensuring that
+ * aligned handles stay aligned. Has no effect for auto and vector type handles.
+ */
+void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
+{
+ set_handle_position(positions_[index],
+ handle_types_left_[index],
+ handle_types_right_[index],
+ value,
+ handle_positions_left_[index],
+ handle_positions_right_[index]);
+}
+
bool BezierSpline::point_is_sharp(const int index) const
{
return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c
index e9cd0b70019..01bccab1bbd 100644
--- a/source/blender/blenkernel/intern/subdiv_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_mesh.c
@@ -50,7 +50,7 @@ typedef struct SubdivMeshContext {
const Mesh *coarse_mesh;
Subdiv *subdiv;
Mesh *subdiv_mesh;
- /* Cached custom data arrays for fastter access. */
+ /* Cached custom data arrays for faster access. */
int *vert_origindex;
int *edge_origindex;
int *loop_origindex;
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index 068d048fd08..3cdb8e927a6 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -3014,6 +3014,61 @@ static int channels_average_error_sort(const void *a, const void *b)
return 0;
}
+static int compare_firstlast_putting_undefined_first(
+ bool inverse, bool a_markerless, int a_value, bool b_markerless, int b_value)
+{
+ if (a_markerless && b_markerless) {
+ /* Neither channel has not-disabled markers, return whatever. */
+ return 0;
+ }
+ if (a_markerless) {
+ /* Put the markerless channel first. */
+ return 0;
+ }
+ if (b_markerless) {
+ /* Put the markerless channel first. */
+ return 1;
+ }
+
+ /* Both channels have markers. */
+
+ if (inverse) {
+ if (a_value < b_value) {
+ return 1;
+ }
+ return 0;
+ }
+
+ if (a_value > b_value) {
+ return 1;
+ }
+ return 0;
+}
+
+static int channels_start_sort(const void *a, const void *b)
+{
+ const MovieTrackingDopesheetChannel *channel_a = a;
+ const MovieTrackingDopesheetChannel *channel_b = b;
+
+ return compare_firstlast_putting_undefined_first(false,
+ channel_a->tot_segment == 0,
+ channel_a->first_not_disabled_marker_framenr,
+ channel_b->tot_segment == 0,
+ channel_b->first_not_disabled_marker_framenr);
+}
+
+static int channels_end_sort(const void *a, const void *b)
+{
+ const MovieTrackingDopesheetChannel *channel_a = a;
+ const MovieTrackingDopesheetChannel *channel_b = b;
+
+ return compare_firstlast_putting_undefined_first(false,
+ channel_a->tot_segment == 0,
+ channel_a->last_not_disabled_marker_framenr,
+ channel_b->tot_segment == 0,
+ channel_b->last_not_disabled_marker_framenr);
+}
+
static int channels_alpha_inverse_sort(const void *a, const void *b)
{
if (channels_alpha_sort(a, b)) {
@@ -3053,22 +3108,51 @@ static int channels_average_error_inverse_sort(const void *a, const void *b)
return 0;
}
+static int channels_start_inverse_sort(const void *a, const void *b)
+{
+ const MovieTrackingDopesheetChannel *channel_a = a;
+ const MovieTrackingDopesheetChannel *channel_b = b;
+
+ return compare_firstlast_putting_undefined_first(true,
+ channel_a->tot_segment == 0,
+ channel_a->first_not_disabled_marker_framenr,
+ channel_b->tot_segment == 0,
+ channel_b->first_not_disabled_marker_framenr);
+}
+
+static int channels_end_inverse_sort(const void *a, const void *b)
+{
+ const MovieTrackingDopesheetChannel *channel_a = a;
+ const MovieTrackingDopesheetChannel *channel_b = b;
+
+ return compare_firstlast_putting_undefined_first(true,
+ channel_a->tot_segment == 0,
+ channel_a->last_not_disabled_marker_framenr,
+ channel_b->tot_segment == 0,
+ channel_b->last_not_disabled_marker_framenr);
+}
+
/* Calculate frames segments at which track is tracked continuously. */
static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChannel *channel)
{
MovieTrackingTrack *track = channel->track;
int i, segment;
+ bool first_not_disabled_marker_framenr_set;
channel->tot_segment = 0;
channel->max_segment = 0;
channel->total_frames = 0;
+ channel->first_not_disabled_marker_framenr = 0;
+ channel->last_not_disabled_marker_framenr = 0;
+
/* TODO(sergey): looks a bit code-duplicated, need to look into
* logic de-duplication here.
*/
/* count */
i = 0;
+ first_not_disabled_marker_framenr_set = false;
while (i < track->markersnr) {
MovieTrackingMarker *marker = &track->markers[i];
@@ -3086,6 +3170,12 @@ static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChan
break;
}
+ if (!first_not_disabled_marker_framenr_set) {
+ channel->first_not_disabled_marker_framenr = marker->framenr;
+ first_not_disabled_marker_framenr_set = true;
+ }
+ channel->last_not_disabled_marker_framenr = marker->framenr;
+
prev_fra = marker->framenr;
len++;
i++;
@@ -3203,6 +3293,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking,
else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) {
BLI_listbase_sort(&dopesheet->channels, channels_average_error_inverse_sort);
}
+ else if (sort_method == TRACKING_DOPE_SORT_START) {
+ BLI_listbase_sort(&dopesheet->channels, channels_start_inverse_sort);
+ }
+ else if (sort_method == TRACKING_DOPE_SORT_END) {
+ BLI_listbase_sort(&dopesheet->channels, channels_end_inverse_sort);
+ }
}
else {
if (sort_method == TRACKING_DOPE_SORT_NAME) {
@@ -3217,6 +3313,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking,
else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) {
BLI_listbase_sort(&dopesheet->channels, channels_average_error_sort);
}
+ else if (sort_method == TRACKING_DOPE_SORT_START) {
+ BLI_listbase_sort(&dopesheet->channels, channels_start_sort);
+ }
+ else if (sort_method == TRACKING_DOPE_SORT_END) {
+ BLI_listbase_sort(&dopesheet->channels, channels_end_sort);
+ }
}
}
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 347ce2caa34..14e61d53845 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,13 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_location(const float3 location)
+ {
+ float4x4 mat = float4x4::identity();
+ copy_v3_v3(mat.values[3], location);
+ return mat;
+ }
+
static float4x4 from_normalized_axis_data(const float3 location,
const float3 forward,
const float3 up)
diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh
index 760ff082d06..7e1655f7864 100644
--- a/source/blender/blenlib/BLI_noise.hh
+++ b/source/blender/blenlib/BLI_noise.hh
@@ -22,36 +22,66 @@
namespace blender::noise {
-/* Perlin noise in the range [-1, 1]. */
+/* --------------------------------------------------------------------
+ * Hash functions.
+
+ * Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh`
+ * these functions produce better randomness but are more expensive to compute.
+ */
+
+/* Hash integers to `uint32_t`. */
+uint32_t hash(uint32_t kx);
+uint32_t hash(uint32_t kx, uint32_t ky);
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz);
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw);
+
+/* Hash floats to `uint32_t`. */
+uint32_t hash_float(float kx);
+uint32_t hash_float(float2 k);
+uint32_t hash_float(float3 k);
+uint32_t hash_float(float4 k);
+
+/* Hash integers to `float` between 0 and 1. */
+float hash_to_float(uint32_t kx);
+float hash_to_float(uint32_t kx, uint32_t ky);
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz);
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw);
+
+/* Hash floats to `float` between 0 and 1. */
+float hash_float_to_float(float k);
+float hash_float_to_float(float2 k);
+float hash_float_to_float(float3 k);
+float hash_float_to_float(float4 k);
+/* --------------------------------------------------------------------
+ * Perlin noise.
+ */
+
+/* Perlin noise in the range [-1, 1]. */
float perlin_signed(float position);
float perlin_signed(float2 position);
float perlin_signed(float3 position);
float perlin_signed(float4 position);
/* Perlin noise in the range [0, 1]. */
-
float perlin(float position);
float perlin(float2 position);
float perlin(float3 position);
float perlin(float4 position);
/* Fractal perlin noise in the range [0, 1]. */
-
float perlin_fractal(float position, float octaves, float roughness);
float perlin_fractal(float2 position, float octaves, float roughness);
float perlin_fractal(float3 position, float octaves, float roughness);
float perlin_fractal(float4 position, float octaves, float roughness);
/* Positive distorted fractal perlin noise. */
-
float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion);
/* Positive distorted fractal perlin noise that outputs a float3. */
-
float3 perlin_float3_fractal_distorted(float position,
float octaves,
float roughness,
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index 2a56d11276a..e4774c58e84 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -55,6 +55,10 @@ bool BLI_path_name_at_index(const char *__restrict path,
int *__restrict r_offset,
int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+/** Return true only if #containee_path is contained in #container_path. */
+bool BLI_path_contains(const char *container_path,
+ const char *containee_path) ATTR_WARN_UNUSED_RESULT;
+
const char *BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
int BLI_path_slash_ensure(char *string) ATTR_NONNULL();
void BLI_path_slash_rstrip(char *string) ATTR_NONNULL();
diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh
index edffb148477..761e1ef834c 100644
--- a/source/blender/blenlib/BLI_resource_scope.hh
+++ b/source/blender/blenlib/BLI_resource_scope.hh
@@ -73,7 +73,6 @@ class ResourceScope : NonCopyable, NonMovable {
*/
template<typename T> T *add(std::unique_ptr<T> resource)
{
- BLI_assert(resource.get() != nullptr);
T *ptr = resource.release();
if (ptr == nullptr) {
return nullptr;
diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h
index 9b85f8e65bc..ed0d31b625f 100644
--- a/source/blender/blenlib/BLI_uuid.h
+++ b/source/blender/blenlib/BLI_uuid.h
@@ -68,9 +68,41 @@ bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL();
#ifdef __cplusplus
}
+# include <initializer_list>
# include <ostream>
/** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */
std::ostream &operator<<(std::ostream &stream, bUUID uuid);
+namespace blender {
+
+class bUUID : public ::bUUID {
+ public:
+ /**
+ * Default constructor, used with `bUUID value{};`, will initialize to the nil UUID.
+ */
+ bUUID() = default;
+
+ /** Initialize from the bUUID DNA struct. */
+ bUUID(const ::bUUID &struct_uuid);
+
+ /** Initialize from 11 integers, 5 for the regular fields and 6 for the `node` array. */
+ bUUID(std::initializer_list<uint32_t> field_values);
+
+ /** Initialize by parsing the string; undefined behavior when the string is invalid. */
+ explicit bUUID(const std::string &string_formatted_uuid);
+
+ uint64_t hash() const;
+}; // namespace blender
+
+bool operator==(bUUID uuid1, bUUID uuid2);
+bool operator!=(bUUID uuid1, bUUID uuid2);
+
+/**
+ * Lexicographic comparison of the UUIDs.
+ * Equivalent to string comparison on the formatted UUIDs. */
+bool operator<(bUUID uuid1, bUUID uuid2);
+
+} // namespace blender
+
#endif
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 532a29b8147..2ef4d1093a8 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -570,6 +570,7 @@ bool BLI_dir_create_recursive(const char *dirname)
* blah1/blah2 (without slash) */
BLI_strncpy(tmp, dirname, sizeof(tmp));
+ BLI_path_slash_native(tmp);
BLI_path_slash_rstrip(tmp);
/* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
@@ -760,7 +761,7 @@ static int recursive_operation(const char *startfrom,
# endif
if (is_dir) {
- /* recursively dig into a subfolder */
+ /* Recurse into sub-directories. */
ret = recursive_operation(
from_path, to_path, callback_dir_pre, callback_file, callback_dir_post);
}
diff --git a/source/blender/blenlib/intern/kdtree_impl.h b/source/blender/blenlib/intern/kdtree_impl.h
index 0c9de0aa128..0b47be1f7ea 100644
--- a/source/blender/blenlib/intern/kdtree_impl.h
+++ b/source/blender/blenlib/intern/kdtree_impl.h
@@ -190,7 +190,7 @@ static uint kdtree_balance(KDTreeNode *nodes, uint nodes_len, uint axis, const u
}
}
- /* set node and sort subnodes */
+ /* Set node and sort sub-nodes. */
node = &nodes[median];
node->d = axis;
axis = (axis + 1) % KD_DIMS;
diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h
index 680044f9ccb..71f7f0e29a8 100644
--- a/source/blender/blenlib/intern/list_sort_impl.h
+++ b/source/blender/blenlib/intern/list_sort_impl.h
@@ -202,7 +202,7 @@ BLI_INLINE list_node *sweep_up(struct SortInfo *si, list_node *list, unsigned in
}
/**
- * The 'ranks' array essentially captures the recursion stack of a mergesort.
+ * The 'ranks' array essentially captures the recursion stack of a merge-sort.
* The merge tree is built in a bottom-up manner. The control loop for
* updating the 'ranks' array is analogous to incrementing a binary integer,
* and the `O(n)` time for counting `upto` n translates to `O(n)` merges when
diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc
index c057c12e543..e80975f618c 100644
--- a/source/blender/blenlib/intern/noise.cc
+++ b/source/blender/blenlib/intern/noise.cc
@@ -109,7 +109,7 @@ BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c)
c -= hash_bit_rotate(b, 24);
}
-BLI_INLINE uint32_t hash(uint32_t kx)
+uint32_t hash(uint32_t kx)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (1 << 2) + 13;
@@ -120,7 +120,7 @@ BLI_INLINE uint32_t hash(uint32_t kx)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky)
+uint32_t hash(uint32_t kx, uint32_t ky)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (2 << 2) + 13;
@@ -132,7 +132,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (3 << 2) + 13;
@@ -145,7 +145,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (4 << 2) + 13;
@@ -161,59 +161,83 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
return c;
}
-/* Hashing a number of uint32_t into a float in the range [0, 1]. */
+BLI_INLINE uint32_t float_as_uint(float f)
+{
+ union {
+ uint32_t i;
+ float f;
+ } u;
+ u.f = f;
+ return u.i;
+}
-BLI_INLINE float hash_to_float(uint32_t kx)
+uint32_t hash_float(float kx)
{
- return static_cast<float>(hash(kx)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(kx));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky)
+uint32_t hash_float(float2 k)
{
- return static_cast<float>(hash(kx, ky)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
+uint32_t hash_float(float3 k)
{
- return static_cast<float>(hash(kx, ky, kz)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+uint32_t hash_float(float4 k)
{
- return static_cast<float>(hash(kx, ky, kz, kw)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
}
-/* Hashing a number of floats into a float in the range [0, 1]. */
+/* Hashing a number of uint32_t into a float in the range [0, 1]. */
-BLI_INLINE uint32_t float_as_uint(float f)
+BLI_INLINE float uint_to_float_01(uint32_t k)
{
- union {
- uint32_t i;
- float f;
- } u;
- u.f = f;
- return u.i;
+ return static_cast<float>(k) / static_cast<float>(0xFFFFFFFFu);
+}
+
+float hash_to_float(uint32_t kx)
+{
+ return uint_to_float_01(hash(kx));
+}
+
+float hash_to_float(uint32_t kx, uint32_t ky)
+{
+ return uint_to_float_01(hash(kx, ky));
}
-BLI_INLINE float hash_to_float(float k)
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
+{
+ return uint_to_float_01(hash(kx, ky, kz));
+}
+
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+{
+ return uint_to_float_01(hash(kx, ky, kz, kw));
+}
+
+/* Hashing a number of floats into a float in the range [0, 1]. */
+
+float hash_float_to_float(float k)
{
- return hash_to_float(float_as_uint(k));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float2 k)
+float hash_float_to_float(float2 k)
{
- return hash_to_float(float_as_uint(k.x), float_as_uint(k.y));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float3 k)
+float hash_float_to_float(float3 k)
{
- return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float4 k)
+float hash_float_to_float(float4 k)
{
- return hash_to_float(
- float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
+ return hash_to_float(hash_float(k));
}
/* ------------
@@ -565,28 +589,28 @@ float perlin_fractal(float4 position, float octaves, float roughness)
BLI_INLINE float random_float_offset(float seed)
{
- return 100.0f + hash_to_float(seed) * 100.0f;
+ return 100.0f + hash_float_to_float(seed) * 100.0f;
}
BLI_INLINE float2 random_float2_offset(float seed)
{
- return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f);
+ return float2(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f);
}
BLI_INLINE float3 random_float3_offset(float seed)
{
- return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f);
+ return float3(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f);
}
BLI_INLINE float4 random_float4_offset(float seed)
{
- return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f);
+ return float4(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 3.0f)) * 100.0f);
}
/* Perlin noises to be added to the position to distort other noises. */
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 4d0678035ba..066749f3a94 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -1935,6 +1935,39 @@ bool BLI_path_name_at_index(const char *__restrict path,
return false;
}
+bool BLI_path_contains(const char *container_path, const char *containee_path)
+{
+ char container_native[PATH_MAX];
+ char containee_native[PATH_MAX];
+
+ /* Keep space for a trailing slash. If the path is truncated by this, the containee path is
+ * longer than PATH_MAX and the result is ill-defined. */
+ BLI_strncpy(container_native, container_path, PATH_MAX - 1);
+ BLI_strncpy(containee_native, containee_path, PATH_MAX);
+
+ BLI_path_slash_native(container_native);
+ BLI_path_slash_native(containee_native);
+
+ BLI_path_normalize(NULL, container_native);
+ BLI_path_normalize(NULL, containee_native);
+
+#ifdef WIN32
+ BLI_str_tolower_ascii(container_native, PATH_MAX);
+ BLI_str_tolower_ascii(containee_native, PATH_MAX);
+#endif
+
+ if (STREQ(container_native, containee_native)) {
+ /* The paths are equal, they contain each other. */
+ return true;
+ }
+
+ /* Add a trailing slash to prevent same-prefix directories from matching.
+ * e.g. "/some/path" doesn't contain "/some/path_lib". */
+ BLI_path_slash_ensure(container_native);
+
+ return BLI_str_startswith(containee_native, container_native);
+}
+
/**
* Returns pointer to the leftmost path separator in string. Not actually used anywhere.
*/
diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c
index f0cf19bf508..8845167f536 100644
--- a/source/blender/blenlib/intern/scanfill.c
+++ b/source/blender/blenlib/intern/scanfill.c
@@ -1040,13 +1040,13 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const
}
/* CURRENT STATUS:
- * - eve->f :1 = available in edges
- * - eve->poly_nr :polynumber
- * - eve->edge_tot :amount of edges connected to vertex
- * - eve->tmp.v :store! original vertex number
+ * - `eve->f`: 1 = available in edges.
+ * - `eve->poly_nr`: poly-number.
+ * - `eve->edge_tot`: amount of edges connected to vertex.
+ * - `eve->tmp.v`: store! original vertex number.
*
- * - eed->f :1 = boundary edge (optionally set by caller)
- * - eed->poly_nr :poly number
+ * - `eed->f`: 1 = boundary edge (optionally set by caller).
+ * - `eed->poly_nr`: poly number.
*/
/* STEP 3: MAKE POLYFILL STRUCT */
diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc
index ae34bcb3d32..3c86238036c 100644
--- a/source/blender/blenlib/intern/uuid.cc
+++ b/source/blender/blenlib/intern/uuid.cc
@@ -18,13 +18,16 @@
* \ingroup bli
*/
+#include "BLI_assert.h"
#include "BLI_uuid.h"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <random>
+#include <sstream>
#include <string>
+#include <tuple>
/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */
static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly");
@@ -137,3 +140,72 @@ std::ostream &operator<<(std::ostream &stream, bUUID uuid)
stream << buffer;
return stream;
}
+
+namespace blender {
+
+bUUID::bUUID(const std::initializer_list<uint32_t> field_values)
+{
+ BLI_assert_msg(field_values.size() == 11, "bUUID requires 5 regular fields + 6 `node` values");
+
+ const auto *field_iter = field_values.begin();
+
+ this->time_low = *field_iter++;
+ this->time_mid = static_cast<uint16_t>(*field_iter++);
+ this->time_hi_and_version = static_cast<uint16_t>(*field_iter++);
+ this->clock_seq_hi_and_reserved = static_cast<uint8_t>(*field_iter++);
+ this->clock_seq_low = static_cast<uint8_t>(*field_iter++);
+
+ std::copy(field_iter, field_values.end(), this->node);
+}
+
+bUUID::bUUID(const std::string &string_formatted_uuid)
+{
+ const bool parsed_ok = BLI_uuid_parse_string(this, string_formatted_uuid.c_str());
+ if (!parsed_ok) {
+ std::stringstream ss;
+ ss << "invalid UUID string " << string_formatted_uuid;
+ throw std::runtime_error(ss.str());
+ }
+}
+
+bUUID::bUUID(const ::bUUID &struct_uuid)
+{
+ *(static_cast<::bUUID *>(this)) = struct_uuid;
+}
+
+uint64_t bUUID::hash() const
+{
+ /* Convert the struct into two 64-bit numbers, and XOR them to get the hash. */
+ const uint64_t *uuid_as_int64 = reinterpret_cast<const uint64_t *>(this);
+ return uuid_as_int64[0] ^ uuid_as_int64[1];
+}
+
+bool operator==(const bUUID uuid1, const bUUID uuid2)
+{
+ return BLI_uuid_equal(uuid1, uuid2);
+}
+
+bool operator!=(const bUUID uuid1, const bUUID uuid2)
+{
+ return !(uuid1 == uuid2);
+}
+
+bool operator<(const bUUID uuid1, const bUUID uuid2)
+{
+ auto simple_fields1 = std::tie(uuid1.time_low,
+ uuid1.time_mid,
+ uuid1.time_hi_and_version,
+ uuid1.clock_seq_hi_and_reserved,
+ uuid1.clock_seq_low);
+ auto simple_fields2 = std::tie(uuid2.time_low,
+ uuid2.time_mid,
+ uuid2.time_hi_and_version,
+ uuid2.clock_seq_hi_and_reserved,
+ uuid2.clock_seq_low);
+ if (simple_fields1 == simple_fields2) {
+ return std::memcmp(uuid1.node, uuid2.node, sizeof(uuid1.node)) < 0;
+ }
+ return simple_fields1 < simple_fields2;
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
index 0329fc156c0..68111fb8eb1 100644
--- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
+++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc
@@ -457,8 +457,8 @@ TEST(mesh_intersect, TwoTris)
{4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */
{4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */
{4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */
- {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */
- /* Following are all coplanar cases. */
+ {0, 14, 6, 2}, /* 12: non-parallel planes, not intersecting, alternate sides. */
+ /* Following are all co-planar cases. */
{15, 16, 6, 8}, /* 13: T16 inside T15. NOTE: dup'd tri is expected. */
{15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */
{15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */
@@ -970,7 +970,7 @@ static void fill_sphere_data(int nrings,
static void spheresphere_test(int nrings, double y_offset, bool use_self)
{
- /* Make two uvspheres with nrings rings ad 2*nrings segments. */
+ /* Make two UV-spheres with nrings rings ad 2*nrings segments. */
if (nrings < 2) {
return;
}
diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc
index cf5135731e2..65b02a19960 100644
--- a/source/blender/blenlib/tests/BLI_path_util_test.cc
+++ b/source/blender/blenlib/tests/BLI_path_util_test.cc
@@ -655,3 +655,34 @@ TEST(path_util, PathRelPath)
# undef PATH_REL
#endif
+
+/* BLI_path_contains */
+TEST(path_util, PathContains)
+{
+ EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path")) << "A path contains itself";
+ EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside"))
+ << "A path contains its subdirectory";
+ EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside"))
+ << "Paths should be normalised";
+ EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside"))
+ << "Windows paths should be supported as well";
+
+ EXPECT_FALSE(BLI_path_contains("C:\\some\\path", "C:\\some\\other\\path"))
+ << "Windows paths should be supported as well";
+ EXPECT_FALSE(BLI_path_contains("/some/path", "/"))
+ << "Root directory not be contained in a subdirectory";
+ EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside"))
+ << "Paths should be normalised";
+ EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library"))
+ << "Just sharing a suffix is not enough, path semantics should be followed";
+ EXPECT_FALSE(BLI_path_contains("/some/path", "./contents"))
+ << "Relative paths are not supported";
+}
+
+#ifdef WIN32
+TEST(path_util, PathContains_Windows_case_insensitive)
+{
+ EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "c:\\SOME\\path\\inside"))
+ << "On Windows path comparison should ignore case";
+}
+#endif
diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc
index 731489c6c9e..b406a0521a1 100644
--- a/source/blender/blenlib/tests/BLI_uuid_test.cc
+++ b/source/blender/blenlib/tests/BLI_uuid_test.cc
@@ -18,6 +18,8 @@
#include "BLI_uuid.h"
+namespace blender::tests {
+
TEST(BLI_uuid, generate_random)
{
const bUUID uuid = BLI_uuid_generate_random();
@@ -38,7 +40,7 @@ TEST(BLI_uuid, generate_many_random)
/* Generate lots of UUIDs to get some indication that the randomness is okay. */
for (int i = 0; i < 1000000; ++i) {
const bUUID uuid = BLI_uuid_generate_random();
- EXPECT_FALSE(BLI_uuid_equal(first_uuid, uuid));
+ EXPECT_NE(first_uuid, uuid);
// Check that the non-random bits are set according to RFC4122.
const uint16_t version = uuid.time_hi_and_version >> 12;
@@ -51,10 +53,13 @@ TEST(BLI_uuid, generate_many_random)
TEST(BLI_uuid, nil_value)
{
const bUUID nil_uuid = BLI_uuid_nil();
- const bUUID zeroes_uuid = {0, 0, 0, 0, 0, 0};
+ const bUUID zeroes_uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ const bUUID default_constructed{};
- EXPECT_TRUE(BLI_uuid_equal(nil_uuid, zeroes_uuid));
+ EXPECT_EQ(nil_uuid, zeroes_uuid);
EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid));
+ EXPECT_TRUE(BLI_uuid_is_nil(default_constructed))
+ << "Default constructor should produce the nil value.";
std::string buffer(36, '\0');
BLI_uuid_format(buffer.data(), nil_uuid);
@@ -66,8 +71,31 @@ TEST(BLI_uuid, equality)
const bUUID uuid1 = BLI_uuid_generate_random();
const bUUID uuid2 = BLI_uuid_generate_random();
- EXPECT_TRUE(BLI_uuid_equal(uuid1, uuid1));
- EXPECT_FALSE(BLI_uuid_equal(uuid1, uuid2));
+ EXPECT_EQ(uuid1, uuid1);
+ EXPECT_NE(uuid1, uuid2);
+}
+
+TEST(BLI_uuid, comparison_trivial)
+{
+ const bUUID uuid0{};
+ const bUUID uuid1("11111111-1111-1111-1111-111111111111");
+ const bUUID uuid2("22222222-2222-2222-2222-222222222222");
+
+ EXPECT_LT(uuid0, uuid1);
+ EXPECT_LT(uuid0, uuid2);
+ EXPECT_LT(uuid1, uuid2);
+}
+
+TEST(BLI_uuid, comparison_byte_order_check)
+{
+ const bUUID uuid0{};
+ /* Chosen to test byte ordering is taken into account correctly when comparing. */
+ const bUUID uuid12("12222222-2222-2222-2222-222222222222");
+ const bUUID uuid21("21111111-1111-1111-1111-111111111111");
+
+ EXPECT_LT(uuid0, uuid12);
+ EXPECT_LT(uuid0, uuid21);
+ EXPECT_LT(uuid12, uuid21);
}
TEST(BLI_uuid, string_formatting)
@@ -91,13 +119,13 @@ TEST(BLI_uuid, string_formatting)
EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer);
/* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */
- const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}};
+ const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b};
BLI_uuid_format(buffer.data(), uuid1);
EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer);
/* Namespace UUID, example listed in RFC4211. */
const bUUID namespace_dns = {
- 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}};
+ 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8};
BLI_uuid_format(buffer.data(), namespace_dns);
EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer);
}
@@ -139,7 +167,9 @@ TEST(BLI_uuid, string_parsing_fail)
TEST(BLI_uuid, stream_operator)
{
std::stringstream ss;
- const bUUID uuid = {3540651616, 5282, 4588, 139, 153, {0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}};
+ const bUUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b};
ss << uuid;
EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b");
}
+
+} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 4b7f29dd7dc..fa29b09af02 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -111,8 +111,18 @@ typedef struct BlendFileReadReport {
/* Some sub-categories of the above `missing_linked_id` counter. */
int missing_obdata;
int missing_obproxies;
+
/* Number of root override IDs that were resynced. */
int resynced_lib_overrides;
+
+ /* Number of (non-converted) linked proxies. */
+ int linked_proxies;
+ /* Number of proxies converted to library overrides. */
+ int proxies_to_lib_overrides_success;
+ /* Number of proxies that failed to convert to library overrides. */
+ int proxies_to_lib_overrides_failures;
+ /* Number of sequencer strips that were not read because were in non-supported channels. */
+ int vse_strips_skipped;
} count;
/* Number of libraries which had overrides that needed to be resynced, and a single linked list
@@ -168,9 +178,8 @@ struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
const bool use_assets_only,
int *r_tot_names);
-struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
- int ofblocktype,
- int *r_tot_info_items);
+struct LinkNode * /*BLODataBlockInfo */ BLO_blendhandle_get_datablock_info(
+ BlendHandle *bh, int ofblocktype, const bool use_assets_only, int *r_tot_info_items);
struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev);
struct PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh,
int ofblocktype,
@@ -209,6 +218,16 @@ typedef enum eBLOLibLinkFlags {
* don't need to remember to set this flag.
*/
BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18,
+ /** Set fake user on appended IDs. */
+ BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19,
+ /** Append (make local) also indirect dependencies of appended IDs. */
+ BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20,
+ /** Try to re-use previously appended matching ID on new append. */
+ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21,
+ /** Instantiate object data IDs (i.e. create objects for them if needed). */
+ BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24,
+ /** Instantiate collections as empties, instead of linking them into current view layer. */
+ BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 25,
} eBLOLibLinkFlags;
/**
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index f88b470809c..3306eb9e454 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -170,17 +170,19 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
}
/**
- * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain
- * type (e.g. all the scene names in a file).
+ * Gets the names and asset-data (if ID is an asset) of data-blocks in a file of a certain type.
+ * The data-blocks can be limited to assets.
*
* \param bh: The blendhandle to access.
* \param ofblocktype: The type of names to get.
+ * \param use_assets_only: Limit the result to assets only.
* \param tot_info_items: The length of the returned list.
* \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should
* be freed with MEM_freeN.
*/
LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
int ofblocktype,
+ const bool use_assets_only,
int *r_tot_info_items)
{
FileData *fd = (FileData *)bh;
@@ -189,27 +191,34 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
int tot = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
+ if (bhead->code == ENDB) {
+ break;
+ }
if (bhead->code == ofblocktype) {
- struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
const char *name = blo_bhead_id_name(fd, bhead) + 2;
+ AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead);
- STRNCPY(info->name, name);
+ const bool is_asset = asset_meta_data != NULL;
+ const bool skip_datablock = use_assets_only && !is_asset;
+ if (skip_datablock) {
+ continue;
+ }
+ struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
/* Lastly, read asset data from the following blocks. */
- info->asset_data = blo_bhead_id_asset_data_address(fd, bhead);
- if (info->asset_data) {
- bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data);
- /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next
- * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
+ if (asset_meta_data) {
+ bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data);
+ /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the
+ * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
bhead = blo_bhead_prev(fd, bhead);
}
+ STRNCPY(info->name, name);
+ info->asset_data = asset_meta_data;
+
BLI_linklist_prepend(&infos, info);
tot++;
}
- else if (bhead->code == ENDB) {
- break;
- }
}
*r_tot_info_items = tot;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 8203f988eed..cb3e81ba6d5 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -4500,7 +4500,7 @@ static void add_loose_objects_to_scene(Main *mainvar,
ViewLayer *view_layer,
const View3D *v3d,
Library *lib,
- const short flag)
+ const int flag)
{
Collection *active_collection = NULL;
const bool do_append = (flag & FILE_LINK) == 0;
@@ -4510,7 +4510,10 @@ static void add_loose_objects_to_scene(Main *mainvar,
/* Give all objects which are LIB_TAG_INDIRECT a base,
* or for a collection when *lib has been set. */
LISTBASE_FOREACH (Object *, ob, &mainvar->objects) {
- bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0;
+ /* NOTE: Even if this is a directly linked object and is tagged for instantiation, it might
+ * have already been instantiated through one of its owner collections, in which case we do not
+ * want to re-instantiate it in the active collection here. */
+ bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0 && !BKE_scene_object_find(scene, ob);
if (do_it ||
((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) {
if (do_append) {
@@ -4560,9 +4563,9 @@ static void add_loose_object_data_to_scene(Main *mainvar,
Scene *scene,
ViewLayer *view_layer,
const View3D *v3d,
- const short flag)
+ const int flag)
{
- if ((flag & FILE_OBDATA_INSTANCE) == 0) {
+ if ((flag & BLO_LIBLINK_OBDATA_INSTANCE) == 0) {
return;
}
@@ -4621,7 +4624,7 @@ static void add_collections_to_scene(Main *mainvar,
ViewLayer *view_layer,
const View3D *v3d,
Library *lib,
- const short flag)
+ const int flag)
{
Collection *active_collection = scene->master_collection;
if (flag & FILE_ACTIVE_COLLECTION) {
@@ -4631,7 +4634,7 @@ static void add_collections_to_scene(Main *mainvar,
/* Give all objects which are tagged a base. */
LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) {
- if ((flag & FILE_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) {
+ if ((flag & BLO_LIBLINK_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) {
/* Any indirect collection should not have been tagged. */
BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0);
@@ -4780,7 +4783,7 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t
if (blo_bhead_is_id_valid_type(bhead) && BKE_idtype_idcode_is_linkable((short)bhead->code) &&
(id_types_mask == 0 ||
(BKE_idtype_idcode_to_idfilter((short)bhead->code) & id_types_mask) != 0)) {
- read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, &id);
+ read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_EXTERN, false, &id);
num_directly_linked++;
}
@@ -4789,6 +4792,13 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t
ListBase *lb = which_libbase(mainl, GS(id->name));
id_sort_by_name(lb, id, NULL);
+ /* Tag as loose object (or data associated with objects)
+ * needing to be instantiated (see also #link_named_part and its usage of
+ * #BLO_LIBLINK_NEEDS_ID_TAG_DOIT above). */
+ if (library_link_idcode_needs_tag_check(GS(id->name), BLO_LIBLINK_NEEDS_ID_TAG_DOIT)) {
+ id->tag |= LIB_TAG_DOIT;
+ }
+
if (bhead->code == ID_OB) {
/* Instead of instancing Base's directly, postpone until after collections are loaded
* otherwise the base's flag is set incorrectly when collections are used */
@@ -4834,7 +4844,7 @@ static bool library_link_idcode_needs_tag_check(const short idcode, const int fl
if (ELEM(idcode, ID_OB, ID_GR)) {
return true;
}
- if (flag & FILE_OBDATA_INSTANCE) {
+ if (flag & BLO_LIBLINK_OBDATA_INSTANCE) {
if (OB_DATA_SUPPORT_ID(idcode)) {
return true;
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 69b67460a5d..d8f4d01a2e9 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -3394,7 +3394,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
SpaceImage *sima = (SpaceImage *)sl;
sima->flag &= ~(SI_FLAG_UNUSED_0 | SI_FLAG_UNUSED_1 | SI_FLAG_UNUSED_3 |
SI_FLAG_UNUSED_6 | SI_FLAG_UNUSED_7 | SI_FLAG_UNUSED_8 |
- SI_FLAG_UNUSED_17 | SI_FLAG_UNUSED_18 | SI_FLAG_UNUSED_23 |
+ SI_FLAG_UNUSED_17 | SI_CUSTOM_GRID | SI_FLAG_UNUSED_23 |
SI_FLAG_UNUSED_24);
break;
}
@@ -3416,8 +3416,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
case SPACE_FILE: {
SpaceFile *sfile = (SpaceFile *)sl;
if (sfile->params) {
- sfile->params->flag &= ~(FILE_APPEND_SET_FAKEUSER | FILE_APPEND_RECURSIVE |
- FILE_OBDATA_INSTANCE);
+ sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 |
+ FILE_PARAMS_FLAG_UNUSED_3);
}
break;
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index be8c4b735be..bf5b0bdbf3c 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1540,7 +1540,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
- STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
+ STRNCPY(node->idname, "GeometryLegacyNodeAttributeRandomize");
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 3bb76a4d4b6..323391d9715 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -376,6 +376,7 @@ static void move_vertex_group_names_to_object_data(Main *bmain)
/* Clear the list in case the it was already assigned from another object. */
BLI_freelistN(new_defbase);
*new_defbase = object->defbase;
+ BKE_object_defgroup_active_index_set(object, object->actdef);
}
}
}
@@ -449,6 +450,79 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis
#undef SEQ_SPEED_COMPRESS_IPO_Y
}
+static bool do_versions_sequencer_color_tags(Sequence *seq, void *UNUSED(user_data))
+{
+ seq->color_tag = SEQUENCE_COLOR_NONE;
+ return true;
+}
+
+static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket)
+{
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tosock == in_socket) {
+ return link;
+ }
+ }
+ return NULL;
+}
+
+static void add_realize_instances_before_socket(bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *geometry_socket)
+{
+ BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
+ bNodeLink *link = find_connected_link(ntree, geometry_socket);
+ if (link == NULL) {
+ return;
+ }
+
+ /* If the realize instances node is already before this socket, no need to continue. */
+ if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) {
+ return;
+ }
+
+ bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES);
+ realize_node->parent = node->parent;
+ realize_node->locx = node->locx - 100;
+ realize_node->locy = node->locy;
+ nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first);
+ link->fromnode = realize_node;
+ link->fromsock = realize_node->outputs.first;
+}
+
+/**
+ * If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize
+ * Instances" node in front of it to avoid changing behavior. Don't do this if the node will be
+ * replaced anyway though.
+ */
+static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
+{
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
+ if (ELEM(node->type,
+ GEO_NODE_ATTRIBUTE_CAPTURE,
+ GEO_NODE_SEPARATE_COMPONENTS,
+ GEO_NODE_CONVEX_HULL,
+ GEO_NODE_CURVE_LENGTH,
+ GEO_NODE_BOOLEAN,
+ GEO_NODE_CURVE_FILLET,
+ GEO_NODE_CURVE_RESAMPLE,
+ GEO_NODE_CURVE_TO_MESH,
+ GEO_NODE_CURVE_TRIM,
+ GEO_NODE_MATERIAL_REPLACE,
+ GEO_NODE_MESH_SUBDIVIDE,
+ GEO_NODE_ATTRIBUTE_REMOVE,
+ GEO_NODE_TRIANGULATE)) {
+ bNodeSocket *geometry_socket = node->inputs.first;
+ add_realize_instances_before_socket(ntree, node, geometry_socket);
+ }
+ /* Also realize instances for the profile input of the curve to mesh node. */
+ if (node->type == GEO_NODE_CURVE_TO_MESH) {
+ bNodeSocket *profile_socket = node->inputs.last;
+ add_realize_instances_before_socket(ntree, node, profile_socket);
+ }
+ }
+}
+
void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
{
if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
@@ -505,6 +579,70 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *tool_settings = scene->toolsettings;
+ ImagePaintSettings *imapaint = &tool_settings->imapaint;
+ if (imapaint->canvas != NULL &&
+ ELEM(imapaint->canvas->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ imapaint->canvas = NULL;
+ }
+ if (imapaint->stencil != NULL &&
+ ELEM(imapaint->stencil->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ imapaint->stencil = NULL;
+ }
+ if (imapaint->clone != NULL &&
+ ELEM(imapaint->clone->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ imapaint->clone = NULL;
+ }
+ }
+
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->clone.image != NULL &&
+ ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ brush->clone.image = NULL;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_geometry_nodes_add_realize_instance_nodes(ntree);
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 30)) {
+ do_versions_idproperty_ui_data(bmain);
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 32)) {
+ /* Update Switch Node Non-Fields switch input to Switch_001. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tonode->type == GEO_NODE_SWITCH) {
+ if (STREQ(link->tosock->identifier, "Switch")) {
+ bNode *to_node = link->tonode;
+
+ uint8_t mode = ((NodeSwitch *)to_node->storage)->input_type;
+ if (ELEM(mode,
+ SOCK_GEOMETRY,
+ SOCK_OBJECT,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL)) {
+ link->tosock = link->tosock->next;
+ }
+ }
+ }
+ }
+ }
+ }
/**
* Versioning code until next subversion bump goes here.
*
@@ -517,7 +655,16 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
- do_versions_idproperty_ui_data(bmain);
+
+ /* This was missing from #move_vertex_group_names_to_object_data. */
+ LISTBASE_FOREACH (Object *, object, &bmain->objects) {
+ if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
+ /* This uses the fact that the active vertex group index starts counting at 1. */
+ if (BKE_object_defgroup_active_index_get(object) == 0) {
+ BKE_object_defgroup_active_index_set(object, object->actdef);
+ }
+ }
+ }
}
}
@@ -656,10 +803,8 @@ static bool geometry_node_is_293_legacy(const short node_type)
switch (node_type) {
/* Not legacy: No attribute inputs or outputs. */
case GEO_NODE_TRIANGULATE:
- case GEO_NODE_EDGE_SPLIT:
case GEO_NODE_TRANSFORM:
case GEO_NODE_BOOLEAN:
- case GEO_NODE_SUBDIVISION_SURFACE:
case GEO_NODE_IS_VIEWPORT:
case GEO_NODE_MESH_SUBDIVIDE:
case GEO_NODE_MESH_PRIMITIVE_CUBE:
@@ -706,16 +851,16 @@ static bool geometry_node_is_293_legacy(const short node_type)
case GEO_NODE_COLLECTION_INFO:
return false;
- /* Maybe legacy: Transferred *all* attributes before, will not transfer all built-ins now. */
- case GEO_NODE_CURVE_ENDPOINTS:
- case GEO_NODE_CURVE_TO_POINTS:
- return false;
-
- /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to generate
- * a mesh for all grids in the volume. */
+ /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to
+ * generate a mesh for all grids in the volume. */
case GEO_NODE_VOLUME_TO_MESH:
return false;
+ /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */
+ case GEO_NODE_LEGACY_CURVE_ENDPOINTS:
+ case GEO_NODE_LEGACY_CURVE_TO_POINTS:
+ return true;
+
/* Legacy: Attribute operation completely replaced by field nodes. */
case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE:
case GEO_NODE_LEGACY_ATTRIBUTE_MATH:
@@ -727,10 +872,10 @@ static bool geometry_node_is_293_legacy(const short node_type)
case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR:
case GEO_NODE_LEGACY_POINT_SCALE:
case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE:
- case GEO_NODE_ATTRIBUTE_VECTOR_ROTATE:
+ case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE:
case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP:
case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE:
- case GEO_NODE_LECAGY_ATTRIBUTE_CLAMP:
+ case GEO_NODE_LEGACY_ATTRIBUTE_CLAMP:
case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH:
case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ:
case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ:
@@ -752,15 +897,17 @@ static bool geometry_node_is_293_legacy(const short node_type)
case GEO_NODE_LEGACY_CURVE_SET_HANDLES:
return true;
- /* Legacy: More complex attribute inputs or outputs. */
- case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */
- case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */
- case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */
- case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */
- case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */
- case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */
- case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */
- case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */
+ /* Legacy: More complex attribute inputs or outputs. */
+ case GEO_NODE_LEGACY_SUBDIVISION_SURFACE: /* Used "crease" attribute. */
+ case GEO_NODE_LEGACY_EDGE_SPLIT: /* Needs selection input version. */
+ case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */
+ case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */
+ case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */
+ case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */
+ case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */
+ case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */
+ case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */
+ case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */
return true;
}
return false;
@@ -785,6 +932,7 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree)
}
}
}
+
static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
@@ -1111,6 +1259,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+ if (ob->type == OB_GPENCIL) {
+ LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+ if (md->type == eGpencilModifierType_Lineart) {
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
+ lmd->flags |= LRT_GPENCIL_USE_CACHE;
+ lmd->chain_smooth_tolerance = 0.2f;
+ }
+ }
+ }
}
}
@@ -1241,7 +1398,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->type == GEO_NODE_SUBDIVISION_SURFACE) {
+ if (node->type == GEO_NODE_LEGACY_SUBDIVISION_SURFACE) {
if (node->storage == NULL) {
NodeGeometrySubdivisionSurface *data = MEM_callocN(
sizeof(NodeGeometrySubdivisionSurface), __func__);
@@ -1312,11 +1469,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
- LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
- if (ntree->type == NTREE_GEOMETRY) {
- version_geometry_nodes_change_legacy_names(ntree);
- }
- }
if (!DNA_struct_elem_find(
fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
@@ -1428,6 +1580,142 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ version_geometry_nodes_add_attribute_input_settings((NodesModifierData *)md);
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ switch (sl->spacetype) {
+ case SPACE_FILE: {
+ SpaceFile *sfile = (SpaceFile *)sl;
+ if (sfile->params) {
+ sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 |
+ FILE_PARAMS_FLAG_UNUSED_3 | FILE_PARAMS_FLAG_UNUSED_4);
+ }
+
+ /* New default import type: Append with reuse. */
+ if (sfile->asset_params) {
+ sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /* Deprecate the random float node in favor of the random value node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != FN_NODE_LEGACY_RANDOM_FLOAT) {
+ continue;
+ }
+ if (strstr(node->idname, "Legacy")) {
+ /* Make sure we haven't changed this idname already. */
+ continue;
+ }
+
+ char temp_idname[sizeof(node->idname)];
+ BLI_strncpy(temp_idname, node->idname, sizeof(node->idname));
+
+ BLI_snprintf(node->idname,
+ sizeof(node->idname),
+ "FunctionNodeLegacy%s",
+ temp_idname + strlen("FunctionNode"));
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ switch (sl->spacetype) {
+ case SPACE_SEQ: {
+ ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
+ &sl->regionbase;
+ LISTBASE_FOREACH (ARegion *, region, regionbase) {
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ region->v2d.max[1] = MAXSEQ;
+ }
+ }
+ break;
+ }
+ case SPACE_IMAGE: {
+ SpaceImage *sima = (SpaceImage *)sl;
+ sima->custom_grid_subdiv = 10;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_geometry_nodes_change_legacy_names(ntree);
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) {
+ /* Swap header with the tool header so the regular header is always on the edge. */
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
+ &sl->regionbase;
+ ARegion *region_tool = NULL, *region_head = NULL;
+ int region_tool_index = -1, region_head_index = -1, i;
+ LISTBASE_FOREACH_INDEX (ARegion *, region, regionbase, i) {
+ if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
+ region_tool = region;
+ region_tool_index = i;
+ }
+ else if (region->regiontype == RGN_TYPE_HEADER) {
+ region_head = region;
+ region_head_index = i;
+ }
+ }
+ if ((region_tool && region_head) && (region_head_index > region_tool_index)) {
+ BLI_listbase_swaplinks(regionbase, region_tool, region_head);
+ }
+ }
+ }
+ }
+
+ /* Set strip color tags to SEQUENCE_COLOR_NONE. */
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, NULL);
+ }
+ }
+
+ /* Show sequencer color tags by default. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
+ }
+ }
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -1439,12 +1727,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
- if (md->type == eModifierType_Nodes) {
- version_geometry_nodes_add_attribute_input_settings((NodesModifierData *)md);
- }
- }
- }
}
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 152ef79a38f..c383c1cc4e5 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -160,7 +160,8 @@ static void blo_update_defaults_screen(bScreen *screen,
seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY;
seq->render_size = SEQ_RENDER_SIZE_PROXY_100;
seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME |
- SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID;
+ SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
+ SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
}
else if (area->spacetype == SPACE_TEXT) {
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index f4853ff803f..cd365b6be78 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -291,6 +291,16 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
btheme->space_sequencer.grid[3] = 255;
}
+ if (!USER_VERSION_ATLEAST(300, 30)) {
+ FROM_DEFAULT_V4_UCHAR(space_node.wire);
+ }
+
+ if (!USER_VERSION_ATLEAST(300, 31)) {
+ for (int i = 0; i < SEQUENCE_COLOR_TOT; ++i) {
+ FROM_DEFAULT_V4_UCHAR(strip_color[i].color);
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -728,7 +738,7 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
- /* patch to set Dupli Lightprobes and Grease Pencil */
+ /* Patch to set dupli light-probes and grease-pencil. */
if (!USER_VERSION_ATLEAST(280, 58)) {
userdef->dupflag |= USER_DUP_LIGHTPROBE;
userdef->dupflag |= USER_DUP_GPENCIL;
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index 86a7d8153f0..103d7621f87 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -1135,11 +1135,13 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi
BMVert *v_other = BM_edge_other_vert(e_face_init ? e_face_init : v_delimit->e, v_delimit);
BLI_SMALLSTACK_PUSH(search, v_other);
- BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK);
+ if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) {
+ BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK);
+ BLI_linklist_prepend_alloca(&vert_stack, v_other);
+ }
while ((v_other = BLI_SMALLSTACK_POP(search))) {
BLI_assert(BM_elem_flag_test(v_other, VERT_NOT_IN_STACK) == false);
- BLI_linklist_prepend_alloca(&vert_stack, v_other);
BMEdge *e_iter = v_other->e;
do {
BMVert *v_step = BM_edge_other_vert(e_iter, v_other);
@@ -1147,6 +1149,7 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi
if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) {
BM_elem_flag_disable(v_step, VERT_NOT_IN_STACK);
BLI_SMALLSTACK_PUSH(search, v_step);
+ BLI_linklist_prepend_alloca(&vert_stack, v_step);
}
}
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e);
diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c
index d5d72cd4ba3..1f1ad0bae5b 100644
--- a/source/blender/bmesh/intern/bmesh_structure.c
+++ b/source/blender/bmesh/intern/bmesh_structure.c
@@ -86,7 +86,8 @@ void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src)
/**
* \section bm_cycles BMesh Cycles
- * (this is somewhat outdate, though bits of its API are still used) - joeedh
+ *
+ * NOTE(@joeedh): this is somewhat outdated, though bits of its API are still used.
*
* Cycles are circular doubly linked lists that form the basis of adjacency
* information in the BME modeler. Full adjacency relations can be derived
diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c
index 8bdf205babd..b8fdd534842 100644
--- a/source/blender/bmesh/intern/bmesh_walkers.c
+++ b/source/blender/bmesh/intern/bmesh_walkers.c
@@ -31,8 +31,7 @@
#include "bmesh_walkers_private.h"
/**
- * - joeedh -
- * design notes:
+ * NOTE(@joeedh): Details on design.
*
* original design: walkers directly emulation recursive functions.
* functions save their state onto a worklist, and also add new states
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index e306fe47770..1f759e9ef43 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -111,7 +111,7 @@ typedef struct EdgeHalf {
bool is_bev;
/** Is e->v2 the vertex at this end? */
bool is_rev;
- /** Is e a seam for custom loopdata (e.g., UVs)? */
+ /** Is e a seam for custom loop-data (e.g., UV's). */
bool is_seam;
/** Used during the custom profile orientation pass. */
bool visited_rpo;
@@ -3582,7 +3582,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle)
}
/* Residue np + 2*i (if cycle) else np - 1 + 2*i:
- * right offset for parm i matches its spec; weighted. */
+ * right offset for parameter i matches its spec; weighted. */
int row = iscycle ? np + 2 * i : np - 1 + 2 * i;
EIG_linear_solver_matrix_add(solver, row, i, weight);
EIG_linear_solver_right_hand_side_add(solver, 0, row, weight * eright->offset_r);
@@ -3595,7 +3595,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle)
#endif
/* Residue np + 2*i + 1 (if cycle) else np - 1 + 2*i + 1:
- * left offset for parm i matches its spec; weighted. */
+ * left offset for parameter i matches its spec; weighted. */
row = row + 1;
EIG_linear_solver_matrix_add(
solver, row, (i == np - 1) ? 0 : i + 1, weight * v->adjchain->sinratio);
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 73c4343a230..9991414aba4 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,11 +18,14 @@
#pragma once
+#include "BLI_float2.hh"
#include "BLI_index_range.hh"
#include "BLI_rect.h"
namespace blender::compositor {
+using Size2f = float2;
+
enum class eExecutionModel {
/**
* Operations are executed from outputs to inputs grouped in execution groups and rendered
@@ -121,26 +124,7 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
-constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1};
-
-constexpr IndexRange XRange(const rcti &area)
-{
- return IndexRange(area.xmin, area.xmax - area.xmin);
-}
-
-constexpr IndexRange YRange(const rcti &area)
-{
- return IndexRange(area.ymin, area.ymax - area.ymin);
-}
-
-constexpr IndexRange XRange(const rcti *area)
-{
- return XRange(*area);
-}
-
-constexpr IndexRange YRange(const rcti *area)
-{
- return YRange(*area);
-}
+constexpr rcti COM_AREA_NONE = {0, 0, 0, 0};
+constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE;
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
index cafdff89c8e..c6530cf6bd1 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.cc
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -24,12 +24,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
{
buffer_ = buffer;
inflated_buffer_ = nullptr;
- /* 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);
+ set_canvas(buffer->get_rect());
addOutputSocket(data_type);
flags.is_constant_operation = buffer_->is_a_single_elem();
flags.is_fullframe_operation = false;
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index a93820b66dc..f5f490b0bf6 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -43,6 +43,12 @@ int CompositorContext::getFramenumber() const
return m_rd->cfra;
}
+Size2f CompositorContext::get_render_size() const
+{
+ return {getRenderData()->xsch * getRenderPercentageAsFactor(),
+ getRenderData()->ysch * getRenderPercentageAsFactor()};
+}
+
eExecutionModel CompositorContext::get_execution_model() const
{
if (U.experimental.use_full_frame_compositor) {
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index c6e83f93777..ae298c5a65a 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -288,6 +288,8 @@ class CompositorContext {
return m_rd->size * 0.01f;
}
+ Size2f get_render_size() const;
+
/**
* Get active execution model.
*/
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 4b103c21c75..ee77beb8a82 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -460,14 +460,16 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
return nullptr;
}
-void COM_convert_resolution(NodeOperationBuilder &builder,
- NodeOperationOutput *fromSocket,
- NodeOperationInput *toSocket)
+void COM_convert_canvas(NodeOperationBuilder &builder,
+ NodeOperationOutput *fromSocket,
+ NodeOperationInput *toSocket)
{
/* Data type conversions are executed before resolutions to ensure convert operations have
* resolution. This method have to ensure same datatypes are linked for new operations. */
BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
+
ResizeMode mode = toSocket->getResizeMode();
+ BLI_assert(mode != ResizeMode::None);
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -477,13 +479,12 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
const float fromHeight = fromOperation->getHeight();
bool doCenter = false;
bool doScale = false;
- float addX = (toWidth - fromWidth) / 2.0f;
- float addY = (toHeight - fromHeight) / 2.0f;
float scaleX = 0;
float scaleY = 0;
switch (mode) {
case ResizeMode::None:
+ case ResizeMode::Align:
break;
case ResizeMode::Center:
doCenter = true;
@@ -518,63 +519,74 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
break;
}
- if (doCenter) {
- NodeOperation *first = nullptr;
- ScaleOperation *scaleOperation = nullptr;
- if (doScale) {
- scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
- scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
- scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
- first = scaleOperation;
- SetValueOperation *sxop = new SetValueOperation();
- sxop->setValue(scaleX);
- builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
- SetValueOperation *syop = new SetValueOperation();
- syop->setValue(scaleY);
- builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
- builder.addOperation(sxop);
- builder.addOperation(syop);
-
- unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
- scaleOperation->setResolution(resolution);
- sxop->setResolution(resolution);
- syop->setResolution(resolution);
- builder.addOperation(scaleOperation);
- }
+ float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
+ float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
+ NodeOperation *first = nullptr;
+ ScaleOperation *scaleOperation = nullptr;
+ if (doScale) {
+ scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
+ scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
+ first = scaleOperation;
+ SetValueOperation *sxop = new SetValueOperation();
+ sxop->setValue(scaleX);
+ builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
+ SetValueOperation *syop = new SetValueOperation();
+ syop->setValue(scaleY);
+ builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
+ builder.addOperation(sxop);
+ builder.addOperation(syop);
- TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
- translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
- translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
- if (!first) {
- first = translateOperation;
+ rcti scale_canvas = fromOperation->get_canvas();
+ if (builder.context().get_execution_model() == eExecutionModel::FullFrame) {
+ ScaleOperation::scale_area(scale_canvas, scaleX, scaleY);
+ scale_canvas.xmax = scale_canvas.xmin + toOperation->getWidth();
+ scale_canvas.ymax = scale_canvas.ymin + toOperation->getHeight();
+ addX = 0;
+ addY = 0;
}
- SetValueOperation *xop = new SetValueOperation();
- xop->setValue(addX);
- builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
- SetValueOperation *yop = new SetValueOperation();
- yop->setValue(addY);
- builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
- builder.addOperation(xop);
- builder.addOperation(yop);
+ scaleOperation->set_canvas(scale_canvas);
+ sxop->set_canvas(scale_canvas);
+ syop->set_canvas(scale_canvas);
+ builder.addOperation(scaleOperation);
+ }
- unsigned int resolution[2] = {toOperation->getWidth(), toOperation->getHeight()};
- translateOperation->setResolution(resolution);
- xop->setResolution(resolution);
- yop->setResolution(resolution);
- builder.addOperation(translateOperation);
+ TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
+ translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
+ if (!first) {
+ first = translateOperation;
+ }
+ SetValueOperation *xop = new SetValueOperation();
+ xop->setValue(addX);
+ builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
+ SetValueOperation *yop = new SetValueOperation();
+ yop->setValue(addY);
+ builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
+ builder.addOperation(xop);
+ builder.addOperation(yop);
- if (doScale) {
- translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
- builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
- }
+ rcti translate_canvas = toOperation->get_canvas();
+ if (mode == ResizeMode::Align) {
+ translate_canvas.xmax = translate_canvas.xmin + fromWidth;
+ translate_canvas.ymax = translate_canvas.ymin + fromHeight;
+ }
+ translateOperation->set_canvas(translate_canvas);
+ xop->set_canvas(translate_canvas);
+ yop->set_canvas(translate_canvas);
+ builder.addOperation(translateOperation);
- /* remove previous link and replace */
- builder.removeInputLink(toSocket);
- first->getInputSocket(0)->setResizeMode(ResizeMode::None);
- toSocket->setResizeMode(ResizeMode::None);
- builder.addLink(fromSocket, first->getInputSocket(0));
- builder.addLink(translateOperation->getOutputSocket(), toSocket);
+ if (doScale) {
+ translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
+
+ /* remove previous link and replace */
+ builder.removeInputLink(toSocket);
+ first->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ toSocket->setResizeMode(ResizeMode::None);
+ builder.addLink(fromSocket, first->getInputSocket(0));
+ builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 28136437103..7f0402d4e70 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -65,8 +65,8 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
* \note Conversion logic is implemented in this function.
* \see InputSocketResizeMode for the possible conversions.
*/
-void COM_convert_resolution(NodeOperationBuilder &builder,
- NodeOperationOutput *fromSocket,
- NodeOperationInput *toSocket);
+void COM_convert_canvas(NodeOperationBuilder &builder,
+ NodeOperationOutput *fromSocket,
+ NodeOperationInput *toSocket);
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index f2dcba65b7c..9e47aa9fcb5 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -162,8 +162,10 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
len += snprintf(str + len,
maxlen > len ? maxlen - len : 0,
- "#%d (%u,%u)",
+ "#%d (%i,%i) (%u,%u)",
operation->get_id(),
+ operation->get_canvas().xmin,
+ operation->get_canvas().ymin,
operation->getWidth(),
operation->getHeight());
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
index bd3a481d691..c44a168390b 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -74,37 +74,61 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads()
}
}
-Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
+/**
+ * Returns input buffers with an offset relative to given output coordinates. Returned memory
+ * buffers must be deleted.
+ */
+Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op,
+ const int output_x,
+ const int output_y)
{
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);
+ NodeOperation *input = op->get_input_operation(i);
+ const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x;
+ const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y;
+ MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input);
+
+ rcti rect = buf->get_rect();
+ BLI_rcti_translate(&rect, offset_x, offset_y);
+ inputs_buffers[i] = new MemoryBuffer(
+ buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem());
}
return inputs_buffers;
}
-MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
+MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op,
+ const int output_x,
+ const int output_y)
{
- rcti op_rect;
- BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
+ rcti rect;
+ BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight());
const DataType data_type = op->getOutputSocket(0)->getDataType();
const bool is_a_single_elem = op->get_flags().is_constant_operation;
- return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
+ return new MemoryBuffer(data_type, rect, is_a_single_elem);
}
void FullFrameExecutionModel::render_operation(NodeOperation *op)
{
- Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
+ /* Output has no offset for easier image algorithms implementation on operations. */
+ constexpr int output_x = 0;
+ constexpr int output_y = 0;
const bool has_outputs = op->getNumberOfOutputSockets() > 0;
- MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
+ MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
if (op->getWidth() > 0 && op->getHeight() > 0) {
- Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ Vector<MemoryBuffer *> input_bufs = get_input_buffers(op, output_x, output_y);
+ const int op_offset_x = output_x - op->get_canvas().xmin;
+ const int op_offset_y = output_y - op->get_canvas().ymin;
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y);
op->render(op_buf, areas, input_bufs);
DebugInfo::operation_rendered(op, op_buf);
+
+ for (MemoryBuffer *buf : input_bufs) {
+ delete buf;
+ }
}
/* Even if operation has no resolution set the empty buffer. It will be clipped with a
* TranslateOperation from convert resolutions if linked to an operation with resolution. */
@@ -190,7 +214,8 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
std::pair<NodeOperation *, rcti> pair = stack.pop_last();
NodeOperation *operation = pair.first;
const rcti &render_area = pair.second;
- if (active_buffers_.is_area_registered(operation, render_area)) {
+ if (BLI_rcti_is_empty(&render_area) ||
+ active_buffers_.is_area_registered(operation, render_area)) {
continue;
}
@@ -199,12 +224,11 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
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());
+ rcti input_area;
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);
+ BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area);
stack.append({input_op, input_area});
}
@@ -243,9 +267,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
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);
+ rcti canvas = output_op->get_canvas();
+ r_area = canvas;
const bool has_viewer_border = border_.use_viewer_border &&
(output_op->get_flags().is_viewer_operation ||
@@ -255,12 +278,13 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
/* 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);
+ /* Return de-normalized border within canvas. */
+ const int w = output_op->getWidth();
+ const int h = output_op->getHeight();
+ r_area.xmin = canvas.xmin + norm_border->xmin * w;
+ r_area.xmax = canvas.xmin + norm_border->xmax * w;
+ r_area.ymin = canvas.ymin + norm_border->ymin * h;
+ r_area.ymax = canvas.ymin + norm_border->ymax * h;
}
}
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
index f75d4f1afdc..66dfb8f052c 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -61,8 +61,10 @@ class FullFrameExecutionModel : public ExecutionModel {
void determine_areas_to_render_and_reads();
void render_operations();
void render_output_dependencies(NodeOperation *output_op);
- Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
- MemoryBuffer *create_operation_buffer(NodeOperation *op);
+ Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op,
+ const int output_x,
+ const int output_y);
+ MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y);
void render_operation(NodeOperation *op);
void operation_finished(NodeOperation *operation);
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 5327be50b53..f57f0f055bf 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -122,6 +122,8 @@ void MemoryBuffer::set_strides()
this->elem_stride = m_num_channels;
this->row_stride = getWidth() * m_num_channels;
}
+ to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0);
+ to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0);
}
void MemoryBuffer::clear()
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index f730d53acec..9e173f73f63 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -114,6 +114,12 @@ class MemoryBuffer {
*/
bool owns_data_;
+ /** Stride to make any x coordinate within buffer positive (non-zero). */
+ int to_positive_x_stride_;
+
+ /** Stride to make any y coordinate within buffer positive (non-zero). */
+ int to_positive_y_stride_;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -166,9 +172,9 @@ class MemoryBuffer {
/**
* Get offset needed to jump from buffer start to given coordinates.
*/
- int get_coords_offset(int x, int y) const
+ intptr_t get_coords_offset(int x, int y) const
{
- return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
+ return ((intptr_t)y - m_rect.ymin) * row_stride + ((intptr_t)x - m_rect.xmin) * elem_stride;
}
/**
@@ -176,7 +182,7 @@ class MemoryBuffer {
*/
float *get_elem(int x, int y)
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -185,7 +191,7 @@ class MemoryBuffer {
*/
const float *get_elem(int x, int y) const
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -196,7 +202,7 @@ class MemoryBuffer {
void read_elem_checked(int x, int y, float *out) const
{
- if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
+ if (!has_coords(x, y)) {
clear_elem(out);
}
else {
@@ -206,12 +212,7 @@ class MemoryBuffer {
void read_elem_checked(float x, float y, float *out) const
{
- if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
- clear_elem(out);
- }
- else {
- read_elem(x, y, out);
- }
+ read_elem_checked(floor_x(x), floor_y(y), out);
}
void read_elem_bilinear(float x, float y, float *out) const
@@ -286,8 +287,7 @@ class MemoryBuffer {
*/
float &get_value(int x, int y, int channel)
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
- channel >= 0 && channel < m_num_channels);
+ BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -296,8 +296,7 @@ class MemoryBuffer {
*/
const float &get_value(int x, int y, int channel) const
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
- channel >= 0 && channel < m_num_channels);
+ BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -306,7 +305,7 @@ class MemoryBuffer {
*/
const float *get_row_end(int y) const
{
- BLI_assert(y >= 0 && y < getHeight());
+ BLI_assert(has_y(y));
return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
}
@@ -681,6 +680,34 @@ class MemoryBuffer {
return y - m_rect.ymin;
}
+ template<typename T> bool has_coords(T x, T y) const
+ {
+ return has_x(x) && has_y(y);
+ }
+
+ template<typename T> bool has_x(T x) const
+ {
+ return x >= m_rect.xmin && x < m_rect.xmax;
+ }
+
+ template<typename T> bool has_y(T y) const
+ {
+ return y >= m_rect.ymin && y < m_rect.ymax;
+ }
+
+ /* Fast `floor(..)` functions. The caller should check result is within buffer bounds.
+ * It `ceil(..)` in near cases and when given coordinate
+ * is negative and less than buffer rect `min - 1`. */
+ int floor_x(float x) const
+ {
+ return (int)(x + to_positive_x_stride_) - to_positive_x_stride_;
+ }
+
+ int floor_y(float y) const
+ {
+ return (int)(y + to_positive_y_stride_) - to_positive_y_stride_;
+ }
+
void copy_single_elem_from(const MemoryBuffer *src,
int channel_offset,
int elem_size,
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 3bbd1b22d60..a6a395261f2 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -35,12 +35,29 @@ namespace blender::compositor {
NodeOperation::NodeOperation()
{
- this->m_resolutionInputSocketIndex = 0;
- this->m_width = 0;
- this->m_height = 0;
+ canvas_input_index_ = 0;
+ canvas_ = COM_AREA_NONE;
this->m_btree = nullptr;
}
+/** Get constant value when operation is constant, otherwise return default_value. */
+float NodeOperation::get_constant_value_default(float default_value)
+{
+ BLI_assert(m_outputs.size() > 0 && getOutputSocket()->getDataType() == DataType::Value);
+ return *get_constant_elem_default(&default_value);
+}
+
+/** Get constant elem when operation is constant, otherwise return default_elem. */
+const float *NodeOperation::get_constant_elem_default(const float *default_elem)
+{
+ BLI_assert(m_outputs.size() > 0);
+ if (get_flags().is_constant_operation) {
+ return static_cast<ConstantOperation *>(this)->get_constant_elem();
+ }
+
+ return default_elem;
+}
+
/**
* Generate a hash that identifies the operation result in the current execution.
* Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned.
@@ -48,7 +65,7 @@ NodeOperation::NodeOperation()
*/
std::optional<NodeOperationHash> NodeOperation::generate_hash()
{
- params_hash_ = get_default_hash_2(m_width, m_height);
+ params_hash_ = get_default_hash_2(canvas_.xmin, canvas_.xmax);
/* Hash subclasses params. */
is_hash_output_params_implemented_ = true;
@@ -57,7 +74,11 @@ std::optional<NodeOperationHash> NodeOperation::generate_hash()
return std::nullopt;
}
- hash_param(getOutputSocket()->getDataType());
+ hash_params(canvas_.ymin, canvas_.ymax);
+ if (m_outputs.size() > 0) {
+ BLI_assert(m_outputs.size() == 1);
+ hash_param(this->getOutputSocket()->getDataType());
+ }
NodeOperationHash hash;
hash.params_hash_ = params_hash_;
@@ -108,48 +129,46 @@ void NodeOperation::addOutputSocket(DataType datatype)
m_outputs.append(NodeOperationOutput(this, datatype));
}
-void NodeOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- unsigned int used_resolution_index = 0;
- if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
+ unsigned int used_canvas_index = 0;
+ if (canvas_input_index_ == RESOLUTION_INPUT_ANY) {
for (NodeOperationInput &input : m_inputs) {
- unsigned int any_resolution[2] = {0, 0};
- input.determineResolution(any_resolution, preferredResolution);
- if (any_resolution[0] * any_resolution[1] > 0) {
- resolution[0] = any_resolution[0];
- resolution[1] = any_resolution[1];
+ rcti any_area = COM_AREA_NONE;
+ const bool determined = input.determine_canvas(preferred_area, any_area);
+ if (determined) {
+ r_area = any_area;
break;
}
- used_resolution_index += 1;
+ used_canvas_index += 1;
}
}
- else if (m_resolutionInputSocketIndex < m_inputs.size()) {
- NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
- input.determineResolution(resolution, preferredResolution);
- used_resolution_index = m_resolutionInputSocketIndex;
+ else if (canvas_input_index_ < m_inputs.size()) {
+ NodeOperationInput &input = m_inputs[canvas_input_index_];
+ input.determine_canvas(preferred_area, r_area);
+ used_canvas_index = canvas_input_index_;
}
- if (modify_determined_resolution_fn_) {
- modify_determined_resolution_fn_(resolution);
+ if (modify_determined_canvas_fn_) {
+ modify_determined_canvas_fn_(r_area);
}
- unsigned int temp2[2] = {resolution[0], resolution[1]};
- unsigned int temp[2];
+ rcti unused_area;
+ const rcti &local_preferred_area = r_area;
for (unsigned int index = 0; index < m_inputs.size(); index++) {
- if (index == used_resolution_index) {
+ if (index == used_canvas_index) {
continue;
}
NodeOperationInput &input = m_inputs[index];
if (input.isConnected()) {
- input.determineResolution(temp, temp2);
+ input.determine_canvas(local_preferred_area, unused_area);
}
}
}
-void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
+void NodeOperation::set_canvas_input_index(unsigned int index)
{
- this->m_resolutionInputSocketIndex = index;
+ this->canvas_input_index_ = index;
}
void NodeOperation::init_data()
@@ -185,6 +204,28 @@ void NodeOperation::deinitExecution()
{
/* pass */
}
+
+void NodeOperation::set_canvas(const rcti &canvas_area)
+{
+ canvas_ = canvas_area;
+ flags.is_canvas_set = true;
+}
+
+const rcti &NodeOperation::get_canvas() const
+{
+ return canvas_;
+}
+
+/**
+ * Mainly used for re-determining canvas of constant operations in cases where preferred canvas
+ * depends on the constant element.
+ */
+void NodeOperation::unset_canvas()
+{
+ BLI_assert(m_inputs.size() == 0);
+ flags.is_canvas_set = false;
+}
+
SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex)
{
return this->getInputSocket(inputSocketIndex)->getReader();
@@ -260,7 +301,7 @@ void NodeOperation::get_area_of_interest(const int input_idx,
/* Non full-frame operations never implement this method. To ensure correctness assume
* whole area is used. */
NodeOperation *input_op = getInputOperation(input_idx);
- BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
+ r_input_area = input_op->get_canvas();
}
}
@@ -420,12 +461,16 @@ SocketReader *NodeOperationInput::getReader()
return nullptr;
}
-void NodeOperationInput::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+/**
+ * \return Whether canvas area could be determined.
+ */
+bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (m_link) {
- m_link->determineResolution(resolution, preferredResolution);
+ m_link->determine_canvas(preferred_area, r_area);
+ return !BLI_rcti_is_empty(&r_area);
}
+ return false;
}
/******************
@@ -437,18 +482,16 @@ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype)
{
}
-void NodeOperationOutput::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
NodeOperation &operation = getOperation();
- if (operation.get_flags().is_resolution_set) {
- resolution[0] = operation.getWidth();
- resolution[1] = operation.getHeight();
+ if (operation.get_flags().is_canvas_set) {
+ r_area = operation.get_canvas();
}
else {
- operation.determineResolution(resolution, preferredResolution);
- if (resolution[0] > 0 && resolution[1] > 0) {
- operation.setResolution(resolution);
+ operation.determine_canvas(preferred_area, r_area);
+ if (!BLI_rcti_is_empty(&r_area)) {
+ operation.set_canvas(r_area);
}
}
}
@@ -470,8 +513,8 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat
if (node_operation_flags.use_viewer_border) {
os << "view_border,";
}
- if (node_operation_flags.is_resolution_set) {
- os << "resolution_set,";
+ if (node_operation_flags.is_canvas_set) {
+ os << "canvas_set,";
}
if (node_operation_flags.is_set_operation) {
os << "set_operation,";
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index ef7cf319222..f507665bee3 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -62,9 +62,13 @@ enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
Center = NS_CR_CENTER,
- /** \brief The bottom left of the input image is the bottom left of the working area of the node,
- * no resizing occurs */
+ /** No resizing or translation. */
None = NS_CR_NONE,
+ /**
+ * Input image is translated so that its bottom left matches the bottom left of the working area
+ * of the node, no resizing occurs.
+ */
+ Align = 100,
/** \brief Fit the width of the input image to the width of the working area of the node */
FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
@@ -130,7 +134,7 @@ class NodeOperationInput {
SocketReader *getReader();
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ bool determine_canvas(const rcti &preferred_area, rcti &r_area);
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
@@ -158,12 +162,7 @@ class NodeOperationOutput {
return m_datatype;
}
- /**
- * \brief determine the resolution of this data going through this socket
- * \param resolution: the result of this operation
- * \param preferredResolution: the preferable resolution as no resolution could be determined
- */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ void determine_canvas(const rcti &preferred_area, rcti &r_area);
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
@@ -211,9 +210,9 @@ struct NodeOperationFlags {
bool use_viewer_border : 1;
/**
- * Is the resolution of the operation set.
+ * Is the canvas of the operation set.
*/
- bool is_resolution_set : 1;
+ bool is_canvas_set : 1;
/**
* Is this a set operation (value, color, vector).
@@ -257,7 +256,7 @@ struct NodeOperationFlags {
open_cl = false;
use_render_border = false;
use_viewer_border = false;
- is_resolution_set = false;
+ is_canvas_set = false;
is_set_operation = false;
is_read_buffer_operation = false;
is_write_buffer_operation = false;
@@ -324,11 +323,11 @@ class NodeOperation {
bool is_hash_output_params_implemented_;
/**
- * \brief the index of the input socket that will be used to determine the resolution
+ * \brief the index of the input socket that will be used to determine the canvas
*/
- unsigned int m_resolutionInputSocketIndex;
+ unsigned int canvas_input_index_;
- std::function<void(unsigned int resolution[2])> modify_determined_resolution_fn_;
+ std::function<void(rcti &canvas)> modify_determined_canvas_fn_;
/**
* \brief mutex reference for very special node initializations
@@ -352,15 +351,7 @@ class NodeOperation {
*/
eExecutionModel execution_model_;
- /**
- * Width of the output of this operation.
- */
- unsigned int m_width;
-
- /**
- * Height of the output of this operation.
- */
- unsigned int m_height;
+ rcti canvas_;
/**
* Flags how to evaluate this operation.
@@ -374,11 +365,6 @@ class NodeOperation {
{
}
- void set_execution_model(const eExecutionModel model)
- {
- execution_model_ = model;
- }
-
void set_name(const std::string name)
{
m_name = name;
@@ -399,6 +385,9 @@ class NodeOperation {
return m_id;
}
+ float get_constant_value_default(float default_value);
+ const float *get_constant_elem_default(const float *default_elem);
+
const NodeOperationFlags get_flags() const
{
return flags;
@@ -424,14 +413,7 @@ class NodeOperation {
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
- * \param resolution: the result of this operation
- * \param preferredResolution: the preferable resolution as no resolution could be determined
- */
- virtual void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]);
+ virtual void determine_canvas(const rcti &preferred_area, rcti &r_area);
/**
* \brief isOutputOperation determines whether this operation is an output of the
@@ -453,6 +435,11 @@ class NodeOperation {
return false;
}
+ void set_execution_model(const eExecutionModel model)
+ {
+ execution_model_ = model;
+ }
+
void setbNodeTree(const bNodeTree *tree)
{
this->m_btree = tree;
@@ -527,18 +514,9 @@ class NodeOperation {
}
virtual void deinitExecution();
- /**
- * \brief set the resolution
- * \param resolution: the resolution to set
- */
- void setResolution(unsigned int resolution[2])
- {
- if (!this->flags.is_resolution_set) {
- this->m_width = resolution[0];
- this->m_height = resolution[1];
- this->flags.is_resolution_set = true;
- }
- }
+ void set_canvas(const rcti &canvas_area);
+ const rcti &get_canvas() const;
+ void unset_canvas();
/**
* \brief is this operation the active viewer output
@@ -557,18 +535,18 @@ class NodeOperation {
rcti *output);
/**
- * \brief set the index of the input socket that will determine the resolution of this
+ * \brief set the index of the input socket that will determine the canvas of this
* operation \param index: the index to set
*/
- void setResolutionInputSocketIndex(unsigned int index);
+ void set_canvas_input_index(unsigned int index);
/**
- * Set a custom function to modify determined resolution from main input just before setting it
- * as preferred resolution for the other inputs.
+ * Set a custom function to modify determined canvas from main input just before setting it
+ * as preferred for the other inputs.
*/
- void set_determined_resolution_modifier(std::function<void(unsigned int resolution[2])> fn)
+ void set_determined_canvas_modifier(std::function<void(rcti &canvas)> fn)
{
- modify_determined_resolution_fn_ = fn;
+ modify_determined_canvas_fn_ = fn;
}
/**
@@ -595,12 +573,12 @@ class NodeOperation {
unsigned int getWidth() const
{
- return m_width;
+ return BLI_rcti_size_x(&get_canvas());
}
unsigned int getHeight() const
{
- return m_height;
+ return BLI_rcti_size_y(&get_canvas());
}
inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
@@ -697,16 +675,18 @@ class NodeOperation {
void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
+ /* TODO(manzanilla): to be removed with tiled implementation. */
void setWidth(unsigned int width)
{
- this->m_width = width;
- this->flags.is_resolution_set = true;
+ canvas_.xmax = canvas_.xmin + width;
+ this->flags.is_canvas_set = true;
}
void setHeight(unsigned int height)
{
- this->m_height = height;
- this->flags.is_resolution_set = true;
+ canvas_.ymax = canvas_.ymin + height;
+ this->flags.is_canvas_set = true;
}
+
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index b2cd76be2c3..acb7f61f6dd 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -33,6 +33,7 @@
#include "COM_SetValueOperation.h"
#include "COM_SetVectorOperation.h"
#include "COM_SocketProxyOperation.h"
+#include "COM_TranslateOperation.h"
#include "COM_ViewerOperation.h"
#include "COM_WriteBufferOperation.h"
@@ -106,7 +107,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
folder.fold_operations();
}
- determineResolutions();
+ determine_canvases();
save_graphviz("compositor_prior_merging");
merge_equal_operations();
@@ -423,41 +424,50 @@ void NodeOperationBuilder::resolve_proxies()
}
}
-void NodeOperationBuilder::determineResolutions()
+void NodeOperationBuilder::determine_canvases()
{
- /* determine all resolutions of the operations (Width/Height) */
+ /* Determine all canvas areas of the operations. */
+ const rcti &preferred_area = COM_AREA_NONE;
for (NodeOperation *op : m_operations) {
if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
- unsigned int resolution[2] = {0, 0};
- unsigned int preferredResolution[2] = {0, 0};
- op->determineResolution(resolution, preferredResolution);
- op->setResolution(resolution);
+ rcti canvas = COM_AREA_NONE;
+ op->determine_canvas(preferred_area, canvas);
+ op->set_canvas(canvas);
}
}
for (NodeOperation *op : m_operations) {
if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
- unsigned int resolution[2] = {0, 0};
- unsigned int preferredResolution[2] = {0, 0};
- op->determineResolution(resolution, preferredResolution);
- op->setResolution(resolution);
+ rcti canvas = COM_AREA_NONE;
+ op->determine_canvas(preferred_area, canvas);
+ op->set_canvas(canvas);
}
}
- /* add convert resolution operations when needed */
+ /* Convert operation canvases when needed. */
{
Vector<Link> convert_links;
for (const Link &link : m_links) {
if (link.to()->getResizeMode() != ResizeMode::None) {
- NodeOperation &from_op = link.from()->getOperation();
- NodeOperation &to_op = link.to()->getOperation();
- if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
+ const rcti &from_canvas = link.from()->getOperation().get_canvas();
+ const rcti &to_canvas = link.to()->getOperation().get_canvas();
+
+ bool needs_conversion;
+ if (link.to()->getResizeMode() == ResizeMode::Align) {
+ needs_conversion = from_canvas.xmin != to_canvas.xmin ||
+ from_canvas.ymin != to_canvas.ymin;
+ }
+ else {
+ needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas);
+ }
+
+ if (needs_conversion) {
convert_links.append(link);
}
}
}
for (const Link &link : convert_links) {
- COM_convert_resolution(*this, link.from(), link.to());
+ COM_convert_canvas(*this, link.from(), link.to());
}
}
}
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
index aca4d043d41..1f9c86b51cd 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
@@ -145,8 +145,8 @@ class NodeOperationBuilder {
/** Replace proxy operations with direct links */
void resolve_proxies();
- /** Calculate resolution for each operation */
- void determineResolutions();
+ /** Calculate canvas area for each operation. */
+ void determine_canvases();
/** Helper function to store connected inputs for replacement */
Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
index 7e0486b0f54..55153bd4f0a 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -76,9 +76,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op)
/**
* Get registered areas given operation needs to render.
*/
-blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
+Vector<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op,
+ const int offset_x,
+ const int offset_y)
{
- return get_buffer_data(op).render_areas.as_span();
+ Span<rcti> render_areas = get_buffer_data(op).render_areas.as_span();
+ Vector<rcti> dst_areas;
+ for (rcti dst : render_areas) {
+ BLI_rcti_translate(&dst, offset_x, offset_y);
+ dst_areas.append(std::move(dst));
+ }
+ return dst_areas;
}
/**
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
index f7763cd8ae4..4461ba75cbe 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -53,7 +53,7 @@ class SharedOperationBuffers {
bool has_registered_reads(NodeOperation *op);
void register_read(NodeOperation *read_op);
- blender::Span<rcti> get_areas_to_render(NodeOperation *op);
+ Vector<rcti> get_areas_to_render(NodeOperation *op, int offset_x, int offset_y);
bool is_operation_rendered(NodeOperation *op);
void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
MemoryBuffer *get_rendered_buffer(NodeOperation *op);
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
index 5e09902aee2..c9038886b0d 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
@@ -51,13 +51,13 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter,
convertProg->setUseValueAlphaMultiply(false);
if (color1Socket->isLinked()) {
- convertProg->setResolutionInputSocketIndex(1);
+ convertProg->set_canvas_input_index(1);
}
else if (color2Socket->isLinked()) {
- convertProg->setResolutionInputSocketIndex(2);
+ convertProg->set_canvas_input_index(2);
}
else {
- convertProg->setResolutionInputSocketIndex(0);
+ convertProg->set_canvas_input_index(0);
}
converter.addOperation(convertProg);
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
index 14f42cc42f7..8017e063a69 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
@@ -62,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc
index 8a2bbba1c1e..dd68780dc19 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc
@@ -37,16 +37,16 @@ void CombineColorNode::convertToOperations(NodeConverter &converter,
CombineChannelsOperation *operation = new CombineChannelsOperation();
if (inputRSocket->isLinked()) {
- operation->setResolutionInputSocketIndex(0);
+ operation->set_canvas_input_index(0);
}
else if (inputGSocket->isLinked()) {
- operation->setResolutionInputSocketIndex(1);
+ operation->set_canvas_input_index(1);
}
else if (inputBSocket->isLinked()) {
- operation->setResolutionInputSocketIndex(2);
+ operation->set_canvas_input_index(2);
}
else {
- operation->setResolutionInputSocketIndex(3);
+ operation->set_canvas_input_index(3);
}
converter.addOperation(operation);
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
index 3b4f5ca8c94..752597ef937 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
@@ -62,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc
index cd0b5306be1..9c26d7c86a9 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.cc
+++ b/source/blender/compositor/nodes/COM_GlareNode.cc
@@ -66,7 +66,7 @@ void GlareNode::convertToOperations(NodeConverter &converter,
mixvalueoperation->setValue(glare->mix);
MixGlareOperation *mixoperation = new MixGlareOperation();
- mixoperation->setResolutionInputSocketIndex(1);
+ mixoperation->set_canvas_input_index(1);
mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny);
converter.addOperation(glareoperation);
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
index 5042d217f9a..e7b1664c354 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
@@ -53,7 +53,7 @@ void HueSaturationValueCorrectNode::convertToOperations(
converter.addOperation(changeHSV);
MixBlendOperation *blend = new MixBlendOperation();
- blend->setResolutionInputSocketIndex(1);
+ blend->set_canvas_input_index(1);
converter.addOperation(blend);
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
index 54d2caa75af..29e5f39a144 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
@@ -56,7 +56,7 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter,
converter.addOperation(changeHSV);
MixBlendOperation *blend = new MixBlendOperation();
- blend->setResolutionInputSocketIndex(1);
+ blend->set_canvas_input_index(1);
converter.addOperation(blend);
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc
index 4b7a9e8af0f..bbf9e8f3aeb 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.cc
+++ b/source/blender/compositor/nodes/COM_MapUVNode.cc
@@ -34,7 +34,7 @@ void MapUVNode::convertToOperations(NodeConverter &converter,
MapUVOperation *operation = new MapUVOperation();
operation->setAlpha((float)node->custom1);
- operation->setResolutionInputSocketIndex(1);
+ operation->set_canvas_input_index(1);
converter.addOperation(operation);
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 819d2e72f30..f1f41375eba 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -52,6 +52,8 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
+
break;
}
case CMP_SCALE_SCENEPERCENT: {
@@ -68,6 +70,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
break;
}
@@ -81,13 +84,13 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
operation->setOffset(bnode->custom3, bnode->custom4);
operation->setNewWidth(rd->xsch * render_size_factor);
operation->setNewHeight(rd->ysch * render_size_factor);
- operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_size() * 3.0f);
break;
}
@@ -102,6 +105,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
break;
}
diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
index dc41c126ba8..c7365b09f71 100644
--- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
@@ -39,7 +39,7 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter,
}
if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) {
- operation->setResolutionInputSocketIndex(1);
+ operation->set_canvas_input_index(1);
}
converter.addOperation(operation);
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 90f62c6d562..3d8f0bbda7e 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -123,17 +123,54 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
break;
}
case eExecutionModel::FullFrame: {
- TransformOperation *transform_op = new TransformOperation();
- transform_op->set_sampler(sampler);
- transform_op->set_convert_rotate_degree_to_rad(false);
- transform_op->set_invert(invert);
- converter.addOperation(transform_op);
- converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
- converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
- converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
- converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
- converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
- converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ scaleOperation->setSampler(sampler);
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ rotateOperation->set_sampler(sampler);
+ TranslateOperation *translateOperation = new TranslateCanvasOperation();
+
+ converter.addOperation(scaleOperation);
+ converter.addOperation(translateOperation);
+ converter.addOperation(rotateOperation);
+
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
+
+ converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
+
+ converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
+ converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
+
+ NodeOperationInput *stabilization_socket = nullptr;
+ if (invert) {
+ /* Translate -> Rotate -> Scale. */
+ stabilization_socket = translateOperation->getInputSocket(0);
+ converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
+
+ converter.addLink(translateOperation->getOutputSocket(),
+ rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
+
+ converter.mapOutputSocket(getOutputSocket(), scaleOperation->getOutputSocket());
+ }
+ else {
+ /* Scale -> Rotate -> Translate. */
+ stabilization_socket = scaleOperation->getInputSocket(0);
+ converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
+
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(),
+ translateOperation->getInputSocket(0));
+
+ converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
+ }
+
+ xAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
+ yAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
+ scaleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
+ angleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
+ break;
}
}
}
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index d2fb7b54633..b38aad78d90 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -73,16 +73,33 @@ void TransformNode::convertToOperations(NodeConverter &converter,
break;
}
case eExecutionModel::FullFrame: {
- TransformOperation *op = new TransformOperation();
- op->set_sampler((PixelSampler)this->getbNode()->custom1);
- converter.addOperation(op);
-
- converter.mapInputSocket(imageInput, op->getInputSocket(0));
- converter.mapInputSocket(xInput, op->getInputSocket(1));
- converter.mapInputSocket(yInput, op->getInputSocket(2));
- converter.mapInputSocket(angleInput, op->getInputSocket(3));
- converter.mapInputSocket(scaleInput, op->getInputSocket(4));
- converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ converter.addOperation(scaleOperation);
+
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ converter.addOperation(rotateOperation);
+
+ TranslateOperation *translateOperation = new TranslateCanvasOperation();
+ converter.addOperation(translateOperation);
+
+ PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
+ scaleOperation->setSampler(sampler);
+ rotateOperation->set_sampler(sampler);
+ scaleOperation->set_scale_canvas_max_size(context.get_render_size());
+
+ converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
+
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
+
+ converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
+ converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
+ converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
+
+ converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
break;
}
}
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 3a3e98c3472..165a03baf41 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -41,7 +41,9 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
NodeInput *inputYSocket = this->getInputSocket(2);
NodeOutput *outputSocket = this->getOutputSocket(0);
- TranslateOperation *operation = new TranslateOperation();
+ TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ?
+ new TranslateOperation() :
+ new TranslateCanvasOperation();
operation->set_wrapping(data->wrap_axis);
if (data->relative) {
const RenderData *rd = context.getRenderData();
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc
index 3833a8d7ca8..4dbcdbe9e40 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewerNode.cc
@@ -60,10 +60,10 @@ void ViewerNode::convertToOperations(NodeConverter &converter,
viewerOperation->setViewSettings(context.getViewSettings());
viewerOperation->setDisplaySettings(context.getDisplaySettings());
- viewerOperation->setResolutionInputSocketIndex(0);
+ viewerOperation->set_canvas_input_index(0);
if (!imageSocket->isLinked()) {
if (alphaSocket->isLinked()) {
- viewerOperation->setResolutionInputSocketIndex(1);
+ viewerOperation->set_canvas_input_index(1);
}
}
diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc
index e29748dc317..9753e812a8b 100644
--- a/source/blender/compositor/nodes/COM_ZCombineNode.cc
+++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc
@@ -64,18 +64,14 @@ void ZCombineNode::convertToOperations(NodeConverter &converter,
NodeOperation *maskoperation;
if (this->getbNode()->custom1) {
maskoperation = new MathGreaterThanOperation();
- converter.addOperation(maskoperation);
-
- converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0));
- converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1));
}
else {
maskoperation = new MathLessThanOperation();
- converter.addOperation(maskoperation);
-
- converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0));
- converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1));
}
+ converter.addOperation(maskoperation);
+
+ converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0));
+ converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1));
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
AntiAliasOperation *antialiasoperation = new AntiAliasOperation();
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
index 058b422c4a5..412632e2e22 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
@@ -212,31 +212,30 @@ void BlurBaseOperation::updateSize()
this->m_sizeavailable = true;
}
-void BlurBaseOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (!m_extend_bounds) {
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
- NodeOperation::determineResolution(resolution, preferredResolution);
- resolution[0] += 2 * m_size * m_data.sizex;
- resolution[1] += 2 * m_size * m_data.sizey;
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ r_area.xmax += 2 * m_size * m_data.sizex;
+ r_area.ymax += 2 * m_size * m_data.sizey;
break;
}
case eExecutionModel::FullFrame: {
/* Setting a modifier ensures all non main inputs have extended bounds as preferred
- * resolution, avoiding unnecessary resolution conversions that would hide constant
+ * canvas, avoiding unnecessary canvas conversions that would hide constant
* operations. */
- set_determined_resolution_modifier([=](unsigned int res[2]) {
+ set_determined_canvas_modifier([=](rcti &canvas) {
/* Rounding to even prevents jiggling in backdrop while switching size values. */
- res[0] += round_to_even(2 * m_size * m_data.sizex);
- res[1] += round_to_even(2 * m_size * m_data.sizey);
+ canvas.xmax += round_to_even(2 * m_size * m_data.sizex);
+ canvas.ymax += round_to_even(2 * m_size * m_data.sizey);
});
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
break;
}
}
@@ -251,7 +250,7 @@ void BlurBaseOperation::get_area_of_interest(const int input_idx,
r_input_area = output_area;
break;
case 1:
- r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA;
+ r_input_area = use_variable_size_ ? output_area : COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h
index 78b1e919aa6..9ab9bf5a173 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.h
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h
@@ -85,8 +85,7 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe
int get_blur_size(eDimension dim) const;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
virtual void get_area_of_interest(int input_idx,
const rcti &output_area,
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
index f2a43b7dbca..93482dd2a54 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
@@ -34,7 +34,7 @@ constexpr int SIZE_INPUT_INDEX = 3;
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
@@ -266,31 +266,30 @@ void BokehBlurOperation::updateSize()
this->m_sizeavailable = true;
}
-void BokehBlurOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (!m_extend_bounds) {
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
- NodeOperation::determineResolution(resolution, preferredResolution);
- const float max_dim = MAX2(resolution[0], resolution[1]);
- resolution[0] += 2 * this->m_size * max_dim / 100.0f;
- resolution[1] += 2 * this->m_size * max_dim / 100.0f;
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area));
+ r_area.xmax += 2 * this->m_size * max_dim / 100.0f;
+ r_area.ymax += 2 * this->m_size * max_dim / 100.0f;
break;
}
case eExecutionModel::FullFrame: {
- set_determined_resolution_modifier([=](unsigned int res[2]) {
- const float max_dim = MAX2(res[0], res[1]);
+ set_determined_canvas_modifier([=](rcti &canvas) {
+ const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas));
/* Rounding to even prevents image jiggling in backdrop while switching size values. */
float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f);
- res[0] += add_size;
- res[1] += add_size;
+ canvas.xmax += add_size;
+ canvas.ymax += add_size;
});
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
break;
}
}
@@ -312,17 +311,14 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx,
}
case BOKEH_INPUT_INDEX: {
NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX);
- r_input_area.xmin = 0;
- r_input_area.xmax = bokeh_input->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = bokeh_input->getHeight();
+ r_input_area = bokeh_input->get_canvas();
break;
}
case BOUNDING_BOX_INPUT_INDEX:
r_input_area = output_area;
break;
case SIZE_INPUT_INDEX: {
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h
index 59c14305393..84f5a8293ba 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h
@@ -80,8 +80,7 @@ class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelp
this->m_extend_bounds = extend_bounds;
}
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc
index bd5b25b5af8..5c9c8b36ee0 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc
@@ -145,11 +145,13 @@ void BokehImageOperation::deinitExecution()
}
}
-void BokehImageOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void BokehImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = COM_BLUR_BOKEH_PIXELS;
- resolution[1] = COM_BLUR_BOKEH_PIXELS;
+ BLI_rcti_init(&r_area,
+ preferred_area.xmin,
+ preferred_area.xmin + COM_BLUR_BOKEH_PIXELS,
+ preferred_area.ymin,
+ preferred_area.ymin + COM_BLUR_BOKEH_PIXELS);
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h
index 2527233fabd..f7fec5d71a5 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.h
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.h
@@ -128,8 +128,7 @@ class BokehImageOperation : public MultiThreadedOperation {
* \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS,
* COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution:
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
/**
* \brief set the node data
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
index 7457ac9e227..a573a9d7eed 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
@@ -27,7 +27,7 @@ namespace blender::compositor {
CalculateMeanOperation::CalculateMeanOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_iscalculated = false;
@@ -165,11 +165,7 @@ void CalculateMeanOperation::get_area_of_interest(int input_idx,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
- NodeOperation *operation = getInputOperation(input_idx);
- r_input_area.xmin = 0;
- r_input_area.ymin = 0;
- r_input_area.xmax = operation->getWidth();
- r_input_area.ymax = operation->getHeight();
+ r_input_area = get_input_operation(input_idx)->get_canvas();
}
void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
index aee8c0d52e8..0b6590ae4c7 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
@@ -40,7 +40,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation()
this->addOutputSocket(DataType::Color);
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
- this->setResolutionInputSocketIndex(1);
+ this->set_canvas_input_index(1);
flags.can_be_constant = true;
}
diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
index 674cb79a238..c658ecd6394 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
@@ -45,7 +45,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation()
this->addOutputSocket(DataType::Color);
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
- this->setResolutionInputSocketIndex(1);
+ this->set_canvas_input_index(1);
flags.can_be_constant = true;
}
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
index 646238460ba..364b310945e 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
@@ -37,7 +37,7 @@ ColorCurveOperation::ColorCurveOperation()
this->m_inputBlackProgram = nullptr;
this->m_inputWhiteProgram = nullptr;
- this->setResolutionInputSocketIndex(1);
+ this->set_canvas_input_index(1);
}
void ColorCurveOperation::initExecution()
{
@@ -139,7 +139,7 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation()
this->m_inputFacProgram = nullptr;
this->m_inputImageProgram = nullptr;
- this->setResolutionInputSocketIndex(1);
+ this->set_canvas_input_index(1);
}
void ConstantLevelColorCurveOperation::initExecution()
{
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index fb9e2e43c60..f7466b5db34 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -236,8 +236,7 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp
depth_buf.copy_from(inputs[2], area);
}
-void CompositorOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
int width = this->m_rd->xsch * this->m_rd->size / 100;
int height = this->m_rd->ysch * this->m_rd->size / 100;
@@ -254,13 +253,19 @@ void CompositorOperation::determineResolution(unsigned int resolution[2],
RE_ReleaseResult(re);
}
- preferredResolution[0] = width;
- preferredResolution[1] = height;
-
- NodeOperation::determineResolution(resolution, preferredResolution);
-
- resolution[0] = width;
- resolution[1] = height;
+ rcti local_preferred;
+ BLI_rcti_init(&local_preferred, 0, width, 0, height);
+
+ switch (execution_model_) {
+ case eExecutionModel::Tiled:
+ NodeOperation::determine_canvas(local_preferred, r_area);
+ r_area = local_preferred;
+ break;
+ case eExecutionModel::FullFrame:
+ set_determined_canvas_modifier([&](rcti &canvas) { canvas = local_preferred; });
+ NodeOperation::determine_canvas(local_preferred, r_area);
+ break;
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h
index 66367ec8bae..6eb96e01b47 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.h
+++ b/source/blender/compositor/operations/COM_CompositorOperation.h
@@ -115,8 +115,7 @@ class CompositorOperation : public MultiThreadedOperation {
{
return eCompositorPriority::Medium;
}
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setUseAlphaInput(bool value)
{
this->m_useAlphaInput = value;
diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc
index 33d51cca432..c127860a89c 100644
--- a/source/blender/compositor/operations/COM_ConstantOperation.cc
+++ b/source/blender/compositor/operations/COM_ConstantOperation.cc
@@ -22,14 +22,14 @@ namespace blender::compositor {
ConstantOperation::ConstantOperation()
{
- needs_resolution_to_get_constant_ = false;
+ needs_canvas_to_get_constant_ = false;
flags.is_constant_operation = true;
flags.is_fullframe_operation = true;
}
bool ConstantOperation::can_get_constant_elem() const
{
- return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set;
+ return !needs_canvas_to_get_constant_ || this->flags.is_canvas_set;
}
void ConstantOperation::update_memory_buffer(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h
index 31b8d30254b..d44d1939424 100644
--- a/source/blender/compositor/operations/COM_ConstantOperation.h
+++ b/source/blender/compositor/operations/COM_ConstantOperation.h
@@ -31,7 +31,7 @@ namespace blender::compositor {
*/
class ConstantOperation : public NodeOperation {
protected:
- bool needs_resolution_to_get_constant_;
+ bool needs_canvas_to_get_constant_;
public:
ConstantOperation();
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 9a3733dda5b..7b2721ebbb2 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -587,7 +587,7 @@ CombineChannelsOperation::CombineChannelsOperation()
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputChannel1Operation = nullptr;
this->m_inputChannel2Operation = nullptr;
this->m_inputChannel3Operation = nullptr;
diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
index 11a077229fd..807223fd45f 100644
--- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
@@ -29,7 +29,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation()
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->flags.complex = true;
}
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index 12833660fcb..6ac30c22ad1 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -23,7 +23,7 @@ namespace blender::compositor {
CropBaseOperation::CropBaseOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
this->m_settings = nullptr;
@@ -142,13 +142,12 @@ void CropImageOperation::get_area_of_interest(const int input_idx,
r_input_area.ymin = output_area.ymin + this->m_ymin;
}
-void CropImageOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
updateArea();
- resolution[0] = this->m_xmax - this->m_xmin;
- resolution[1] = this->m_ymax - this->m_ymin;
+ r_area.xmax = r_area.xmin + (m_xmax - m_xmin);
+ r_area.ymax = r_area.ymin + (m_ymax - m_ymin);
}
void CropImageOperation::executePixelSampled(float output[4],
diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h
index 57caa4e5834..a156727402b 100644
--- a/source/blender/compositor/operations/COM_CropOperation.h
+++ b/source/blender/compositor/operations/COM_CropOperation.h
@@ -66,8 +66,7 @@ class CropImageOperation : public CropBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc
index 0c660e0b723..f8a575acc3a 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.cc
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc
@@ -153,10 +153,7 @@ void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
- r_input_area.xmin = 0;
- r_input_area.xmax = this->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = this->getHeight();
+ r_input_area = this->get_canvas();
}
DenoiseOperation::DenoiseOperation()
diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc
index 19bd7b2af6f..df637ee6709 100644
--- a/source/blender/compositor/operations/COM_DespeckleOperation.cc
+++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc
@@ -29,7 +29,7 @@ DespeckleOperation::DespeckleOperation()
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->flags.complex = true;
}
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
index 28b40021cd9..b7fd714ba5b 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
@@ -783,7 +783,8 @@ static void step_update_memory_buffer(MemoryBuffer *output,
start = half_window + (i - 1) * window + 1;
for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
- result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]);
+ result.get_value(x + area.xmin, y + start + area.ymin, 0) = selector(temp[y],
+ temp[y + window - 1]);
}
}
}
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
index 102025ed915..e69124205d0 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
@@ -152,10 +152,7 @@ void DirectionalBlurOperation::get_area_of_interest(const int input_idx,
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
- r_input_area.xmin = 0;
- r_input_area.xmax = this->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = this->getHeight();
+ r_input_area = this->get_canvas();
}
void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc
index a4c01fda7ca..d08ff60d5d0 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc
@@ -211,10 +211,7 @@ void DisplaceOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case 0: {
- r_input_area.xmin = 0;
- r_input_area.ymin = 0;
- r_input_area.xmax = getInputOperation(input_idx)->getWidth();
- r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ r_input_area = getInputOperation(input_idx)->get_canvas();
break;
}
case 1: {
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
index e1c531bd49e..712b61be805 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
@@ -138,10 +138,7 @@ void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case 0: {
- r_input_area.xmin = 0;
- r_input_area.ymin = 0;
- r_input_area.xmax = getInputOperation(input_idx)->getWidth();
- r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ r_input_area = get_input_operation(input_idx)->get_canvas();
break;
}
default: {
diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc
index 875b161e208..aa18ff1e827 100644
--- a/source/blender/compositor/operations/COM_DotproductOperation.cc
+++ b/source/blender/compositor/operations/COM_DotproductOperation.cc
@@ -25,7 +25,7 @@ DotproductOperation::DotproductOperation()
this->addInputSocket(DataType::Vector);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Value);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_input1Operation = nullptr;
this->m_input2Operation = nullptr;
flags.can_be_constant = true;
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index 55ae19ad194..d112334b749 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -1399,10 +1399,7 @@ void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
- r_input_area.xmax = this->getWidth();
- r_input_area.xmin = 0;
- r_input_area.ymax = this->getHeight();
- r_input_area.ymin = 0;
+ r_input_area = this->get_canvas();
}
void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
index eb1fd98a590..bf6eee6d3f9 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
@@ -162,13 +162,13 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output,
const float half_h = this->m_data->height / 2.0f;
const float tx = half_w * half_w;
const float ty = half_h * half_h;
- for (const int y : YRange(area)) {
+ for (int y = area.ymin; y < area.ymax; y++) {
const float op_ry = y / op_h;
const float dy = (op_ry - this->m_data->y) / m_aspectRatio;
float *out = output->get_elem(area.xmin, y);
const float *mask = input_mask->get_elem(area.xmin, y);
const float *value = input_value->get_elem(area.xmin, y);
- for (const int x : XRange(area)) {
+ for (int x = area.xmin; x < area.xmax; x++) {
const float op_rx = x / op_w;
const float dx = op_rx - this->m_data->x;
const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy);
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index e0fc45811cb..f45b77c6ebc 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -271,10 +271,7 @@ void FastGaussianBlurOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case IMAGE_INPUT_INDEX:
- r_input_area.xmin = 0;
- r_input_area.xmax = getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = getHeight();
+ r_input_area = this->get_canvas();
break;
default:
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
@@ -411,10 +408,7 @@ void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
- r_input_area.xmin = 0;
- r_input_area.xmax = getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = getHeight();
+ r_input_area = this->get_canvas();
}
void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc
index d0dc6c0b570..2d8865e41e0 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.cc
+++ b/source/blender/compositor/operations/COM_FlipOperation.cc
@@ -22,9 +22,9 @@ namespace blender::compositor {
FlipOperation::FlipOperation()
{
- this->addInputSocket(DataType::Color);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_flipX = true;
this->m_flipY = false;
@@ -75,6 +75,24 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ rcti input_area = r_area;
+ if (m_flipX) {
+ const int width = BLI_rcti_size_x(&input_area) - 1;
+ r_area.xmax = (width - input_area.xmin) + 1;
+ r_area.xmin = (width - input_area.xmax) + 1;
+ }
+ if (m_flipY) {
+ const int height = BLI_rcti_size_y(&input_area) - 1;
+ r_area.ymax = (height - input_area.ymin) + 1;
+ r_area.ymin = (height - input_area.ymax) + 1;
+ }
+ }
+}
+
void FlipOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
@@ -84,7 +102,7 @@ void FlipOperation::get_area_of_interest(const int input_idx,
if (this->m_flipX) {
const int w = (int)this->getWidth() - 1;
r_input_area.xmax = (w - output_area.xmin) + 1;
- r_input_area.xmin = (w - output_area.xmax) - 1;
+ r_input_area.xmin = (w - output_area.xmax) + 1;
}
else {
r_input_area.xmin = output_area.xmin;
@@ -93,7 +111,7 @@ void FlipOperation::get_area_of_interest(const int input_idx,
if (this->m_flipY) {
const int h = (int)this->getHeight() - 1;
r_input_area.ymax = (h - output_area.ymin) + 1;
- r_input_area.ymin = (h - output_area.ymax) - 1;
+ r_input_area.ymin = (h - output_area.ymax) + 1;
}
else {
r_input_area.ymin = output_area.ymin;
@@ -106,10 +124,12 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[0];
+ const int input_offset_x = input_img->get_rect().xmin;
+ const int input_offset_y = input_img->get_rect().ymin;
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
- input_img->read_elem(nx, ny, it.out);
+ input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out);
}
}
diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h
index dba7f82c341..963996a5b0d 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.h
+++ b/source/blender/compositor/operations/COM_FlipOperation.h
@@ -46,6 +46,7 @@ class FlipOperation : public MultiThreadedOperation {
this->m_flipY = flipY;
}
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
index 90755d9f27a..cd4607b1dde 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
@@ -26,6 +26,8 @@ GlareBaseOperation::GlareBaseOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_settings = nullptr;
+ flags.is_fullframe_operation = true;
+ is_output_rendered_ = false;
}
void GlareBaseOperation::initExecution()
{
@@ -69,4 +71,36 @@ bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void GlareBaseOperation::get_area_of_interest(const int input_idx,
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmin = 0;
+ r_input_area.xmax = this->getWidth();
+ r_input_area.ymin = 0;
+ r_input_area.ymax = this->getHeight();
+}
+
+void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (!is_output_rendered_) {
+ MemoryBuffer *input = inputs[0];
+ const bool is_input_inflated = input->is_a_single_elem();
+ if (is_input_inflated) {
+ input = input->inflate();
+ }
+
+ this->generateGlare(output->getBuffer(), input, m_settings);
+ is_output_rendered_ = true;
+
+ if (is_input_inflated) {
+ delete input;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h
index 6dac6f5ecc7..5ca240a9e66 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.h
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h
@@ -49,6 +49,8 @@ class GlareBaseOperation : public SingleThreadedOperation {
*/
NodeGlare *m_settings;
+ bool is_output_rendered_;
+
public:
/**
* Initialize the execution
@@ -68,6 +70,14 @@ class GlareBaseOperation : public SingleThreadedOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
+ void get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area) final;
+
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
protected:
GlareBaseOperation();
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
index 1d3402f5b7b..1bf7cf5ae07 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
@@ -30,12 +30,13 @@ GlareThresholdOperation::GlareThresholdOperation()
this->m_inputProgram = nullptr;
}
-void GlareThresholdOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- NodeOperation::determineResolution(resolution, preferredResolution);
- resolution[0] = resolution[0] / (1 << this->m_settings->quality);
- resolution[1] = resolution[1] / (1 << this->m_settings->quality);
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ const int width = BLI_rcti_size_x(&r_area) / (1 << this->m_settings->quality);
+ const int height = BLI_rcti_size_y(&r_area) / (1 << this->m_settings->quality);
+ r_area.xmax = r_area.xmin + width;
+ r_area.ymax = r_area.ymin + height;
}
void GlareThresholdOperation::initExecution()
@@ -70,4 +71,24 @@ void GlareThresholdOperation::deinitExecution()
this->m_inputProgram = nullptr;
}
+void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float threshold = this->m_settings->threshold;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ if (IMB_colormanagement_get_luminance(color) >= threshold) {
+ it.out[0] = color[0] - threshold;
+ it.out[1] = color[1] - threshold;
+ it.out[2] = color[2] - threshold;
+
+ CLAMP3_MIN(it.out, 0.0f);
+ }
+ else {
+ zero_v3(it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
index a6e971dada7..44f2d717c0e 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_light_types.h"
namespace blender::compositor {
-class GlareThresholdOperation : public NodeOperation {
+class GlareThresholdOperation : public MultiThreadedOperation {
private:
/**
* \brief Cached reference to the inputProgram
@@ -58,8 +58,10 @@ class GlareThresholdOperation : public NodeOperation {
this->m_settings = settings;
}
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc
index e78d389410f..ff389093f5a 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cc
+++ b/source/blender/compositor/operations/COM_ImageOperation.cc
@@ -112,17 +112,14 @@ void BaseImageOperation::deinitExecution()
}
}
-void BaseImageOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
ImBuf *stackbuf = getImBuf();
- resolution[0] = 0;
- resolution[1] = 0;
+ r_area = COM_AREA_NONE;
if (stackbuf) {
- resolution[0] = stackbuf->x;
- resolution[1] = stackbuf->y;
+ BLI_rcti_init(&r_area, 0, stackbuf->x, 0, stackbuf->y);
}
BKE_image_release_ibuf(this->m_image, stackbuf, nullptr);
@@ -222,7 +219,7 @@ void ImageDepthOperation::executePixelSampled(float output[4],
output[0] = 0.0f;
}
else {
- int offset = y * this->m_width + x;
+ int offset = y * getWidth() + x;
output[0] = this->m_depthBuffer[offset];
}
}
diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h
index f8b4239c9f8..e2fdfbf6f81 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.h
+++ b/source/blender/compositor/operations/COM_ImageOperation.h
@@ -54,8 +54,7 @@ class BaseImageOperation : public MultiThreadedOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
virtual ImBuf *getImBuf();
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc
index 5e76c41752c..5d440dd7d76 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.cc
+++ b/source/blender/compositor/operations/COM_InpaintOperation.cc
@@ -293,10 +293,7 @@ void InpaintSimpleOperation::get_area_of_interest(const int input_idx,
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
- r_input_area.xmin = 0;
- r_input_area.xmax = this->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = this->getHeight();
+ r_input_area = this->get_canvas();
}
void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc
index 4f71a1d0d1d..d406b809c7a 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.cc
+++ b/source/blender/compositor/operations/COM_InvertOperation.cc
@@ -29,7 +29,7 @@ InvertOperation::InvertOperation()
this->m_inputColorProgram = nullptr;
this->m_color = true;
this->m_alpha = false;
- setResolutionInputSocketIndex(1);
+ set_canvas_input_index(1);
this->flags.can_be_constant = true;
}
void InvertOperation::initExecution()
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
index c00aafc19a2..b4840926d55 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
@@ -303,11 +303,9 @@ void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data)
MEM_freeN(tile_data);
}
-void KeyingScreenOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = 0;
- resolution[1] = 0;
+ r_area = COM_AREA_NONE;
if (this->m_movieClip) {
MovieClipUser user = {0};
@@ -317,9 +315,9 @@ void KeyingScreenOperation::determineResolution(unsigned int resolution[2],
BKE_movieclip_user_set_frame(&user, clip_frame);
BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height);
-
- resolution[0] = width;
- resolution[1] = height;
+ r_area = preferred_area;
+ r_area.xmax = r_area.xmin + width;
+ r_area.ymax = r_area.ymin + height;
}
}
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
index 0bc47dbea30..7a7dda27710 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -57,8 +57,7 @@ class KeyingScreenOperation : public MultiThreadedOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
TriangulationData *buildVoronoiTriangulation();
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index ad047c619f8..ba38e583b30 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -23,12 +23,12 @@ namespace blender::compositor {
MapUVOperation::MapUVOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Color);
this->m_alpha = 0.0f;
this->flags.complex = true;
- setResolutionInputSocketIndex(1);
+ set_canvas_input_index(UV_INPUT_INDEX);
this->m_inputUVProgram = nullptr;
this->m_inputColorProgram = nullptr;
@@ -36,11 +36,11 @@ MapUVOperation::MapUVOperation()
void MapUVOperation::init_data()
{
- NodeOperation *image_input = get_input_operation(0);
+ NodeOperation *image_input = get_input_operation(IMAGE_INPUT_INDEX);
image_width_ = image_input->getWidth();
image_height_ = image_input->getHeight();
- NodeOperation *uv_input = get_input_operation(1);
+ NodeOperation *uv_input = get_input_operation(UV_INPUT_INDEX);
uv_width_ = uv_input->getWidth();
uv_height_ = uv_input->getHeight();
}
@@ -205,14 +205,11 @@ void MapUVOperation::get_area_of_interest(const int input_idx,
rcti &r_input_area)
{
switch (input_idx) {
- case 0: {
- r_input_area.xmin = 0;
- r_input_area.xmax = image_width_;
- r_input_area.ymin = 0;
- r_input_area.ymax = image_height_;
+ case IMAGE_INPUT_INDEX: {
+ r_input_area = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
break;
}
- case 1: {
+ case UV_INPUT_INDEX: {
r_input_area = output_area;
expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
break;
@@ -224,7 +221,7 @@ void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
const rcti &UNUSED(area),
Span<MemoryBuffer *> inputs)
{
- const MemoryBuffer *uv_input = inputs[1];
+ const MemoryBuffer *uv_input = inputs[UV_INPUT_INDEX];
uv_input_read_fn_ = [=](float x, float y, float *out) {
uv_input->read_elem_bilinear(x, y, out);
};
@@ -234,7 +231,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
- const MemoryBuffer *input_image = inputs[0];
+ const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
float xy[2] = {(float)it.x, (float)it.y};
float uv[2];
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h
index 65fbcb461c9..49c6689f700 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.h
+++ b/source/blender/compositor/operations/COM_MapUVOperation.h
@@ -24,6 +24,8 @@ namespace blender::compositor {
class MapUVOperation : public MultiThreadedOperation {
private:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int UV_INPUT_INDEX = 1;
/**
* Cached reference to the inputProgram
*/
diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc
index 84992f23924..65b89a8c79a 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.cc
+++ b/source/blender/compositor/operations/COM_MaskOperation.cc
@@ -109,22 +109,15 @@ void MaskOperation::deinitExecution()
}
}
-void MaskOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (this->m_maskWidth == 0 || this->m_maskHeight == 0) {
- NodeOperation::determineResolution(resolution, preferredResolution);
+ r_area = COM_AREA_NONE;
}
else {
- unsigned int nr[2];
-
- nr[0] = this->m_maskWidth;
- nr[1] = this->m_maskHeight;
-
- NodeOperation::determineResolution(resolution, nr);
-
- resolution[0] = this->m_maskWidth;
- resolution[1] = this->m_maskHeight;
+ r_area = preferred_area;
+ r_area.xmax = r_area.xmin + m_maskWidth;
+ r_area.ymax = r_area.ymin + m_maskHeight;
}
}
diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h
index 81e344c0451..cc7eb0c022c 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.h
+++ b/source/blender/compositor/operations/COM_MaskOperation.h
@@ -54,8 +54,7 @@ class MaskOperation : public MultiThreadedOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
public:
MaskOperation();
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc
index 2256dce011b..d3fb83caf7c 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc
@@ -51,22 +51,19 @@ void MathBaseOperation::deinitExecution()
this->m_inputValue3Operation = nullptr;
}
-void MathBaseOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
NodeOperationInput *socket;
- unsigned int tempPreferredResolution[2] = {0, 0};
- unsigned int tempResolution[2];
-
+ rcti temp_area;
socket = this->getInputSocket(0);
- socket->determineResolution(tempResolution, tempPreferredResolution);
- if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) {
- this->setResolutionInputSocketIndex(0);
+ const bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area);
+ if (determined) {
+ this->set_canvas_input_index(0);
}
else {
- this->setResolutionInputSocketIndex(1);
+ this->set_canvas_input_index(1);
}
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
}
void MathBaseOperation::clampIfNeeded(float *color)
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h
index d2da05db68e..0188eb50fa8 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.h
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.h
@@ -75,8 +75,7 @@ class MathBaseOperation : public MultiThreadedOperation {
/**
* Determine resolution
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setUseClamp(bool value)
{
diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc
index 77ecbf60356..895d32e6fee 100644
--- a/source/blender/compositor/operations/COM_MixOperation.cc
+++ b/source/blender/compositor/operations/COM_MixOperation.cc
@@ -66,29 +66,27 @@ void MixBaseOperation::executePixelSampled(float output[4], float x, float y, Pi
output[3] = inputColor1[3];
}
-void MixBaseOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
NodeOperationInput *socket;
- unsigned int tempPreferredResolution[2] = {0, 0};
- unsigned int tempResolution[2];
+ rcti temp_area;
socket = this->getInputSocket(1);
- socket->determineResolution(tempResolution, tempPreferredResolution);
- if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) {
- this->setResolutionInputSocketIndex(1);
+ bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area);
+ if (determined) {
+ this->set_canvas_input_index(1);
}
else {
socket = this->getInputSocket(2);
- socket->determineResolution(tempResolution, tempPreferredResolution);
- if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) {
- this->setResolutionInputSocketIndex(2);
+ determined = socket->determine_canvas(COM_AREA_NONE, temp_area);
+ if (determined) {
+ this->set_canvas_input_index(2);
}
else {
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
}
}
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
}
void MixBaseOperation::deinitExecution()
@@ -111,7 +109,7 @@ void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
p.value_stride = input_value->elem_stride;
p.color1_stride = input_color1->elem_stride;
p.color2_stride = input_color2->elem_stride;
- for (const int y : YRange(area)) {
+ for (int y = area.ymin; y < area.ymax; y++) {
p.out = output->get_elem(area.xmin, y);
p.row_end = p.out + width * output->elem_stride;
p.value = input_value->get_elem(area.xmin, y);
diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h
index 7ef9d78d58f..fabbea422eb 100644
--- a/source/blender/compositor/operations/COM_MixOperation.h
+++ b/source/blender/compositor/operations/COM_MixOperation.h
@@ -87,8 +87,7 @@ class MixBaseOperation : public MultiThreadedOperation {
*/
void deinitExecution() override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setUseValueAlphaMultiply(const bool value)
{
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
index e36e93984fb..aed91d4d46e 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
@@ -29,8 +29,9 @@ MovieClipAttributeOperation::MovieClipAttributeOperation()
this->m_framenumber = 0;
this->m_attribute = MCA_X;
this->m_invert = false;
- needs_resolution_to_get_constant_ = true;
+ needs_canvas_to_get_constant_ = true;
is_value_calculated_ = false;
+ stabilization_resolution_socket_ = nullptr;
}
void MovieClipAttributeOperation::initExecution()
@@ -42,7 +43,7 @@ void MovieClipAttributeOperation::initExecution()
void MovieClipAttributeOperation::calc_value()
{
- BLI_assert(this->get_flags().is_resolution_set);
+ BLI_assert(this->get_flags().is_canvas_set);
is_value_calculated_ = true;
if (this->m_clip == nullptr) {
return;
@@ -53,8 +54,17 @@ void MovieClipAttributeOperation::calc_value()
scale = 1.0f;
angle = 0.0f;
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber);
- BKE_tracking_stabilization_data_get(
- this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle);
+ NodeOperation &stabilization_operation =
+ stabilization_resolution_socket_ ?
+ stabilization_resolution_socket_->getLink()->getOperation() :
+ *this;
+ BKE_tracking_stabilization_data_get(this->m_clip,
+ clip_framenr,
+ stabilization_operation.getWidth(),
+ stabilization_operation.getHeight(),
+ loc,
+ &scale,
+ &angle);
switch (this->m_attribute) {
case MCA_SCALE:
this->m_value = scale;
@@ -87,11 +97,9 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4],
output[0] = this->m_value;
}
-void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void MovieClipAttributeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
const float *MovieClipAttributeOperation::get_constant_elem()
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
index 28c39d4dad3..50680653aea 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
@@ -42,6 +42,7 @@ class MovieClipAttributeOperation : public ConstantOperation {
bool m_invert;
MovieClipAttribute m_attribute;
bool is_value_calculated_;
+ NodeOperationInput *stabilization_resolution_socket_;
public:
/**
@@ -55,8 +56,7 @@ class MovieClipAttributeOperation : public ConstantOperation {
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
const float *get_constant_elem() override;
@@ -77,6 +77,14 @@ class MovieClipAttributeOperation : public ConstantOperation {
this->m_invert = invert;
}
+ /**
+ * Set an operation socket which input will be used to get the resolution for stabilization.
+ */
+ void set_socket_input_resolution_for_stabilization(NodeOperationInput *input_socket)
+ {
+ stabilization_resolution_socket_ = input_socket;
+ }
+
private:
void calc_value();
};
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index e520b928edf..1ca33b12432 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -71,19 +71,13 @@ void MovieClipBaseOperation::deinitExecution()
}
}
-void MovieClipBaseOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
- resolution[0] = 0;
- resolution[1] = 0;
-
+ r_area = COM_AREA_NONE;
if (this->m_movieClip) {
int width, height;
-
BKE_movieclip_get_size(this->m_movieClip, this->m_movieClipUser, &width, &height);
-
- resolution[0] = width;
- resolution[1] = height;
+ BLI_rcti_init(&r_area, 0, width, 0, height);
}
}
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h
index 0a0c4c00f81..465ccd4fc00 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.h
@@ -41,8 +41,7 @@ class MovieClipBaseOperation : public MultiThreadedOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
public:
MovieClipBaseOperation();
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
index d3424959061..49f43d2c1a7 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
@@ -29,15 +29,14 @@ MovieDistortionOperation::MovieDistortionOperation(bool distortion)
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_movieClip = nullptr;
this->m_apply = distortion;
}
-void MovieDistortionOperation::initExecution()
+void MovieDistortionOperation::init_data()
{
- this->m_inputOperation = this->getInputSocketReader(0);
if (this->m_movieClip) {
MovieTracking *tracking = &this->m_movieClip->tracking;
MovieClipUser clipUser = {0};
@@ -49,10 +48,10 @@ void MovieDistortionOperation::initExecution()
float delta[2];
rcti full_frame;
full_frame.xmin = full_frame.ymin = 0;
- full_frame.xmax = this->m_width;
- full_frame.ymax = this->m_height;
+ full_frame.xmax = this->getWidth();
+ full_frame.ymax = this->getHeight();
BKE_tracking_max_distortion_delta_across_bound(
- tracking, this->m_width, this->m_height, &full_frame, !this->m_apply, delta);
+ tracking, this->getWidth(), this->getHeight(), &full_frame, !this->m_apply, delta);
/* 5 is just in case we didn't hit real max of distortion in
* BKE_tracking_max_undistortion_delta_across_bound
@@ -60,15 +59,25 @@ void MovieDistortionOperation::initExecution()
m_margin[0] = delta[0] + 5;
m_margin[1] = delta[1] + 5;
- this->m_distortion = BKE_tracking_distortion_new(
- tracking, calibration_width, calibration_height);
this->m_calibration_width = calibration_width;
this->m_calibration_height = calibration_height;
this->m_pixel_aspect = tracking->camera.pixel_aspect;
}
else {
m_margin[0] = m_margin[1] = 0;
- this->m_distortion = nullptr;
+ }
+}
+
+void MovieDistortionOperation::initExecution()
+{
+ m_inputOperation = this->getInputSocketReader(0);
+ if (m_movieClip) {
+ MovieTracking *tracking = &m_movieClip->tracking;
+ m_distortion = BKE_tracking_distortion_new(
+ tracking, m_calibration_width, m_calibration_height);
+ }
+ else {
+ m_distortion = nullptr;
}
}
@@ -89,8 +98,8 @@ void MovieDistortionOperation::executePixelSampled(float output[4],
if (this->m_distortion != nullptr) {
/* float overscan = 0.0f; */
const float pixel_aspect = this->m_pixel_aspect;
- const float w = (float)this->m_width /* / (1 + overscan) */;
- const float h = (float)this->m_height /* / (1 + overscan) */;
+ const float w = (float)this->getWidth() /* / (1 + overscan) */;
+ const float h = (float)this->getHeight() /* / (1 + overscan) */;
const float aspx = w / (float)this->m_calibration_width;
const float aspy = h / (float)this->m_calibration_height;
float in[2];
@@ -152,8 +161,8 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output
/* `float overscan = 0.0f;` */
const float pixel_aspect = this->m_pixel_aspect;
- const float w = (float)this->m_width /* `/ (1 + overscan)` */;
- const float h = (float)this->m_height /* `/ (1 + overscan)` */;
+ const float w = (float)this->getWidth() /* `/ (1 + overscan)` */;
+ const float h = (float)this->getHeight() /* `/ (1 + overscan)` */;
const float aspx = w / (float)this->m_calibration_width;
const float aspy = h / (float)this->m_calibration_height;
float xy[2];
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
index 69c2f9c269c..abf0553b75b 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
@@ -44,6 +44,7 @@ class MovieDistortionOperation : public MultiThreadedOperation {
MovieDistortionOperation(bool distortion);
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc
index c3e72d2575f..7d79b375b45 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.cc
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc
@@ -133,11 +133,7 @@ void NormalizeOperation::get_area_of_interest(const int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
- NodeOperation *input = get_input_operation(0);
- r_input_area.xmin = 0;
- r_input_area.xmax = input->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = input->getHeight();
+ r_input_area = get_input_operation(0)->get_canvas();
}
void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
index 5b6f650d40e..d436b00a6e3 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
@@ -86,8 +86,8 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam
/* prepare the file with all the channels */
- if (IMB_exr_begin_write(
- exrhandle, filename, width, height, this->m_format->exr_codec, nullptr) == 0) {
+ if (!IMB_exr_begin_write(
+ exrhandle, filename, width, height, this->m_format->exr_codec, nullptr)) {
printf("Error Writing Singlelayer Multiview Openexr\n");
IMB_exr_close(exrhandle);
}
@@ -200,8 +200,7 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename
/* prepare the file with all the channels for the header */
StampData *stamp_data = createStampData();
- if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) ==
- 0) {
+ if (!IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) {
printf("Error Writing Multilayer Multiview Openexr\n");
IMB_exr_close(exrhandle);
BKE_stamp_data_free(stamp_data);
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc
index 402d29893a4..79be95bb686 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc
@@ -339,7 +339,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *
this->m_exr_codec = exr_codec;
this->m_exr_half_float = exr_half_float;
this->m_viewName = viewName;
- this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY);
+ this->set_canvas_input_index(RESOLUTION_INPUT_ANY);
}
void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc
index 94827cd1b02..4fc238bc094 100644
--- a/source/blender/compositor/operations/COM_PixelateOperation.cc
+++ b/source/blender/compositor/operations/COM_PixelateOperation.cc
@@ -24,7 +24,7 @@ PixelateOperation::PixelateOperation(DataType datatype)
{
this->addInputSocket(datatype);
this->addOutputSocket(datatype);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
}
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
index d2a06ddd7c4..65cd08456ef 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
@@ -209,15 +209,13 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect)
return data;
}
-void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void PlaneCornerPinMaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (execution_model_ == eExecutionModel::FullFrame) {
- /* Determine inputs resolution. */
- PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution);
+ /* Determine input canvases. */
+ PlaneDistortMaskOperation::determine_canvas(preferred_area, r_area);
}
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx),
@@ -225,7 +223,7 @@ void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_id
rcti &r_input_area)
{
/* All corner inputs are used as constants. */
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
}
/* ******** PlaneCornerPinWarpImageOperation ******** */
@@ -322,7 +320,7 @@ void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx,
}
else {
/* Corner inputs are used as constants. */
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
}
}
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
index 2831e937147..7b3917923d2 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
@@ -43,8 +43,7 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
void *initializeTileData(rcti *rect) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
};
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index ccabb3cf11c..31ef41789fd 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -73,7 +73,7 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo
PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
this->flags.complex = true;
@@ -196,10 +196,7 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
}
/* TODO: figure out the area needed for warping and EWA filtering. */
- r_input_area.xmin = 0;
- r_input_area.ymin = 0;
- r_input_area.xmax = get_input_operation(0)->getWidth();
- r_input_area.ymax = get_input_operation(0)->getHeight();
+ r_input_area = get_input_operation(0)->get_canvas();
/* Old implementation but resulting coordinates are way out of input operation bounds and in some
* cases the area result may incorrectly cause cropping. */
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
index bf24f843ca2..7226a133a52 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -83,19 +83,17 @@ void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame)
}
}
-void PlaneTrackCommon::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = 0;
- resolution[1] = 0;
-
+ r_area = COM_AREA_NONE;
if (this->m_movieClip) {
int width, height;
MovieClipUser user = {0};
BKE_movieclip_user_set_frame(&user, this->m_framenumber);
BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height);
- resolution[0] = width;
- resolution[1] = height;
+ r_area = preferred_area;
+ r_area.xmax = r_area.xmin + width;
+ r_area.ymax = r_area.ymin + height;
}
}
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
index d2027755162..60845aac514 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -41,7 +41,7 @@ class PlaneTrackCommon {
* implementation classes must make wrappers to use these methods, see below.
*/
void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op);
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ void determine_canvas(const rcti &preferred_area, rcti &r_area);
public:
PlaneTrackCommon();
@@ -77,13 +77,13 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr
void initExecution() override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override
{
- PlaneTrackCommon::determineResolution(resolution, preferredResolution);
+ PlaneTrackCommon::determine_canvas(preferred_area, r_area);
- unsigned int temp[2];
- NodeOperation::determineResolution(temp, resolution);
+ rcti unused;
+ rcti &preferred = r_area;
+ NodeOperation::determine_canvas(preferred, unused);
}
};
@@ -98,12 +98,13 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation,
void initExecution() override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override
{
- PlaneTrackCommon::determineResolution(resolution, preferredResolution);
- unsigned int temp[2];
- NodeOperation::determineResolution(temp, resolution);
+ PlaneTrackCommon::determine_canvas(preferred_area, r_area);
+
+ rcti unused;
+ rcti &preferred = r_area;
+ NodeOperation::determine_canvas(preferred, unused);
}
};
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc
index fa8b5ffcabf..34520264d54 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.cc
+++ b/source/blender/compositor/operations/COM_PreviewOperation.cc
@@ -41,7 +41,7 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
const unsigned int defaultHeight)
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->m_preview = nullptr;
this->m_outputBuffer = nullptr;
this->m_input = nullptr;
@@ -130,14 +130,14 @@ bool PreviewOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
-void PreviewOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
/* Use default preview resolution as preferred ensuring it has size so that
* generated inputs (which don't have resolution on their own) are displayed */
BLI_assert(this->m_defaultWidth > 0 && this->m_defaultHeight > 0);
- unsigned int previewPreferredRes[2] = {this->m_defaultWidth, this->m_defaultHeight};
- NodeOperation::determineResolution(resolution, previewPreferredRes);
+ rcti local_preferred;
+ BLI_rcti_init(&local_preferred, 0, m_defaultWidth, 0, m_defaultHeight);
+ NodeOperation::determine_canvas(local_preferred, r_area);
/* If resolution is 0 there are two possible scenarios:
* - Either node is not connected at all
@@ -148,8 +148,8 @@ void PreviewOperation::determineResolution(unsigned int resolution[2],
* The latter case would only happen if an input doesn't set any resolution ignoring output
* preferred resolution. In such case preview size will be 0 too.
*/
- int width = resolution[0];
- int height = resolution[1];
+ int width = BLI_rcti_size_x(&r_area);
+ int height = BLI_rcti_size_y(&r_area);
this->m_divider = 0.0f;
if (width > 0 && height > 0) {
if (width > height) {
@@ -162,8 +162,7 @@ void PreviewOperation::determineResolution(unsigned int resolution[2],
width = width * this->m_divider;
height = height * this->m_divider;
- resolution[0] = width;
- resolution[1] = height;
+ BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height);
}
eCompositorPriority PreviewOperation::getRenderPriority() const
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h
index 05dae9c4dd8..4bd0f07d882 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.h
+++ b/source/blender/compositor/operations/COM_PreviewOperation.h
@@ -58,8 +58,7 @@ class PreviewOperation : public MultiThreadedOperation {
eCompositorPriority getRenderPriority() const override;
void executeRegion(rcti *rect, unsigned int tileNumber) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
index fcab5dd5751..2982f59a019 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
@@ -131,13 +131,30 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->unlockMutex();
}
+void ProjectorLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ switch (execution_model_) {
+ case eExecutionModel::FullFrame: {
+ set_determined_canvas_modifier([=](rcti &canvas) {
+ /* Ensure screen space. */
+ BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin);
+ });
+ break;
+ }
+ default:
+ break;
+ }
+
+ NodeOperation::determine_canvas(preferred_area, r_area);
+}
+
void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx == 1) {
/* Dispersion input is used as constant only. */
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
return;
}
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
index 7c7626bf271..b42fa3a361c 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
@@ -62,6 +62,7 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation {
void updateDispersion();
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
index d35d2516f16..599370751bb 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
@@ -36,16 +36,17 @@ void *ReadBufferOperation::initializeTileData(rcti * /*rect*/)
return m_buffer;
}
-void ReadBufferOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (this->m_memoryProxy != nullptr) {
WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation();
- operation->determineResolution(resolution, preferredResolution);
- operation->setResolution(resolution);
+ operation->determine_canvas(preferred_area, r_area);
+ operation->set_canvas(r_area);
/** \todo may not occur! But does with blur node. */
if (this->m_memoryProxy->getExecutor()) {
+ uint resolution[2] = {static_cast<uint>(BLI_rcti_size_x(&r_area)),
+ static_cast<uint>(BLI_rcti_size_y(&r_area))};
this->m_memoryProxy->getExecutor()->setResolution(resolution);
}
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h
index 8b96b961a43..02f6eccd246 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.h
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h
@@ -43,8 +43,7 @@ class ReadBufferOperation : public NodeOperation {
return this->m_memoryProxy;
}
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void *initializeTileData(rcti *rect) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc
index 72e2c92c9cf..2ac551ffe6f 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cc
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc
@@ -196,15 +196,13 @@ void RenderLayersProg::deinitExecution()
}
}
-void RenderLayersProg::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
Scene *sce = this->getScene();
Render *re = (sce) ? RE_GetSceneRender(sce) : nullptr;
RenderResult *rr = nullptr;
- resolution[0] = 0;
- resolution[1] = 0;
+ r_area = COM_AREA_NONE;
if (re) {
rr = RE_AcquireResultRead(re);
@@ -215,8 +213,7 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
if (view_layer) {
RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name);
if (rl) {
- resolution[0] = rl->rectx;
- resolution[1] = rl->recty;
+ BLI_rcti_init(&r_area, 0, rl->rectx, 0, rl->recty);
}
}
}
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h
index dd76a56d645..b499afd45df 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.h
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.h
@@ -73,8 +73,7 @@ class RenderLayersProg : public MultiThreadedOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
/**
* retrieve the reference to the float buffer of the renderer.
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index e3c482c15cb..9e26c93feac 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -25,10 +25,10 @@ namespace blender::compositor {
RotateOperation::RotateOperation()
{
- this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Value);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Value, ResizeMode::None);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_imageSocket = nullptr;
this->m_degreeSocket = nullptr;
this->m_doDegree2RadConversion = false;
@@ -36,6 +36,21 @@ RotateOperation::RotateOperation()
sampler_ = PixelSampler::Bilinear;
}
+void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y)
+{
+ r_x = (BLI_rcti_size_x(&area) - 1) / 2.0;
+ r_y = (BLI_rcti_size_y(&area) - 1) / 2.0;
+}
+
+void RotateOperation::get_rotation_offset(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ float &r_offset_x,
+ float &r_offset_y)
+{
+ r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f;
+ r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f;
+}
+
void RotateOperation::get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
@@ -48,14 +63,14 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area,
const float dxmax = area.xmax - center_x;
const float dymax = area.ymax - center_y;
- const float x1 = center_x + (cosine * dxmin + sine * dymin);
- const float x2 = center_x + (cosine * dxmax + sine * dymin);
- const float x3 = center_x + (cosine * dxmin + sine * dymax);
- const float x4 = center_x + (cosine * dxmax + sine * dymax);
- const float y1 = center_y + (-sine * dxmin + cosine * dymin);
- const float y2 = center_y + (-sine * dxmax + cosine * dymin);
- const float y3 = center_y + (-sine * dxmin + cosine * dymax);
- const float y4 = center_y + (-sine * dxmax + cosine * dymax);
+ const float x1 = center_x + (cosine * dxmin + (-sine) * dymin);
+ const float x2 = center_x + (cosine * dxmax + (-sine) * dymin);
+ const float x3 = center_x + (cosine * dxmin + (-sine) * dymax);
+ const float x4 = center_x + (cosine * dxmax + (-sine) * dymax);
+ const float y1 = center_y + (sine * dxmin + cosine * dymin);
+ const float y2 = center_y + (sine * dxmax + cosine * dymin);
+ const float y3 = center_y + (sine * dxmin + cosine * dymax);
+ const float y4 = center_y + (sine * dxmax + cosine * dymax);
const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4)));
const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4)));
const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4)));
@@ -67,10 +82,56 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area,
r_bounds.ymax = ceil(maxy);
}
+void RotateOperation::get_area_rotation_bounds_inverted(const rcti &area,
+ const float center_x,
+ const float center_y,
+ const float sine,
+ const float cosine,
+ rcti &r_bounds)
+{
+ get_area_rotation_bounds(area, center_x, center_y, -sine, cosine, r_bounds);
+}
+
+void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ const float sine,
+ const float cosine,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ float center_x, center_y;
+ get_rotation_center(input_canvas, center_x, center_y);
+
+ float rotate_offset_x, rotate_offset_y;
+ get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y);
+
+ r_input_area = output_area;
+ BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y);
+ get_area_rotation_bounds_inverted(r_input_area, center_x, center_y, sine, cosine, r_input_area);
+}
+
+void RotateOperation::get_rotation_canvas(const rcti &input_canvas,
+ const float sine,
+ const float cosine,
+ rcti &r_canvas)
+{
+ float center_x, center_y;
+ get_rotation_center(input_canvas, center_x, center_y);
+
+ rcti rot_bounds;
+ get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds);
+
+ float offset_x, offset_y;
+ get_rotation_offset(input_canvas, rot_bounds, offset_x, offset_y);
+ r_canvas = rot_bounds;
+ BLI_rcti_translate(&r_canvas, -offset_x, -offset_y);
+}
+
void RotateOperation::init_data()
{
- this->m_centerX = (getWidth() - 1) / 2.0;
- this->m_centerY = (getHeight() - 1) / 2.0;
+ if (execution_model_ == eExecutionModel::Tiled) {
+ get_rotation_center(get_canvas(), m_centerX, m_centerY);
+ }
}
void RotateOperation::initExecution()
@@ -94,11 +155,7 @@ inline void RotateOperation::ensureDegree()
this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
break;
case eExecutionModel::FullFrame:
- NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
- const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
- degree[0] = is_constant_degree ?
- static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
- 0.0f;
+ degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
break;
}
@@ -159,18 +216,40 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ if (execution_model_ == eExecutionModel::Tiled) {
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ return;
+ }
+
+ const bool image_determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (image_determined) {
+ rcti input_canvas = r_area;
+ rcti unused;
+ getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused);
+
+ ensureDegree();
+
+ get_rotation_canvas(input_canvas, m_sine, m_cosine, r_area);
+ }
+}
+
void RotateOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx == DEGREE_INPUT_INDEX) {
- /* Degrees input is always used as constant. */
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
return;
}
ensureDegree();
- get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area);
+
+ const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
+ get_rotation_area_of_interest(
+ input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area);
expand_area_for_sampler(r_input_area, sampler_);
}
@@ -178,13 +257,20 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
- ensureDegree();
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
+
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ float center_x, center_y;
+ get_rotation_center(image_op->get_canvas(), center_x, center_y);
+ float rotate_offset_x, rotate_offset_y;
+ get_rotation_offset(
+ image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y);
+
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
- float x = it.x;
- float y = it.y;
- rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine);
- input_img->read_elem_sampled(x, y, sampler_, it.out);
+ float x = rotate_offset_x + it.x + canvas_.xmin;
+ float y = rotate_offset_y + it.y + canvas_.ymin;
+ rotate_coords(x, y, center_x, center_y, m_sine, m_cosine);
+ input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out);
}
}
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index f0de699f9ce..76f203cfbc0 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -29,8 +29,10 @@ class RotateOperation : public MultiThreadedOperation {
SocketReader *m_imageSocket;
SocketReader *m_degreeSocket;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
float m_centerX;
float m_centerY;
+
float m_cosine;
float m_sine;
bool m_doDegree2RadConversion;
@@ -49,12 +51,33 @@ class RotateOperation : public MultiThreadedOperation {
y = center_y + (-sine * dx + cosine * dy);
}
+ static void get_rotation_center(const rcti &area, float &r_x, float &r_y);
+ static void get_rotation_offset(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ float &r_offset_x,
+ float &r_offset_y);
static void get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
const float sine,
const float cosine,
rcti &r_bounds);
+ static void get_area_rotation_bounds_inverted(const rcti &area,
+ const float center_x,
+ const float center_y,
+ const float sine,
+ const float cosine,
+ rcti &r_bounds);
+ static void get_rotation_area_of_interest(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ const float sine,
+ const float cosine,
+ const rcti &output_area,
+ rcti &r_input_area);
+ static void get_rotation_canvas(const rcti &input_canvas,
+ const float sine,
+ const float cosine,
+ rcti &r_canvas);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
@@ -80,6 +103,8 @@ class RotateOperation : public MultiThreadedOperation {
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
+
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index 0161b837915..dbd8faf0f1d 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -38,17 +38,21 @@ BaseScaleOperation::BaseScaleOperation()
m_variable_size = false;
}
+void BaseScaleOperation::set_scale_canvas_max_size(Size2f size)
+{
+ max_scale_canvas_size_ = size;
+}
+
ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color)
{
}
ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation()
{
- this->addInputSocket(data_type);
+ this->addInputSocket(data_type, ResizeMode::None);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(data_type);
- this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
this->m_inputXOperation = nullptr;
this->m_inputYOperation = nullptr;
@@ -64,34 +68,52 @@ float ScaleOperation::get_constant_scale(const int input_op_idx, const float fac
return 1.0f;
}
-float ScaleOperation::get_constant_scale_x()
+float ScaleOperation::get_constant_scale_x(const float width)
+{
+ return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width));
+}
+
+float ScaleOperation::get_constant_scale_y(const float height)
{
- return get_constant_scale(1, get_relative_scale_x_factor());
+ return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height));
}
-float ScaleOperation::get_constant_scale_y()
+bool ScaleOperation::is_scaling_variable()
{
- return get_constant_scale(2, get_relative_scale_y_factor());
+ return !get_input_operation(X_INPUT_INDEX)->get_flags().is_constant_operation ||
+ !get_input_operation(Y_INPUT_INDEX)->get_flags().is_constant_operation;
}
-void ScaleOperation::scale_area(
- rcti &rect, float center_x, float center_y, float scale_x, float scale_y)
+void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
{
- rect.xmin = scale_coord(rect.xmin, center_x, scale_x);
- rect.xmax = scale_coord(rect.xmax, center_x, scale_x);
- rect.ymin = scale_coord(rect.ymin, center_y, scale_y);
- rect.ymax = scale_coord(rect.ymax, center_y, scale_y);
+ const rcti src_area = area;
+ const float center_x = BLI_rcti_size_x(&area) / 2.0f;
+ const float center_y = BLI_rcti_size_y(&area) / 2.0f;
+ area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x));
+ area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x));
+ area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y));
+ area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y));
+
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y);
+ BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y);
}
-void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
+void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size)
{
- scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
+
+ if (BLI_rcti_size_x(&area) > max_size.x) {
+ area.xmax = area.xmin + max_size.x;
+ }
+ if (BLI_rcti_size_y(&area) > max_size.y) {
+ area.ymax = area.ymin + max_size.y;
+ }
}
void ScaleOperation::init_data()
{
- m_centerX = getWidth() / 2.0f;
- m_centerY = getHeight() / 2.0f;
+ canvas_center_x_ = canvas_.xmin + getWidth() / 2.0f;
+ canvas_center_y_ = canvas_.ymin + getHeight() / 2.0f;
}
void ScaleOperation::initExecution()
@@ -108,18 +130,52 @@ void ScaleOperation::deinitExecution()
this->m_inputYOperation = nullptr;
}
+void ScaleOperation::get_scale_offset(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ float &r_scale_offset_x,
+ float &r_scale_offset_y)
+{
+ r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f;
+ r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f;
+}
+
+void ScaleOperation::get_scale_area_of_interest(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ const float relative_scale_x,
+ const float relative_scale_y,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f;
+ const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y);
+
+ r_input_area.xmin = floorf(
+ scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x));
+ r_input_area.xmax = ceilf(
+ scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x));
+ r_input_area.ymin = floorf(
+ scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y));
+ r_input_area.ymax = ceilf(
+ scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y));
+}
+
void ScaleOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
r_input_area = output_area;
- if (input_idx != 0 || m_variable_size) {
+ if (input_idx != 0 || is_scaling_variable()) {
return;
}
- float scale_x = get_constant_scale_x();
- float scale_y = get_constant_scale_y();
- scale_area(r_input_area, scale_x, scale_y);
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const float scale_x = get_constant_scale_x(image_op->getWidth());
+ const float scale_y = get_constant_scale_y(image_op->getHeight());
+
+ get_scale_area_of_interest(
+ image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@@ -127,18 +183,70 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
- const MemoryBuffer *input_img = inputs[0];
- MemoryBuffer *input_x = inputs[1];
- MemoryBuffer *input_y = inputs[2];
- const float scale_x_factor = get_relative_scale_x_factor();
- const float scale_y_factor = get_relative_scale_y_factor();
+ NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const int input_image_width = input_image_op->getWidth();
+ const int input_image_height = input_image_op->getHeight();
+ const float scale_x_factor = get_relative_scale_x_factor(input_image_width);
+ const float scale_y_factor = get_relative_scale_y_factor(input_image_height);
+ const float scale_center_x = input_image_width / 2.0f;
+ const float scale_center_y = input_image_height / 2.0f;
+ float from_scale_offset_x, from_scale_offset_y;
+ ScaleOperation::get_scale_offset(
+ input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y);
+
+ const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX];
+ MemoryBuffer *input_x = inputs[X_INPUT_INDEX];
+ MemoryBuffer *input_y = inputs[Y_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area);
for (; !it.is_end(); ++it) {
const float rel_scale_x = *it.in(0) * scale_x_factor;
const float rel_scale_y = *it.in(1) * scale_y_factor;
- const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x);
- const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y);
- input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out);
+ const float scaled_x = scale_coord_inverted(
+ from_scale_offset_x + canvas_.xmin + it.x, scale_center_x, rel_scale_x);
+ const float scaled_y = scale_coord_inverted(
+ from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y);
+
+ input_image->read_elem_sampled(
+ scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)m_sampler, it.out);
+ }
+}
+
+void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ if (execution_model_ == eExecutionModel::Tiled) {
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ return;
+ }
+
+ const bool image_determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (image_determined) {
+ rcti image_canvas = r_area;
+ rcti unused;
+ NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
+ NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
+ x_socket->determine_canvas(image_canvas, unused);
+ y_socket->determine_canvas(image_canvas, unused);
+ if (is_scaling_variable()) {
+ /* Do not scale canvas. */
+ return;
+ }
+
+ /* Determine scaled canvas. */
+ const float input_width = BLI_rcti_size_x(&r_area);
+ const float input_height = BLI_rcti_size_y(&r_area);
+ const float scale_x = get_constant_scale_x(input_width);
+ const float scale_y = get_constant_scale_y(input_height);
+ scale_area(r_area, scale_x, scale_y);
+ const Size2f max_scale_size = {MAX2(input_width, max_scale_canvas_size_.x),
+ MAX2(input_height, max_scale_canvas_size_.y)};
+ clamp_area_size_max(r_area, max_scale_size);
+
+ /* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */
+ get_input_operation(X_INPUT_INDEX)->unset_canvas();
+ get_input_operation(Y_INPUT_INDEX)->unset_canvas();
+ x_socket->determine_canvas(r_area, unused);
+ y_socket->determine_canvas(r_area, unused);
}
}
@@ -166,8 +274,8 @@ void ScaleRelativeOperation::executePixelSampled(float output[4],
const float scx = scaleX[0];
const float scy = scaleY[0];
- float nx = this->m_centerX + (x - this->m_centerX) / scx;
- float ny = this->m_centerY + (y - this->m_centerY) / scy;
+ float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx;
+ float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@@ -186,10 +294,10 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input,
const float scx = scaleX[0];
const float scy = scaleY[0];
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx + 1;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx - 1;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy + 1;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy - 1;
+ newInput.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1;
+ newInput.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1;
+ newInput.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1;
+ newInput.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1;
}
else {
newInput.xmax = this->getWidth();
@@ -222,8 +330,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4],
float relativeXScale = scx / width;
float relativeYScale = scy / height;
- float nx = this->m_centerX + (x - this->m_centerX) / relativeXScale;
- float ny = this->m_centerY + (y - this->m_centerY) / relativeYScale;
+ float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale;
+ float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@@ -248,10 +356,14 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
float relateveXScale = scx / width;
float relateveYScale = scy / height;
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale;
+ newInput.xmax = this->canvas_center_x_ +
+ (input->xmax - this->canvas_center_x_) / relateveXScale;
+ newInput.xmin = this->canvas_center_x_ +
+ (input->xmin - this->canvas_center_x_) / relateveXScale;
+ newInput.ymax = this->canvas_center_y_ +
+ (input->ymax - this->canvas_center_y_) / relateveYScale;
+ newInput.ymin = this->canvas_center_y_ +
+ (input->ymin - this->canvas_center_y_) / relateveYScale;
}
else {
newInput.xmax = this->getWidth();
@@ -267,16 +379,17 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_is_offset = false;
}
-void ScaleFixedSizeOperation::init_data()
+void ScaleFixedSizeOperation::init_data(const rcti &input_canvas)
{
- const NodeOperation *input_op = getInputOperation(0);
- this->m_relX = input_op->getWidth() / (float)this->m_newWidth;
- this->m_relY = input_op->getHeight() / (float)this->m_newHeight;
+ const int input_width = BLI_rcti_size_x(&input_canvas);
+ const int input_height = BLI_rcti_size_y(&input_canvas);
+ this->m_relX = input_width / (float)this->m_newWidth;
+ this->m_relY = input_height / (float)this->m_newHeight;
/* *** all the options below are for a fairly special case - camera framing *** */
if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) {
@@ -294,8 +407,8 @@ void ScaleFixedSizeOperation::init_data()
if (this->m_is_aspect) {
/* apply aspect from clip */
- const float w_src = input_op->getWidth();
- const float h_src = input_op->getHeight();
+ const float w_src = input_width;
+ const float h_src = input_height;
/* destination aspect is already applied from the camera frame */
const float w_dst = this->m_newWidth;
@@ -310,12 +423,32 @@ void ScaleFixedSizeOperation::init_data()
const float div = asp_src / asp_dst;
this->m_relX /= div;
this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f;
+ if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
+ int fit_width = m_newWidth * div;
+ if (fit_width > max_scale_canvas_size_.x) {
+ fit_width = max_scale_canvas_size_.x;
+ }
+
+ const int added_width = fit_width - m_newWidth;
+ m_newWidth += added_width;
+ m_offsetX += added_width / 2.0f;
+ }
}
else {
/* fit Y */
const float div = asp_dst / asp_src;
this->m_relY /= div;
this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f;
+ if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
+ int fit_height = m_newHeight * div;
+ if (fit_height > max_scale_canvas_size_.y) {
+ fit_height = max_scale_canvas_size_.y;
+ }
+
+ const int added_height = fit_height - m_newHeight;
+ m_newHeight += added_height;
+ m_offsetY += added_height / 2.0f;
+ }
}
this->m_is_offset = true;
@@ -366,15 +499,26 @@ bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input,
return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
-void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- unsigned int nr[2];
- nr[0] = this->m_newWidth;
- nr[1] = this->m_newHeight;
- BaseScaleOperation::determineResolution(resolution, nr);
- resolution[0] = this->m_newWidth;
- resolution[1] = this->m_newHeight;
+ rcti local_preferred = preferred_area;
+ local_preferred.xmax = local_preferred.xmin + m_newWidth;
+ local_preferred.ymax = local_preferred.ymin + m_newHeight;
+ rcti input_canvas;
+ const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas);
+ if (input_determined) {
+ init_data(input_canvas);
+ r_area = input_canvas;
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ r_area.xmin /= m_relX;
+ r_area.ymin /= m_relY;
+ r_area.xmin += m_offsetX;
+ r_area.ymin += m_offsetY;
+ }
+
+ r_area.xmax = r_area.xmin + m_newWidth;
+ r_area.ymax = r_area.ymin + m_newHeight;
+ }
}
void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
@@ -383,10 +527,11 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
- r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX;
- r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX;
- r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY;
- r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY;
+
+ r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * this->m_relX);
+ r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * this->m_relX);
+ r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * this->m_relY);
+ r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * this->m_relY);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@@ -399,14 +544,17 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output,
BuffersIterator<float> it = output->iterate_with({}, area);
if (this->m_is_offset) {
for (; !it.is_end(); ++it) {
- const float nx = (it.x - this->m_offsetX) * this->m_relX;
- const float ny = (it.y - this->m_offsetY) * this->m_relY;
- input_img->read_elem_sampled(nx, ny, sampler, it.out);
+ const float nx = (canvas_.xmin + it.x - this->m_offsetX) * this->m_relX;
+ const float ny = (canvas_.ymin + it.y - this->m_offsetY) * this->m_relY;
+ input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out);
}
}
else {
for (; !it.is_end(); ++it) {
- input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out);
+ input_img->read_elem_sampled((canvas_.xmin + it.x) * this->m_relX - canvas_.xmin,
+ (canvas_.ymin + it.y) * this->m_relY - canvas_.ymin,
+ sampler,
+ it.out);
}
}
}
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 65762d1ce62..746e490d900 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -24,6 +24,9 @@ namespace blender::compositor {
class BaseScaleOperation : public MultiThreadedOperation {
public:
+ static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000;
+
+ public:
void setSampler(PixelSampler sampler)
{
this->m_sampler = (int)sampler;
@@ -33,6 +36,8 @@ class BaseScaleOperation : public MultiThreadedOperation {
m_variable_size = variable_size;
};
+ void set_scale_canvas_max_size(Size2f size);
+
protected:
BaseScaleOperation();
@@ -41,20 +46,26 @@ class BaseScaleOperation : public MultiThreadedOperation {
return (m_sampler == -1) ? sampler : (PixelSampler)m_sampler;
}
+ Size2f max_scale_canvas_size_ = {DEFAULT_MAX_SCALE_CANVAS_SIZE, DEFAULT_MAX_SCALE_CANVAS_SIZE};
int m_sampler;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_variable_size;
};
class ScaleOperation : public BaseScaleOperation {
public:
- static constexpr float MIN_SCALE = 0.0001f;
+ static constexpr float MIN_RELATIVE_SCALE = 0.0001f;
protected:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int X_INPUT_INDEX = 1;
+ static constexpr int Y_INPUT_INDEX = 2;
+
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
SocketReader *m_inputYOperation;
- float m_centerX;
- float m_centerY;
+ float canvas_center_x_;
+ float canvas_center_y_;
public:
ScaleOperation();
@@ -62,9 +73,28 @@ class ScaleOperation : public BaseScaleOperation {
static float scale_coord(const float coord, const float center, const float relative_scale)
{
- return center + (coord - center) / MAX2(relative_scale, MIN_SCALE);
+ return center + (coord - center) * MAX2(relative_scale, MIN_RELATIVE_SCALE);
}
- static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y);
+
+ static float scale_coord_inverted(const float coord,
+ const float center,
+ const float relative_scale)
+ {
+ return center + (coord - center) / MAX2(relative_scale, MIN_RELATIVE_SCALE);
+ }
+
+ static void get_scale_offset(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ float &r_scale_offset_x,
+ float &r_scale_offset_y);
+ static void scale_area(rcti &area, float relative_scale_x, float relative_scale_y);
+ static void get_scale_area_of_interest(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ const float relative_scale_x,
+ const float relative_scale_y,
+ const rcti &output_area,
+ rcti &r_input_area);
+ static void clamp_area_size_max(rcti &area, Size2f max_size);
void init_data() override;
void initExecution() override;
@@ -75,15 +105,17 @@ class ScaleOperation : public BaseScaleOperation {
const rcti &area,
Span<MemoryBuffer *> inputs) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+
protected:
- virtual float get_relative_scale_x_factor() = 0;
- virtual float get_relative_scale_y_factor() = 0;
+ virtual float get_relative_scale_x_factor(float width) = 0;
+ virtual float get_relative_scale_y_factor(float height) = 0;
private:
+ bool is_scaling_variable();
float get_constant_scale(int input_op_idx, float factor);
- float get_constant_scale_x();
- float get_constant_scale_y();
- void scale_area(rcti &rect, float scale_x, float scale_y);
+ float get_constant_scale_x(float width);
+ float get_constant_scale_y(float height);
};
class ScaleRelativeOperation : public ScaleOperation {
@@ -94,11 +126,13 @@ class ScaleRelativeOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- float get_relative_scale_x_factor() override
+
+ float get_relative_scale_x_factor(float UNUSED(width)) override
{
return 1.0f;
}
- float get_relative_scale_y_factor() override
+
+ float get_relative_scale_y_factor(float UNUSED(height)) override
{
return 1.0f;
}
@@ -110,13 +144,15 @@ class ScaleAbsoluteOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- float get_relative_scale_x_factor() override
+
+ float get_relative_scale_x_factor(float width) override
{
- return 1.0f / getWidth();
+ return 1.0f / width;
}
- float get_relative_scale_y_factor() override
+
+ float get_relative_scale_y_factor(float height) override
{
- return 1.0f / getHeight();
+ return 1.0f / height;
}
};
@@ -141,11 +177,9 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void init_data() override;
void initExecution() override;
void deinitExecution() override;
void setNewWidth(int width)
@@ -174,6 +208,9 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
+
+ private:
+ void init_data(const rcti &input_canvas);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
index 628da686a42..21d9210bdac 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
@@ -382,13 +382,30 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
+void ScreenLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ switch (execution_model_) {
+ case eExecutionModel::FullFrame: {
+ set_determined_canvas_modifier([=](rcti &canvas) {
+ /* Ensure screen space. */
+ BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin);
+ });
+ break;
+ }
+ default:
+ break;
+ }
+
+ NodeOperation::determine_canvas(preferred_area, r_area);
+}
+
void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx,
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
if (input_idx != 0) {
/* Dispersion and distortion inputs are used as constants only. */
- r_input_area = COM_SINGLE_ELEM_AREA;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
}
/* XXX the original method of estimating the area-of-interest does not work
@@ -398,10 +415,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx,
*/
#if 1
NodeOperation *image = getInputOperation(0);
- r_input_area.xmax = image->getWidth();
- r_input_area.xmin = 0;
- r_input_area.ymax = image->getHeight();
- r_input_area.ymin = 0;
+ r_input_area = image->get_canvas();
#else /* Original method in tiled implementation. */
rcti newInput;
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
index 616fc8883b0..93681b2f934 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
@@ -86,6 +86,7 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc
index dbe45fa60db..1e7a950e727 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetColorOperation.cc
@@ -34,11 +34,9 @@ void SetColorOperation::executePixelSampled(float output[4],
copy_v4_v4(output, this->m_color);
}
-void SetColorOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void SetColorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h
index f546d5e7668..34ea35bdcbc 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.h
+++ b/source/blender/compositor/operations/COM_SetColorOperation.h
@@ -83,8 +83,7 @@ class SetColorOperation : public ConstantOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc
index ef43cf64653..b7c50f94887 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.cc
+++ b/source/blender/compositor/operations/COM_SetValueOperation.cc
@@ -34,11 +34,9 @@ void SetValueOperation::executePixelSampled(float output[4],
output[0] = this->m_value;
}
-void SetValueOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void SetValueOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h
index 726624c1c6a..e72652450a9 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.h
+++ b/source/blender/compositor/operations/COM_SetValueOperation.h
@@ -54,8 +54,8 @@ class SetValueOperation : public ConstantOperation {
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc
index 7b8cf44048c..3e8514f1f59 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc
@@ -37,11 +37,9 @@ void SetVectorOperation::executePixelSampled(float output[4],
output[2] = vector_.z;
}
-void SetVectorOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void SetVectorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h
index 41fd06659d6..920ea8051e4 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.h
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.h
@@ -84,8 +84,7 @@ class SetVectorOperation : public ConstantOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setVector(const float vector[3])
{
diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc
index b2a50e2c740..47b2bbb7e94 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.cc
+++ b/source/blender/compositor/operations/COM_SplitOperation.cc
@@ -67,16 +67,14 @@ void SplitOperation::executePixelSampled(float output[4],
}
}
-void SplitOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void SplitOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- unsigned int tempPreferredResolution[2] = {0, 0};
- unsigned int tempResolution[2];
+ rcti unused_area;
- this->getInputSocket(0)->determineResolution(tempResolution, tempPreferredResolution);
- this->setResolutionInputSocketIndex((tempResolution[0] && tempResolution[1]) ? 0 : 1);
+ const bool determined = this->getInputSocket(0)->determine_canvas(COM_AREA_NONE, unused_area);
+ this->set_canvas_input_index(determined ? 0 : 1);
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
}
void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h
index 2d09d2a07dc..f923c9f8b7a 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.h
+++ b/source/blender/compositor/operations/COM_SplitOperation.h
@@ -35,8 +35,7 @@ class SplitOperation : public MultiThreadedOperation {
void initExecution() override;
void deinitExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setSplitPercentage(float splitPercentage)
{
this->m_splitPercentage = splitPercentage;
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
index ad96e3a02ba..494506389c5 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
@@ -25,7 +25,7 @@ SunBeamsOperation::SunBeamsOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->flags.complex = true;
}
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index c8e0844d35f..c06e3ac7cb0 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -72,34 +72,20 @@ void TextureBaseOperation::deinitExecution()
NodeOperation::deinitExecution();
}
-void TextureBaseOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- switch (execution_model_) {
- case eExecutionModel::Tiled: {
- if (preferredResolution[0] == 0 || preferredResolution[1] == 0) {
- int width = this->m_rd->xsch * this->m_rd->size / 100;
- int height = this->m_rd->ysch * this->m_rd->size / 100;
- resolution[0] = width;
- resolution[1] = height;
- }
- else {
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
- }
- break;
- }
- case eExecutionModel::FullFrame: {
- /* Determine inputs resolutions. */
- unsigned int temp[2];
- NodeOperation::determineResolution(temp, preferredResolution);
-
- /* We don't use inputs resolutions because they are only used as parameters, not image data.
- */
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
- break;
- }
+ r_area = preferred_area;
+ if (BLI_rcti_is_empty(&preferred_area)) {
+ int width = this->m_rd->xsch * this->m_rd->size / 100;
+ int height = this->m_rd->ysch * this->m_rd->size / 100;
+ r_area.xmax = preferred_area.xmin + width;
+ r_area.ymax = preferred_area.ymin + height;
+ }
+
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ /* Determine inputs. */
+ rcti temp;
+ NodeOperation::determine_canvas(r_area, temp);
}
}
diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h
index 1e95cb270d0..5bc21da1f96 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.h
+++ b/source/blender/compositor/operations/COM_TextureOperation.h
@@ -46,8 +46,7 @@ class TextureBaseOperation : public MultiThreadedOperation {
/**
* Determine the output resolution.
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
/**
* Constructor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index 20da468eeb1..cb671c54abe 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -28,7 +28,7 @@ namespace blender::compositor {
TonemapOperation::TonemapOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_imageReader = nullptr;
this->m_data = nullptr;
@@ -160,11 +160,7 @@ void TonemapOperation::get_area_of_interest(const int input_idx,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
- NodeOperation *operation = getInputOperation(input_idx);
- r_input_area.xmin = 0;
- r_input_area.ymin = 0;
- r_input_area.xmax = operation->getWidth();
- r_input_area.ymax = operation->getHeight();
+ r_input_area = get_input_operation(input_idx)->get_canvas();
}
struct Luminance {
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
index 0f4be16a620..1929c578177 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
@@ -157,11 +157,9 @@ const float *TrackPositionOperation::get_constant_elem()
return &track_position_;
}
-void TrackPositionOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void TrackPositionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ r_area = preferred_area;
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h
index f716bd97737..a47d3e03ed4 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.h
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h
@@ -53,8 +53,7 @@ class TrackPositionOperation : public ConstantOperation {
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
*/
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
public:
TrackPositionOperation();
diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc
index 2feaa0ae16d..5f6e9ed4d21 100644
--- a/source/blender/compositor/operations/COM_TransformOperation.cc
+++ b/source/blender/compositor/operations/COM_TransformOperation.cc
@@ -27,55 +27,40 @@ namespace blender::compositor {
TransformOperation::TransformOperation()
{
- addInputSocket(DataType::Color);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
+ addInputSocket(DataType::Color, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
addOutputSocket(DataType::Color);
translate_factor_x_ = 1.0f;
translate_factor_y_ = 1.0f;
convert_degree_to_rad_ = false;
sampler_ = PixelSampler::Bilinear;
invert_ = false;
+ max_scale_canvas_size_ = {ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE,
+ ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE};
+}
+
+void TransformOperation::set_scale_canvas_max_size(Size2f size)
+{
+ max_scale_canvas_size_ = size;
}
void TransformOperation::init_data()
{
- /* Translation. */
- translate_x_ = 0;
- NodeOperation *x_op = getInputOperation(X_INPUT_INDEX);
- if (x_op->get_flags().is_constant_operation) {
- translate_x_ = static_cast<ConstantOperation *>(x_op)->get_constant_elem()[0] *
- translate_factor_x_;
- }
- translate_y_ = 0;
- NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX);
- if (y_op->get_flags().is_constant_operation) {
- translate_y_ = static_cast<ConstantOperation *>(y_op)->get_constant_elem()[0] *
- translate_factor_y_;
- }
- /* Scaling. */
- scale_center_x_ = getWidth() / 2.0;
- scale_center_y_ = getHeight() / 2.0;
- constant_scale_ = 1.0f;
- NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX);
- if (scale_op->get_flags().is_constant_operation) {
- constant_scale_ = static_cast<ConstantOperation *>(scale_op)->get_constant_elem()[0];
- }
+ translate_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f) *
+ translate_factor_x_;
+ translate_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f) *
+ translate_factor_y_;
- /* Rotation. */
- rotate_center_x_ = (getWidth() - 1.0) / 2.0;
- rotate_center_y_ = (getHeight() - 1.0) / 2.0;
- NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
- const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
- const float degree = is_constant_degree ?
- static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
- 0.0f;
+ const float degree = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree;
rotate_cosine_ = cos(rad);
rotate_sine_ = sin(rad);
+
+ scale_ = get_input_operation(SCALE_INPUT_INDEX)->get_constant_value_default(1.0f);
}
void TransformOperation::get_area_of_interest(const int input_idx,
@@ -84,26 +69,41 @@ void TransformOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
- BLI_rcti_translate(&r_input_area, translate_x_, translate_y_);
- ScaleOperation::scale_area(
- r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_);
- RotateOperation::get_area_rotation_bounds(r_input_area,
- rotate_center_x_,
- rotate_center_y_,
- rotate_sine_,
- rotate_cosine_,
- r_input_area);
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const rcti &image_canvas = image_op->get_canvas();
+ if (invert_) {
+ /* Scale -> Rotate -> Translate. */
+ r_input_area = output_area;
+ BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
+ RotateOperation::get_rotation_area_of_interest(scale_canvas_,
+ rotate_canvas_,
+ rotate_sine_,
+ rotate_cosine_,
+ r_input_area,
+ r_input_area);
+ ScaleOperation::get_scale_area_of_interest(
+ image_canvas, scale_canvas_, scale_, scale_, r_input_area, r_input_area);
+ }
+ else {
+ /* Translate -> Rotate -> Scale. */
+ ScaleOperation::get_scale_area_of_interest(
+ rotate_canvas_, scale_canvas_, scale_, scale_, output_area, r_input_area);
+ RotateOperation::get_rotation_area_of_interest(translate_canvas_,
+ rotate_canvas_,
+ rotate_sine_,
+ rotate_cosine_,
+ r_input_area,
+ r_input_area);
+ BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
+ }
expand_area_for_sampler(r_input_area, sampler_);
break;
}
case X_INPUT_INDEX:
case Y_INPUT_INDEX:
- case DEGREE_INPUT_INDEX: {
- r_input_area = COM_SINGLE_ELEM_AREA;
- break;
- }
+ case DEGREE_INPUT_INDEX:
case SCALE_INPUT_INDEX: {
- r_input_area = output_area;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
@@ -114,8 +114,7 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
- MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX];
- BuffersIterator<float> it = output->iterate_with({input_scale}, area);
+ BuffersIterator<float> it = output->iterate_with({}, area);
if (invert_) {
transform_inverted(it, input_img);
}
@@ -124,31 +123,111 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
+void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ const bool image_determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (image_determined) {
+ rcti image_canvas = r_area;
+ rcti unused;
+ getInputSocket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+
+ init_data();
+ if (invert_) {
+ /* Scale -> Rotate -> Translate. */
+ scale_canvas_ = image_canvas;
+ ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
+ const Size2f max_scale_size = {
+ MAX2(BLI_rcti_size_x(&image_canvas), max_scale_canvas_size_.x),
+ MAX2(BLI_rcti_size_y(&image_canvas), max_scale_canvas_size_.y)};
+ ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size);
+
+ RotateOperation::get_rotation_canvas(
+ scale_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
+
+ translate_canvas_ = rotate_canvas_;
+ BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
+
+ r_area = translate_canvas_;
+ }
+ else {
+ /* Translate -> Rotate -> Scale. */
+ translate_canvas_ = image_canvas;
+ BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
+
+ RotateOperation::get_rotation_canvas(
+ translate_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
+
+ scale_canvas_ = rotate_canvas_;
+ ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
+
+ const Size2f max_scale_size = {
+ MAX2(BLI_rcti_size_x(&rotate_canvas_), max_scale_canvas_size_.x),
+ MAX2(BLI_rcti_size_y(&rotate_canvas_), max_scale_canvas_size_.y)};
+ ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size);
+
+ r_area = scale_canvas_;
+ }
+ }
+}
+
+/** Translate -> Rotate -> Scale. */
void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
{
+ float rotate_center_x, rotate_center_y;
+ RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y);
+ float rotate_offset_x, rotate_offset_y;
+ RotateOperation::get_rotation_offset(
+ translate_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
+
+ const float scale_center_x = BLI_rcti_size_x(&rotate_canvas_) / 2.0f;
+ const float scale_center_y = BLI_rcti_size_y(&rotate_canvas_) / 2.0f;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(rotate_canvas_, scale_canvas_, scale_offset_x, scale_offset_y);
+
for (; !it.is_end(); ++it) {
- const float scale = *it.in(0);
- float x = it.x - translate_x_;
- float y = it.y - translate_y_;
+ float x = ScaleOperation::scale_coord_inverted(it.x + scale_offset_x, scale_center_x, scale_);
+ float y = ScaleOperation::scale_coord_inverted(it.y + scale_offset_y, scale_center_y, scale_);
+
+ x = rotate_offset_x + x;
+ y = rotate_offset_y + y;
RotateOperation::rotate_coords(
- x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
- x = ScaleOperation::scale_coord(x, scale_center_x_, scale);
- y = ScaleOperation::scale_coord(y, scale_center_y_, scale);
- input_img->read_elem_sampled(x, y, sampler_, it.out);
+ x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
+
+ input_img->read_elem_sampled(x - translate_x_, y - translate_y_, sampler_, it.out);
}
}
+/** Scale -> Rotate -> Translate. */
void TransformOperation::transform_inverted(BuffersIterator<float> &it,
const MemoryBuffer *input_img)
{
+ const rcti &image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
+ const float scale_center_x = BLI_rcti_size_x(&image_canvas) / 2.0f - translate_x_;
+ const float scale_center_y = BLI_rcti_size_y(&image_canvas) / 2.0f - translate_y_;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(image_canvas, scale_canvas_, scale_offset_x, scale_offset_y);
+
+ float rotate_center_x, rotate_center_y;
+ RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y);
+ rotate_center_x -= translate_x_;
+ rotate_center_y -= translate_y_;
+ float rotate_offset_x, rotate_offset_y;
+ RotateOperation::get_rotation_offset(
+ scale_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
+
for (; !it.is_end(); ++it) {
- const float scale = *it.in(0);
- float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale);
- float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale);
+ float x = rotate_offset_x + (it.x - translate_x_);
+ float y = rotate_offset_y + (it.y - translate_y_);
RotateOperation::rotate_coords(
- x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
- x -= translate_x_;
- y -= translate_y_;
+ x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
+
+ x = ScaleOperation::scale_coord_inverted(x + scale_offset_x, scale_center_x, scale_);
+ y = ScaleOperation::scale_coord_inverted(y + scale_offset_y, scale_center_y, scale_);
+
input_img->read_elem_sampled(x, y, sampler_, it.out);
}
}
diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h
index 480998a0207..3c5584a1bea 100644
--- a/source/blender/compositor/operations/COM_TransformOperation.h
+++ b/source/blender/compositor/operations/COM_TransformOperation.h
@@ -30,15 +30,14 @@ class TransformOperation : public MultiThreadedOperation {
constexpr static int DEGREE_INPUT_INDEX = 3;
constexpr static int SCALE_INPUT_INDEX = 4;
- float scale_center_x_;
- float scale_center_y_;
- float rotate_center_x_;
- float rotate_center_y_;
float rotate_cosine_;
float rotate_sine_;
- float translate_x_;
- float translate_y_;
- float constant_scale_;
+ int translate_x_;
+ int translate_y_;
+ float scale_;
+ rcti scale_canvas_;
+ rcti rotate_canvas_;
+ rcti translate_canvas_;
/* Set variables. */
PixelSampler sampler_;
@@ -46,6 +45,7 @@ class TransformOperation : public MultiThreadedOperation {
float translate_factor_x_;
float translate_factor_y_;
bool invert_;
+ Size2f max_scale_canvas_size_;
public:
TransformOperation();
@@ -71,16 +71,18 @@ class TransformOperation : public MultiThreadedOperation {
invert_ = value;
}
+ void set_scale_canvas_max_size(Size2f size);
+
void init_data() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+
private:
- /** Translate -> Rotate -> Scale. */
void transform(BuffersIterator<float> &it, const MemoryBuffer *input_img);
- /** Scale -> Rotate -> Translate. */
void transform_inverted(BuffersIterator<float> &it, const MemoryBuffer *input_img);
};
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc
index a3db086e974..9868f21654e 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.cc
+++ b/source/blender/compositor/operations/COM_TranslateOperation.cc
@@ -23,13 +23,13 @@ namespace blender::compositor {
TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color)
{
}
-TranslateOperation::TranslateOperation(DataType data_type)
+TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode)
{
- this->addInputSocket(data_type);
- this->addInputSocket(DataType::Value);
- this->addInputSocket(DataType::Value);
+ this->addInputSocket(data_type, resize_mode);
+ this->addInputSocket(DataType::Value, ResizeMode::None);
+ this->addInputSocket(DataType::Value, ResizeMode::None);
this->addOutputSocket(data_type);
- this->setResolutionInputSocketIndex(0);
+ this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_inputXOperation = nullptr;
this->m_inputYOperation = nullptr;
@@ -39,6 +39,7 @@ TranslateOperation::TranslateOperation(DataType data_type)
this->x_extend_mode_ = MemoryBufferExtend::Clip;
this->y_extend_mode_ = MemoryBufferExtend::Clip;
}
+
void TranslateOperation::initExecution()
{
this->m_inputOperation = this->getInputSocketReader(0);
@@ -122,6 +123,9 @@ void TranslateOperation::get_area_of_interest(const int input_idx,
BLI_rcti_translate(&r_input_area, 0, -delta_y);
}
}
+ else {
+ r_input_area = output_area;
+ }
}
void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
@@ -142,4 +146,27 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
+TranslateCanvasOperation::TranslateCanvasOperation()
+ : TranslateOperation(DataType::Color, ResizeMode::None)
+{
+}
+
+void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ const bool determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (determined) {
+ NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
+ NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
+ rcti unused;
+ x_socket->determine_canvas(r_area, unused);
+ y_socket->determine_canvas(r_area, unused);
+
+ ensureDelta();
+ const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f;
+ const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f;
+ BLI_rcti_translate(&r_area, delta_x, delta_y);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h
index ce1965cecef..c5a3d1ffd99 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.h
+++ b/source/blender/compositor/operations/COM_TranslateOperation.h
@@ -24,6 +24,11 @@
namespace blender::compositor {
class TranslateOperation : public MultiThreadedOperation {
+ protected:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int X_INPUT_INDEX = 1;
+ static constexpr int Y_INPUT_INDEX = 2;
+
private:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
@@ -33,12 +38,14 @@ class TranslateOperation : public MultiThreadedOperation {
bool m_isDeltaSet;
float m_factorX;
float m_factorY;
+
+ protected:
MemoryBufferExtend x_extend_mode_;
MemoryBufferExtend y_extend_mode_;
public:
TranslateOperation();
- TranslateOperation(DataType data_type);
+ TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
@@ -67,16 +74,8 @@ class TranslateOperation : public MultiThreadedOperation {
this->m_deltaY = tempDelta[0];
}
else {
- this->m_deltaX = 0;
- NodeOperation *x_op = getInputOperation(1);
- if (x_op->get_flags().is_constant_operation) {
- this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0];
- }
- this->m_deltaY = 0;
- NodeOperation *y_op = getInputOperation(2);
- if (y_op->get_flags().is_constant_operation) {
- this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0];
- }
+ m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
+ m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
}
this->m_isDeltaSet = true;
@@ -93,4 +92,10 @@ class TranslateOperation : public MultiThreadedOperation {
Span<MemoryBuffer *> inputs) override;
};
+class TranslateCanvasOperation : public TranslateOperation {
+ public:
+ TranslateCanvasOperation();
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+};
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
index b8274576cb5..c524447a4fa 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
@@ -28,8 +28,8 @@ namespace blender::compositor {
VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */
- this->addInputSocket(DataType::Value); /* Radius. */
+ this->addInputSocket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */
+ this->addInputSocket(DataType::Value); /* Radius. */
#ifdef COM_DEFOCUS_SEARCH
/* Inverse search radius optimization structure. */
this->addInputSocket(DataType::Color, ResizeMode::None);
@@ -77,7 +77,7 @@ void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect)
this->determineDependingAreaOfInterest(
rect, (ReadBufferOperation *)this->m_inputSizeProgram, &rect2);
- const float max_dim = MAX2(m_width, m_height);
+ const float max_dim = MAX2(this->getWidth(), this->getHeight());
const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar);
@@ -105,7 +105,7 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y,
float multiplier_accum[4];
float color_accum[4];
- const float max_dim = MAX2(m_width, m_height);
+ const float max_dim = MAX2(getWidth(), getHeight());
const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
int maxBlurScalar = tileData->maxBlurScalar;
@@ -125,8 +125,8 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y,
#else
int minx = MAX2(x - maxBlurScalar, 0);
int miny = MAX2(y - maxBlurScalar, 0);
- int maxx = MIN2(x + maxBlurScalar, (int)m_width);
- int maxy = MIN2(y + maxBlurScalar, (int)m_height);
+ int maxx = MIN2(x + maxBlurScalar, (int)getWidth());
+ int maxy = MIN2(y + maxBlurScalar, (int)getHeight());
#endif
{
inputSizeBuffer->readNoCheck(tempSize, x, y);
@@ -200,7 +200,7 @@ void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device,
MemoryBuffer *sizeMemoryBuffer = this->m_inputSizeProgram->getInputMemoryBuffer(
inputMemoryBuffers);
- const float max_dim = MAX2(m_width, m_height);
+ const float max_dim = MAX2(getWidth(), getHeight());
cl_float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)this->m_maxBlur);
@@ -238,7 +238,7 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest(
rcti newInput;
rcti bokehInput;
- const float max_dim = MAX2(m_width, m_height);
+ const float max_dim = MAX2(getWidth(), getHeight());
const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f;
int maxBlurScalar = this->m_maxBlur * scalar;
@@ -294,10 +294,9 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx,
break;
}
case BOKEH_INPUT_INDEX: {
- r_input_area.xmax = COM_BLUR_BOKEH_PIXELS;
- r_input_area.xmin = 0;
- r_input_area.ymax = COM_BLUR_BOKEH_PIXELS;
- r_input_area.ymin = 0;
+ r_input_area = output_area;
+ r_input_area.xmax = r_input_area.xmin + COM_BLUR_BOKEH_PIXELS;
+ r_input_area.ymax = r_input_area.ymin + COM_BLUR_BOKEH_PIXELS;
break;
}
#ifdef COM_DEFOCUS_SEARCH
@@ -441,7 +440,7 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *
/* #InverseSearchRadiusOperation. */
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
{
- this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */
+ this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */
this->addOutputSocket(DataType::Color);
this->flags.complex = true;
this->m_inputRadius = nullptr;
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
index 56b4677087b..3634bc2f1d7 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
@@ -130,8 +130,7 @@ class InverseSearchRadiusOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setMaxBlur(int maxRadius)
{
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
index 5405e6d424a..63956410b60 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
@@ -126,10 +126,7 @@ void VectorBlurOperation::get_area_of_interest(const int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
- r_input_area.xmin = 0;
- r_input_area.xmax = this->getWidth();
- r_input_area.ymin = 0;
- r_input_area.ymax = this->getHeight();
+ r_input_area = this->get_canvas();
}
void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output,
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index a038e8994d8..1faff0fd07f 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -23,6 +23,7 @@
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "COM_ExecutionSystem.h"
#include "MEM_guardedalloc.h"
#include "PIL_time.h"
#include "WM_api.h"
@@ -34,6 +35,8 @@
namespace blender::compositor {
+static int MAX_VIEWER_TRANSLATION_PADDING = 12000;
+
ViewerOperation::ViewerOperation()
{
this->setImage(nullptr);
@@ -67,7 +70,7 @@ void ViewerOperation::initExecution()
this->m_depthInput = getInputSocketReader(2);
this->m_doDepthBuffer = (this->m_depthInput != nullptr);
- if (isActiveViewerOutput()) {
+ if (isActiveViewerOutput() && !exec_system_->is_breaked()) {
initImage();
}
}
@@ -122,15 +125,16 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
updateImage(rect);
}
-void ViewerOperation::determineResolution(unsigned int resolution[2],
- unsigned int /*preferredResolution*/[2])
+void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
const int sceneRenderWidth = this->m_rd->xsch * this->m_rd->size / 100;
const int sceneRenderHeight = this->m_rd->ysch * this->m_rd->size / 100;
- unsigned int localPrefRes[2] = {static_cast<unsigned int>(sceneRenderWidth),
- static_cast<unsigned int>(sceneRenderHeight)};
- NodeOperation::determineResolution(resolution, localPrefRes);
+ rcti local_preferred = preferred_area;
+ local_preferred.xmax = local_preferred.xmin + sceneRenderWidth;
+ local_preferred.ymax = local_preferred.ymin + sceneRenderHeight;
+
+ NodeOperation::determine_canvas(local_preferred, r_area);
}
void ViewerOperation::initImage()
@@ -155,13 +159,24 @@ void ViewerOperation::initImage()
BLI_thread_unlock(LOCK_DRAW_IMAGE);
return;
}
- if (ibuf->x != (int)getWidth() || ibuf->y != (int)getHeight()) {
+ int padding_x = abs(canvas_.xmin) * 2;
+ int padding_y = abs(canvas_.ymin) * 2;
+ if (padding_x > MAX_VIEWER_TRANSLATION_PADDING) {
+ padding_x = MAX_VIEWER_TRANSLATION_PADDING;
+ }
+ if (padding_y > MAX_VIEWER_TRANSLATION_PADDING) {
+ padding_y = MAX_VIEWER_TRANSLATION_PADDING;
+ }
+
+ display_width_ = getWidth() + padding_x;
+ display_height_ = getHeight() + padding_y;
+ if (ibuf->x != display_width_ || ibuf->y != display_height_) {
imb_freerectImBuf(ibuf);
imb_freerectfloatImBuf(ibuf);
IMB_freezbuffloatImBuf(ibuf);
- ibuf->x = getWidth();
- ibuf->y = getHeight();
+ ibuf->x = display_width_;
+ ibuf->y = display_height_;
/* zero size can happen if no image buffers exist to define a sensible resolution */
if (ibuf->x > 0 && ibuf->y > 0) {
imb_addrectfloatImBuf(ibuf);
@@ -193,11 +208,15 @@ void ViewerOperation::initImage()
void ViewerOperation::updateImage(const rcti *rect)
{
+ if (exec_system_->is_breaked()) {
+ return;
+ }
+
float *buffer = m_outputBuffer;
IMB_partial_display_buffer_update(this->m_ibuf,
buffer,
nullptr,
- getWidth(),
+ display_width_,
0,
0,
this->m_viewSettings,
@@ -227,29 +246,46 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
return;
}
+ const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0);
+ const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0);
MemoryBuffer output_buffer(
- m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight());
+ m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_);
const MemoryBuffer *input_image = inputs[0];
- output_buffer.copy_from(input_image, area);
+ output_buffer.copy_from(input_image, area, offset_x, offset_y);
if (this->m_useAlphaInput) {
const MemoryBuffer *input_alpha = inputs[1];
- output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3);
+ output_buffer.copy_from(
+ input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3);
}
if (m_depthBuffer) {
MemoryBuffer depth_buffer(
- m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight());
+ m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_);
const MemoryBuffer *input_depth = inputs[2];
- depth_buffer.copy_from(input_depth, area);
+ depth_buffer.copy_from(input_depth, area, offset_x, offset_y);
}
- updateImage(&area);
+ rcti display_area;
+ BLI_rcti_init(&display_area,
+ offset_x,
+ offset_x + BLI_rcti_size_x(&area),
+ offset_y,
+ offset_y + BLI_rcti_size_y(&area));
+ updateImage(&display_area);
}
void ViewerOperation::clear_display_buffer()
{
BLI_assert(isActiveViewerOutput());
+ if (exec_system_->is_breaked()) {
+ return;
+ }
+
initImage();
+ if (m_outputBuffer == nullptr) {
+ return;
+ }
+
size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float);
if (buf_bytes > 0) {
memset(m_outputBuffer, 0, buf_bytes);
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index 06ac501a535..95ee982f692 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -50,13 +50,15 @@ class ViewerOperation : public MultiThreadedOperation {
SocketReader *m_alphaInput;
SocketReader *m_depthInput;
+ int display_width_;
+ int display_height_;
+
public:
ViewerOperation();
void initExecution() override;
void deinitExecution() override;
void executeRegion(rcti *rect, unsigned int tileNumber) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
bool isOutputOperation(bool /*rendering*/) const override
{
if (G.background) {
diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc
index 888602114cc..393128ded41 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.cc
+++ b/source/blender/compositor/operations/COM_WrapOperation.cc
@@ -33,7 +33,7 @@ inline float WrapOperation::getWrappedOriginalXPos(float x)
return 0;
}
while (x < 0) {
- x += this->m_width;
+ x += this->getWidth();
}
return fmodf(x, this->getWidth());
}
@@ -44,7 +44,7 @@ inline float WrapOperation::getWrappedOriginalYPos(float y)
return 0;
}
while (y < 0) {
- y += this->m_height;
+ y += this->getHeight();
}
return fmodf(y, this->getHeight());
}
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
index 6380f6bf3df..a1c1e514eb7 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
@@ -50,7 +50,7 @@ void WriteBufferOperation::executePixelSampled(float output[4],
void WriteBufferOperation::initExecution()
{
this->m_input = this->getInputOperation(0);
- this->m_memoryProxy->allocate(this->m_width, this->m_height);
+ this->m_memoryProxy->allocate(this->getWidth(), this->getHeight());
}
void WriteBufferOperation::deinitExecution()
@@ -206,18 +206,17 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device,
delete clKernelsToCleanUp;
}
-void WriteBufferOperation::determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2])
+void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- NodeOperation::determineResolution(resolution, preferredResolution);
+ NodeOperation::determine_canvas(preferred_area, r_area);
/* make sure there is at least one pixel stored in case the input is a single value */
m_single_value = false;
- if (resolution[0] == 0) {
- resolution[0] = 1;
+ if (BLI_rcti_size_x(&r_area) == 0) {
+ r_area.xmax += 1;
m_single_value = true;
}
- if (resolution[1] == 0) {
- resolution[1] = 1;
+ if (BLI_rcti_size_y(&r_area) == 0) {
+ r_area.ymax += 1;
m_single_value = true;
}
}
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h
index 2817fbe24b9..b7dededc69f 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.h
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h
@@ -56,8 +56,7 @@ class WriteBufferOperation : public NodeOperation {
unsigned int chunkNumber,
MemoryBuffer **memoryBuffers,
MemoryBuffer *outputBuffer) override;
- void determineResolution(unsigned int resolution[2],
- unsigned int preferredResolution[2]) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void readResolutionFromInputSocket();
inline NodeOperation *getInput()
{
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 36c6b56caae..a09f79ffa39 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -63,6 +63,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_world_types.h"
#include "BKE_action.h"
@@ -1466,7 +1467,7 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key)
}
/* ObData Geometry Evaluation */
-// XXX: what happens if the datablock is shared!
+/* XXX: what happens if the datablock is shared! */
void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_object_visible)
{
OperationNode *op_node;
@@ -1764,6 +1765,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
else if (id_type == ID_MC) {
build_movieclip((MovieClip *)id);
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -1780,7 +1784,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
build_idproperties(socket->prop);
}
- // TODO: link from nodetree to owner_component?
+ /* TODO: link from nodetree to owner_component? */
}
/* Recursively build graph for material */
@@ -2015,6 +2019,17 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation)
});
}
+void DepsgraphNodeBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+ add_operation_node(
+ &vfont->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE);
+}
+
static bool seq_node_build_cb(Sequence *seq, void *user_data)
{
DepsgraphNodeBuilder *nb = (DepsgraphNodeBuilder *)user_data;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 2378f3fc100..d31290ecbff 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -55,6 +55,7 @@ struct Scene;
struct Simulation;
struct Speaker;
struct Tex;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -235,6 +236,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
/* Per-ID information about what was already in the dependency graph.
* Allows to re-use certain values, to speed up following evaluation. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 28cfc5a9e1a..55e8c5ed033 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -65,6 +65,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_volume_types.h"
#include "DNA_world_types.h"
@@ -2496,6 +2497,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL);
add_relation(clip_key, shading_key, "Clip -> Node");
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK);
+ add_relation(vfont_key, shading_key, "VFont -> Node");
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -2842,6 +2848,15 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer
}
}
+void DepsgraphRelationBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+}
+
void DepsgraphRelationBuilder::build_copy_on_write_relations()
{
for (IDNode *id_node : graph_->id_nodes) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 1ad61c25305..f0393544511 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -72,6 +72,7 @@ struct Simulation;
struct Speaker;
struct Tex;
struct ViewLayer;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -296,6 +297,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
virtual void build_nested_datablock(ID *owner, ID *id);
virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree);
diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc
index fee5342df59..16089ba27dd 100644
--- a/source/blender/depsgraph/intern/node/deg_node.cc
+++ b/source/blender/depsgraph/intern/node/deg_node.cc
@@ -177,7 +177,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type)
case NodeType::SIMULATION:
return DEG_SCENE_COMP_PARAMETERS;
}
- BLI_assert_msg(0, "Unhandled node type, not suppsed to happen.");
+ BLI_assert_msg(0, "Unhandled node type, not supposed to happen.");
return DEG_SCENE_COMP_PARAMETERS;
}
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index a29618cefa8..0947fad7670 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -90,7 +90,7 @@ ComponentNode::ComponentNode()
void ComponentNode::init(const ID * /*id*/, const char * /*subdata*/)
{
/* hook up eval context? */
- // XXX: maybe this needs a special API?
+ /* XXX: maybe this needs a special API? */
}
/* Free 'component' node */
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 49780abc6f4..ea60dd0753e 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -255,7 +255,7 @@ static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
{
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
- DRW_shgroup_hair_create_sub(ob, psys, md, grp);
+ DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 9ecb737192e..a627bcd9488 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -769,15 +769,16 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
if (matcache.depth_grp) {
- *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
+ *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
}
if (matcache.shading_grp) {
- *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
+ *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
}
if (matcache.shadow_grp) {
- *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
+ *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
*cast_shadow = true;
}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index 2e200c8e053..e8c2514d908 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -269,7 +269,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
- grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp);
+ grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 34fe29055d6..674aca29662 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -298,14 +298,14 @@ typedef struct GPENCIL_PrivateData {
/* Current frame */
int cfra;
/* 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) */
+ * NOTE: set to false for viewport and opengl rendering (including sequencer 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;
/* True in selection and auto_depth drawing */
bool draw_depth_only;
- /* Is shading set to wireframe. */
+ /* Is shading set to wire-frame. */
bool draw_wireframe;
/* Used by the depth merge step. */
int is_stroke_order_3d;
diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c
index 985f8a6785c..983df1ceac8 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_uv.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c
@@ -340,34 +340,42 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
if (pd->edit_uv.do_stencil_overlay) {
const Brush *brush = BKE_paint_brush(&ts->imapaint.paint);
-
- DRW_PASS_CREATE(psl->edit_uv_stencil_ps,
- DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL);
- GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image();
- GPUBatch *geom = DRW_cache_quad_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps);
Image *stencil_image = brush->clone.image;
ImBuf *stencil_ibuf = BKE_image_acquire_ibuf(stencil_image, NULL, &pd->edit_uv.stencil_lock);
- pd->edit_uv.stencil_ibuf = stencil_ibuf;
- pd->edit_uv.stencil_image = stencil_image;
- GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf);
- DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture);
- DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true);
- DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha});
-
- float size_image[2];
- BKE_image_get_size_fl(image, NULL, size_image);
- float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y};
- float obmat[4][4];
- unit_m4(obmat);
- obmat[3][1] = brush->clone.offset[1];
- obmat[3][0] = brush->clone.offset[0];
- obmat[0][0] = size_stencil_image[0] / size_image[0];
- obmat[1][1] = size_stencil_image[1] / size_image[1];
+ if (stencil_ibuf == NULL) {
+ pd->edit_uv.stencil_ibuf = NULL;
+ pd->edit_uv.stencil_image = NULL;
+ }
+ else {
+ DRW_PASS_CREATE(psl->edit_uv_stencil_ps,
+ DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS |
+ DRW_STATE_BLEND_ALPHA_PREMUL);
+ GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image();
+ GPUBatch *geom = DRW_cache_quad_get();
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps);
+ pd->edit_uv.stencil_ibuf = stencil_ibuf;
+ pd->edit_uv.stencil_image = stencil_image;
+ GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf);
+ DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture);
+ DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true);
+ DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true);
+ DRW_shgroup_uniform_vec4_copy(
+ grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha});
+
+ float size_image[2];
+ BKE_image_get_size_fl(image, NULL, size_image);
+ float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y};
+
+ float obmat[4][4];
+ unit_m4(obmat);
+ obmat[3][1] = brush->clone.offset[1];
+ obmat[3][0] = brush->clone.offset[0];
+ obmat[0][0] = size_stencil_image[0] / size_image[0];
+ obmat[1][1] = size_stencil_image[1] / size_image[1];
- DRW_shgroup_call_obmat(grp, geom, obmat);
+ DRW_shgroup_call_obmat(grp, geom, obmat);
+ }
}
else {
pd->edit_uv.stencil_ibuf = NULL;
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 8f9db7f6f13..98db7136398 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -696,7 +696,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Lightprobe
+/** \name Light-probe
* \{ */
void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob)
diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c
index ea8735419f9..cc8ab00baef 100644
--- a/source/blender/draw/engines/overlay/overlay_grid.c
+++ b/source/blender/draw/engines/overlay/overlay_grid.c
@@ -23,6 +23,7 @@
#include "DRW_render.h"
#include "DNA_camera_types.h"
+#include "DNA_screen_types.h"
#include "DEG_depsgraph_query.h"
@@ -46,6 +47,7 @@ enum {
GRID_BACK = (1 << 9),
GRID_CAMERA = (1 << 10),
PLANE_IMAGE = (1 << 11),
+ CUSTOM_GRID = (1 << 12),
};
void OVERLAY_grid_init(OVERLAY_Data *vedata)
@@ -61,6 +63,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
if (pd->space_type == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
+ View2D *v2d = &draw_ctx->region->v2d;
if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) {
shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID;
}
@@ -68,15 +71,21 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
shd->grid_flag = 0;
}
+ if (sima->flag & SI_CUSTOM_GRID) {
+ shd->grid_flag |= CUSTOM_GRID;
+ }
+
shd->grid_distance = 1.0f;
copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f);
if (sima->mode == SI_MODE_UV) {
shd->grid_size[0] = (float)sima->tile_grid_shape[0];
shd->grid_size[1] = (float)sima->tile_grid_shape[1];
}
- for (int step = 0; step < 8; step++) {
- shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f);
- }
+
+ const int grid_size = SI_GRID_STEPS_LEN;
+ shd->zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
+ ED_space_image_grid_steps(sima, shd->grid_steps, grid_size);
+
return;
}
@@ -257,6 +266,7 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata)
grp = DRW_shgroup_create(sh, psl->grid_ps);
DRW_shgroup_uniform_int(grp, "gridFlag", &shd->grid_flag, 1);
+ DRW_shgroup_uniform_float_copy(grp, "zoomFactor", shd->zoom_factor);
DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->grid_axes, 1);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index 23df571e8de..def278f98df 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -139,9 +139,10 @@ typedef struct OVERLAY_ShadingData {
/** Grid */
float grid_axes[3], grid_distance;
float zplane_axes[3], grid_size[3];
- float grid_steps[8];
+ float grid_steps[SI_GRID_STEPS_LEN];
float inv_viewport_size[2];
float grid_line_size;
+ float zoom_factor; /* Only for UV editor */
int grid_flag;
int zpos_flag;
int zneg_flag;
diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
index 3220adbff36..9feca644bd3 100644
--- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
@@ -15,8 +15,9 @@ uniform float lineKernel = 0.0;
uniform sampler2D depthBuffer;
uniform int gridFlag;
+uniform float zoomFactor;
-#define STEPS_LEN 8
+#define STEPS_LEN 8 /* Match: #SI_GRID_STEPS_LEN */
uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0);
#define AXIS_X (1 << 0)
@@ -28,6 +29,8 @@ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0,
#define PLANE_YZ (1 << 6)
#define GRID_BACK (1 << 9) /* grid is behind objects */
#define GRID_CAMERA (1 << 10) /* In camera view */
+#define PLANE_IMAGE (1 << 11) /* UV/Image Image editor */
+#define CUSTOM_GRID (1 << 12) /* UV Editor only */
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
@@ -122,9 +125,17 @@ void main()
* would be more accurate, but not really necessary. */
float grid_res = dot(dFdxPos, screenVecs[0].xyz);
- /* The gride begins to appear when it comprises 4 pixels */
+ /* The grid begins to appear when it comprises 4 pixels */
grid_res *= 4;
+ /* For UV/Image editor use zoomFactor */
+ if ((gridFlag & PLANE_IMAGE) != 0 &&
+ /* Grid begins to appear when the length of one grid unit is at least
+ * (256/grid_size) pixels Value of grid_size defined in `overlay_grid.c`. */
+ (gridFlag & CUSTOM_GRID) == 0) {
+ grid_res = zoomFactor;
+ }
+
/* from biggest to smallest */
vec4 scale;
#if 0
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 635aa7cef25..a5281427fa8 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -238,7 +238,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd,
workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, state) :
workbench_material_hair_setup(wpd, ob, matnr, color_type);
- DRW_shgroup_hair_create_sub(ob, psys, md, grp);
+ DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
/**
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc
index 0804745fab5..dc8f382b7f8 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -342,6 +342,9 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
case CD_ORCO:
*cd_layers |= CD_MASK_ORCO;
break;
+ case CD_HAIRLENGTH:
+ *cd_layers |= CD_MASK_HAIRLENGTH;
+ break;
}
}
}
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
index 6424b21666d..41a0cca8a8f 100644
--- a/source/blender/draw/intern/draw_cache_impl_hair.c
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -27,6 +27,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
@@ -37,6 +38,7 @@
#include "BKE_hair.h"
#include "GPU_batch.h"
+#include "GPU_material.h"
#include "GPU_texture.h"
#include "draw_cache_impl.h" /* own include */
@@ -141,7 +143,9 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
}
}
-static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step)
+static void hair_batch_cache_fill_segments_proc_pos(Hair *hair,
+ GPUVertBufRaw *attr_step,
+ GPUVertBufRaw *length_step)
{
/* TODO: use hair radius layer if available. */
HairCurve *curve = hair->curves;
@@ -162,6 +166,8 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
seg_data[3] = total_len;
co_prev = curve_co[j];
}
+ /* Assign length value*/
+ *(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
@@ -171,28 +177,48 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
}
}
-static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache)
+static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
+ ParticleHairCache *cache,
+ GPUMaterial *gpu_material)
{
- if (cache->proc_point_buf != NULL) {
- return;
- }
+ if (cache->proc_point_buf == NULL) {
+ /* initialize vertex format */
+ GPUVertFormat format = {0};
+ uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- /* initialize vertex format */
- GPUVertFormat format = {0};
- uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
- cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
+ GPUVertBufRaw point_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &point_step);
- GPUVertBufRaw pos_step;
- GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
+ GPUVertFormat length_format = {0};
+ uint length_id = GPU_vertformat_attr_add(
+ &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- hair_batch_cache_fill_segments_proc_pos(hair, &pos_step);
+ cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
- /* Create vbo immediately to bind to texture buffer. */
- GPU_vertbuf_use(cache->proc_point_buf);
+ GPUVertBufRaw length_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
+
+ hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step);
- cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_point_buf);
+ cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
+ }
+
+ if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex) {
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
+ if (attr->type == CD_HAIRLENGTH) {
+ GPU_vertbuf_use(cache->proc_length_buf);
+ cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
+ break;
+ }
+ }
+ }
}
static void hair_batch_cache_fill_strands_data(Hair *hair,
@@ -310,6 +336,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
bool hair_ensure_procedural_data(Object *object,
ParticleHairCache **r_hair_cache,
+ GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@@ -325,7 +352,7 @@ bool hair_ensure_procedural_data(Object *object,
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
ensure_seg_pt_count(hair, &cache->hair);
- hair_batch_cache_ensure_procedural_pos(hair, &cache->hair);
+ hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material);
need_ft_update = true;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index 5c51f24a435..96bdca7d935 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -46,6 +46,7 @@
#include "ED_particle.h"
#include "GPU_batch.h"
+#include "GPU_material.h"
#include "DEG_depsgraph_query.h"
@@ -183,7 +184,9 @@ void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO: more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
+ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_length_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
+ DRW_TEXTURE_FREE_SAFE(hair_cache->length_tex);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_seg_buf);
@@ -609,7 +612,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys,
static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_cache,
const int num_path_keys,
- GPUVertBufRaw *attr_step)
+ GPUVertBufRaw *attr_step,
+ GPUVertBufRaw *length_step)
{
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
@@ -630,6 +634,8 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_
seg_data[3] = total_len;
co_prev = path[j].co;
}
+ /* Assign length value*/
+ *(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j <= path->segments; j++, seg_data_first += 4) {
@@ -1079,40 +1085,64 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit,
static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit,
ParticleSystem *psys,
- ParticleHairCache *cache)
+ ParticleHairCache *cache,
+ GPUMaterial *gpu_material)
{
- if (cache->proc_point_buf != NULL) {
- return;
- }
+ if (cache->proc_point_buf == NULL) {
+ /* initialize vertex format */
+ GPUVertFormat pos_format = {0};
+ uint pos_id = GPU_vertformat_attr_add(
+ &pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- /* initialize vertex format */
- GPUVertFormat format = {0};
- uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format);
+ GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
- cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
+ GPUVertBufRaw pos_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
- GPUVertBufRaw pos_step;
- GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
+ GPUVertFormat length_format = {0};
+ uint length_id = GPU_vertformat_attr_add(
+ &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- if (edit != NULL && edit->pathcache != NULL) {
- particle_batch_cache_fill_segments_proc_pos(edit->pathcache, edit->totcached, &pos_step);
- }
- else {
- if ((psys->pathcache != NULL) &&
- (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
- particle_batch_cache_fill_segments_proc_pos(psys->pathcache, psys->totpart, &pos_step);
+ cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
+
+ GPUVertBufRaw length_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
+
+ if (edit != NULL && edit->pathcache != NULL) {
+ particle_batch_cache_fill_segments_proc_pos(
+ edit->pathcache, edit->totcached, &pos_step, &length_step);
}
- if (psys->childcache) {
- const int child_count = psys->totchild * psys->part->disp / 100;
- particle_batch_cache_fill_segments_proc_pos(psys->childcache, child_count, &pos_step);
+ else {
+ if ((psys->pathcache != NULL) &&
+ (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
+ particle_batch_cache_fill_segments_proc_pos(
+ psys->pathcache, psys->totpart, &pos_step, &length_step);
+ }
+ if (psys->childcache) {
+ const int child_count = psys->totchild * psys->part->disp / 100;
+ particle_batch_cache_fill_segments_proc_pos(
+ psys->childcache, child_count, &pos_step, &length_step);
+ }
}
- }
- /* Create vbo immediately to bind to texture buffer. */
- GPU_vertbuf_use(cache->proc_point_buf);
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_point_buf);
+ cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
+ }
- cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
+ /* Checking hair length separately, only allocating gpu memory when needed. */
+ if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) {
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
+ if (attr->type == CD_HAIRLENGTH) {
+ GPU_vertbuf_use(cache->proc_length_buf);
+ cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
+ break;
+ }
+ }
+ }
}
static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
@@ -1649,6 +1679,7 @@ bool particles_ensure_procedural_data(Object *object,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache **r_hair_cache,
+ GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@@ -1666,9 +1697,11 @@ bool particles_ensure_procedural_data(Object *object,
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
/* Refreshed on combing and simulation. */
- if ((*r_hair_cache)->proc_point_buf == NULL) {
+ if ((*r_hair_cache)->proc_point_buf == NULL ||
+ (gpu_material && (*r_hair_cache)->length_tex == NULL)) {
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
- particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair);
+ particle_batch_cache_ensure_procedural_pos(
+ source.edit, source.psys, &cache->hair, gpu_material);
need_ft_update = true;
}
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 1eaf2bee236..2913877c9c1 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -29,6 +29,7 @@ struct Object;
struct ParticleSystem;
struct RegionView3D;
struct ViewLayer;
+struct GPUMaterial;
#define UBO_FIRST_COLOR colorWire
#define UBO_LAST_COLOR colorUVShadow
@@ -175,7 +176,8 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis);
struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
- struct DRWShadingGroup *shgrp);
+ struct DRWShadingGroup *shgrp,
+ struct GPUMaterial *gpu_material);
struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md);
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index c2e25389091..5c7eb083fc9 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -38,6 +38,7 @@
#include "GPU_batch.h"
#include "GPU_capabilities.h"
#include "GPU_compute.h"
+#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
@@ -172,18 +173,23 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache
}
}
-static ParticleHairCache *drw_hair_particle_cache_get(
- Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
+static ParticleHairCache *drw_hair_particle_cache_get(Object *object,
+ ParticleSystem *psys,
+ ModifierData *md,
+ GPUMaterial *gpu_material,
+ int subdiv,
+ int thickness_res)
{
bool update;
ParticleHairCache *cache;
if (psys) {
/* Old particle hair. */
- update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res);
+ update = particles_ensure_procedural_data(
+ object, psys, md, &cache, gpu_material, subdiv, thickness_res);
}
else {
/* New hair object. */
- update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res);
+ update = hair_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
}
if (update) {
@@ -206,7 +212,8 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
- ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res);
+ ParticleHairCache *cache = drw_hair_particle_cache_get(
+ object, psys, md, NULL, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
@@ -248,7 +255,8 @@ void DRW_hair_duplimat_get(Object *object,
DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
ParticleSystem *psys,
ModifierData *md,
- DRWShadingGroup *shgrp_parent)
+ DRWShadingGroup *shgrp_parent,
+ GPUMaterial *gpu_material)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
@@ -258,7 +266,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache = drw_hair_particle_cache_get(
- object, psys, md, subdiv, thickness_res);
+ object, psys, md, gpu_material, subdiv, thickness_res);
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
@@ -308,6 +316,9 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
}
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
+ if (hair_cache->length_tex) {
+ DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex);
+ }
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h
index 1f58d8d0ead..289a1690fc6 100644
--- a/source/blender/draw/intern/draw_hair_private.h
+++ b/source/blender/draw/intern/draw_hair_private.h
@@ -66,6 +66,10 @@ typedef struct ParticleHairCache {
GPUVertBuf *proc_strand_buf;
GPUTexture *strand_tex;
+ /* Hair Length */
+ GPUVertBuf *proc_length_buf;
+ GPUTexture *length_tex;
+
GPUVertBuf *proc_strand_seg_buf;
GPUTexture *strand_seg_tex;
@@ -93,11 +97,13 @@ bool particles_ensure_procedural_data(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct ParticleHairCache **r_hair_cache,
+ struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);
bool hair_ensure_procedural_data(struct Object *object,
struct ParticleHairCache **r_hair_cache,
+ struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 02c335ddae2..6cc7f09a852 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -210,6 +210,12 @@ void hair_get_pos_tan_binor_time(bool is_persp,
}
}
+float hair_get_customdata_float(const samplerBuffer cd_buf)
+{
+ int id = hair_get_strand_id();
+ return texelFetch(cd_buf, id).r;
+}
+
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index b12e0ae5cab..e1d046428a8 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -3087,7 +3087,10 @@ static size_t animdata_filter_dopesheet_movieclips(bAnimContext *ac,
}
/* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */
-static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_mode)
+static bool animdata_filter_base_is_ok(bDopeSheet *ads,
+ Base *base,
+ const eObjectMode object_mode,
+ int filter_mode)
{
Object *ob = base->object;
@@ -3144,10 +3147,21 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m
}
/* check selection and object type filters */
- if ((ads->filterflag & ADS_FILTER_ONLYSEL) &&
- !((base->flag & BASE_SELECTED) /*|| (base == sce->basact) */)) {
- /* only selected should be shown */
- return false;
+ if (ads->filterflag & ADS_FILTER_ONLYSEL) {
+ if (object_mode & OB_MODE_POSE) {
+ /* When in pose-mode handle all pose-mode objects.
+ * This avoids problems with pose-mode where objects may be unselected,
+ * where a selected bone of an unselected object would be hidden. see: T81922. */
+ if (!(base->object->mode & object_mode)) {
+ return false;
+ }
+ }
+ else {
+ /* only selected should be shown (ignore active) */
+ if (!(base->flag & BASE_SELECTED)) {
+ return false;
+ }
+ }
}
/* check if object belongs to the filtering group if option to filter
@@ -3185,7 +3199,7 @@ static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads,
Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases");
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if (animdata_filter_base_is_ok(ads, base, filter_mode)) {
+ if (animdata_filter_base_is_ok(ads, base, OB_MODE_OBJECT, filter_mode)) {
sorted_bases[num_bases++] = base;
}
}
@@ -3278,8 +3292,10 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac,
/* Filter and add contents of each base (i.e. object) without them sorting first
* NOTE: This saves performance in cases where order doesn't matter
*/
+ Object *obact = OBACT(view_layer);
+ const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if (animdata_filter_base_is_ok(ads, base, filter_mode)) {
+ if (animdata_filter_base_is_ok(ads, base, object_mode, filter_mode)) {
/* since we're still here, this object should be usable */
items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode);
}
diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c
index 33b4882927a..6fe32699907 100644
--- a/source/blender/editors/animation/anim_ipo_utils.c
+++ b/source/blender/editors/animation/anim_ipo_utils.c
@@ -126,9 +126,10 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
structname = RNA_struct_ui_name(ptr.type);
}
- /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but
- * displaying the struct name alone is no meaningful information (and also cannot be
- * filtered well), same for modifiers. So display strip name alongside as well. */
+ /* For the sequencer, a strip's 'Transform' or 'Crop' is a nested (under Sequence)
+ * struct, but displaying the struct name alone is no meaningful information
+ * (and also cannot be filtered well), same for modifiers.
+ * So display strip name alongside as well. */
if (GS(ptr.owner_id->name) == ID_SCE) {
char stripname[256];
if (BLI_str_quoted_substr(
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index b4ea33920b2..3958c7f9e34 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -74,9 +74,16 @@ static bool change_frame_poll(bContext *C)
* this shouldn't show up in 3D editor (or others without 2D timeline view) via search
*/
if (area) {
- if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH)) {
+ if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP)) {
return true;
}
+ if (area->spacetype == SPACE_GRAPH) {
+ const SpaceGraph *sipo = area->spacedata.first;
+ /* Driver Editor's X axis is not time. */
+ if (sipo->mode != SIPO_MODE_DRIVERS) {
+ return true;
+ }
+ }
}
CTX_wm_operator_poll_msg_set(C, "Expected an animation area to be active");
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index bfaa76b3bf9..dbf379971fa 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -1115,7 +1115,7 @@ static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *
}
/* 2) Show editing panel for setting up this driver */
- /* TODO: Use a different one from the editing popever, so we can have the single/all toggle? */
+ /* TODO: Use a different one from the editing popover, so we can have the single/all toggle? */
UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports);
}
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index ac7db9f4f46..e3ea8f0ab21 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -146,31 +146,31 @@ void draw_keyframe_shape(float x,
/* Handle type to outline shape. */
switch (handle_type) {
case KEYFRAME_HANDLE_AUTO_CLAMP:
- flags = 0x2;
+ flags = GPU_KEYFRAME_SHAPE_CIRCLE;
break; /* circle */
case KEYFRAME_HANDLE_AUTO:
- flags = 0x12;
+ flags = GPU_KEYFRAME_SHAPE_CIRCLE | GPU_KEYFRAME_SHAPE_INNER_DOT;
break; /* circle with dot */
case KEYFRAME_HANDLE_VECTOR:
- flags = 0xC;
+ flags = GPU_KEYFRAME_SHAPE_SQUARE;
break; /* square */
case KEYFRAME_HANDLE_ALIGNED:
- flags = 0x5;
+ flags = GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL;
break; /* clipped diamond */
case KEYFRAME_HANDLE_FREE:
default:
- flags = 1; /* diamond */
+ flags = GPU_KEYFRAME_SHAPE_DIAMOND; /* diamond */
}
/* Extreme type to arrow-like shading. */
if (extreme_type & KEYFRAME_EXTREME_MAX) {
- flags |= 0x100;
+ flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MAX;
}
if (extreme_type & KEYFRAME_EXTREME_MIN) {
- flags |= 0x200;
+ flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MIN;
}
- if (extreme_type & KEYFRAME_EXTREME_MIXED) {
+ if (extreme_type & GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) {
flags |= 0x400;
}
}
@@ -584,7 +584,7 @@ static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2
sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, visible_key_len);
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 8dc4aed9f0e..1ef7ee755ea 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -2107,10 +2107,12 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k
return OPERATOR_CANCELLED;
}
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success");
+ bool confirm = (prop != NULL && RNA_property_boolean_get(op->ptr, prop));
+
if (num_channels > 0) {
/* if the appropriate properties have been set, make a note that we've inserted something */
- PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success");
- if (prop != NULL && RNA_property_boolean_get(op->ptr, prop)) {
+ if (confirm) {
BKE_reportf(op->reports,
RPT_INFO,
"Successfully removed %d keyframes for keying set '%s'",
@@ -2121,7 +2123,7 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k
/* send notifiers that keyframes have been changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL);
}
- else {
+ else if (confirm) {
BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes");
}
@@ -2289,6 +2291,8 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op)
int selected_objects_success_len = 0;
int success_multi = 0;
+ bool confirm = op->flag & OP_IS_INVOKE;
+
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
ID *id = &ob->id;
int success = 0;
@@ -2370,20 +2374,20 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op)
/* report success (or failure) */
if (selected_objects_success_len) {
- BKE_reportf(op->reports,
- RPT_INFO,
- "%d object(s) successfully had %d keyframes removed",
- selected_objects_success_len,
- success_multi);
+ if (confirm) {
+ BKE_reportf(op->reports,
+ RPT_INFO,
+ "%d object(s) successfully had %d keyframes removed",
+ selected_objects_success_len,
+ success_multi);
+ }
+ /* send updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
}
- else {
+ else if (confirm) {
BKE_reportf(
op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len);
}
-
- /* send updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
-
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index 0206aabd359..e1fd3b07f46 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -610,7 +610,7 @@ void ANIM_keyingset_info_unregister(Main *bmain, KeyingSetInfo *ksi)
KeyingSet *ks, *ksn;
/* find relevant builtin KeyingSets which use this, and remove them */
- /* TODO: this isn't done now, since unregister is really only used atm when we
+ /* TODO: this isn't done now, since unregister is really only used at the moment when we
* reload the scripts, which kindof defeats the purpose of "builtin"? */
for (ks = builtin_keyingsets.first; ks; ks = ksn) {
ksn = ks->next;
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index 8aeb6a57124..b0eb014c5d9 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -91,8 +91,7 @@ static void draw_current_frame(const Scene *scene,
bool display_seconds,
const View2D *v2d,
const rcti *scrub_region_rect,
- int current_frame,
- bool draw_line)
+ int current_frame)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
int frame_x = UI_view2d_view_to_region_x(v2d, current_frame);
@@ -106,21 +105,18 @@ static void draw_current_frame(const Scene *scene,
float bg_color[4];
UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color);
- if (draw_line) {
- /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen.
- */
- const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene));
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformThemeColor(TH_CFRAME);
- immRectf(pos,
- subframe_x - U.pixelsize,
- scrub_region_rect->ymax - box_padding,
- subframe_x + U.pixelsize,
- 0.0f);
- immUnbindProgram();
- }
+ /* Draw vertical line from the bottom of the current frame box to the bottom of the screen. */
+ const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene));
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformThemeColor(TH_CFRAME);
+ immRectf(pos,
+ subframe_x - U.pixelsize,
+ scrub_region_rect->ymax - box_padding,
+ subframe_x + U.pixelsize,
+ 0.0f);
+ immUnbindProgram();
UI_draw_roundbox_corner_set(UI_CNR_ALL);
@@ -152,8 +148,7 @@ static void draw_current_frame(const Scene *scene,
void ED_time_scrub_draw_current_frame(const ARegion *region,
const Scene *scene,
- bool display_seconds,
- bool draw_line)
+ bool display_seconds)
{
const View2D *v2d = &region->v2d;
GPU_matrix_push_projection();
@@ -162,7 +157,7 @@ void ED_time_scrub_draw_current_frame(const ARegion *region,
rcti scrub_region_rect;
get_time_scrub_region_rect(region, &scrub_region_rect);
- draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra, draw_line);
+ draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra);
GPU_matrix_pop_projection();
}
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 3798ca308ed..279f79ac44b 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -1184,7 +1184,7 @@ static int pose_clear_transform_generic_exec(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
- /* XXX: UGLY HACK (for autokey + clear transforms) */
+ /* XXX: UGLY HACK (for auto-key + clear transforms). */
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
ListBase dsources = {NULL, NULL};
bool changed = false;
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
index 31c07580570..b6657bfca63 100644
--- a/source/blender/editors/asset/CMakeLists.txt
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC_SYS
)
set(SRC
+ intern/asset_catalog.cc
intern/asset_filter.cc
intern/asset_handle.cc
intern/asset_library_reference.cc
@@ -40,6 +41,7 @@ set(SRC
intern/asset_ops.cc
intern/asset_temp_id_consumer.cc
+ ED_asset_catalog.hh
ED_asset_filter.h
ED_asset_handle.h
ED_asset_library.h
diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh
new file mode 100644
index 00000000000..cffd7728a60
--- /dev/null
+++ b/source/blender/editors/asset/ED_asset_catalog.hh
@@ -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 edasset
+ */
+
+#pragma once
+
+#include "BKE_asset_catalog.hh"
+
+#include "BLI_string_ref.hh"
+
+struct AssetLibrary;
+namespace blender::bke {
+class AssetCatalog;
+} // namespace blender::bke
+
+blender::bke::AssetCatalog *ED_asset_catalog_add(AssetLibrary *library,
+ blender::StringRefNull name,
+ blender::StringRef parent_path = nullptr);
+void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogID &catalog_id);
diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h
index 7524ec6b02a..d8b8f15a109 100644
--- a/source/blender/editors/asset/ED_asset_mark_clear.h
+++ b/source/blender/editors/asset/ED_asset_mark_clear.h
@@ -27,7 +27,22 @@ extern "C" {
struct ID;
struct bContext;
+/**
+ * Mark the datablock as asset.
+ *
+ * To ensure the datablock is saved, this sets Fake User.
+ *
+ * \return whether the datablock was marked as asset; false when it is not capable of becoming an
+ * asset, or when it already was an asset. */
bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
+
+/**
+ * Remove the asset metadata, turning the ID into a "normal" ID.
+ *
+ * This clears the Fake User. If for some reason the datablock is meant to be saved anyway, the
+ * caller is responsible for explicitly setting the Fake User.
+ *
+ * \return whether the asset metadata was actually removed; false when the ID was not an asset. */
bool ED_asset_clear_id(struct ID *id);
bool ED_asset_can_mark_single_from_context(const struct bContext *C);
diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc
new file mode 100644
index 00000000000..6e49ca2dd5c
--- /dev/null
+++ b/source/blender/editors/asset/intern/asset_catalog.cc
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup edasset
+ */
+
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_catalog_path.hh"
+#include "BKE_asset_library.hh"
+
+#include "BLI_string_utils.h"
+
+#include "ED_asset_catalog.hh"
+
+using namespace blender;
+using namespace blender::bke;
+
+struct CatalogUniqueNameFnData {
+ const AssetCatalogService &catalog_service;
+ StringRef parent_path;
+};
+
+static bool catalog_name_exists_fn(void *arg, const char *name)
+{
+ CatalogUniqueNameFnData &fn_data = *static_cast<CatalogUniqueNameFnData *>(arg);
+ AssetCatalogPath fullpath = AssetCatalogPath(fn_data.parent_path) / name;
+ return fn_data.catalog_service.find_catalog_by_path(fullpath);
+}
+
+static std::string catalog_name_ensure_unique(AssetCatalogService &catalog_service,
+ StringRefNull name,
+ StringRef parent_path)
+{
+ CatalogUniqueNameFnData fn_data = {catalog_service, parent_path};
+
+ char unique_name[MAX_NAME] = "";
+ BLI_uniquename_cb(
+ catalog_name_exists_fn, &fn_data, name.c_str(), '.', unique_name, sizeof(unique_name));
+
+ return unique_name;
+}
+
+AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library,
+ StringRefNull name,
+ StringRef parent_path)
+{
+ bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library);
+ if (!catalog_service) {
+ return nullptr;
+ }
+
+ std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path);
+ AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name;
+
+ return catalog_service->create_catalog(fullpath);
+}
+
+void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_id)
+{
+ bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library);
+ if (!catalog_service) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ catalog_service->delete_catalog(catalog_id);
+}
diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc
index 57c25e1614b..400b3572c9b 100644
--- a/source/blender/editors/asset/intern/asset_list.cc
+++ b/source/blender/editors/asset/intern/asset_list.cc
@@ -157,7 +157,7 @@ void AssetList::setup()
/* Relevant bits from file_refresh(). */
/* TODO pass options properly. */
- filelist_setrecursion(files, 1);
+ filelist_setrecursion(files, FILE_SELECT_MAX_RECURSIONS);
filelist_setsorting(files, FILE_SORT_ALPHA, false);
filelist_setlibrary(files, &library_ref_);
filelist_setfilter_options(
@@ -390,7 +390,7 @@ std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_file
{
switch (library_reference.type) {
case ASSET_LIBRARY_CUSTOM:
- return FILE_LOADLIB;
+ return FILE_ASSET_LIBRARY;
case ASSET_LIBRARY_LOCAL:
return FILE_MAIN_ASSET;
}
diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc
index ba348e38823..8290124c209 100644
--- a/source/blender/editors/asset/intern/asset_mark_clear.cc
+++ b/source/blender/editors/asset/intern/asset_mark_clear.cc
@@ -67,8 +67,7 @@ bool ED_asset_clear_id(ID *id)
return false;
}
BKE_asset_metadata_free(&id->asset_data);
- /* Don't clear fake user here, there's no guarantee that it was actually set by
- * #ED_asset_mark_id(), it might have been something/someone else. */
+ id_fake_user_clear(id);
/* Important for asset storage to update properly! */
ED_assetlist_storage_tag_main_data_dirty();
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index d69a2cae94d..5424bae77b4 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -18,27 +18,55 @@
* \ingroup edasset
*/
+#include "BKE_asset_catalog.hh"
#include "BKE_context.h"
+#include "BKE_lib_id.h"
#include "BKE_report.h"
+#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "ED_asset.h"
+#include "ED_asset_catalog.hh"
+/* XXX needs access to the file list, should all be done via the asset system in future. */
+#include "ED_fileselect.h"
#include "RNA_access.h"
+#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
+using namespace blender;
+
/* -------------------------------------------------------------------- */
using PointerRNAVec = blender::Vector<PointerRNA>;
+static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C);
+static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C);
+static bool asset_type_is_nonexperimental(const ID_Type id_type);
+
static bool asset_operation_poll(bContext * /*C*/)
{
+ /* At this moment only the pose library is non-experimental. Still, directly marking arbitrary
+ * Actions as asset is not part of the stable functionality; instead, the pose library "Create
+ * Pose Asset" operator should be used. Actions can still be marked as asset via
+ * `the_action.asset_mark()` (so a function call instead of this operator), which is what the
+ * pose library uses internally. */
return U.experimental.use_extended_asset_browser;
}
+static bool asset_clear_poll(bContext *C)
+{
+ if (asset_operation_poll(C)) {
+ return true;
+ }
+
+ PointerRNAVec pointers = asset_operation_get_nonexperimental_ids_from_context(C);
+ return !pointers.is_empty();
+}
+
/**
* Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or
* multiple ones ("selected_ids" context member).
@@ -64,6 +92,28 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C)
return ids;
}
+static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C)
+{
+ PointerRNAVec nonexperimental;
+ PointerRNAVec pointers = asset_operation_get_ids_from_context(C);
+ for (PointerRNA &ptr : pointers) {
+ BLI_assert(RNA_struct_is_ID(ptr.type));
+
+ ID *id = static_cast<ID *>(ptr.data);
+ if (asset_type_is_nonexperimental(GS(id->name))) {
+ nonexperimental.append(ptr);
+ }
+ }
+ return nonexperimental;
+}
+
+static bool asset_type_is_nonexperimental(const ID_Type id_type)
+{
+ /* At this moment only the pose library is non-experimental. For simplicity, allow asset
+ * operations on all Action datablocks (even though pose assets are limited to single frames). */
+ return ELEM(id_type, ID_AC);
+}
+
/* -------------------------------------------------------------------- */
class AssetMarkHelper {
@@ -166,7 +216,13 @@ static void ASSET_OT_mark(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
class AssetClearHelper {
+ const bool set_fake_user_;
+
public:
+ AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user)
+ {
+ }
+
void operator()(PointerRNAVec &ids);
void reportResults(const bContext *C, ReportList &reports) const;
@@ -191,10 +247,16 @@ void AssetClearHelper::operator()(PointerRNAVec &ids)
continue;
}
- if (ED_asset_clear_id(id)) {
- stats.tot_cleared++;
- stats.last_id = id;
+ if (!ED_asset_clear_id(id)) {
+ continue;
}
+
+ if (set_fake_user_) {
+ id_fake_user_set(id);
+ }
+
+ stats.tot_cleared++;
+ stats.last_id = id;
}
}
@@ -234,7 +296,8 @@ static int asset_clear_exec(bContext *C, wmOperator *op)
{
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
- AssetClearHelper clear_helper;
+ const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user");
+ AssetClearHelper clear_helper(set_fake_user);
clear_helper(ids);
clear_helper.reportResults(C, *op->reports);
@@ -248,18 +311,39 @@ static int asset_clear_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static char *asset_clear_get_description(struct bContext *UNUSED(C),
+ struct wmOperatorType *UNUSED(op),
+ struct PointerRNA *values)
+{
+ const bool set_fake_user = RNA_boolean_get(values, "set_fake_user");
+ if (!set_fake_user) {
+ return nullptr;
+ }
+
+ return BLI_strdup(
+ "Delete all asset metadata, turning the selected asset data-blocks back into normal "
+ "data-blocks, and set Fake User to ensure the data-blocks will still be saved");
+}
+
static void ASSET_OT_clear(wmOperatorType *ot)
{
ot->name = "Clear Asset";
ot->description =
"Delete all asset metadata and turn the selected asset data-blocks back into normal "
"data-blocks";
+ ot->get_description = asset_clear_get_description;
ot->idname = "ASSET_OT_clear";
ot->exec = asset_clear_exec;
- ot->poll = asset_operation_poll;
+ ot->poll = asset_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna,
+ "set_fake_user",
+ false,
+ "Set Fake User",
+ "Ensure the data-block is saved, even when it is no longer marked as asset");
}
/* -------------------------------------------------------------------- */
@@ -295,10 +379,91 @@ static void ASSET_OT_list_refresh(struct wmOperatorType *ot)
/* -------------------------------------------------------------------- */
+static bool asset_catalog_operator_poll(bContext *C)
+{
+ const SpaceFile *sfile = CTX_wm_space_file(C);
+ return asset_operation_poll(C) && sfile && ED_fileselect_active_asset_library_get(sfile);
+}
+
+static int asset_catalog_new_exec(bContext *C, wmOperator *op)
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
+ char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
+
+ ED_asset_catalog_add(asset_library, "Catalog", parent_path);
+
+ MEM_freeN(parent_path);
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_catalog_new(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Asset Catalog";
+ ot->description = "Create a new catalog to put assets in";
+ ot->idname = "ASSET_OT_catalog_new";
+
+ /* api callbacks */
+ ot->exec = asset_catalog_new_exec;
+ ot->poll = asset_catalog_operator_poll;
+
+ RNA_def_string(ot->srna,
+ "parent_path",
+ nullptr,
+ 0,
+ "Parent Path",
+ "Optional path defining the location to put the new catalog under");
+}
+
+static int asset_catalog_delete_exec(bContext *C, wmOperator *op)
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
+ char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr);
+ bke::CatalogID catalog_id;
+ if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_asset_catalog_remove(asset_library, catalog_id);
+
+ MEM_freeN(catalog_id_str);
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_catalog_delete(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete Asset Catalog";
+ ot->description =
+ "Remove an asset catalog from the asset library (contained assets will not be affected and "
+ "show up as unassigned)";
+ ot->idname = "ASSET_OT_catalog_delete";
+
+ /* api callbacks */
+ ot->exec = asset_catalog_delete_exec;
+ ot->invoke = WM_operator_confirm;
+ ot->poll = asset_catalog_operator_poll;
+
+ RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete");
+}
+
+/* -------------------------------------------------------------------- */
+
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
WM_operatortype_append(ASSET_OT_clear);
+ WM_operatortype_append(ASSET_OT_catalog_new);
+ WM_operatortype_append(ASSET_OT_catalog_delete);
+
WM_operatortype_append(ASSET_OT_list_refresh);
}
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 68e2aece6e2..59ea105fbbb 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -2104,7 +2104,7 @@ static void annotation_draw_apply_event(
p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP;
}
}
- /* We are using the temporal stabilizer flag atm,
+ /* We are using the temporal stabilizer flag at the moment,
* but shift is not pressed as well as the permanent flag is not used,
* so we don't need the cursor anymore. */
else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 75ddfa47c57..1f31c60367e 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -4729,7 +4729,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
/* Remove unused slots. */
int actcol = ob_dst->actcol;
for (int slot = 1; slot <= ob_dst->totcol; slot++) {
- while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst->data, slot)) {
+ while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst, slot)) {
ob_dst->actcol = slot;
BKE_object_material_slot_remove(bmain, ob_dst);
if (actcol >= slot) {
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index 0939d53736b..079089786d0 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -345,7 +345,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
/* Remove unused materials. */
int actcol = ob_gpencil->actcol;
for (int slot = 1; slot <= ob_gpencil->totcol; slot++) {
- while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil->data, slot)) {
+ while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) {
ob_gpencil->actcol = slot;
BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 9b157224178..957d8087bbd 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1001,8 +1001,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
gps->dvert = NULL;
- /* drawing batch cache is dirty now */
- gpencil_update_cache(p->gpd);
/* set pointer to first non-initialized point */
pt = gps->points + (gps->totpoints - totelem);
if (gps->dvert != NULL) {
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 6a0a42ee77b..e9601220f2e 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -861,7 +861,7 @@ void ED_operatormacros_action(void);
/* XXX: Should we be doing these here, or at all? */
/* Action Editor - Action Management */
-struct AnimData *ED_actedit_animdata_from_context(struct bContext *C);
+struct AnimData *ED_actedit_animdata_from_context(struct bContext *C, struct ID **r_adt_id_owner);
void ED_animedit_unlink_action(struct bContext *C,
struct ID *id,
struct AnimData *adt,
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 82057c726a5..423d619f41a 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -142,6 +142,7 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
bool ED_fileselect_is_file_browser(const struct SpaceFile *sfile);
bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
+struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile);
struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile);
/* Activate the file that corresponds to the given ID.
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index 6b0b9f4a27c..9532035a1cd 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -41,6 +41,16 @@ struct SpaceImage;
struct bContext;
struct wmOperator;
struct wmWindowManager;
+struct View2D;
+
+/* image_draw.c */
+float ED_space_image_zoom_level(const struct View2D *v2d, const int grid_dimension);
+void ED_space_image_grid_steps(struct SpaceImage *sima,
+ float grid_steps[SI_GRID_STEPS_LEN],
+ const int grid_dimension);
+float ED_space_image_increment_snap_value(const int grid_dimesnions,
+ const float grid_steps[SI_GRID_STEPS_LEN],
+ const float zoom_factor);
/* image_edit.c, exported for transform */
struct Image *ED_space_image(struct SpaceImage *sima);
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index 61e37f20b1b..6a7037c3eed 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -41,7 +41,7 @@ struct bDopeSheet;
struct bGPDlayer;
/* draw simple diamond-shape keyframe */
-/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND,
+/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_SHAPE,
* immBegin(GPU_PRIM_POINTS, n), then call this n times */
typedef struct KeyframeShaderBindings {
uint pos_id;
diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h
index 2284c82b3d5..6e012ec1a91 100644
--- a/source/blender/editors/include/ED_text.h
+++ b/source/blender/editors/include/ED_text.h
@@ -34,6 +34,8 @@ struct UndoStep;
struct UndoType;
struct bContext;
+bool ED_text_activate_in_screen(struct bContext *C, struct Text *text);
+
void ED_text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, bool center);
bool ED_text_region_location_from_cursor(struct SpaceText *st,
diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h
index 6420aaf5ef0..812cb31c9b0 100644
--- a/source/blender/editors/include/ED_time_scrub_ui.h
+++ b/source/blender/editors/include/ED_time_scrub_ui.h
@@ -33,8 +33,7 @@ struct wmEvent;
void ED_time_scrub_draw_current_frame(const struct ARegion *region,
const struct Scene *scene,
- bool display_seconds,
- bool draw_line);
+ bool display_seconds);
void ED_time_scrub_draw(const struct ARegion *region,
const struct Scene *scene,
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index ea3d921f2c5..f3aba12a924 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -237,6 +237,18 @@ void ED_image_draw_cursor(struct ARegion *region, const float cursor[2]);
void ED_uvedit_buttons_register(struct ARegionType *art);
/* uvedit_islands.c */
+
+struct UVMapUDIM_Params {
+ const struct Image *image;
+ /** Copied from #SpaceImage.tile_grid_shape */
+ int grid_shape[2];
+ bool use_target_udim;
+ int target_udim;
+};
+bool ED_uvedit_udim_params_from_image_space(const struct SpaceImage *sima,
+ bool use_active,
+ struct UVMapUDIM_Params *udim_params);
+
struct UVPackIsland_Params {
uint rotate : 1;
/** -1 not to align to axis, otherwise 0,1 for X,Y. */
@@ -246,9 +258,14 @@ struct UVPackIsland_Params {
uint use_seams : 1;
uint correct_aspect : 1;
};
+
+bool uv_coords_isect_udim(const struct Image *image,
+ const int udim_grid[2],
+ const float coords[2]);
void ED_uvedit_pack_islands_multi(const struct Scene *scene,
Object **objects,
const uint objects_len,
+ const struct UVMapUDIM_Params *udim_params,
const struct UVPackIsland_Params *params);
#ifdef __cplusplus
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index ddd9ca4a98c..8a7df5b54ff 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -989,6 +989,16 @@ DEF_ICON_VECTOR(COLLECTION_COLOR_06)
DEF_ICON_VECTOR(COLLECTION_COLOR_07)
DEF_ICON_VECTOR(COLLECTION_COLOR_08)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_01)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_02)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_03)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_04)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_05)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_06)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_07)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_08)
+DEF_ICON_VECTOR(SEQUENCE_COLOR_09)
+
/* Events. */
DEF_ICON_COLOR(EVENT_A)
DEF_ICON_COLOR(EVENT_B)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 0f74d81f7bb..59d688ad2d0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -84,6 +84,10 @@ typedef struct uiBlock uiBlock;
typedef struct uiBut uiBut;
typedef struct uiLayout uiLayout;
typedef struct uiPopupBlockHandle uiPopupBlockHandle;
+/* C handle for C++ #ui::AbstractTreeView type. */
+typedef struct uiTreeViewHandle uiTreeViewHandle;
+/* C handle for C++ #ui::AbstractTreeViewItem type. */
+typedef struct uiTreeViewItemHandle uiTreeViewItemHandle;
/* Defines */
@@ -389,6 +393,8 @@ typedef enum {
UI_BTYPE_GRIP = 57 << 9,
UI_BTYPE_DECORATOR = 58 << 9,
UI_BTYPE_DATASETROW = 59 << 9,
+ /* An item in a tree view. Parent items may be collapsible. */
+ UI_BTYPE_TREEROW = 60 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@@ -1672,6 +1678,7 @@ void UI_but_datasetrow_component_set(uiBut *but, uint8_t geometry_component_type
void UI_but_datasetrow_domain_set(uiBut *but, uint8_t attribute_domain);
uint8_t UI_but_datasetrow_component_get(uiBut *but);
uint8_t UI_but_datasetrow_domain_get(uiBut *but);
+void UI_but_treerow_indentation_set(uiBut *but, int indentation);
void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]);
@@ -2587,6 +2594,7 @@ typedef struct uiDragColorHandle {
void ED_operatortypes_ui(void);
void ED_keymap_ui(struct wmKeyConfig *keyconf);
+void ED_dropboxes_ui(void);
void ED_uilisttypes_ui(void);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
@@ -2755,6 +2763,17 @@ void UI_interface_tag_script_reload(void);
/* Support click-drag motion which presses the button and closes a popover (like a menu). */
#define USE_UI_POPOVER_ONCE
+bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
+bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
+bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag);
+bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags);
+char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item,
+ const struct bContext *C,
+ const struct wmDrag *drag,
+ const struct wmEvent *event);
+
+uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
new file mode 100644
index 00000000000..4a583d0225e
--- /dev/null
+++ b/source/blender/editors/include/UI_interface.hh
@@ -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 editorui
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "BLI_string_ref.hh"
+
+struct uiBlock;
+namespace blender::ui {
+class AbstractTreeView;
+}
+
+blender::ui::AbstractTreeView *UI_block_add_view(
+ uiBlock &block,
+ blender::StringRef idname,
+ std::unique_ptr<blender::ui::AbstractTreeView> tree_view);
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
new file mode 100644
index 00000000000..d36e688dd65
--- /dev/null
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -0,0 +1,257 @@
+/*
+ * 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 editorui
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "BLI_function_ref.hh"
+#include "BLI_vector.hh"
+
+#include "UI_resources.h"
+
+struct bContext;
+struct PointerRNA;
+struct uiBlock;
+struct uiBut;
+struct uiButTreeRow;
+struct uiLayout;
+struct wmEvent;
+struct wmDrag;
+
+namespace blender::ui {
+
+class AbstractTreeView;
+class AbstractTreeViewItem;
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Item Container
+ * \{ */
+
+/**
+ * Helper base class to expose common child-item data and functionality to both #AbstractTreeView
+ * and #AbstractTreeViewItem.
+ *
+ * That means this type can be used whenever either a #AbstractTreeView or a
+ * #AbstractTreeViewItem is needed.
+ */
+class TreeViewItemContainer {
+ friend class AbstractTreeView;
+ friend class AbstractTreeViewItem;
+
+ /* Private constructor, so only the friends above can create this! */
+ TreeViewItemContainer() = default;
+
+ protected:
+ Vector<std::unique_ptr<AbstractTreeViewItem>> children_;
+ /** Adding the first item to the root will set this, then it's passed on to all children. */
+ TreeViewItemContainer *root_ = nullptr;
+ /** Pointer back to the owning item. */
+ AbstractTreeViewItem *parent_ = nullptr;
+
+ public:
+ enum class IterOptions {
+ None = 0,
+ SkipCollapsed = 1 << 0,
+
+ /* Keep ENUM_OPERATORS() below updated! */
+ };
+ using ItemIterFn = FunctionRef<void(AbstractTreeViewItem &)>;
+
+ /**
+ * Convenience wrapper taking the arguments needed to construct an item of type \a ItemT. Calls
+ * the version just below.
+ */
+ template<class ItemT, typename... Args> ItemT &add_tree_item(Args &&...args)
+ {
+ static_assert(std::is_base_of<AbstractTreeViewItem, ItemT>::value,
+ "Type must derive from and implement the AbstractTreeViewItem interface");
+
+ return dynamic_cast<ItemT &>(
+ add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
+ }
+
+ AbstractTreeViewItem &add_tree_item(std::unique_ptr<AbstractTreeViewItem> item);
+
+ protected:
+ void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+};
+
+ENUM_OPERATORS(TreeViewItemContainer::IterOptions,
+ TreeViewItemContainer::IterOptions::SkipCollapsed);
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Builders
+ * \{ */
+
+class TreeViewBuilder {
+ uiBlock &block_;
+
+ public:
+ TreeViewBuilder(uiBlock &block);
+
+ void build_tree_view(AbstractTreeView &tree_view);
+};
+
+class TreeViewLayoutBuilder {
+ uiBlock &block_;
+
+ friend TreeViewBuilder;
+
+ public:
+ void build_row(AbstractTreeViewItem &item) const;
+ uiBlock &block() const;
+ uiLayout *current_layout() const;
+
+ private:
+ /* Created through #TreeViewBuilder. */
+ TreeViewLayoutBuilder(uiBlock &block);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Base Class
+ * \{ */
+
+class AbstractTreeView : public TreeViewItemContainer {
+ friend TreeViewBuilder;
+ friend TreeViewLayoutBuilder;
+
+ public:
+ virtual ~AbstractTreeView() = default;
+
+ void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+
+ protected:
+ virtual void build_tree() = 0;
+
+ private:
+ /** Match the tree-view against an earlier version of itself (if any) and copy the old UI state
+ * (e.g. collapsed, active, selected) to the new one. See
+ * #AbstractTreeViewItem.update_from_old(). */
+ void update_from_old(uiBlock &new_block);
+ static void update_children_from_old_recursive(const TreeViewItemContainer &new_items,
+ const TreeViewItemContainer &old_items);
+ static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item,
+ const TreeViewItemContainer &items);
+ void build_layout_from_tree(const TreeViewLayoutBuilder &builder);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Item Type
+ * \{ */
+
+/** \brief Abstract base class for defining a customizable tree-view item.
+ *
+ * The tree-view item defines how to build its data into a tree-row. There are implementations for
+ * common layouts, e.g. #BasicTreeViewItem.
+ * It also stores state information that needs to be persistent over redraws, like the collapsed
+ * state.
+ */
+class AbstractTreeViewItem : public TreeViewItemContainer {
+ friend class AbstractTreeView;
+
+ bool is_open_ = false;
+ bool is_active_ = false;
+
+ protected:
+ /** This label is used for identifying an item (together with its parent's labels). */
+ std::string label_{};
+
+ public:
+ virtual ~AbstractTreeViewItem() = default;
+
+ virtual void build_row(uiLayout &row) = 0;
+
+ virtual void on_activate();
+ virtual bool on_drop(const wmDrag &drag);
+ virtual bool can_drop(const wmDrag &drag) const;
+ /** Custom text to display when dragging over a tree item. Should explain what happens when
+ * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop()
+ * returns true, so the implementing override doesn't have to check that again.
+ * The returned value must be a translated string. */
+ virtual std::string drop_tooltip(const bContext &C,
+ const wmDrag &drag,
+ const wmEvent &event) const;
+
+ /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of
+ * the last redraw to this item. If sub-classes introduce more advanced state they should
+ * override this and make it update their state accordingly. */
+ virtual void update_from_old(const AbstractTreeViewItem &old);
+ /** Compare this item to \a other to check if they represent the same data. This is critical for
+ * being able to recognize an item from a previous redraw, to be able to keep its state (e.g.
+ * open/closed, active, etc.). Items are only matched if their parents also match.
+ * By default this just matches the items names/labels (if their parents match!). If that isn't
+ * good enough for a sub-class, that can override it. */
+ virtual bool matches(const AbstractTreeViewItem &other) const;
+
+ const AbstractTreeView &get_tree_view() const;
+ int count_parents() const;
+ void set_active(bool value = true);
+ bool is_active() const;
+ void toggle_collapsed();
+ bool is_collapsed() const;
+ void set_collapsed(bool collapsed);
+ bool is_collapsible() const;
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Predefined Tree-View Item Types
+ *
+ * Common, Basic Tree-View Item Types.
+ * \{ */
+
+/**
+ * The most basic type, just a label with an icon.
+ */
+class BasicTreeViewItem : public AbstractTreeViewItem {
+ public:
+ using ActivateFn = std::function<void(BasicTreeViewItem &new_active)>;
+ BIFIconID icon;
+
+ BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE, ActivateFn activate_fn = nullptr);
+
+ void build_row(uiLayout &row) override;
+ void on_activate() override;
+
+ protected:
+ /** Created in the #build() function. */
+ uiButTreeRow *tree_row_but_ = nullptr;
+ /** Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree
+ * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement
+ * custom activation behavior (a common thing to do). */
+ ActivateFn activate_fn_;
+
+ uiBut *button();
+ BIFIconID get_draw_icon() const;
+};
+
+/** \} */
+
+} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index e3c02b4c249..fdfa07a7e02 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -123,6 +123,7 @@ void UI_view2d_region_reinit(struct View2D *v2d, short type, int winx, int winy)
void UI_view2d_curRect_validate(struct View2D *v2d);
void UI_view2d_curRect_reset(struct View2D *v2d);
+bool UI_view2d_area_supports_sync(struct ScrArea *area);
void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag);
/* Perform all required updates after `v2d->cur` as been modified.
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 64c874e26a9..f27ff9694c2 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -42,6 +42,7 @@ set(SRC
interface_button_group.c
interface_context_menu.c
interface_draw.c
+ interface_dropboxes.cc
interface_eyedropper.c
interface_eyedropper_color.c
interface_eyedropper_colorband.c
@@ -73,8 +74,10 @@ set(SRC
interface_templates.c
interface_undo.c
interface_utils.c
+ interface_view.cc
interface_widgets.c
resources.c
+ tree_view.cc
view2d.c
view2d_draw.c
view2d_edge_pan.c
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index fd75be5b847..c53bffca778 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -743,6 +743,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
return false;
}
+ if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) {
+ uiButTreeRow *but_treerow = (uiButTreeRow *)but;
+ uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut;
+ if (!but_treerow->tree_item || !oldbut_treerow->tree_item ||
+ !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) {
+ return false;
+ }
+ }
+
return true;
}
@@ -856,10 +865,21 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
oldbut->hardmax = but->hardmax;
}
- if (oldbut->type == UI_BTYPE_PROGRESS_BAR) {
- uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut;
- uiButProgressbar *progress_but = (uiButProgressbar *)but;
- progress_oldbut->progress = progress_but->progress;
+ switch (oldbut->type) {
+ case UI_BTYPE_PROGRESS_BAR: {
+ uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut;
+ uiButProgressbar *progress_but = (uiButProgressbar *)but;
+ progress_oldbut->progress = progress_but->progress;
+ break;
+ }
+ case UI_BTYPE_TREEROW: {
+ uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut;
+ uiButTreeRow *treerow_newbut = (uiButTreeRow *)but;
+ SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item);
+ break;
+ }
+ default:
+ break;
}
/* move/copy string from the new button to the old */
@@ -2203,6 +2223,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
}
}
break;
+ case UI_BTYPE_TREEROW: {
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
+
+ is_push = -1;
+ if (tree_row_but->tree_item) {
+ is_push = UI_tree_view_item_is_active(tree_row_but->tree_item);
+ }
+ break;
+ }
default:
is_push = -1;
break;
@@ -3447,6 +3476,7 @@ void UI_block_free(const bContext *C, uiBlock *block)
BLI_freelistN(&block->color_pickers.list);
ui_block_free_button_groups(block);
+ ui_block_free_views(block);
MEM_freeN(block);
}
@@ -3942,6 +3972,10 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButDatasetRow);
alloc_str = "uiButDatasetRow";
break;
+ case UI_BTYPE_TREEROW:
+ alloc_size = sizeof(uiButTreeRow);
+ alloc_str = "uiButTreeRow";
+ break;
default:
alloc_size = sizeof(uiBut);
alloc_str = "uiBut";
@@ -4141,6 +4175,7 @@ static uiBut *ui_def_but(uiBlock *block,
UI_BTYPE_BUT_MENU,
UI_BTYPE_SEARCH_MENU,
UI_BTYPE_DATASETROW,
+ UI_BTYPE_TREEROW,
UI_BTYPE_POPOVER)) {
but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
}
@@ -6198,6 +6233,13 @@ void UI_but_drag_set_asset(uiBut *but,
asset_drag->id_type = ED_asset_handle_get_id_type(asset);
asset_drag->import_type = import_type;
+ /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the
+ * #wmDropBox.
+ * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its
+ * copy callback.
+ * */
+ asset_drag->evil_C = but->block->evil_C;
+
but->dragtype = WM_DRAG_ASSET;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
@@ -6878,6 +6920,15 @@ void UI_but_datasetrow_indentation_set(uiBut *but, int indentation)
BLI_assert(indentation >= 0);
}
+void UI_but_treerow_indentation_set(uiBut *but, int indentation)
+{
+ uiButTreeRow *but_row = (uiButTreeRow *)but;
+ BLI_assert(but->type == UI_BTYPE_TREEROW);
+
+ but_row->indentation = indentation;
+ BLI_assert(indentation >= 0);
+}
+
/**
* Adds a hint to the button which draws right aligned, grayed out and never clipped.
*/
diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc
new file mode 100644
index 00000000000..cb33e7f736e
--- /dev/null
+++ b/source/blender/editors/interface/interface_dropboxes.cc
@@ -0,0 +1,66 @@
+/*
+ * 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 edinterface
+ */
+
+#include "BKE_context.h"
+
+#include "DNA_space_types.h"
+
+#include "WM_api.h"
+
+#include "UI_interface.h"
+
+static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
+{
+ const ARegion *region = CTX_wm_region(C);
+ const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
+ region, event->x, event->y);
+ if (!hovered_tree_item) {
+ return false;
+ }
+
+ return UI_tree_view_item_can_drop(hovered_tree_item, drag);
+}
+
+static char *ui_tree_view_drop_tooltip(bContext *C,
+ wmDrag *drag,
+ const wmEvent *event,
+ wmDropBox *UNUSED(drop))
+{
+ const ARegion *region = CTX_wm_region(C);
+ const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
+ region, event->x, event->y);
+ if (!hovered_tree_item) {
+ return nullptr;
+ }
+
+ return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event);
+}
+
+void ED_dropboxes_ui()
+{
+ ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0);
+
+ WM_dropbox_add(lb,
+ "UI_OT_tree_view_drop",
+ ui_tree_view_drop_poll,
+ nullptr,
+ nullptr,
+ ui_tree_view_drop_tooltip);
+}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 77ae16d7cc7..aee66ec3a93 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -384,6 +384,8 @@ typedef struct uiHandleButtonData {
/* booleans (could be made into flags) */
bool cancel, escapecancel;
bool applied, applied_interactive;
+ /* Button is being applied through an extra icon. */
+ bool apply_through_extra_icon;
bool changed_cursor;
wmTimer *flashtimer;
@@ -1164,6 +1166,16 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
data->applied = true;
}
+static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ if (data->apply_through_extra_icon) {
+ /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons.
+ */
+ return;
+ }
+ ui_apply_but_ROW(C, block, but, data);
+}
+
/**
* \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now.
*
@@ -2307,6 +2319,9 @@ static void ui_apply_but(
case UI_BTYPE_ROW:
ui_apply_but_ROW(C, block, but, data);
break;
+ case UI_BTYPE_TREEROW:
+ ui_apply_but_TREEROW(C, block, but, data);
+ break;
case UI_BTYPE_LISTROW:
ui_apply_but_LISTROW(C, block, but, data);
break;
@@ -4194,6 +4209,8 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon)
{
+ but->active->apply_through_extra_icon = true;
+
if (but->active->interactive) {
ui_apply_but(C, but->block, but, but->active, true);
}
@@ -4737,7 +4754,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
/* Behave like other menu items. */
do_activate = (event->val == KM_RELEASE);
}
- else {
+ else if (!ui_do_but_extra_operator_icon(C, but, data, event)) {
/* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */
do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK);
}
@@ -7966,6 +7983,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_CHECKBOX:
case UI_BTYPE_CHECKBOX_N:
case UI_BTYPE_ROW:
+ case UI_BTYPE_TREEROW:
case UI_BTYPE_DATASETROW:
retval = ui_do_but_TOG(C, but, data, event);
break;
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index f739830cfdb..c20129b4184 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -47,6 +47,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
+#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "RNA_access.h"
@@ -309,7 +310,7 @@ static void vicon_keytype_draw_wrapper(
sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
immBegin(GPU_PRIM_POINTS, 1);
@@ -480,6 +481,35 @@ DEF_ICON_COLLECTION_COLOR_DRAW(08, COLLECTION_COLOR_08);
# undef DEF_ICON_COLLECTION_COLOR_DRAW
+static void vicon_strip_color_draw(
+ short color_tag, int x, int y, int w, int UNUSED(h), float UNUSED(alpha))
+{
+ bTheme *btheme = UI_GetTheme();
+ const ThemeStripColor *strip_color = &btheme->strip_color[color_tag];
+
+ const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w;
+
+ UI_icon_draw_ex(x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true);
+}
+
+# define DEF_ICON_STRIP_COLOR_DRAW(index, color) \
+ static void vicon_strip_color_draw_##index(int x, int y, int w, int h, float alpha) \
+ { \
+ vicon_strip_color_draw(color, x, y, w, h, alpha); \
+ }
+
+DEF_ICON_STRIP_COLOR_DRAW(01, SEQUENCE_COLOR_01);
+DEF_ICON_STRIP_COLOR_DRAW(02, SEQUENCE_COLOR_02);
+DEF_ICON_STRIP_COLOR_DRAW(03, SEQUENCE_COLOR_03);
+DEF_ICON_STRIP_COLOR_DRAW(04, SEQUENCE_COLOR_04);
+DEF_ICON_STRIP_COLOR_DRAW(05, SEQUENCE_COLOR_05);
+DEF_ICON_STRIP_COLOR_DRAW(06, SEQUENCE_COLOR_06);
+DEF_ICON_STRIP_COLOR_DRAW(07, SEQUENCE_COLOR_07);
+DEF_ICON_STRIP_COLOR_DRAW(08, SEQUENCE_COLOR_08);
+DEF_ICON_STRIP_COLOR_DRAW(09, SEQUENCE_COLOR_09);
+
+# undef DEF_ICON_STRIP_COLOR_DRAW
+
/* Dynamically render icon instead of rendering a plain color to a texture/buffer
* This is not strictly a "vicon", as it needs access to icon->obj to get the color info,
* but it works in a very similar way.
@@ -995,6 +1025,16 @@ static void init_internal_icons(void)
def_internal_vicon(ICON_COLLECTION_COLOR_06, vicon_collection_color_draw_06);
def_internal_vicon(ICON_COLLECTION_COLOR_07, vicon_collection_color_draw_07);
def_internal_vicon(ICON_COLLECTION_COLOR_08, vicon_collection_color_draw_08);
+
+ def_internal_vicon(ICON_SEQUENCE_COLOR_01, vicon_strip_color_draw_01);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_02, vicon_strip_color_draw_02);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_03, vicon_strip_color_draw_03);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_04, vicon_strip_color_draw_04);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_05, vicon_strip_color_draw_05);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_06, vicon_strip_color_draw_06);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_07, vicon_strip_color_draw_07);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_08, vicon_strip_color_draw_08);
+ def_internal_vicon(ICON_SEQUENCE_COLOR_09, vicon_strip_color_draw_09);
}
static void init_iconfile_list(struct ListBase *list)
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index d61104f094e..8b45d9faae6 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -360,6 +360,14 @@ typedef struct uiButDatasetRow {
int indentation;
} uiButDatasetRow;
+/** Derived struct for #UI_BTYPE_TREEROW. */
+typedef struct uiButTreeRow {
+ uiBut but;
+
+ uiTreeViewItemHandle *tree_item;
+ int indentation;
+} uiButTreeRow;
+
/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
uiBut but;
@@ -488,6 +496,11 @@ struct uiBlock {
ListBase contexts;
+ /** A block can store "views" on data-sets. Currently tree-views (#AbstractTreeView) only.
+ * Others are imaginable, e.g. table-views, grid-views, etc. These are stored here to support
+ * state that is persistent over redraws (e.g. collapsed tree-view items). */
+ ListBase views;
+
char name[UI_MAX_NAME_STR];
float winmat[4][4];
@@ -1158,6 +1171,7 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region,
uiBut *ui_list_row_find_from_index(const struct ARegion *region,
const int index,
uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y);
typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
@@ -1274,6 +1288,11 @@ bool ui_jump_to_target_button_poll(struct bContext *C);
/* interface_queries.c */
void ui_interface_tag_script_reload_queries(void);
+/* interface_view.cc */
+void ui_block_free_views(struct uiBlock *block);
+uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
+ const uiTreeViewHandle *new_view);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 66c75c63050..64c16e57f56 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -288,35 +288,85 @@ static bool ui_layout_variable_size(uiLayout *layout)
return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size;
}
-/* estimated size of text + icon */
-static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact)
+/**
+ * Factors to apply to #UI_UNIT_X when calculating button width.
+ * This is used when the layout is a varying size, see #ui_layout_variable_size.
+ */
+struct uiTextIconPadFactor {
+ float text;
+ float icon;
+ float icon_only;
+};
+
+/**
+ * This adds over an icons width of padding even when no icon is used,
+ * this is done because most buttons need additional space (drop-down chevron for example).
+ * menus and labels use much smaller `text` values compared to this default.
+ *
+ * \note It may seem odd that the icon only adds 0.25
+ * but taking margins into account its fine,
+ * except for #ui_text_pad_compact where a bit more margin is required.
+ */
+static const struct uiTextIconPadFactor ui_text_pad_default = {
+ .text = 1.50f,
+ .icon = 0.25f,
+ .icon_only = 0.0f,
+};
+
+/** #ui_text_pad_default scaled down. */
+static const struct uiTextIconPadFactor ui_text_pad_compact = {
+ .text = 1.25f,
+ .icon = 0.35f,
+ .icon_only = 0.0f,
+};
+
+/** Least amount of padding not to clip the text or icon. */
+static const struct uiTextIconPadFactor ui_text_pad_none = {
+ .text = 0.25f,
+ .icon = 1.50f,
+ .icon_only = 0.0f,
+};
+
+/**
+ * Estimated size of text + icon.
+ */
+static int ui_text_icon_width_ex(uiLayout *layout,
+ const char *name,
+ int icon,
+ const struct uiTextIconPadFactor *pad_factor)
{
const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f);
+ /* When there is no text, always behave as if this is an icon-only button
+ * since it's not useful to return empty space. */
if (icon && !name[0]) {
- return unit_x; /* icon only */
+ return unit_x * (1.0f + pad_factor->icon_only);
}
if (ui_layout_variable_size(layout)) {
if (!icon && !name[0]) {
- return unit_x; /* No icon or name. */
+ return unit_x * (1.0f + pad_factor->icon_only);
}
+
if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) {
layout->item.flag |= UI_ITEM_FIXED_SIZE;
}
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- float margin = compact ? 1.25 : 1.50;
+ float margin = pad_factor->text;
if (icon) {
- /* It may seem odd that the icon only adds (unit_x / 4)
- * but taking margins into account its fine, except
- * in compact mode a bit more margin is required. */
- margin += compact ? 0.35 : 0.25;
+ margin += pad_factor->icon;
}
return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin);
}
return unit_x * 10;
}
+static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact)
+{
+ return ui_text_icon_width_ex(
+ layout, name, icon, compact ? &ui_text_pad_compact : &ui_text_pad_default);
+}
+
static void ui_item_size(uiItem *item, int *r_w, int *r_h)
{
if (item->type == ITEM_BUTTON) {
@@ -2857,23 +2907,23 @@ static uiBut *ui_item_menu(uiLayout *layout,
icon = ICON_BLANK1;
}
- int w = ui_text_icon_width(layout, name, icon, 1);
- const int h = UI_UNIT_Y;
-
+ struct uiTextIconPadFactor pad_factor = ui_text_pad_compact;
if (layout->root->type == UI_LAYOUT_HEADER) { /* Ugly! */
if (icon == ICON_NONE && force_menu) {
/* pass */
}
else if (force_menu) {
- w += 0.6f * UI_UNIT_X;
+ pad_factor.text = 1.85;
+ pad_factor.icon_only = 0.6f;
}
else {
- if (name[0]) {
- w -= UI_UNIT_X / 2;
- }
+ pad_factor.text = 0.75f;
}
}
+ const int w = ui_text_icon_width_ex(layout, name, icon, &pad_factor);
+ const int h = UI_UNIT_Y;
+
if (heading_layout) {
ui_layout_heading_label_add(layout, heading_layout, true, true);
}
@@ -3133,8 +3183,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon)
icon = ICON_BLANK1;
}
- const int w = ui_text_icon_width(layout, name, icon, 0);
-
+ const int w = ui_text_icon_width_ex(layout, name, icon, &ui_text_pad_none);
uiBut *but;
if (icon && name[0]) {
but = uiDefIconTextBut(
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index dd10d942fc9..1fc07bce341 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1381,16 +1381,7 @@ static int editsource_text_edit(bContext *C,
/* naughty!, find text area to set, not good behavior
* but since this is a developer tool lets allow it - campbell */
- ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
- if (area) {
- SpaceText *st = area->spacedata.first;
- ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
- st->text = text;
- if (region) {
- ED_text_scroll_to_cursor(st, region, true);
- }
- }
- else {
+ if (!ED_text_activate_in_screen(C, text)) {
BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
}
@@ -1927,6 +1918,51 @@ static void UI_OT_list_start_filter(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name UI Tree-View Drop Operator
+ * \{ */
+
+static bool ui_tree_view_drop_poll(bContext *C)
+{
+ const wmWindow *win = CTX_wm_window(C);
+ const ARegion *region = CTX_wm_region(C);
+ const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
+ region, win->eventstate->x, win->eventstate->y);
+
+ return hovered_tree_item != NULL;
+}
+
+static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ if (event->custom != EVT_DATA_DRAGDROP) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ const ARegion *region = CTX_wm_region(C);
+ uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
+ region, event->x, event->y);
+
+ if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_tree_view_drop(wmOperatorType *ot)
+{
+ ot->name = "Tree View drop";
+ ot->idname = "UI_OT_tree_view_drop";
+ ot->description = "Drag and drop items onto a tree item";
+
+ ot->invoke = ui_tree_view_drop_invoke;
+ ot->poll = ui_tree_view_drop_poll;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Operator & Keymap Registration
* \{ */
@@ -1953,6 +1989,8 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_list_start_filter);
+ WM_operatortype_append(UI_OT_tree_view_drop);
+
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
WM_operatortype_append(UI_OT_eyedropper_colorramp);
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 09429bb6df5..2f6bda3252d 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -69,7 +69,8 @@ bool ui_but_is_toggle(const uiBut *but)
UI_BTYPE_CHECKBOX,
UI_BTYPE_CHECKBOX_N,
UI_BTYPE_ROW,
- UI_BTYPE_DATASETROW);
+ UI_BTYPE_DATASETROW,
+ UI_BTYPE_TREEROW);
}
/**
@@ -462,6 +463,16 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut
return ui_but_find(region, ui_but_is_listrow_at_index, &data);
}
+static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata))
+{
+ return but->type == UI_BTYPE_TREEROW;
+}
+
+uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int y)
+{
+ return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
new file mode 100644
index 00000000000..b199ce9562e
--- /dev/null
+++ b/source/blender/editors/interface/interface_view.cc
@@ -0,0 +1,133 @@
+/*
+ * 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 edinterface
+ *
+ * This part of the UI-View API is mostly needed to support persistent state of items within the
+ * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
+ * can compare the old view items with the new view items and keep state persistent for matching
+ * ones.
+ */
+
+#include <memory>
+#include <variant>
+
+#include "DNA_screen_types.h"
+
+#include "BLI_listbase.h"
+
+#include "interface_intern.h"
+
+#include "UI_interface.hh"
+#include "UI_tree_view.hh"
+
+using namespace blender;
+using namespace blender::ui;
+
+/**
+ * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a
+ * #std::variant.
+ */
+struct ViewLink : public Link {
+ using TreeViewPtr = std::unique_ptr<AbstractTreeView>;
+
+ std::string idname;
+ /* Note: Can't use std::get() on this until minimum macOS deployment target is 10.14. */
+ std::variant<TreeViewPtr> view;
+};
+
+template<class T> T *get_view_from_link(ViewLink &link)
+{
+ auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view);
+ return t_uptr ? t_uptr->get() : nullptr;
+}
+
+/**
+ * Override this for all available tree types.
+ */
+AbstractTreeView *UI_block_add_view(uiBlock &block,
+ StringRef idname,
+ std::unique_ptr<AbstractTreeView> tree_view)
+{
+ ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink);
+ BLI_addtail(&block.views, view_link);
+
+ view_link->view = std::move(tree_view);
+ view_link->idname = idname;
+
+ return get_view_from_link<AbstractTreeView>(*view_link);
+}
+
+void ui_block_free_views(uiBlock *block)
+{
+ LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
+ OBJECT_GUARDED_DELETE(link, ViewLink);
+ }
+}
+
+/**
+ * \param x, y: Coordinate to find a tree-row item at, in window space.
+ */
+uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region,
+ const int x,
+ const int y)
+{
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, x, y);
+ if (!tree_row_but) {
+ return nullptr;
+ }
+
+ return tree_row_but->tree_item;
+}
+
+static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
+{
+ /* First get the idname the of the view we're looking for. */
+ LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
+ if (get_view_from_link<AbstractTreeView>(*view_link) == &view) {
+ return view_link->idname;
+ }
+ }
+
+ return {};
+}
+
+uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
+ const uiTreeViewHandle *new_view_handle)
+{
+ const AbstractTreeView &needle_view = reinterpret_cast<const AbstractTreeView &>(
+ *new_view_handle);
+
+ uiBlock *old_block = new_block->oldblock;
+ if (!old_block) {
+ return nullptr;
+ }
+
+ StringRef idname = ui_block_view_find_idname(*new_block, needle_view);
+ if (idname.is_empty()) {
+ return nullptr;
+ }
+
+ LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
+ if (old_view_link->idname == idname) {
+ return reinterpret_cast<uiTreeViewHandle *>(
+ get_view_from_link<AbstractTreeView>(*old_view_link));
+ }
+ }
+
+ return nullptr;
+}
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0dc7c2d3f9a..466deedf3bb 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -115,6 +115,7 @@ typedef enum {
UI_WTYPE_PROGRESSBAR,
UI_WTYPE_NODESOCKET,
UI_WTYPE_DATASETROW,
+ UI_WTYPE_TREEROW,
} uiWidgetTypeEnum;
/* Button state argument shares bits with 'uiBut.flag'.
@@ -3679,10 +3680,9 @@ static void widget_progressbar(
widgetbase_draw(&wtb_bar, wcol);
}
-static void widget_datasetrow(
- uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign))
+static void widget_treerow_exec(
+ uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign), int indentation)
{
- uiButDatasetRow *but_componentrow = (uiButDatasetRow *)but;
uiWidgetBase wtb;
widget_init(&wtb);
@@ -3695,10 +3695,24 @@ static void widget_datasetrow(
widgetbase_draw(&wtb, wcol);
}
- BLI_rcti_resize(rect,
- BLI_rcti_size_x(rect) - UI_UNIT_X * but_componentrow->indentation,
- BLI_rcti_size_y(rect));
- BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * but_componentrow->indentation, 0);
+ BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect));
+ BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0);
+}
+
+static void widget_treerow(
+ uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiButTreeRow *tree_row = (uiButTreeRow *)but;
+ BLI_assert(but->type == UI_BTYPE_TREEROW);
+ widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation);
+}
+
+static void widget_datasetrow(
+ uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiButDatasetRow *dataset_row = (uiButDatasetRow *)but;
+ BLI_assert(but->type == UI_BTYPE_DATASETROW);
+ widget_treerow_exec(wcol, rect, state, roundboxalign, dataset_row->indentation);
}
static void widget_nodesocket(
@@ -4492,6 +4506,10 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_datasetrow;
break;
+ case UI_WTYPE_TREEROW:
+ wt.custom = widget_treerow;
+ break;
+
case UI_WTYPE_NODESOCKET:
wt.custom = widget_nodesocket;
break;
@@ -4824,6 +4842,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
fstyle = &style->widgetlabel;
break;
+ case UI_BTYPE_TREEROW:
+ wt = widget_type(UI_WTYPE_TREEROW);
+ fstyle = &style->widgetlabel;
+ break;
+
case UI_BTYPE_SCROLL:
wt = widget_type(UI_WTYPE_SCROLL);
break;
@@ -5348,7 +5371,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
}
}
else {
- BLI_assert_msg(0, "Unknwon menu item separator type");
+ BLI_assert_msg(0, "Unknown menu item separator type");
}
}
}
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
new file mode 100644
index 00000000000..0ea15a2a5bb
--- /dev/null
+++ b/source/blender/editors/interface/tree_view.cc
@@ -0,0 +1,381 @@
+/*
+ * 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 edinterface
+ */
+
+#include "DNA_userdef_types.h"
+
+#include "BLT_translation.h"
+
+#include "interface_intern.h"
+
+#include "UI_interface.h"
+
+#include "UI_tree_view.hh"
+
+namespace blender::ui {
+
+/* ---------------------------------------------------------------------- */
+
+/**
+ * Add a tree-item to the container. This is the only place where items should be added, it handles
+ * important invariants!
+ */
+AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
+ std::unique_ptr<AbstractTreeViewItem> item)
+{
+ children_.append(std::move(item));
+
+ /* The first item that will be added to the root sets this. */
+ if (root_ == nullptr) {
+ root_ = this;
+ }
+
+ AbstractTreeViewItem &added_item = *children_.last();
+ added_item.root_ = root_;
+ if (root_ != this) {
+ /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely
+ * nice to static_cast this, but well... */
+ added_item.parent_ = static_cast<AbstractTreeViewItem *>(this);
+ }
+
+ return added_item;
+}
+
+void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const
+{
+ for (const auto &child : children_) {
+ iter_fn(*child);
+ if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) {
+ continue;
+ }
+
+ child->foreach_item_recursive(iter_fn, options);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const
+{
+ foreach_item_recursive(iter_fn, options);
+}
+
+void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &builder)
+{
+ uiLayout *prev_layout = builder.current_layout();
+
+ uiLayoutColumn(prev_layout, true);
+
+ foreach_item([&builder](AbstractTreeViewItem &item) { builder.build_row(item); },
+ IterOptions::SkipCollapsed);
+
+ UI_block_layout_set_current(&builder.block(), prev_layout);
+}
+
+void AbstractTreeView::update_from_old(uiBlock &new_block)
+{
+ uiBlock *old_block = new_block.oldblock;
+ if (!old_block) {
+ return;
+ }
+
+ uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block(
+ &new_block, reinterpret_cast<uiTreeViewHandle *>(this));
+ if (!old_view_handle) {
+ return;
+ }
+
+ AbstractTreeView &old_view = reinterpret_cast<AbstractTreeView &>(*old_view_handle);
+ update_children_from_old_recursive(*this, old_view);
+}
+
+void AbstractTreeView::update_children_from_old_recursive(const TreeViewItemContainer &new_items,
+ const TreeViewItemContainer &old_items)
+{
+ for (const auto &new_item : new_items.children_) {
+ AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item, old_items);
+ if (!matching_old_item) {
+ continue;
+ }
+
+ new_item->update_from_old(*matching_old_item);
+
+ /* Recurse into children of the matched item. */
+ update_children_from_old_recursive(*new_item, *matching_old_item);
+ }
+}
+
+AbstractTreeViewItem *AbstractTreeView::find_matching_child(
+ const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items)
+{
+ for (const auto &iter_item : items.children_) {
+ if (lookup_item.matches(*iter_item)) {
+ /* We have a matching item! */
+ return iter_item.get();
+ }
+ }
+
+ return nullptr;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void AbstractTreeViewItem::on_activate()
+{
+ /* Do nothing by default. */
+}
+
+bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/)
+{
+ /* Do nothing by default. */
+ return false;
+}
+
+bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const
+{
+ return false;
+}
+
+std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/,
+ const wmDrag & /*drag*/,
+ const wmEvent & /*event*/) const
+{
+ return TIP_("Drop into/onto tree item");
+}
+
+void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
+{
+ is_open_ = old.is_open_;
+ is_active_ = old.is_active_;
+}
+
+bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
+{
+ return label_ == other.label_;
+}
+
+const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
+{
+ return static_cast<AbstractTreeView &>(*root_);
+}
+
+int AbstractTreeViewItem::count_parents() const
+{
+ int i = 0;
+ for (TreeViewItemContainer *parent = parent_; parent; parent = parent->parent_) {
+ i++;
+ }
+ return i;
+}
+
+void AbstractTreeViewItem::set_active(bool value)
+{
+ if (value && !is_active()) {
+ /* Deactivate other items in the tree. */
+ get_tree_view().foreach_item([](auto &item) { item.set_active(false); });
+ on_activate();
+ }
+ is_active_ = value;
+}
+
+bool AbstractTreeViewItem::is_active() const
+{
+ return is_active_;
+}
+
+bool AbstractTreeViewItem::is_collapsed() const
+{
+ return is_collapsible() && !is_open_;
+}
+
+void AbstractTreeViewItem::toggle_collapsed()
+{
+ is_open_ = !is_open_;
+}
+
+void AbstractTreeViewItem::set_collapsed(bool collapsed)
+{
+ is_open_ = !collapsed;
+}
+
+bool AbstractTreeViewItem::is_collapsible() const
+{
+ return !children_.is_empty();
+}
+
+/* ---------------------------------------------------------------------- */
+
+TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block)
+{
+}
+
+void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view)
+{
+ tree_view.build_tree();
+ tree_view.update_from_old(block_);
+ tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_));
+}
+
+/* ---------------------------------------------------------------------- */
+
+TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block)
+{
+}
+
+void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
+{
+ uiLayout *prev_layout = current_layout();
+ uiLayout *row = uiLayoutRow(prev_layout, false);
+
+ item.build_row(*row);
+
+ UI_block_layout_set_current(&block(), prev_layout);
+}
+
+uiBlock &TreeViewLayoutBuilder::block() const
+{
+ return block_;
+}
+
+uiLayout *TreeViewLayoutBuilder::current_layout() const
+{
+ return block().curlayout;
+}
+
+/* ---------------------------------------------------------------------- */
+
+BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_, ActivateFn activate_fn)
+ : icon(icon_), activate_fn_(activate_fn)
+{
+ label_ = label;
+}
+
+static void tree_row_click_fn(struct bContext *UNUSED(C), void *but_arg1, void *UNUSED(arg2))
+{
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1;
+ AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(
+ *tree_row_but->tree_item);
+
+ /* Let a click on an opened item activate it, a second click will close it then.
+ * TODO Should this be for asset catalogs only? */
+ if (tree_item.is_collapsed() || tree_item.is_active()) {
+ tree_item.toggle_collapsed();
+ }
+ tree_item.set_active();
+}
+
+void BasicTreeViewItem::build_row(uiLayout &row)
+{
+ uiBlock *block = uiLayoutGetBlock(&row);
+ tree_row_but_ = (uiButTreeRow *)uiDefIconTextBut(block,
+ UI_BTYPE_TREEROW,
+ 0,
+ /* TODO allow icon besides the chevron icon? */
+ get_draw_icon(),
+ label_.data(),
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+
+ tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this);
+ UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr);
+ UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents());
+}
+
+void BasicTreeViewItem::on_activate()
+{
+ if (activate_fn_) {
+ activate_fn_(*this);
+ }
+}
+
+BIFIconID BasicTreeViewItem::get_draw_icon() const
+{
+ if (icon) {
+ return icon;
+ }
+
+ if (is_collapsible()) {
+ return is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN;
+ }
+
+ return ICON_NONE;
+}
+
+uiBut *BasicTreeViewItem::button()
+{
+ return &tree_row_but_->but;
+}
+
+} // namespace blender::ui
+
+using namespace blender::ui;
+
+bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
+{
+ const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
+ return item.is_active();
+}
+
+bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
+ const uiTreeViewItemHandle *b_handle)
+{
+ const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle);
+ const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle);
+ return a.matches(b);
+}
+
+bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag)
+{
+ const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
+ return item.can_drop(*drag);
+}
+
+char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_,
+ const bContext *C,
+ const wmDrag *drag,
+ const wmEvent *event)
+{
+ const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
+ return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str());
+}
+
+/**
+ * Let a tree-view item handle a drop event.
+ * \return True if the drop was handled by the tree-view item.
+ */
+bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags)
+{
+ AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_);
+
+ LISTBASE_FOREACH (const wmDrag *, drag, drags) {
+ if (item.can_drop(*drag)) {
+ return item.on_drop(*drag);
+ }
+ }
+
+ return false;
+}
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index 23c8a0d35bf..0036a812a87 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -866,6 +866,11 @@ void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
/* ------------------ */
+bool UI_view2d_area_supports_sync(ScrArea *area)
+{
+ return ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH);
+}
+
/* Called by menus to activate it, or by view2d operators
* to make sure 'related' views stay in synchrony */
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
@@ -903,6 +908,9 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
/* check if doing whole screen syncing (i.e. time/horizontal) */
if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
+ if (!UI_view2d_area_supports_sync(area_iter)) {
+ continue;
+ }
LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
/* don't operate on self */
if (v2dcur != &region->v2d) {
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index eee4aec7459..64c008acf8e 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -113,7 +113,7 @@ typedef struct KnifeColors {
uchar axis_extra[3];
} KnifeColors;
-/* Knifetool Operator. */
+/* Knife-tool Operator. */
typedef struct KnifeVert {
Object *ob;
uint base_index;
@@ -333,6 +333,9 @@ enum {
KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE,
KNF_MODAL_DEPTH_TEST_TOGGLE,
KNF_MODAL_PANNING,
+ KNF_MODAL_X_AXIS,
+ KNF_MODAL_Y_AXIS,
+ KNF_MODAL_Z_AXIS,
KNF_MODAL_ADD_CUT_CLOSED,
};
@@ -366,7 +369,6 @@ enum {
/** \name Drawing
* \{ */
-#if 1
static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3])
{
float planes[4][4];
@@ -380,10 +382,6 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3],
float lambda_best[2] = {-FLT_MAX, FLT_MAX};
int i;
- /* We (sometimes) need the lines to be at the same depth before projecting. */
-# if 0
- sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
-# else
{
float curr_cage_adjust[3];
float co_depth[3];
@@ -393,7 +391,6 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3],
sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
}
-# endif
for (i = 0; i < 4; i++) {
float ray_hit[3];
@@ -445,7 +442,7 @@ static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd)
if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) {
float v1[3], v2[3];
- /* This is causing buggyness when prev.cage and curr.cage are too close together. */
+ /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */
knifetool_raycast_planes(kcd, v1, v2);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -480,7 +477,6 @@ static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd)
immUnbindProgram();
}
}
-#endif
static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
{
@@ -1112,7 +1108,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
"%s: start/define cut, %s: close cut, %s: new cut, "
"%s: midpoint snap (%s), %s: ignore snap (%s), "
"%s: angle constraint %.2f(%.2f) (%s%s%s%s), %s: cut through (%s), "
- "%s: panning, XYZ: orientation lock (%s), "
+ "%s: panning, %s%s%s: orientation lock (%s), "
"%s: distance/angle measurements (%s), "
"%s: x-ray (%s)"),
WM_MODALKEY(KNF_MODAL_CONFIRM),
@@ -1144,6 +1140,9 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE),
WM_bool_as_string(kcd->cut_through),
WM_MODALKEY(KNF_MODAL_PANNING),
+ WM_MODALKEY(KNF_MODAL_X_AXIS),
+ WM_MODALKEY(KNF_MODAL_Y_AXIS),
+ WM_MODALKEY(KNF_MODAL_Z_AXIS),
(kcd->axis_constrained ? kcd->axis_string : WM_bool_as_string(kcd->axis_constrained)),
WM_MODALKEY(KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE),
WM_bool_as_string(kcd->show_dist_angle),
@@ -3900,71 +3899,69 @@ static void knifetool_undo(KnifeTool_OpData *kcd)
KnifeUndoFrame *undo;
BLI_mempool_iter iterkfe;
- if (!BLI_stack_is_empty(kcd->undostack)) {
- undo = BLI_stack_peek(kcd->undostack);
+ undo = BLI_stack_peek(kcd->undostack);
- /* Undo edge splitting. */
- for (int i = 0; i < undo->splits; i++) {
- BLI_stack_pop(kcd->splitstack, &newkfe);
- BLI_stack_pop(kcd->splitstack, &kfe);
- knife_join_edge(newkfe, kfe);
- }
+ /* Undo edge splitting. */
+ for (int i = 0; i < undo->splits; i++) {
+ BLI_stack_pop(kcd->splitstack, &newkfe);
+ BLI_stack_pop(kcd->splitstack, &kfe);
+ knife_join_edge(newkfe, kfe);
+ }
- for (int i = 0; i < undo->cuts; i++) {
+ for (int i = 0; i < undo->cuts; i++) {
- BLI_mempool_iternew(kcd->kedges, &iterkfe);
- for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) {
- if (!kfe->is_cut || kfe->is_invalid || kfe->splits) {
- continue;
- }
- lastkfe = kfe;
+ BLI_mempool_iternew(kcd->kedges, &iterkfe);
+ for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) {
+ if (!kfe->is_cut || kfe->is_invalid || kfe->splits) {
+ continue;
}
+ lastkfe = kfe;
+ }
- if (lastkfe) {
- lastkfe->is_invalid = true;
-
- /* TODO: Are they always guaranteed to be in this order? */
- v1 = lastkfe->v1;
- v2 = lastkfe->v2;
-
- /* Only remove first vertex if it is the start segment of the cut. */
- if (!v1->is_invalid && !v1->is_splitting) {
- v1->is_invalid = true;
- /* If the first vertex is touching any other cut edges don't remove it. */
- for (ref = v1->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- if (kfe->is_cut && !kfe->is_invalid) {
- v1->is_invalid = false;
- break;
- }
+ if (lastkfe) {
+ lastkfe->is_invalid = true;
+
+ /* TODO: Are they always guaranteed to be in this order? */
+ v1 = lastkfe->v1;
+ v2 = lastkfe->v2;
+
+ /* Only remove first vertex if it is the start segment of the cut. */
+ if (!v1->is_invalid && !v1->is_splitting) {
+ v1->is_invalid = true;
+ /* If the first vertex is touching any other cut edges don't remove it. */
+ for (ref = v1->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ if (kfe->is_cut && !kfe->is_invalid) {
+ v1->is_invalid = false;
+ break;
}
}
+ }
- /* Only remove second vertex if it is the end segment of the cut. */
- if (!v2->is_invalid && !v2->is_splitting) {
- v2->is_invalid = true;
- /* If the second vertex is touching any other cut edges don't remove it. */
- for (ref = v2->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- if (kfe->is_cut && !kfe->is_invalid) {
- v2->is_invalid = false;
- break;
- }
+ /* Only remove second vertex if it is the end segment of the cut. */
+ if (!v2->is_invalid && !v2->is_splitting) {
+ v2->is_invalid = true;
+ /* If the second vertex is touching any other cut edges don't remove it. */
+ for (ref = v2->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ if (kfe->is_cut && !kfe->is_invalid) {
+ v2->is_invalid = false;
+ break;
}
}
}
}
+ }
- if (kcd->mode == MODE_DRAGGING) {
- /* Restore kcd->prev. */
- kcd->prev = undo->pos;
- }
+ if (kcd->mode == MODE_DRAGGING) {
+ /* Restore kcd->prev. */
+ kcd->prev = undo->pos;
+ }
- /* Restore data for distance and angle measurements. */
- kcd->mdata = undo->mdata;
+ /* Restore data for distance and angle measurements. */
+ kcd->mdata = undo->mdata;
- BLI_stack_discard(kcd->undostack);
- }
+ BLI_stack_discard(kcd->undostack);
}
/** \} */
@@ -4100,7 +4097,7 @@ static void knifetool_init(bContext *C,
kcd->axis_string[0] = ' ';
kcd->axis_string[1] = '\0';
- /* Initialise num input handling for angle snapping. */
+ /* Initialize number input handling for angle snapping. */
initNumInput(&kcd->num);
kcd->num.idx_max = 0;
kcd->num.val_flag[0] |= NUM_NO_NEGATIVE;
@@ -4151,7 +4148,7 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd)
MEM_freeN(kcd->cagecos);
knife_bvh_free(kcd);
- /* Linehits cleanup. */
+ /* Line-hits cleanup. */
if (kcd->linehits) {
MEM_freeN(kcd->linehits);
}
@@ -4307,6 +4304,9 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
{KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
{KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""},
{KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""},
+ {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""},
+ {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""},
+ {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -4404,6 +4404,12 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED;
case KNF_MODAL_UNDO:
+ if (BLI_stack_is_empty(kcd->undostack)) {
+ ED_region_tag_redraw(kcd->region);
+ knifetool_exit(op);
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_CANCELLED;
+ }
knifetool_undo(kcd);
knife_update_active(C, kcd);
ED_region_tag_redraw(kcd->region);
@@ -4614,52 +4620,58 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (kcd->num.str_cur >= 2) {
knife_reset_snap_angle_input(kcd);
}
- /* Modal numinput inactive, try to handle numeric inputs last... */
- if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) {
- applyNumInput(&kcd->num, &snapping_increment_temp);
- /* Restrict number key input to 0 - 90 degree range. */
- if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
- snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) {
- kcd->angle_snapping_increment = snapping_increment_temp;
+ if (event->type != EVT_MODAL_MAP) {
+ /* Modal number-input inactive, try to handle numeric inputs last. */
+ if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) {
+ applyNumInput(&kcd->num, &snapping_increment_temp);
+ /* Restrict number key input to 0 - 90 degree range. */
+ if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
+ snapping_increment_temp < KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) {
+ kcd->angle_snapping_increment = snapping_increment_temp;
+ }
+ knife_update_active(C, kcd);
+ knife_update_header(C, op, kcd);
+ ED_region_tag_redraw(kcd->region);
+ return OPERATOR_RUNNING_MODAL;
}
- knife_update_active(C, kcd);
- knife_update_header(C, op, kcd);
- ED_region_tag_redraw(kcd->region);
- return OPERATOR_RUNNING_MODAL;
}
}
/* Constrain axes with X,Y,Z keys. */
- if (event->val == KM_PRESS && ELEM(event->type, EVT_XKEY, EVT_YKEY, EVT_ZKEY)) {
- if (event->type == EVT_XKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) {
- kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X;
- kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
- kcd->axis_string[0] = 'X';
- }
- else if (event->type == EVT_YKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) {
- kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y;
- kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
- kcd->axis_string[0] = 'Y';
- }
- else if (event->type == EVT_ZKEY && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) {
- kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z;
- kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
- kcd->axis_string[0] = 'Z';
- }
- else {
- /* Cycle through modes with repeated key presses. */
- if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) {
- kcd->constrain_axis_mode++;
- kcd->axis_string[0] += 32; /* Lower case. */
+ if (event->type == EVT_MODAL_MAP) {
+ if (ELEM(event->val, KNF_MODAL_X_AXIS, KNF_MODAL_Y_AXIS, KNF_MODAL_Z_AXIS)) {
+ if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) {
+ kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X;
+ kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
+ kcd->axis_string[0] = 'X';
+ }
+ else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) {
+ kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y;
+ kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
+ kcd->axis_string[0] = 'Y';
+ }
+ else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) {
+ kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z;
+ kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL;
+ kcd->axis_string[0] = 'Z';
}
else {
- kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE;
- kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE;
+ /* Cycle through modes with repeated key presses. */
+ if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) {
+ kcd->constrain_axis_mode++;
+ kcd->axis_string[0] += 32; /* Lower case. */
+ }
+ else {
+ kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE;
+ kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE;
+ }
}
+ kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE);
+ knifetool_disable_angle_snapping(kcd);
+ knife_update_header(C, op, kcd);
+ ED_region_tag_redraw(kcd->region);
+ do_refresh = true;
}
- kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE);
- knifetool_disable_angle_snapping(kcd);
- knife_update_header(C, op, kcd);
}
if (kcd->mode == MODE_DRAGGING) {
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index beadbf2689e..cc4f2acc346 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -75,6 +75,7 @@
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_light.h"
@@ -2015,6 +2016,15 @@ static int object_delete_exec(bContext *C, wmOperator *op)
continue;
}
+ if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) {
+ /* Can this case ever happen? */
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Cannot delete object '%s' as it used by override collections",
+ ob->id.name + 2);
+ continue;
+ }
+
if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
BKE_library_ID_is_indirectly_used(bmain, ob)) {
BKE_reportf(op->reports,
@@ -2828,8 +2838,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* Remove unused materials. */
int actcol = ob_gpencil->actcol;
for (int slot = 1; slot <= ob_gpencil->totcol; slot++) {
- while (slot <= ob_gpencil->totcol &&
- !BKE_object_material_slot_used(ob_gpencil->data, slot)) {
+ while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) {
ob_gpencil->actcol = slot;
BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil);
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 5697c2c973d..2bd0ae5f121 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1739,7 +1739,7 @@ void OBJECT_OT_mode_set_with_submode(wmOperatorType *ot)
OBJECT_OT_mode_set(ot);
/* identifiers */
- ot->name = "Set Object Mode with Submode";
+ ot->name = "Set Object Mode with Sub-mode";
ot->idname = "OBJECT_OT_mode_set_with_submode";
/* properties */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 50dd9322c5c..d00e6efeb29 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -78,7 +78,6 @@ void OBJECT_OT_mode_set(struct wmOperatorType *ot);
void OBJECT_OT_mode_set_with_submode(struct wmOperatorType *ot);
void OBJECT_OT_editmode_toggle(struct wmOperatorType *ot);
void OBJECT_OT_posemode_toggle(struct wmOperatorType *ot);
-void OBJECT_OT_proxy_make(struct wmOperatorType *ot);
void OBJECT_OT_shade_smooth(struct wmOperatorType *ot);
void OBJECT_OT_shade_flat(struct wmOperatorType *ot);
void OBJECT_OT_paths_calculate(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index b9942bc563a..efe19785f31 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -1044,6 +1044,10 @@ bool edit_modifier_poll_generic(bContext *C,
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
ModifierData *mod = ptr.data; /* May be NULL. */
+ if (mod == NULL && ob != NULL) {
+ mod = BKE_object_active_modifier(ob);
+ }
+
if (!ob || ID_IS_LINKED(ob)) {
return false;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index aa9ae082317..fa0208a7022 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -56,7 +56,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_mode_set_with_submode);
WM_operatortype_append(OBJECT_OT_editmode_toggle);
WM_operatortype_append(OBJECT_OT_posemode_toggle);
- WM_operatortype_append(OBJECT_OT_proxy_make);
WM_operatortype_append(OBJECT_OT_shade_smooth);
WM_operatortype_append(OBJECT_OT_shade_flat);
WM_operatortype_append(OBJECT_OT_paths_calculate);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 75269dffec8..5c7e1e1fa01 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -331,167 +331,6 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
-/** \name Make Proxy Operator
- * \{ */
-
-/* set the object to proxify */
-static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = ED_object_active_context(C);
-
- /* sanity checks */
- if (!scene || ID_IS_LINKED(scene) || !ob) {
- return OPERATOR_CANCELLED;
- }
-
- /* Get object to work on - use a menu if we need to... */
- if (ob->instance_collection && ID_IS_LINKED(ob->instance_collection)) {
- /* gives menu with list of objects in group */
- /* proxy_group_objects_menu(C, op, ob, ob->instance_collection); */
- WM_enum_search_invoke(C, op, event);
- return OPERATOR_CANCELLED;
- }
- if (ID_IS_LINKED(ob)) {
- uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
- uiLayout *layout = UI_popup_menu_layout(pup);
-
- /* create operator menu item with relevant properties filled in */
- PointerRNA opptr_dummy;
- uiItemFullO_ptr(
- layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy);
-
- /* present the menu and be done... */
- UI_popup_menu_end(C, pup);
-
- /* this invoke just calls another instance of this operator... */
- return OPERATOR_INTERFACE;
- }
-
- /* error.. cannot continue */
- BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection");
- return OPERATOR_CANCELLED;
-}
-
-static int make_proxy_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Object *ob, *gob = ED_object_active_context(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
-
- if (gob->instance_collection != NULL) {
- const ListBase instance_collection_objects = BKE_collection_object_cache_get(
- gob->instance_collection);
- Base *base = BLI_findlink(&instance_collection_objects, RNA_enum_get(op->ptr, "object"));
- ob = base->object;
- }
- else {
- ob = gob;
- gob = NULL;
- }
-
- if (ob) {
- Object *newob;
- char name[MAX_ID_NAME + 4];
-
- BLI_snprintf(name, sizeof(name), "%s_proxy", ((ID *)(gob ? gob : ob))->name + 2);
-
- /* Add new object for the proxy */
- newob = BKE_object_add_from(bmain, scene, view_layer, OB_EMPTY, name, gob ? gob : ob);
-
- /* set layers OK */
- BKE_object_make_proxy(bmain, newob, ob, gob);
-
- /* Set back pointer immediately so dependency graph knows that this is
- * is a proxy and will act accordingly. Otherwise correctness of graph
- * will depend on order of bases.
- *
- * TODO(sergey): We really need to get rid of this bi-directional links
- * in proxies with something like library overrides.
- */
- if (newob->proxy != NULL) {
- newob->proxy->proxy_from = newob;
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "Unable to assign proxy");
- }
-
- /* depsgraph flushes are needed for the new data */
- DEG_relations_tag_update(bmain);
- DEG_id_tag_update(&newob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, newob);
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "No object to make proxy for");
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-/* Generic itemf's for operators that take library args */
-static const EnumPropertyItem *proxy_collection_object_itemf(bContext *C,
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- EnumPropertyItem item_tmp = {0}, *item = NULL;
- int totitem = 0;
- int i = 0;
- Object *ob = ED_object_active_context(C);
-
- if (!ob || !ob->instance_collection) {
- return DummyRNA_DEFAULT_items;
- }
-
- /* find the object to affect */
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, object) {
- item_tmp.identifier = item_tmp.name = object->id.name + 2;
- item_tmp.value = i++;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-
-void OBJECT_OT_proxy_make(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Make Proxy";
- ot->idname = "OBJECT_OT_proxy_make";
- ot->description = "Add empty object to become local replacement data of a library-linked object";
-
- /* callbacks */
- ot->invoke = make_proxy_invoke;
- ot->exec = make_proxy_exec;
- ot->poll = ED_operator_object_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- /* XXX, relies on hard coded ID at the moment */
- prop = RNA_def_enum(ot->srna,
- "object",
- DummyRNA_DEFAULT_items,
- 0,
- "Proxy Object",
- "Name of library-linked/collection object to make a proxy for");
- RNA_def_enum_funcs(prop, proxy_collection_object_itemf);
- RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
- ot->prop = prop;
-}
-
-/** \} */
-
-/* ------------------------------------------------------------------- */
/** \name Clear Parent Operator
* \{ */
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 8a3d8f9636b..7b2667905ff 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -690,7 +690,7 @@ static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
Object *ob = objects[ob_index];
int actcol = ob->actcol;
for (int slot = 1; slot <= ob->totcol; slot++) {
- while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) {
+ while (slot <= ob->totcol && !BKE_object_material_slot_used(ob, slot)) {
ob->actcol = slot;
BKE_object_material_slot_remove(bmain, ob);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index c71e68df2fd..833c9accf95 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1284,8 +1284,8 @@ bool ED_region_is_overlap(int spacetype, int regiontype)
RGN_TYPE_TOOLS,
RGN_TYPE_UI,
RGN_TYPE_TOOL_PROPS,
- RGN_TYPE_HEADER,
- RGN_TYPE_FOOTER)) {
+ RGN_TYPE_FOOTER,
+ RGN_TYPE_TOOL_HEADER)) {
return true;
}
}
@@ -1699,6 +1699,9 @@ static void ed_default_handlers(
wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
+ ListBase *dropboxes = WM_dropboxmap_find("User Interface", 0, 0);
+ WM_event_add_dropbox_handler(handlers, dropboxes);
+
/* user interface widgets */
UI_region_handlers_add(handlers);
}
@@ -3000,7 +3003,7 @@ void ED_region_panels_layout_ex(const bContext *C,
/* before setting the view */
if (region_layout_based) {
- /* XXX, only single panel support atm.
+ /* XXX, only single panel support at the moment.
* Can't use x/y values calculated above because they're not using the real height of panels,
* instead they calculate offsets for the next panel to start drawing. */
Panel *panel = region->panels.last;
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 2ccefb993c7..3d447d90626 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -1073,9 +1073,14 @@ static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *
{
wmWindow *win = CTX_wm_window(C);
ARegion *region = CTX_wm_region(C);
- uiList *list = UI_list_find_mouse_over(region, win->eventstate);
- CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
- return CTX_RESULT_OK;
+ if (region) {
+ uiList *list = UI_list_find_mouse_over(region, win->eventstate);
+ if (list) {
+ CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
+ return CTX_RESULT_OK;
+ }
+ }
+ return CTX_RESULT_NO_DATA;
}
/* Registry of context callback functions. */
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index 4016ef84bfd..04ee62b1631 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -62,7 +62,7 @@ typedef enum eScreenAxis {
#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac)
/* Expanded interaction influence of area borders. */
-#define BORDERPADDING (U.dpi_fac + U.pixelsize)
+#define BORDERPADDING ((2.0f * U.dpi_fac) + U.pixelsize)
/* area.c */
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free);
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index b99cb831bee..4b81e713080 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -310,7 +310,14 @@ static int workspace_append_activate_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "filepath", filepath);
WorkSpace *appended_workspace = (WorkSpace *)WM_file_append_datablock(
- bmain, CTX_data_scene(C), CTX_data_view_layer(C), CTX_wm_view3d(C), filepath, ID_WS, idname);
+ bmain,
+ CTX_data_scene(C),
+ CTX_data_view_layer(C),
+ CTX_wm_view3d(C),
+ filepath,
+ ID_WS,
+ idname,
+ BLO_LIBLINK_APPEND_RECURSIVE);
if (appended_workspace) {
/* Set defaults. */
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index a58b1947b0c..0176b4a1b13 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1659,7 +1659,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps,
if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) {
const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index];
- const float *lt_other_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other)};
+ const float *lt_other_tri_uv[3] = {ps->mloopuv_stencil_eval[lt_other->tri[0]].uv,
+ ps->mloopuv_stencil_eval[lt_other->tri[1]].uv,
+ ps->mloopuv_stencil_eval[lt_other->tri[2]].uv};
/* #BKE_image_acquire_ibuf - TODO: this may be slow. */
uchar rgba_ub[4];
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 38165b7622f..1bfe8e1cbf1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -395,7 +395,12 @@ void SCULPT_smooth(Sculpt *sd,
void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
- if (ss->cache->bstrength <= 0.0f) {
+
+ /* NOTE: The enhance brush needs to initialize its state on the first brush step. The stroke
+ * strength can become 0 during the stroke, but it can not change sign (the sign is determined
+ * in the beginning of the stroke. So here it is important to not switch to enhance brush in the
+ * middle of the stroke. */
+ if (ss->cache->bstrength < 0.0f) {
/* Invert mode, intensify details. */
SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
}
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index 717d87c4972..a4fd2d81778 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -71,7 +71,7 @@
/* ACTION CREATION */
/* Helper function to find the active AnimData block from the Action Editor context */
-AnimData *ED_actedit_animdata_from_context(bContext *C)
+AnimData *ED_actedit_animdata_from_context(bContext *C, ID **r_adt_id_owner)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
Object *ob = CTX_data_active_object(C);
@@ -82,12 +82,18 @@ AnimData *ED_actedit_animdata_from_context(bContext *C)
/* Currently, "Action Editor" means object-level only... */
if (ob) {
adt = ob->adt;
+ if (r_adt_id_owner) {
+ *r_adt_id_owner = &ob->id;
+ }
}
}
else if (saction->mode == SACTCONT_SHAPEKEY) {
Key *key = BKE_key_from_object(ob);
if (key) {
adt = key->adt;
+ if (r_adt_id_owner) {
+ *r_adt_id_owner = &key->id;
+ }
}
}
@@ -212,6 +218,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
bAction *oldact = NULL;
AnimData *adt = NULL;
+ ID *adt_id_owner = NULL;
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -225,13 +232,14 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
/* stash the old action to prevent it from being lost */
if (ptr.type == &RNA_AnimData) {
adt = ptr.data;
+ adt_id_owner = ptr.owner_id;
}
else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
- adt = ED_actedit_animdata_from_context(C);
+ adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
}
}
else {
- adt = ED_actedit_animdata_from_context(C);
+ adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
oldact = adt->action;
}
{
@@ -239,8 +247,9 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
/* Perform stashing operation - But only if there is an action */
if (adt && oldact) {
+ BLI_assert(adt_id_owner != NULL);
/* stash the action */
- if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ptr.owner_id))) {
+ if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
/* The stash operation will remove the user already
* (and unlink the action from the AnimData action slot).
* Hence, we must unset the ref to the action in the
@@ -306,7 +315,7 @@ static bool action_pushdown_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Check for AnimData, Actions, and that tweak-mode is off. */
if (adt && saction->action) {
@@ -326,7 +335,8 @@ static bool action_pushdown_poll(bContext *C)
static int action_pushdown_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ ID *adt_id_owner = NULL;
+ AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Do the deed... */
if (adt) {
@@ -339,8 +349,7 @@ static int action_pushdown_exec(bContext *C, wmOperator *op)
}
/* action can be safely added */
- const Object *ob = CTX_data_active_object(C);
- BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ob));
+ BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
/* Stop displaying this action in this editor
* NOTE: The editor itself doesn't set a user...
@@ -373,7 +382,8 @@ void ACTION_OT_push_down(wmOperatorType *ot)
static int action_stash_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ ID *adt_id_owner = NULL;
+ AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Perform stashing operation */
if (adt) {
@@ -385,8 +395,7 @@ static int action_stash_exec(bContext *C, wmOperator *op)
}
/* stash the action */
- Object *ob = CTX_data_active_object(C);
- if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) {
+ if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
/* The stash operation will remove the user already,
* so the flushing step later shouldn't double up
* the user-count fixes. Hence, we must unset this ref
@@ -439,7 +448,7 @@ void ACTION_OT_stash(wmOperatorType *ot)
static bool action_stash_create_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
/* NOTE: unlike for pushdown,
@@ -471,7 +480,8 @@ static bool action_stash_create_poll(bContext *C)
static int action_stash_create_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ ID *adt_id_owner = NULL;
+ AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Check for no action... */
if (saction->action == NULL) {
@@ -488,8 +498,7 @@ static int action_stash_create_exec(bContext *C, wmOperator *op)
}
/* stash the action */
- Object *ob = CTX_data_active_object(C);
- if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) {
+ if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
bAction *new_action = NULL;
/* Create new action not based on the old one
@@ -636,7 +645,7 @@ static bool action_unlink_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Only when there's an active action, in the right modes... */
if (saction->action && adt) {
@@ -650,7 +659,7 @@ static bool action_unlink_poll(bContext *C)
static int action_unlink_exec(bContext *C, wmOperator *op)
{
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
if (adt && adt->action) {
@@ -775,7 +784,7 @@ static bool action_layer_next_poll(bContext *C)
{
/* Action Editor's action editing modes only */
if (ED_operator_action_active(C)) {
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
if (adt) {
/* only allow if we're in tweak-mode, and there's something above us... */
if (adt->flag & ADT_NLA_EDIT_ON) {
@@ -809,7 +818,7 @@ static bool action_layer_next_poll(bContext *C)
static int action_layer_next_exec(bContext *C, wmOperator *op)
{
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
NlaTrack *act_track;
Scene *scene = CTX_data_scene(C);
@@ -886,7 +895,7 @@ static bool action_layer_prev_poll(bContext *C)
{
/* Action Editor's action editing modes only */
if (ED_operator_action_active(C)) {
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
if (adt) {
if (adt->flag & ADT_NLA_EDIT_ON) {
/* Tweak Mode: We need to check if there are any tracks below the active one
@@ -920,7 +929,7 @@ static bool action_layer_prev_poll(bContext *C)
static int action_layer_prev_exec(bContext *C, wmOperator *op)
{
- AnimData *adt = ED_actedit_animdata_from_context(C);
+ AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
NlaTrack *act_track;
NlaTrack *nlt;
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index f59429ba981..738eeb21e2e 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -247,7 +247,7 @@ static void action_main_region_draw_overlay(const bContext *C, ARegion *region)
View2D *v2d = &region->v2d;
/* scrubbing region */
- ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME, true);
+ ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME);
/* scrollers */
UI_view2d_scrollers_draw(v2d, NULL);
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 404c5698b42..149067a94fe 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -177,6 +177,7 @@ void ED_spacemacros_init(void)
ED_operatormacros_gpencil();
/* Register dropboxes (can use macros). */
+ ED_dropboxes_ui();
const ListBase *spacetypes = BKE_spacetypes_list();
LISTBASE_FOREACH (const SpaceType *, type, spacetypes) {
if (type->dropboxes) {
diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c
index 8aaf3faffec..42fda3ef464 100644
--- a/source/blender/editors/space_clip/clip_dopesheet_draw.c
+++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c
@@ -224,7 +224,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene)
uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f(
"ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index ff62bcf0cfa..4965099642b 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -417,7 +417,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
data->pos = marker->pos;
data->offset = track->offset;
data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr,
- "slide marekrs");
+ "slide markers");
for (int a = 0; a < track->markersnr; a++) {
copy_v2_v2(data->old_markers[a], track->markers[a].pos);
}
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index 0a99d1020f6..0e78a3e9a1e 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -196,8 +196,8 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa
/* XXX: silly to store this, but this data is needed to update scene and
* movie-clip numbers when tracking is finished. This introduces
* better feedback for artists.
- * Maybe there's another way to solve this problem, but can't think
- * better way atm.
+ * Maybe there's another way to solve this problem,
+ * but can't think better way at the moment.
* Anyway, this way isn't more unstable as animation rendering
* animation which uses the same approach (except storing screen).
*/
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index 993a52b9084..4b508f16c1e 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -34,6 +34,7 @@ set(INC
)
set(SRC
+ asset_catalog_tree_view.cc
file_draw.c
file_ops.c
file_panels.c
@@ -49,6 +50,7 @@ set(SRC
)
set(LIB
+ bf_blenkernel
)
if(WITH_HEADLESS)
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
new file mode 100644
index 00000000000..7eea9af925b
--- /dev/null
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -0,0 +1,230 @@
+/*
+ * 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) 2007 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spfile
+ */
+
+#include "ED_fileselect.h"
+
+#include "DNA_space_types.h"
+
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+
+#include "BLI_string_ref.hh"
+
+#include "BLT_translation.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_interface.hh"
+#include "UI_resources.h"
+#include "UI_tree_view.hh"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "file_intern.h"
+
+using namespace blender;
+using namespace blender::bke;
+
+namespace blender::ed::asset_browser {
+
+class AssetCatalogTreeView : public ui::AbstractTreeView {
+ /** The asset catalog tree this tree-view represents. */
+ bke::AssetCatalogTree *catalog_tree_;
+ FileAssetSelectParams *params_;
+
+ friend class AssetCatalogTreeViewItem;
+
+ public:
+ AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params);
+
+ void build_tree() override;
+
+ private:
+ ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewItemContainer &view_parent_item,
+ AssetCatalogTreeItem &catalog);
+
+ void add_all_item();
+ void add_unassigned_item();
+ bool is_active_catalog(CatalogID catalog_id) const;
+};
+/* ---------------------------------------------------------------------- */
+
+class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
+ /** The catalog tree item this tree view item represents. */
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item)
+ : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item)
+ {
+ }
+
+ void on_activate() override
+ {
+ const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
+ get_tree_view());
+ tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
+ tree_view.params_->catalog_id = catalog_item_.get_catalog_id();
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+ }
+
+ void build_row(uiLayout &row) override
+ {
+ ui::BasicTreeViewItem::build_row(row);
+
+ if (!is_active()) {
+ return;
+ }
+
+ PointerRNA *props;
+ const CatalogID catalog_id = catalog_item_.get_catalog_id();
+
+ props = UI_but_extra_operator_icon_add(
+ button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
+
+ /* Tree items without a catalog ID represent components of catalog paths that are not
+ * associated with an actual catalog. They exist merely by the presence of a child catalog, and
+ * thus cannot be deleted themselves. */
+ if (!BLI_uuid_is_nil(catalog_id)) {
+ char catalog_id_str_buffer[UUID_STRING_LEN] = "";
+ BLI_uuid_format(catalog_id_str_buffer, catalog_id);
+
+ props = UI_but_extra_operator_icon_add(
+ button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X);
+ RNA_string_set(props, "catalog_id", catalog_id_str_buffer);
+ }
+ }
+};
+
+/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level
+ * catalog. */
+class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
+ using BasicTreeViewItem::BasicTreeViewItem;
+
+ void build_row(uiLayout &row) override
+ {
+ ui::BasicTreeViewItem::build_row(row);
+
+ if (!is_active()) {
+ return;
+ }
+
+ PointerRNA *props;
+ props = UI_but_extra_operator_icon_add(
+ button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ /* No parent path to use the root level. */
+ RNA_string_set(props, "parent_path", nullptr);
+ }
+};
+
+AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params)
+ : catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params)
+{
+}
+
+void AssetCatalogTreeView::build_tree()
+{
+ add_all_item();
+
+ if (catalog_tree_) {
+ catalog_tree_->foreach_root_item([this](AssetCatalogTreeItem &item) {
+ ui::BasicTreeViewItem &child_view_item = build_catalog_items_recursive(*this, item);
+
+ /* Open root-level items by default. */
+ child_view_item.set_collapsed(false);
+ });
+ }
+
+ add_unassigned_item();
+}
+
+ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive(
+ ui::TreeViewItemContainer &view_parent_item, AssetCatalogTreeItem &catalog)
+{
+ ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item<AssetCatalogTreeViewItem>(
+ &catalog);
+ if (is_active_catalog(catalog.get_catalog_id())) {
+ view_item.set_active();
+ }
+
+ catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) {
+ build_catalog_items_recursive(view_item, child);
+ });
+ return view_item;
+}
+
+void AssetCatalogTreeView::add_all_item()
+{
+ FileAssetSelectParams *params = params_;
+
+ ui::AbstractTreeViewItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(
+ IFACE_("All"), ICON_HOME, [params](ui::BasicTreeViewItem & /*item*/) {
+ params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS;
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+ });
+ if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) {
+ item.set_active();
+ }
+}
+
+void AssetCatalogTreeView::add_unassigned_item()
+{
+ FileAssetSelectParams *params = params_;
+
+ ui::AbstractTreeViewItem &item = add_tree_item<ui::BasicTreeViewItem>(
+ IFACE_("Unassigned"), ICON_FILE_HIDDEN, [params](ui::BasicTreeViewItem & /*item*/) {
+ params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG;
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+ });
+ if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) {
+ item.set_active();
+ }
+}
+
+bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
+{
+ return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) &&
+ (params_->catalog_id == catalog_id);
+}
+
+} // namespace blender::ed::asset_browser
+
+/* ---------------------------------------------------------------------- */
+
+void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
+ uiLayout *layout,
+ FileAssetSelectParams *params)
+{
+ uiBlock *block = uiLayoutGetBlock(layout);
+
+ ui::AbstractTreeView *tree_view = UI_block_add_view(
+ *block,
+ "asset catalog tree view",
+ std::make_unique<ed::asset_browser::AssetCatalogTreeView>(asset_library, params));
+
+ ui::TreeViewBuilder builder(*block);
+ builder.build_tree_view(*tree_view);
+}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 905c0aeb8e0..d39aefff691 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -23,12 +23,19 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* internal exports only */
struct ARegion;
struct ARegionType;
+struct AssetLibrary;
struct FileSelectParams;
+struct FileAssetSelectParams;
struct SpaceFile;
+struct uiLayout;
struct View2D;
/* file_draw.c */
@@ -147,8 +154,19 @@ void file_on_reload_callback_register(struct SpaceFile *sfile,
/* file_panels.c */
void file_tool_props_region_panels_register(struct ARegionType *art);
void file_execute_region_panels_register(struct ARegionType *art);
+void file_tools_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
+
+/* asset_catalog_tree_view.cc */
+
+void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library,
+ struct uiLayout *layout,
+ struct FileAssetSelectParams *params);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 2f1acd2ca4d..d0f2a4fdc4c 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -536,7 +536,7 @@ static rcti file_select_mval_to_select_rect(const int mval[2])
return rect;
}
-static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int file_select_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -549,17 +549,27 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected");
/* Used so right mouse clicks can do both, activate and spawn the context menu. */
const bool pass_through = RNA_boolean_get(op->ptr, "pass_through");
+ bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
if (region->regiontype != RGN_TYPE_WINDOW) {
return OPERATOR_CANCELLED;
}
- rect = file_select_mval_to_select_rect(event->mval);
+ int mval[2];
+ mval[0] = RNA_int_get(op->ptr, "mouse_x");
+ mval[1] = RNA_int_get(op->ptr, "mouse_y");
+ rect = file_select_mval_to_select_rect(mval);
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
+ if (extend || fill) {
+ wait_to_deselect_others = false;
+ }
+
+ int ret_val = OPERATOR_FINISHED;
+
const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (sfile && params) {
int idx = params->highlight_file;
@@ -571,6 +581,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (only_activate_if_selected && is_selected) {
/* Don't deselect other items. */
}
+ else if (wait_to_deselect_others && is_selected) {
+ ret_val = OPERATOR_RUNNING_MODAL;
+ }
/* single select, deselect all selected first */
else if (!extend) {
file_select_deselect_all(sfile, FILE_SEL_SELECTED);
@@ -601,7 +614,10 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
- return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED;
+ if ((ret_val == OPERATOR_FINISHED) && pass_through) {
+ ret_val |= OPERATOR_PASS_THROUGH;
+ }
+ return ret_val;
}
void FILE_OT_select(wmOperatorType *ot)
@@ -614,11 +630,14 @@ void FILE_OT_select(wmOperatorType *ot)
ot->description = "Handle mouse clicks to select and activate items";
/* api callbacks */
- ot->invoke = file_select_invoke;
+ ot->invoke = WM_generic_select_invoke;
+ ot->exec = file_select_exec;
+ ot->modal = WM_generic_select_modal;
/* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
+ WM_operator_properties_generic_select(ot);
prop = RNA_def_boolean(ot->srna,
"extend",
false,
diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c
index 7032d55b331..95aad202f1a 100644
--- a/source/blender/editors/space_file/file_panels.c
+++ b/source/blender/editors/space_file/file_panels.c
@@ -47,6 +47,7 @@
#include "WM_types.h"
#include "file_intern.h"
+#include "filelist.h"
#include "fsmenu.h"
#include <string.h>
@@ -57,6 +58,12 @@ static bool file_panel_operator_poll(const bContext *C, PanelType *UNUSED(pt))
return (sfile && sfile->op);
}
+static bool file_panel_asset_browsing_poll(const bContext *C, PanelType *UNUSED(pt))
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ return sfile && sfile->files && ED_fileselect_is_asset_browser(sfile);
+}
+
static void file_panel_operator_header(const bContext *C, Panel *panel)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -222,3 +229,28 @@ void file_execute_region_panels_register(ARegionType *art)
pt->draw = file_panel_execution_buttons_draw;
BLI_addtail(&art->paneltypes, pt);
}
+
+static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel)
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ /* May be null if the library wasn't loaded yet. */
+ struct AssetLibrary *asset_library = filelist_asset_library(sfile->files);
+ FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(params != NULL);
+
+ file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, params);
+}
+
+void file_tools_region_panels_register(ARegionType *art)
+{
+ PanelType *pt;
+
+ pt = MEM_callocN(sizeof(PanelType), "spacetype file asset catalog buttons");
+ strcpy(pt->idname, "FILE_PT_asset_catalog_buttons");
+ strcpy(pt->label, N_("Asset Catalogs"));
+ strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ pt->flag = PANEL_TYPE_NO_HEADER;
+ pt->poll = file_panel_asset_browsing_poll;
+ pt->draw = file_panel_asset_catalog_buttons_draw;
+ BLI_addtail(&art->paneltypes, pt);
+}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 511b5b255e9..bdf61e792d3 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -50,12 +50,14 @@
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_uuid.h"
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include "BKE_asset.h"
+#include "BKE_asset_library.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_icons.h"
@@ -368,6 +370,9 @@ typedef struct FileListFilter {
char filter_glob[FILE_MAXFILE];
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
short flags;
+
+ eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility;
+ bUUID asset_catalog_id;
} FileListFilter;
/* FileListFilter.flags */
@@ -386,6 +391,7 @@ typedef struct FileList {
eFileSelectType type;
/* The library this list was created for. Stored here so we know when to re-read. */
AssetLibraryReference *asset_library_ref;
+ struct AssetLibrary *asset_library;
short flags;
@@ -471,6 +477,10 @@ static void filelist_readjob_dir(struct FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
+static void filelist_readjob_asset_library(struct FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress);
static void filelist_readjob_main_assets(struct FileListReadJob *job_params,
short *stop,
short *do_update,
@@ -887,6 +897,39 @@ static bool is_filtered_id_file(const FileListInternEntry *file,
return is_filtered;
}
+/**
+ * Get the asset metadata of a file, if it represents an asset. This may either be of a local ID
+ * (ID in the current #Main) or read from an external asset library.
+ */
+static AssetMetaData *filelist_file_internal_get_asset_data(const FileListInternEntry *file)
+{
+ const ID *local_id = file->local_data.id;
+ return local_id ? local_id->asset_data : file->imported_asset_data;
+}
+
+static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter)
+{
+ const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file);
+ bool is_visible = false;
+
+ switch (filter->asset_catalog_visibility) {
+ case FILE_SHOW_ASSETS_WITHOUT_CATALOG:
+ is_visible = BLI_uuid_is_nil(asset_data->catalog_id);
+ break;
+ case FILE_SHOW_ASSETS_FROM_CATALOG:
+ /* TODO show all assets that are in child catalogs of the selected catalog. */
+ is_visible = !BLI_uuid_is_nil(filter->asset_catalog_id) &&
+ BLI_uuid_equal(filter->asset_catalog_id, asset_data->catalog_id);
+ break;
+ case FILE_SHOW_ASSETS_ALL_CATALOGS:
+ /* All asset files should be visible. */
+ is_visible = true;
+ break;
+ }
+
+ return is_visible;
+}
+
static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
{
bool is_filtered;
@@ -904,6 +947,13 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
return is_filtered;
}
+static bool is_filtered_asset_library(FileListInternEntry *file,
+ const char *root,
+ FileListFilter *filter)
+{
+ return is_filtered_lib(file, root, filter) && is_filtered_asset(file, filter);
+}
+
static bool is_filtered_main(FileListInternEntry *file,
const char *UNUSED(dir),
FileListFilter *filter)
@@ -916,7 +966,8 @@ static bool is_filtered_main_assets(FileListInternEntry *file,
FileListFilter *filter)
{
/* "Filtered" means *not* being filtered out... So return true if the file should be visible. */
- return is_filtered_id_file(file, file->relpath, file->name, filter);
+ return is_filtered_id_file(file, file->relpath, file->name, filter) &&
+ is_filtered_asset(file, filter);
}
static void filelist_filter_clear(FileList *filelist)
@@ -1032,6 +1083,33 @@ void filelist_setfilter_options(FileList *filelist,
}
/**
+ * \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is
+ * #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise.
+ */
+void filelist_set_asset_catalog_filter_options(
+ FileList *filelist,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ const bUUID *catalog_id)
+{
+ bool update = false;
+
+ if (filelist->filter_data.asset_catalog_visibility != catalog_visibility) {
+ filelist->filter_data.asset_catalog_visibility = catalog_visibility;
+ update = true;
+ }
+
+ if (filelist->filter_data.asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG &&
+ catalog_id && !BLI_uuid_equal(filelist->filter_data.asset_catalog_id, *catalog_id)) {
+ filelist->filter_data.asset_catalog_id = *catalog_id;
+ update = true;
+ }
+
+ if (update) {
+ filelist_filter_clear(filelist);
+ }
+}
+
+/**
* Checks two libraries for equality.
* \return True if the libraries match.
*/
@@ -1723,6 +1801,11 @@ void filelist_settype(FileList *filelist, short type)
filelist->read_job_fn = filelist_readjob_lib;
filelist->filter_fn = is_filtered_lib;
break;
+ case FILE_ASSET_LIBRARY:
+ filelist->check_dir_fn = filelist_checkdir_lib;
+ filelist->read_job_fn = filelist_readjob_asset_library;
+ filelist->filter_fn = is_filtered_asset_library;
+ break;
case FILE_MAIN_ASSET:
filelist->check_dir_fn = filelist_checkdir_main_assets;
filelist->read_job_fn = filelist_readjob_main_assets;
@@ -1739,7 +1822,10 @@ void filelist_settype(FileList *filelist, short type)
filelist->flags |= FL_FORCE_RESET;
}
-void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
+void filelist_clear_ex(struct FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection)
{
if (!filelist) {
return;
@@ -1758,11 +1844,18 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo
if (do_selection && filelist->selection_state) {
BLI_ghash_clear(filelist->selection_state, NULL, NULL);
}
+
+ if (do_asset_library && (filelist->asset_library != NULL)) {
+ /* There is no way to refresh the catalogs stored by the AssetLibrary struct, so instead of
+ * "clearing" it, the entire struct is freed. It will be reallocated when needed. */
+ BKE_asset_library_free(filelist->asset_library);
+ filelist->asset_library = NULL;
+ }
}
void filelist_clear(struct FileList *filelist)
{
- filelist_clear_ex(filelist, true, true);
+ filelist_clear_ex(filelist, true, true, true);
}
void filelist_free(struct FileList *filelist)
@@ -1773,7 +1866,7 @@ void filelist_free(struct FileList *filelist)
}
/* No need to clear cache & selection_state, we free them anyway. */
- filelist_clear_ex(filelist, false, false);
+ filelist_clear_ex(filelist, true, false, false);
filelist_cache_free(&filelist->filelist_cache);
if (filelist->selection_state) {
@@ -1788,6 +1881,11 @@ void filelist_free(struct FileList *filelist)
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
}
+AssetLibrary *filelist_asset_library(FileList *filelist)
+{
+ return filelist->asset_library;
+}
+
void filelist_freelib(struct FileList *filelist)
{
if (filelist->libfiledata) {
@@ -2879,76 +2977,129 @@ static int filelist_readjob_list_dir(const char *root,
return nbr_entries;
}
-static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
+typedef enum ListLibOptions {
+ /* Will read both the groups + actual ids from the library. Reduces the amount of times that
+ * a library needs to be opened. */
+ LIST_LIB_RECURSIVE = (1 << 0),
+
+ /* Will only list assets. */
+ LIST_LIB_ASSETS_ONLY = (1 << 1),
+
+ /* Add given root as result. */
+ LIST_LIB_ADD_PARENT = (1 << 2),
+} ListLibOptions;
+
+static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode,
+ const char *group_name)
+{
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(group_name);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR;
+ entry->blentype = idcode;
+ return entry;
+}
+
+static void filelist_readjob_list_lib_add_datablocks(ListBase *entries,
+ LinkNode *datablock_infos,
+ const bool prefix_relpath_with_group_name,
+ const int idcode,
+ const char *group_name)
+{
+ for (LinkNode *ln = datablock_infos; ln; ln = ln->next) {
+ struct BLODataBlockInfo *info = ln->link;
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ if (prefix_relpath_with_group_name) {
+ entry->relpath = BLI_sprintfN("%s/%s", group_name, info->name);
+ }
+ else {
+ entry->relpath = BLI_strdup(info->name);
+ }
+ entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (info && info->asset_data) {
+ entry->typeflag |= FILE_TYPE_ASSET;
+ /* Moves ownership! */
+ entry->imported_asset_data = info->asset_data;
+ }
+ entry->blentype = idcode;
+ BLI_addtail(entries, entry);
+ }
+}
+
+static int filelist_readjob_list_lib(const char *root,
+ ListBase *entries,
+ const ListLibOptions options)
{
- FileListInternEntry *entry;
- LinkNode *ln, *names = NULL, *datablock_infos = NULL;
- int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
- bool ok;
struct BlendHandle *libfiledata = NULL;
- /* name test */
- ok = BLO_library_path_explode(root, dir, &group, NULL);
- if (!ok) {
- return nbr_entries;
+ /* Check if the given root is actually a library. All folders are passed to
+ * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do`
+ * will do a dir listing only when this function does not return any entries. */
+ /* TODO: We should consider introducing its own function to detect if it is a lib and
+ * call it directly from `filelist_readjob_do` to increase readability. */
+ const bool is_lib = BLO_library_path_explode(root, dir, &group, NULL);
+ if (!is_lib) {
+ return 0;
}
- /* there we go */
+ /* Open the library file. */
BlendFileReadReport bf_reports = {.reports = NULL};
libfiledata = BLO_blendhandle_from_file(dir, &bf_reports);
if (libfiledata == NULL) {
- return nbr_entries;
- }
-
- /* memory for strings is passed into filelist[i].entry->relpath
- * and freed in filelist_entry_free. */
- if (group) {
- idcode = groupname_to_code(group);
- datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
- }
- else {
- names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nitems = BLI_linklist_count(names);
+ return 0;
}
- BLO_blendhandle_close(libfiledata);
-
- if (!skip_currpar) {
- entry = MEM_callocN(sizeof(*entry), __func__);
+ /* Add current parent when requested. */
+ int parent_len = 0;
+ if (options & LIST_LIB_ADD_PARENT) {
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(FILENAME_PARENT);
entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
BLI_addtail(entries, entry);
- nbr_entries++;
+ parent_len = 1;
}
- for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) {
- struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL;
- const char *blockname = info ? info->name : ln->link;
-
- entry = MEM_callocN(sizeof(*entry), __func__);
- entry->relpath = BLI_strdup(blockname);
- entry->typeflag |= FILE_TYPE_BLENDERLIB;
- if (info && info->asset_data) {
- entry->typeflag |= FILE_TYPE_ASSET;
- /* Moves ownership! */
- entry->imported_asset_data = info->asset_data;
- }
- if (!(group && idcode)) {
- entry->typeflag |= FILE_TYPE_DIR;
- entry->blentype = groupname_to_code(blockname);
- }
- else {
- entry->blentype = idcode;
+ int group_len = 0;
+ int datablock_len = 0;
+ const bool group_came_from_path = group != NULL;
+ if (group_came_from_path) {
+ const int idcode = groupname_to_code(group);
+ LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len);
+ filelist_readjob_list_lib_add_datablocks(entries, datablock_infos, false, idcode, group);
+ BLI_linklist_freeN(datablock_infos);
+ }
+ else {
+ LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata);
+ group_len = BLI_linklist_count(groups);
+
+ for (LinkNode *ln = groups; ln; ln = ln->next) {
+ const char *group_name = ln->link;
+ const int idcode = groupname_to_code(group_name);
+ FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode,
+ group_name);
+ BLI_addtail(entries, group_entry);
+
+ if (options & LIST_LIB_RECURSIVE) {
+ int group_datablock_len;
+ LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len);
+ filelist_readjob_list_lib_add_datablocks(
+ entries, group_datablock_infos, true, idcode, group_name);
+ BLI_linklist_freeN(group_datablock_infos);
+ datablock_len += group_datablock_len;
+ }
}
- BLI_addtail(entries, entry);
- nbr_entries++;
+
+ BLI_linklist_freeN(groups);
}
- BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
+ BLO_blendhandle_close(libfiledata);
- return nbr_entries;
+ /* Return the number of items added to entries. */
+ int added_entries_len = group_len + datablock_len + parent_len;
+ return added_entries_len;
}
#if 0
@@ -3136,11 +3287,43 @@ typedef struct FileListReadJob {
* The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist
* into #filelist in a thread-safe way.
*
+ * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, and
+ * moved to #filelist once all categories are loaded.
+ *
* NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set
* to NULL to avoid double-freeing them. */
struct FileList *tmp_filelist;
} FileListReadJob;
+static bool filelist_readjob_should_recurse_into_entry(const int max_recursion,
+ const int current_recursion_level,
+ FileListInternEntry *entry)
+{
+ if (max_recursion == 0) {
+ /* Recursive loading is disabled. */
+ return false;
+ }
+ if (current_recursion_level >= max_recursion) {
+ /* No more levels of recursion left. */
+ return false;
+ }
+ if (entry->typeflag & FILE_TYPE_BLENDERLIB) {
+ /* Libraries are already loaded recursively when recursive loaded is used. No need to add
+ * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */
+ return false;
+ }
+ if (!(entry->typeflag & FILE_TYPE_DIR)) {
+ /* Cannot recurse into regular file entries. */
+ return false;
+ }
+ if (FILENAME_IS_CURRPAR(entry->relpath)) {
+ /* Don't schedule go to parent entry, (`..`) */
+ return false;
+ }
+
+ return true;
+}
+
static void filelist_readjob_do(const bool do_lib,
FileListReadJob *job_params,
const short *stop,
@@ -3177,7 +3360,6 @@ static void filelist_readjob_do(const bool do_lib,
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
FileListInternEntry *entry;
int nbr_entries = 0;
- bool is_lib = do_lib;
char *subdir;
char rel_subdir[FILE_MAX_LIBEXTRA];
@@ -3200,45 +3382,54 @@ static void filelist_readjob_do(const bool do_lib,
BLI_path_normalize_dir(root, rel_subdir);
BLI_path_rel(rel_subdir, root);
+ bool is_lib = false;
if (do_lib) {
- nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar);
+ ListLibOptions list_lib_options = 0;
+ if (!skip_currpar) {
+ list_lib_options |= LIST_LIB_ADD_PARENT;
+ }
+
+ /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is
+ * still a recursion level over. */
+ if (max_recursion > 0) {
+ list_lib_options |= LIST_LIB_RECURSIVE;
+ }
+ /* Only load assets when browsing an asset library. For normal file browsing we return all
+ * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user.*/
+ if (filelist->asset_library_ref) {
+ list_lib_options |= LIST_LIB_ASSETS_ONLY;
+ }
+ nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options);
+ if (nbr_entries > 0) {
+ is_lib = true;
+ }
}
- if (!nbr_entries) {
- is_lib = false;
+
+ if (!is_lib) {
nbr_entries = filelist_readjob_list_dir(
subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
for (entry = entries.first; entry; entry = entry->next) {
- BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
-
entry->uid = filelist_uid_generate(filelist);
- /* Only thing we change in direntry here, so we need to free it first. */
+ /* When loading entries recursive, the rel_path should be relative from the root dir.
+ * we combine the relative path to the subdir with the relative path of the entry. */
+ BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
MEM_freeN(entry->relpath);
entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//'
* added by BLI_path_rel to rel_subdir. */
entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir);
entry->free_name = true;
- /* Here we decide whether current filedirentry is to be listed too, or not. */
- if (max_recursion && (is_lib || (recursion_level <= max_recursion))) {
- if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) {
- /* Skip... */
- }
- else if (!is_lib && (recursion_level >= max_recursion) &&
- ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) {
- /* Do not recurse in real directories in this case, only in .blend libs. */
- }
- else {
- /* We have a directory we want to list, add it to todo list! */
- BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
- BLI_path_normalize_dir(job_params->main_name, dir);
- td_dir = BLI_stack_push_r(todo_dirs);
- td_dir->level = recursion_level + 1;
- td_dir->dir = BLI_strdup(dir);
- nbr_todo_dirs++;
- }
+ if (filelist_readjob_should_recurse_into_entry(max_recursion, recursion_level, entry)) {
+ /* We have a directory we want to list, add it to todo list! */
+ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
+ BLI_path_normalize_dir(job_params->main_name, dir);
+ td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir->level = recursion_level + 1;
+ td_dir->dir = BLI_strdup(dir);
+ nbr_todo_dirs++;
}
}
@@ -3284,6 +3475,47 @@ static void filelist_readjob_lib(FileListReadJob *job_params,
filelist_readjob_do(true, job_params, stop, do_update, progress);
}
+static void filelist_asset_library_path(const FileListReadJob *job_params,
+ char r_library_root_path[FILE_MAX])
+{
+ if (job_params->filelist->type == FILE_MAIN_ASSET) {
+ /* For the "Current File" library (#FILE_MAIN_ASSET) we get the asset library root path based
+ * on main. */
+ BKE_asset_library_find_suitable_root_path_from_main(job_params->current_main,
+ r_library_root_path);
+ }
+ else {
+ BLI_strncpy(r_library_root_path, job_params->tmp_filelist->filelist.root, FILE_MAX);
+ }
+}
+
+/**
+ * Load asset library data, which currently means loading the asset catalogs for the library.
+ */
+static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update)
+{
+ FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+
+ if (job_params->filelist->asset_library_ref != NULL) {
+ char library_root_path[FILE_MAX];
+ filelist_asset_library_path(job_params, library_root_path);
+
+ /* Load asset catalogs, into the temp filelist for thread-safety.
+ * #filelist_readjob_endjob() will move it into the real filelist. */
+ tmp_filelist->asset_library = BKE_asset_library_load(library_root_path);
+ *do_update = true;
+ }
+}
+
+static void filelist_readjob_asset_library(FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+ filelist_readjob_lib(job_params, stop, do_update, progress);
+}
+
static void filelist_readjob_main(FileListReadJob *job_params,
short *stop,
short *do_update,
@@ -3305,6 +3537,8 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params,
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
(filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+
/* A valid, but empty directory from now. */
filelist->filelist.nbr_entries = 0;
@@ -3355,6 +3589,8 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
BLI_mutex_lock(&flrj->lock);
BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist);
+ BLI_assert_msg(flrj->filelist->asset_library == NULL,
+ "Asset library should not yet be assigned at start of read job");
flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
@@ -3394,11 +3630,17 @@ static void filelist_readjob_update(void *flrjv)
flrj->tmp_filelist->filelist.nbr_entries = 0;
}
+ if (flrj->tmp_filelist->asset_library) {
+ flrj->filelist->asset_library = flrj->tmp_filelist->asset_library;
+ flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */
+ }
+
BLI_mutex_unlock(&flrj->lock);
if (new_nbr_entries) {
- /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! */
- filelist_clear_ex(flrj->filelist, true, false);
+ /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep
+ * the asset library data we just read. */
+ filelist_clear_ex(flrj->filelist, false, true, false);
flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
}
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 1fb05e0f9ac..d1f37b5b365 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -31,6 +31,7 @@ struct AssetLibraryReference;
struct BlendHandle;
struct FileList;
struct FileSelection;
+struct bUUID;
struct wmWindowManager;
struct FileDirEntry;
@@ -71,6 +72,10 @@ void filelist_setfilter_options(struct FileList *filelist,
const bool filter_assets_only,
const char *filter_glob,
const char *filter_search);
+void filelist_set_asset_catalog_filter_options(
+ struct FileList *filelist,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ const struct bUUID *catalog_id);
void filelist_filter(struct FileList *filelist);
void filelist_setlibrary(struct FileList *filelist,
const struct AssetLibraryReference *asset_library_ref);
@@ -86,7 +91,10 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m
struct FileList *filelist_new(short type);
void filelist_settype(struct FileList *filelist, short type);
void filelist_clear(struct FileList *filelist);
-void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
+void filelist_clear_ex(struct FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection);
void filelist_free(struct FileList *filelist);
const char *filelist_dir(struct FileList *filelist);
@@ -141,6 +149,8 @@ void filelist_entry_parent_select_set(struct FileList *filelist,
void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
+struct AssetLibrary *filelist_asset_library(struct FileList *filelist);
+
struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group);
void filelist_freelib(struct FileList *filelist);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index f7bdb4326a5..83b33fe8aa9 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -120,19 +120,16 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL;
asset_params->asset_library_ref.custom_library_index = -1;
- asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
+ asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE;
}
FileSelectParams *base_params = &asset_params->base_params;
base_params->file[0] = '\0';
base_params->filter_glob[0] = '\0';
- /* TODO: this way of using filters to form categories is notably slower than specifying a
- * "group" to read. That's because all types are read and filtering is applied afterwards. Would
- * be nice if we could lazy-read individual groups. */
base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER;
base_params->flag &= ~FILE_DIRSEL_ONLY;
base_params->filter |= FILE_TYPE_BLENDERLIB;
- base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR;
+ base_params->filter_id = FILTER_ID_ALL;
base_params->display = FILE_IMGDISPLAY;
base_params->sort = FILE_SORT_ALPHA;
/* Asset libraries include all sub-directories, so enable maximal recursion. */
@@ -440,7 +437,8 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
break;
}
- base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+ base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET :
+ FILE_ASSET_LIBRARY;
}
void fileselect_refresh_params(SpaceFile *sfile)
@@ -461,6 +459,15 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
+struct AssetLibrary *ED_fileselect_active_asset_library_get(const SpaceFile *sfile)
+{
+ if (!ED_fileselect_is_asset_browser(sfile) || !sfile->files) {
+ return NULL;
+ }
+
+ return filelist_asset_library(sfile->files);
+}
+
struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
{
if (!ED_fileselect_is_asset_browser(sfile)) {
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 776bb0b3bb7..091c2d5f434 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -958,7 +958,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
found = 1;
}
if (endmntent(fp) == 0) {
- fprintf(stderr, "could not close the list of mounted filesystems\n");
+ fprintf(stderr, "could not close the list of mounted file-systems\n");
}
}
/* Check gvfs shares. */
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 42a9c4aa2d5..a563f24e24e 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -335,9 +335,9 @@ static void file_refresh(const bContext *C, ScrArea *area)
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
- if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) {
+ if (ED_fileselect_is_asset_browser(sfile)) {
/* Only poses supported as non-experimental right now. */
- params->filter_id = FILTER_ID_AC;
+ params->filter_id = U.experimental.use_extended_asset_browser ? FILTER_ID_ALL : FILTER_ID_AC;
}
filelist_settype(sfile->files, params->type);
@@ -355,6 +355,10 @@ static void file_refresh(const bContext *C, ScrArea *area)
(params->flag & FILE_ASSETS_ONLY) != 0,
params->filter_glob,
params->filter_search);
+ if (asset_params) {
+ filelist_set_asset_catalog_filter_options(
+ sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id);
+ }
/* Update the active indices of bookmarks & co. */
sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
@@ -1050,6 +1054,7 @@ void ED_spacetype_file(void)
art->init = file_tools_region_init;
art->draw = file_tools_region_draw;
BLI_addhead(&st->regiontypes, art);
+ file_tools_region_panels_register(art);
/* regions: tool properties */
art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 720d69eaf4f..0e2c9b85bc6 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -237,29 +237,27 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
v2d->tot.xmax += 10.0f;
}
- if (((sipo->flag & SIPO_NODRAWCURSOR) == 0) || (sipo->mode == SIPO_MODE_DRIVERS)) {
+ if (((sipo->flag & SIPO_NODRAWCURSOR) == 0)) {
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
/* horizontal component of value-cursor (value line before the current frame line) */
- if ((sipo->flag & SIPO_NODRAWCURSOR) == 0) {
- float y = sipo->cursorVal;
+ float y = sipo->cursorVal;
- /* Draw a green line to indicate the cursor value */
- immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50);
- GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_width(2.0);
+ /* Draw a line to indicate the cursor value. */
+ immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_line_width(2.0);
- immBegin(GPU_PRIM_LINES, 2);
- immVertex2f(pos, v2d->cur.xmin, y);
- immVertex2f(pos, v2d->cur.xmax, y);
- immEnd();
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2f(pos, v2d->cur.xmin, y);
+ immVertex2f(pos, v2d->cur.xmax, y);
+ immEnd();
- GPU_blend(GPU_BLEND_NONE);
- }
+ GPU_blend(GPU_BLEND_NONE);
- /* current frame or vertical component of vertical component of the cursor */
+ /* Vertical component of of the cursor. */
if (sipo->mode == SIPO_MODE_DRIVERS) {
/* cursor x-value */
float x = sipo->cursorTime;
@@ -311,12 +309,17 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
const SpaceGraph *sipo = CTX_wm_space_graph(C);
+
+ /* Driver Editor's X axis is not time. */
+ if (sipo->mode == SIPO_MODE_DRIVERS) {
+ return;
+ }
+
const Scene *scene = CTX_data_scene(C);
- const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS;
View2D *v2d = &region->v2d;
/* scrubbing region */
- ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME, draw_vert_line);
+ ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME);
/* scrollers */
/* FIXME: args for scrollers depend on the type of data being shown. */
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index d2af26aa1d7..fc04ec1fe02 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -34,6 +34,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "DNA_view2d_types.h"
#include "PIL_time.h"
@@ -576,3 +577,62 @@ void draw_image_cache(const bContext *C, ARegion *region)
ED_mask_draw_frames(mask, region, cfra, sfra, efra);
}
}
+
+float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension)
+{
+ /* UV-space length per pixel */
+ float xzoom = (v2d->cur.xmax - v2d->cur.xmin) / ((float)(v2d->mask.xmax - v2d->mask.xmin));
+ float yzoom = (v2d->cur.ymax - v2d->cur.ymin) / ((float)(v2d->mask.ymax - v2d->mask.ymin));
+
+ /* Zoom_factor for UV/Image editor is calculated based on:
+ * - Default grid size on startup, which is 256x256 pixels
+ * - How blend factor for grid lines is set up in the fragment shader `grid_frag.glsl`. */
+ float zoom_factor;
+ zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy. */
+ zoom_factor *= 256.0f / (powf(grid_dimension, 2));
+ return zoom_factor;
+}
+
+void ED_space_image_grid_steps(SpaceImage *sima,
+ float grid_steps[SI_GRID_STEPS_LEN],
+ const int grid_dimension)
+{
+ if (sima->flag & SI_CUSTOM_GRID) {
+ for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
+ grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->custom_grid_subdiv));
+ }
+ }
+ else {
+ for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
+ grid_steps[step] = powf(grid_dimension, step) *
+ (1.0f / (powf(grid_dimension, SI_GRID_STEPS_LEN)));
+ }
+ }
+}
+
+/**
+ * Calculate the increment snapping value for UV/image editor based on the zoom factor
+ * The code in here (except the offset part) is used in `grid_frag.glsl` (see `grid_res`) for
+ * drawing the grid overlay for the UV/Image editor.
+ */
+float ED_space_image_increment_snap_value(const int grid_dimesnions,
+ const float grid_steps[SI_GRID_STEPS_LEN],
+ const float zoom_factor)
+{
+ /* Small offset on each grid_steps[] so that snapping value doesn't change until grid lines are
+ * significantly visible.
+ * `Offset = 3/4 * (grid_steps[i] - (grid_steps[i] / grid_dimesnsions))`
+ *
+ * Refer `grid_frag.glsl` to find out when grid lines actually start appearing */
+
+ for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
+ float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimesnions));
+
+ if ((grid_steps[step] - offset) > zoom_factor) {
+ return grid_steps[step];
+ }
+ }
+
+ /* Fallback */
+ return grid_steps[0];
+}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index de8e4684d45..f14a8266cdd 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -126,13 +126,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(
simage->tile_grid_shape[0] = 1;
simage->tile_grid_shape[1] = 1;
- /* tool header */
- region = MEM_callocN(sizeof(ARegion), "tool header for image");
-
- BLI_addtail(&simage->regionbase, region);
- region->regiontype = RGN_TYPE_TOOL_HEADER;
- region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
- region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
+ simage->custom_grid_subdiv = 10;
/* header */
region = MEM_callocN(sizeof(ARegion), "header for image");
@@ -141,6 +135,14 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(
region->regiontype = RGN_TYPE_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
+ /* tool header */
+ region = MEM_callocN(sizeof(ARegion), "tool header for image");
+
+ BLI_addtail(&simage->regionbase, region);
+ region->regiontype = RGN_TYPE_TOOL_HEADER;
+ region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
+
/* buttons/list view */
region = MEM_callocN(sizeof(ARegion), "buttons for image");
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index a99396ecdf0..8e37e5fe9a8 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -564,7 +564,7 @@ void FILE_OT_find_missing_files(wmOperatorType *ot)
/** \name Report Box Operator
* \{ */
-/* NOTE(matt): Hard to decide whether to keep this as an operator,
+/* NOTE(@broken): Hard to decide whether to keep this as an operator,
* or turn it into a hard_coded UI control feature,
* handling TIMER events for all regions in `interface_handlers.c`.
* Not sure how good that is to be accessing UI data from
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index c1b308d213f..4694d8652f6 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -152,7 +152,7 @@ static void nla_action_draw_keyframes(
format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, key_len);
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 987d06cfe5c..8b44c26f07c 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -289,7 +289,7 @@ static void nla_main_region_draw_overlay(const bContext *C, ARegion *region)
View2D *v2d = &region->v2d;
/* scrubbing region */
- ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME, true);
+ ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME);
/* scrollers */
UI_view2d_scrollers_draw(v2d, NULL);
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 62f40152416..8d6d56fc383 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -164,6 +164,11 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
+static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiTemplateCurveMapping(layout, ptr, "mapping", 0, false, false, false, false);
+}
+
#define SAMPLE_FLT_ISNONE FLT_MAX
/* Bad bad, 2.5 will do better? ... no it won't! */
static float _sample_col[4] = {SAMPLE_FLT_ISNONE};
@@ -1183,6 +1188,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_CURVE_RGB:
ntype->draw_buttons = node_buts_curvecol;
break;
+ case SH_NODE_CURVE_FLOAT:
+ ntype->draw_buttons = node_buts_curvefloat;
+ break;
case SH_NODE_MAPPING:
ntype->draw_buttons = node_shader_buts_mapping;
break;
@@ -1563,7 +1571,7 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
-/* qdn: glare node */
+/* glare node */
static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE);
@@ -2079,7 +2087,7 @@ 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, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "channel", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -3933,9 +3941,13 @@ static struct {
uint p0_id, p1_id, p2_id, p3_id;
uint colid_id, muted_id;
uint dim_factor_id;
+ uint thickness_id;
+ uint dash_factor_id;
GPUVertBufRaw p0_step, p1_step, p2_step, p3_step;
GPUVertBufRaw colid_step, muted_step;
GPUVertBufRaw dim_factor_step;
+ GPUVertBufRaw thickness_step;
+ GPUVertBufRaw dash_factor_step;
uint count;
bool enabled;
} g_batch_link;
@@ -3952,6 +3964,10 @@ static void nodelink_batch_reset()
g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step);
GPU_vertbuf_attr_get_raw_data(
g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step);
+ GPU_vertbuf_attr_get_raw_data(
+ g_batch_link.inst_vbo, g_batch_link.thickness_id, &g_batch_link.thickness_step);
+ GPU_vertbuf_attr_get_raw_data(
+ g_batch_link.inst_vbo, g_batch_link.dash_factor_id, &g_batch_link.dash_factor_step);
g_batch_link.count = 0;
}
@@ -4071,6 +4087,10 @@ static void nodelink_batch_init()
&format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT);
g_batch_link.dim_factor_id = GPU_vertformat_attr_add(
&format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ g_batch_link.thickness_id = GPU_vertformat_attr_add(
+ &format_inst, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ g_batch_link.dash_factor_id = GPU_vertformat_attr_add(
+ &format_inst, "dash_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM);
/* Alloc max count but only draw the range we need. */
GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE);
@@ -4147,7 +4167,9 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
int th_col3,
bool drawarrow,
bool drawmuted,
- float dim_factor)
+ float dim_factor,
+ float thickness,
+ float dash_factor)
{
/* Only allow these colors. If more is needed, you need to modify the shader accordingly. */
BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
@@ -4167,6 +4189,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step);
muted[0] = drawmuted;
*(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = dash_factor;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
nodelink_batch_draw(snode);
@@ -4182,6 +4206,16 @@ void node_draw_link_bezier(const View2D *v2d,
int th_col3)
{
const float dim_factor = node_link_dim_factor(v2d, link);
+ float thickness = 1.5f;
+ float dash_factor = 1.0f;
+ if (snode->edittree->type == NTREE_GEOMETRY) {
+ if (link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) {
+ /* Make field links a bit thinner. */
+ thickness = 1.0f;
+ /* Draw field as dashes. */
+ dash_factor = 0.75f;
+ }
+ }
float vec[4][2];
const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT;
@@ -4205,7 +4239,9 @@ void node_draw_link_bezier(const View2D *v2d,
th_col3,
drawarrow,
drawmuted,
- dim_factor);
+ dim_factor,
+ thickness,
+ dash_factor);
}
else {
/* Draw single link. */
@@ -4231,6 +4267,8 @@ void node_draw_link_bezier(const View2D *v2d,
GPU_batch_uniform_1i(batch, "doArrow", drawarrow);
GPU_batch_uniform_1i(batch, "doMuted", drawmuted);
GPU_batch_uniform_1f(batch, "dim_factor", dim_factor);
+ GPU_batch_uniform_1f(batch, "thickness", thickness);
+ GPU_batch_uniform_1f(batch, "dash_factor", dash_factor);
GPU_batch_draw(batch);
}
}
@@ -4282,6 +4320,13 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
// th_col3 = -1; /* no shadow */
}
}
+ /* Links from field to non-field sockets are not allowed. */
+ if (snode->edittree->type == NTREE_GEOMETRY && !(link->flag & NODE_LINK_DRAGGED)) {
+ if ((link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) &&
+ (link->tosock && link->tosock->display_shape == SOCK_DISPLAY_SHAPE_CIRCLE)) {
+ th_col1 = th_col2 = th_col3 = TH_REDALERT;
+ }
+ }
node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3);
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 10a3285be8b..9b243290566 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -79,6 +79,7 @@
#include "RNA_access.h"
#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_node_declaration.hh"
#include "FN_field_cpp_type.hh"
@@ -733,12 +734,6 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const
GPU_blend(GPU_BLEND_NONE);
}
-/* Flags used in gpu_shader_keyframe_diamond_frag.glsl. */
-#define MARKER_SHAPE_DIAMOND 0x1
-#define MARKER_SHAPE_SQUARE 0xC
-#define MARKER_SHAPE_CIRCLE 0x2
-#define MARKER_SHAPE_INNER_DOT 0x10
-
static void node_socket_draw(const bNodeSocket *sock,
const float color[4],
const float color_outline[4],
@@ -757,16 +752,16 @@ static void node_socket_draw(const bNodeSocket *sock,
switch (sock->display_shape) {
case SOCK_DISPLAY_SHAPE_DIAMOND:
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT:
- flags = MARKER_SHAPE_DIAMOND;
+ flags = GPU_KEYFRAME_SHAPE_DIAMOND;
break;
case SOCK_DISPLAY_SHAPE_SQUARE:
case SOCK_DISPLAY_SHAPE_SQUARE_DOT:
- flags = MARKER_SHAPE_SQUARE;
+ flags = GPU_KEYFRAME_SHAPE_SQUARE;
break;
default:
case SOCK_DISPLAY_SHAPE_CIRCLE:
case SOCK_DISPLAY_SHAPE_CIRCLE_DOT:
- flags = MARKER_SHAPE_CIRCLE;
+ flags = GPU_KEYFRAME_SHAPE_CIRCLE;
break;
}
@@ -774,7 +769,7 @@ static void node_socket_draw(const bNodeSocket *sock,
SOCK_DISPLAY_SHAPE_DIAMOND_DOT,
SOCK_DISPLAY_SHAPE_SQUARE_DOT,
SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) {
- flags |= MARKER_SHAPE_INNER_DOT;
+ flags |= GPU_KEYFRAME_SHAPE_INNER_DOT;
}
immAttr4fv(col_id, color);
@@ -1091,12 +1086,37 @@ static void node_socket_draw_nested(const bContext *C,
but,
[](bContext *C, void *argN, const char *UNUSED(tip)) {
SocketTooltipData *data = (SocketTooltipData *)argN;
- std::optional<std::string> str = create_socket_inspection_string(
+ std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
C, *data->ntree, *data->node, *data->socket);
- if (str.has_value()) {
- return BLI_strdup(str->c_str());
+
+ std::stringstream output;
+ if (data->node->declaration != nullptr) {
+ ListBase *list;
+ Span<blender::nodes::SocketDeclarationPtr> decl_list;
+
+ if (data->socket->in_out == SOCK_IN) {
+ list = &data->node->inputs;
+ decl_list = data->node->declaration->inputs();
+ }
+ else {
+ list = &data->node->outputs;
+ decl_list = data->node->declaration->outputs();
+ }
+
+ const int socket_index = BLI_findindex(list, data->socket);
+ const blender::nodes::SocketDeclaration &socket_decl = *decl_list[socket_index];
+ blender::StringRef description = socket_decl.description();
+ if (!description.is_empty()) {
+ output << TIP_(description.data()) << ".\n\n";
+ }
+ }
+ if (socket_inspection_str.has_value()) {
+ output << *socket_inspection_str;
+ }
+ else {
+ output << TIP_("The socket value has not been computed yet");
}
- return BLI_strdup(TIP_("The socket value has not been computed yet"));
+ return BLI_strdup(output.str().c_str());
},
data,
MEM_freeN);
@@ -1132,7 +1152,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 0.7f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
@@ -1277,7 +1297,7 @@ void node_draw_sockets(const View2D *v2d,
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 0.7f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index 7d95659e403..b69e7e98bca 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -220,6 +220,7 @@ static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bN
if (node_connected_to_output(bmain, snode->edittree, node)) {
oplink->flag |= NODE_LINK_TEST;
}
+ oplink->flag |= NODE_LINK_DRAGGED;
return linkdata;
}
@@ -894,6 +895,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
*/
do_tag_update |= (link->flag & NODE_LINK_TEST) != 0;
+ link->flag &= ~NODE_LINK_DRAGGED;
+
if (apply_links && link->tosock && link->fromsock) {
/* before actually adding the link,
* let nodes perform special link insertion handling
@@ -1097,6 +1100,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
*oplink = *link;
oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
+ oplink->flag |= NODE_LINK_DRAGGED;
/* The link could be disconnected and in that case we
* wouldn't be able to check whether tag update is
@@ -1150,6 +1154,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
*oplink = *link_to_pick;
oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
+ oplink->flag |= NODE_LINK_DRAGGED;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) {
oplink->flag |= NODE_LINK_TEST;
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 581892ebb3a..5e409db0059 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -34,6 +34,7 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_text_types.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
@@ -63,6 +64,7 @@
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_sequencer.h"
+#include "ED_text.h"
#include "ED_undo.h"
#include "SEQ_select.h"
@@ -737,6 +739,12 @@ static void tree_element_layer_collection_activate(bContext *C, TreeElement *te)
WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL);
}
+static void tree_element_text_activate(bContext *C, TreeElement *te)
+{
+ Text *text = (Text *)te->store_elem->id;
+ ED_text_activate_in_screen(C, text);
+}
+
/* ---------------------------------------------- */
/* generic call for ID data check or make/check active in UI */
@@ -764,6 +772,9 @@ void tree_element_activate(bContext *C,
case ID_CA:
tree_element_camera_activate(C, tvc->scene, te);
break;
+ case ID_TXT:
+ tree_element_text_activate(C, te);
+ break;
}
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 53f1c35776c..dc5e11b6998 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -107,36 +107,53 @@
static Sequence *special_seq_update = NULL;
-void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
-{
+void color3ubv_from_seq(const Scene *curscene,
+ const Sequence *seq,
+ const bool show_strip_color_tag,
+ uchar r_col[3])
+{
+ if (show_strip_color_tag && (uint)seq->color_tag < SEQUENCE_COLOR_TOT &&
+ seq->color_tag != SEQUENCE_COLOR_NONE) {
+ bTheme *btheme = UI_GetTheme();
+ const ThemeStripColor *strip_color = &btheme->strip_color[seq->color_tag];
+ copy_v3_v3_uchar(r_col, strip_color->color);
+ return;
+ }
+
uchar blendcol[3];
+ /* Sometimes the active theme is not the sequencer theme, e.g. when an operator invokes the file
+ * browser. This makes sure we get the right color values for the theme. */
+ struct bThemeState theme_state;
+ UI_Theme_Store(&theme_state);
+ UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW);
+
switch (seq->type) {
case SEQ_TYPE_IMAGE:
- UI_GetThemeColor3ubv(TH_SEQ_IMAGE, col);
+ UI_GetThemeColor3ubv(TH_SEQ_IMAGE, r_col);
break;
case SEQ_TYPE_META:
- UI_GetThemeColor3ubv(TH_SEQ_META, col);
+ UI_GetThemeColor3ubv(TH_SEQ_META, r_col);
break;
case SEQ_TYPE_MOVIE:
- UI_GetThemeColor3ubv(TH_SEQ_MOVIE, col);
+ UI_GetThemeColor3ubv(TH_SEQ_MOVIE, r_col);
break;
case SEQ_TYPE_MOVIECLIP:
- UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, col);
+ UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, r_col);
break;
case SEQ_TYPE_MASK:
- UI_GetThemeColor3ubv(TH_SEQ_MASK, col);
+ UI_GetThemeColor3ubv(TH_SEQ_MASK, r_col);
break;
case SEQ_TYPE_SCENE:
- UI_GetThemeColor3ubv(TH_SEQ_SCENE, col);
+ UI_GetThemeColor3ubv(TH_SEQ_SCENE, r_col);
if (seq->scene == curscene) {
- UI_GetColorPtrShade3ubv(col, col, 20);
+ UI_GetColorPtrShade3ubv(r_col, r_col, 20);
}
break;
@@ -144,9 +161,9 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
case SEQ_TYPE_CROSS:
case SEQ_TYPE_GAMCROSS:
case SEQ_TYPE_WIPE:
- col[0] = 130;
- col[1] = 130;
- col[2] = 130;
+ r_col[0] = 130;
+ r_col[1] = 130;
+ r_col[2] = 130;
break;
/* Effects. */
@@ -163,72 +180,74 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
case SEQ_TYPE_ADJUSTMENT:
case SEQ_TYPE_GAUSSIAN_BLUR:
case SEQ_TYPE_COLORMIX:
- UI_GetThemeColor3ubv(TH_SEQ_EFFECT, col);
+ UI_GetThemeColor3ubv(TH_SEQ_EFFECT, r_col);
/* Slightly offset hue to distinguish different effects. */
if (seq->type == SEQ_TYPE_ADD) {
- rgb_byte_set_hue_float_offset(col, 0.03);
+ rgb_byte_set_hue_float_offset(r_col, 0.03);
}
else if (seq->type == SEQ_TYPE_SUB) {
- rgb_byte_set_hue_float_offset(col, 0.06);
+ rgb_byte_set_hue_float_offset(r_col, 0.06);
}
else if (seq->type == SEQ_TYPE_MUL) {
- rgb_byte_set_hue_float_offset(col, 0.13);
+ rgb_byte_set_hue_float_offset(r_col, 0.13);
}
else if (seq->type == SEQ_TYPE_ALPHAOVER) {
- rgb_byte_set_hue_float_offset(col, 0.16);
+ rgb_byte_set_hue_float_offset(r_col, 0.16);
}
else if (seq->type == SEQ_TYPE_ALPHAUNDER) {
- rgb_byte_set_hue_float_offset(col, 0.23);
+ rgb_byte_set_hue_float_offset(r_col, 0.23);
}
else if (seq->type == SEQ_TYPE_OVERDROP) {
- rgb_byte_set_hue_float_offset(col, 0.26);
+ rgb_byte_set_hue_float_offset(r_col, 0.26);
}
else if (seq->type == SEQ_TYPE_COLORMIX) {
- rgb_byte_set_hue_float_offset(col, 0.33);
+ rgb_byte_set_hue_float_offset(r_col, 0.33);
}
else if (seq->type == SEQ_TYPE_GAUSSIAN_BLUR) {
- rgb_byte_set_hue_float_offset(col, 0.43);
+ rgb_byte_set_hue_float_offset(r_col, 0.43);
}
else if (seq->type == SEQ_TYPE_GLOW) {
- rgb_byte_set_hue_float_offset(col, 0.46);
+ rgb_byte_set_hue_float_offset(r_col, 0.46);
}
else if (seq->type == SEQ_TYPE_ADJUSTMENT) {
- rgb_byte_set_hue_float_offset(col, 0.55);
+ rgb_byte_set_hue_float_offset(r_col, 0.55);
}
else if (seq->type == SEQ_TYPE_SPEED) {
- rgb_byte_set_hue_float_offset(col, 0.65);
+ rgb_byte_set_hue_float_offset(r_col, 0.65);
}
else if (seq->type == SEQ_TYPE_TRANSFORM) {
- rgb_byte_set_hue_float_offset(col, 0.75);
+ rgb_byte_set_hue_float_offset(r_col, 0.75);
}
else if (seq->type == SEQ_TYPE_MULTICAM) {
- rgb_byte_set_hue_float_offset(col, 0.85);
+ rgb_byte_set_hue_float_offset(r_col, 0.85);
}
break;
case SEQ_TYPE_COLOR:
- UI_GetThemeColor3ubv(TH_SEQ_COLOR, col);
+ UI_GetThemeColor3ubv(TH_SEQ_COLOR, r_col);
break;
case SEQ_TYPE_SOUND_RAM:
- UI_GetThemeColor3ubv(TH_SEQ_AUDIO, col);
+ UI_GetThemeColor3ubv(TH_SEQ_AUDIO, r_col);
blendcol[0] = blendcol[1] = blendcol[2] = 128;
if (seq->flag & SEQ_MUTE) {
- UI_GetColorPtrBlendShade3ubv(col, blendcol, col, 0.5, 20);
+ UI_GetColorPtrBlendShade3ubv(r_col, blendcol, r_col, 0.5, 20);
}
break;
case SEQ_TYPE_TEXT:
- UI_GetThemeColor3ubv(TH_SEQ_TEXT, col);
+ UI_GetThemeColor3ubv(TH_SEQ_TEXT, r_col);
break;
default:
- col[0] = 10;
- col[1] = 255;
- col[2] = 40;
+ r_col[0] = 10;
+ r_col[1] = 255;
+ r_col[2] = 40;
break;
}
+
+ UI_Theme_Restore(&theme_state);
}
typedef struct WaveVizData {
@@ -558,7 +577,13 @@ static void draw_seq_waveform_overlay(View2D *v2d,
}
}
-static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2)
+static void drawmeta_contents(Scene *scene,
+ Sequence *seqm,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ const bool show_strip_color_tag)
{
Sequence *seq;
uchar col[4];
@@ -614,7 +639,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
rgb_float_to_uchar(col, colvars->col);
}
else {
- color3ubv_from_seq(scene, seq, col);
+ color3ubv_from_seq(scene, seq, show_strip_color_tag, col);
}
if ((seqm->flag & SEQ_MUTE) || (seq->flag & SEQ_MUTE)) {
@@ -953,7 +978,8 @@ static void draw_seq_text_overlay(View2D *v2d,
UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col);
}
-static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely)
+static void draw_sequence_extensions_overlay(
+ Scene *scene, Sequence *seq, uint pos, float pixely, const bool show_strip_color_tag)
{
float x1, x2, y1, y2;
uchar col[4], blend_col[3];
@@ -966,7 +992,7 @@ static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint p
GPU_blend(GPU_BLEND_ALPHA);
- color3ubv_from_seq(scene, seq, col);
+ color3ubv_from_seq(scene, seq, show_strip_color_tag, col);
if (seq->flag & SELECT) {
UI_GetColorPtrShade3ubv(col, col, 50);
}
@@ -1036,7 +1062,8 @@ static void draw_seq_background(Scene *scene,
float x2,
float y1,
float y2,
- bool is_single_image)
+ bool is_single_image,
+ bool show_strip_color_tag)
{
uchar col[4];
GPU_blend(GPU_BLEND_ALPHA);
@@ -1049,11 +1076,11 @@ static void draw_seq_background(Scene *scene,
rgb_float_to_uchar(col, colvars->col);
}
else {
- color3ubv_from_seq(scene, seq1, col);
+ color3ubv_from_seq(scene, seq1, show_strip_color_tag, col);
}
}
else {
- color3ubv_from_seq(scene, seq, col);
+ color3ubv_from_seq(scene, seq, show_strip_color_tag, col);
}
/* Draw muted strips semi-transparent. */
@@ -1108,7 +1135,7 @@ static void draw_seq_background(Scene *scene,
rgb_float_to_uchar(col, colvars->col);
}
else {
- color3ubv_from_seq(scene, seq2, col);
+ color3ubv_from_seq(scene, seq2, show_strip_color_tag, col);
/* If the transition inputs are of the same type, draw the right side slightly darker. */
if (seq1->type == seq2->type) {
UI_GetColorPtrShade3ubv(col, col, -15);
@@ -1822,6 +1849,10 @@ static void draw_seq_strip(const bContext *C,
/* Check if we are doing "solo preview". */
bool is_single_image = (char)SEQ_transform_single_image_check(seq);
+ /* Use the seq->color_tag to display the tag color. */
+ const bool show_strip_color_tag = (sseq->timeline_overlay.flag &
+ SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG);
+
/* Draw strip body. */
x1 = (seq->startstill) ? seq->start : seq->startdisp;
y1 = seq->machine + SEQ_STRIP_OFSBOTTOM;
@@ -1852,7 +1883,7 @@ static void draw_seq_strip(const bContext *C,
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image);
+ draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image, show_strip_color_tag);
/* Draw a color band inside color strip. */
if (seq->type == SEQ_TYPE_COLOR && y_threshold) {
@@ -1864,7 +1895,7 @@ static void draw_seq_strip(const bContext *C,
if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS) ||
(seq == special_seq_update)) {
- draw_sequence_extensions_overlay(scene, seq, pos, pixely);
+ draw_sequence_extensions_overlay(scene, seq, pos, pixely, show_strip_color_tag);
}
}
}
@@ -1875,7 +1906,7 @@ static void draw_seq_strip(const bContext *C,
if ((seq->type == SEQ_TYPE_META) ||
((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) {
- drawmeta_contents(scene, seq, x1, y1, x2, y2);
+ drawmeta_contents(scene, seq, x1, y1, x2, y2, show_strip_color_tag);
}
if ((sseq->flag & SEQ_SHOW_OVERLAY) &&
@@ -2585,7 +2616,7 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene)
return preview_frame;
}
-static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq)
+static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq, bool is_active_seq)
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
if ((seq->flag & SELECT) == 0) {
@@ -2628,7 +2659,12 @@ static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
float col[3];
- UI_GetThemeColor3fv(TH_SEQ_SELECTED, col);
+ if (is_active_seq) {
+ UI_GetThemeColor3fv(TH_SEQ_ACTIVE, col);
+ }
+ else {
+ UI_GetThemeColor3fv(TH_SEQ_SELECTED, col);
+ }
immUniformColor3fv(col);
immUniform1f("lineWidth", U.pixelsize);
immBegin(GPU_PRIM_LINE_LOOP, 4);
@@ -2719,12 +2755,15 @@ void sequencer_draw_preview(const bContext *C,
sequencer_draw_borders_overlay(sseq, v2d, scene);
}
- SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0);
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, collection) {
- seq_draw_image_origin_and_outline(C, seq);
+ if (!draw_backdrop && scene->ed != NULL) {
+ SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0);
+ Sequence *seq;
+ Sequence *active_seq = SEQ_select_active_get(scene);
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ seq_draw_image_origin_and_outline(C, seq, seq == active_seq);
+ }
+ SEQ_collection_free(collection);
}
- SEQ_collection_free(collection);
if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) {
sequencer_draw_gpencil_overlay(C);
@@ -3292,6 +3331,6 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
}
- ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
+ ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES));
UI_view2d_scrollers_draw(v2d, NULL);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 9f21fc0676c..9be947b9112 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -63,6 +63,7 @@
#include "WM_types.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
/* For menu, popup, icons, etc. */
#include "ED_numinput.h"
@@ -869,9 +870,9 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even
void SEQUENCER_OT_slip(struct wmOperatorType *ot)
{
/* Identifiers. */
- ot->name = "Trim Strips";
+ ot->name = "Slip Strips";
ot->idname = "SEQUENCER_OT_slip";
- ot->description = "Trim the contents of the active strip";
+ ot->description = "Slip the contents of selected strips";
/* Api callbacks. */
ot->invoke = sequencer_slip_invoke;
@@ -3322,4 +3323,38 @@ void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot)
"Scale fit fit_method");
}
+static int sequencer_strip_color_tag_set_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = SEQ_editing_get(scene);
+ const short color_tag = RNA_enum_get(op->ptr, "color");
+
+ LISTBASE_FOREACH (Sequence *, seq, &ed->seqbase) {
+ if (seq->flag & SELECT) {
+ seq->color_tag = color_tag;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Set Color Tag";
+ ot->idname = "SEQUENCER_OT_strip_color_tag_set";
+ ot->description = "Set a color tag for the selected strips";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_strip_color_tag_set_exec;
+ ot->poll = sequencer_edit_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(
+ ot->srna, "color", rna_enum_strip_color_items, SEQUENCE_COLOR_NONE, "Color Tag", "");
+}
+
/** \} */
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 5b5c381509f..202eda85dca 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -51,7 +51,10 @@ void sequencer_draw_preview(const struct bContext *C,
int offset,
bool draw_overlay,
bool draw_backdrop);
-void color3ubv_from_seq(struct Scene *curscene, struct Sequence *seq, unsigned char col[3]);
+void color3ubv_from_seq(const struct Scene *curscene,
+ const struct Sequence *seq,
+ const bool show_strip_color_tag,
+ uchar r_col[3]);
void sequencer_special_update_set(Sequence *seq);
float sequence_handle_size_get_clamped(struct Sequence *seq, const float pixelx);
@@ -148,6 +151,8 @@ void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot);
void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot);
void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot);
+void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot);
+
/* sequencer_select.c */
void SEQUENCER_OT_select_all(struct wmOperatorType *ot);
void SEQUENCER_OT_select(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index 48e6cfcdcd0..95f7b44264c 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -79,6 +79,8 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_strip_transform_clear);
WM_operatortype_append(SEQUENCER_OT_strip_transform_fit);
+ WM_operatortype_append(SEQUENCER_OT_strip_color_tag_set);
+
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);
WM_operatortype_append(SEQUENCER_OT_select);
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 99b75f82922..ad0ceb82709 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -104,25 +104,25 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE |
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
- SEQ_TIMELINE_SHOW_FCURVES;
+ SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f);
sseq->runtime.last_displayed_thumbnails = NULL;
- /* Tool header. */
- region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
+ /* Header. */
+ region = MEM_callocN(sizeof(ARegion), "header for sequencer");
BLI_addtail(&sseq->regionbase, region);
- region->regiontype = RGN_TYPE_TOOL_HEADER;
+ region->regiontype = RGN_TYPE_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
- region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
- /* Header. */
- region = MEM_callocN(sizeof(ARegion), "header for sequencer");
+ /* Tool header. */
+ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
BLI_addtail(&sseq->regionbase, region);
- region->regiontype = RGN_TYPE_HEADER;
+ region->regiontype = RGN_TYPE_TOOL_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
/* Buttons/list view. */
region = MEM_callocN(sizeof(ARegion), "buttons for sequencer");
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 99fcb2092c3..b541b65d676 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -48,6 +48,9 @@
#include "text_format.h"
#include "text_intern.h"
+#include "WM_api.h"
+#include "WM_types.h"
+
/******************** text font drawing ******************/
typedef struct TextDrawContext {
@@ -1734,6 +1737,23 @@ void text_update_character_width(SpaceText *st)
text_font_end(&tdc);
}
+bool ED_text_activate_in_screen(bContext *C, Text *text)
+{
+ ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
+ if (area) {
+ SpaceText *st = area->spacedata.first;
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
+ st->text = text;
+ if (region) {
+ ED_text_scroll_to_cursor(st, region, true);
+ }
+ WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
+ return true;
+ }
+
+ return false;
+}
+
/* Moves the view to the cursor location,
* also used to make sure the view isn't outside the file */
void ED_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index cbc70fc7497..bedc24a6287 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -276,20 +276,20 @@ static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene)
v3d->camera = scene->camera;
}
- /* tool header */
- region = MEM_callocN(sizeof(ARegion), "tool header for view3d");
+ /* header */
+ region = MEM_callocN(sizeof(ARegion), "header for view3d");
BLI_addtail(&v3d->regionbase, region);
- region->regiontype = RGN_TYPE_TOOL_HEADER;
+ region->regiontype = RGN_TYPE_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
- region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
- /* header */
- region = MEM_callocN(sizeof(ARegion), "header for view3d");
+ /* tool header */
+ region = MEM_callocN(sizeof(ARegion), "tool header for view3d");
BLI_addtail(&v3d->regionbase, region);
- region->regiontype = RGN_TYPE_HEADER;
+ region->regiontype = RGN_TYPE_TOOL_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER;
/* tool shelf */
region = MEM_callocN(sizeof(ARegion), "toolshelf for view3d");
@@ -539,6 +539,11 @@ static char *view3d_mat_drop_tooltip(bContext *C,
return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event);
}
+static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
+{
+ return view3d_drop_id_in_main_region_poll(C, drag, event, ID_WO);
+}
+
static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event);
@@ -732,6 +737,12 @@ static void view3d_dropboxes(void)
view3d_id_drop_copy_with_type,
WM_drag_free_imported_drag_ID,
view3d_object_data_drop_tooltip);
+ WM_dropbox_add(lb,
+ "VIEW3D_OT_drop_world",
+ view3d_world_drop_poll,
+ view3d_id_drop_copy,
+ WM_drag_free_imported_drag_ID,
+ NULL);
}
static void view3d_widgets(void)
@@ -1555,11 +1566,16 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params)
switch (wmn->category) {
case NC_SCENE:
switch (wmn->data) {
- case ND_WORLD:
- if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
+ case ND_WORLD: {
+ const bool use_scene_world = ((v3d->shading.type == OB_MATERIAL) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) ||
+ ((v3d->shading.type == OB_RENDER) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER));
+ if (v3d->flag2 & V3D_HIDE_OVERLAYS || use_scene_world) {
ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW);
}
break;
+ }
}
break;
case NC_WORLD:
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 8ed134c7fd1..15ccf5891d4 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -34,10 +34,12 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_world_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_dial_2d.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -210,6 +212,9 @@ typedef struct ViewOpsData {
* If we want the value before running the operator, add a separate member.
*/
char persp;
+
+ /** Used for roll */
+ Dial *dial;
} init;
/** Previous state (previous modal event handled). */
@@ -577,6 +582,10 @@ static void viewops_data_free(bContext *C, wmOperator *op)
WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
}
+ if (vod->init.dial) {
+ MEM_freeN(vod->init.dial);
+ }
+
MEM_freeN(vod);
op->customdata = NULL;
}
@@ -4352,18 +4361,9 @@ static void view_roll_angle(
rv3d->view = RV3D_VIEW_USER;
}
-static void viewroll_apply(ViewOpsData *vod, int x, int UNUSED(y))
+static void viewroll_apply(ViewOpsData *vod, int x, int y)
{
- float angle = 0.0;
-
- {
- float len1, len2, tot;
-
- tot = vod->region->winrct.xmax - vod->region->winrct.xmin;
- len1 = (vod->region->winrct.xmax - x) / tot;
- len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) / tot;
- angle = (len1 - len2) * (float)M_PI * 4.0f;
- }
+ float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y});
if (angle != 0.0f) {
view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle);
@@ -4409,6 +4409,13 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
}
+ else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
+ /* Note this does not remove auto-keys on locked cameras. */
+ copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+ viewops_data_free(C, op);
+ return OPERATOR_CANCELLED;
+ }
else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
event_code = VIEW_CONFIRM;
}
@@ -4517,6 +4524,9 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
viewops_data_alloc(C, op);
viewops_data_create(C, op, event, viewops_flag_from_prefs());
vod = op->customdata;
+ vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct),
+ BLI_rcti_cent_y(&vod->region->winrct)},
+ FLT_EPSILON);
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
@@ -4865,6 +4875,59 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Drop World Operator
+ * \{ */
+
+static int drop_world_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+
+ char name[MAX_ID_NAME - 2];
+
+ RNA_string_get(op->ptr, "name", name);
+ World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name);
+ if (world == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ id_us_plus(&world->id);
+ scene->world = world;
+
+ DEG_id_tag_update(&scene->id, 0);
+ DEG_relations_tag_update(bmain);
+
+ WM_event_add_notifier(C, NC_SCENE | ND_WORLD, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool drop_world_poll(bContext *C)
+{
+ return ED_operator_scene_editable(C);
+}
+
+void VIEW3D_OT_drop_world(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Drop World";
+ ot->description = "Drop a world into the scene";
+ ot->idname = "VIEW3D_OT_drop_world";
+
+ /* api callbacks */
+ ot->exec = drop_world_exec;
+ ot->poll = drop_world_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ /* properties */
+ RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name View Clipping Planes Operator
*
* Draw border or toggle off.
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index ab80928e0c1..a21fc006b02 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -80,6 +80,7 @@ void VIEW3D_OT_view_persportho(struct wmOperatorType *ot);
void VIEW3D_OT_navigate(struct wmOperatorType *ot);
void VIEW3D_OT_background_image_add(struct wmOperatorType *ot);
void VIEW3D_OT_background_image_remove(struct wmOperatorType *ot);
+void VIEW3D_OT_drop_world(struct wmOperatorType *ot);
void VIEW3D_OT_view_orbit(struct wmOperatorType *ot);
void VIEW3D_OT_view_roll(struct wmOperatorType *ot);
void VIEW3D_OT_clip_border(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 56dedbbdbb2..eb8c043319c 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -169,6 +169,7 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_view_persportho);
WM_operatortype_append(VIEW3D_OT_background_image_add);
WM_operatortype_append(VIEW3D_OT_background_image_remove);
+ WM_operatortype_append(VIEW3D_OT_drop_world);
WM_operatortype_append(VIEW3D_OT_view_selected);
WM_operatortype_append(VIEW3D_OT_view_lock_clear);
WM_operatortype_append(VIEW3D_OT_view_lock_to_active);
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index bca4f8b4857..ac5a06d22bb 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2082,7 +2082,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
/**
* \param has_bones: When true, skip non-bone hits, also allow bases to be used
* that are visible but not select-able,
- * since you may be in pose mode with an unselect-able object.
+ * since you may be in pose mode with an un-selectable object.
*
* \return the active base or NULL.
*/
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index e58e524e341..6ed2c28a7eb 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -28,6 +28,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_mask_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_screen_types.h"
#include "BLI_math.h"
#include "BLI_rect.h"
@@ -1609,8 +1610,16 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
}
}
else if (t->spacetype == SPACE_IMAGE) {
- r_snap[0] = 0.0625f;
- r_snap[1] = 0.03125f;
+ SpaceImage *sima = t->area->spacedata.first;
+ View2D *v2d = &t->region->v2d;
+ int grid_size = SI_GRID_STEPS_LEN;
+ float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
+ float grid_steps[SI_GRID_STEPS_LEN];
+
+ ED_space_image_grid_steps(sima, grid_steps, grid_size);
+ /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
+ r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor);
+ r_snap[1] = r_snap[0] / 2.0f;
}
else if (t->spacetype == SPACE_CLIP) {
r_snap[0] = 0.125f;
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index d3e0f55b127..d19ff123037 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -1496,8 +1496,10 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb)
}
}
-/* Sets transform flags in the bones.
- * Returns total number of bones with `BONE_TRANSFORM`. */
+/**
+ * Sets transform flags in the bones.
+ * Returns total number of bones with #BONE_TRANSFORM.
+ */
int transform_convert_pose_transflags_update(Object *ob,
const int mode,
const short around,
@@ -1730,7 +1732,7 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t)
BKE_pose_where_is(t->depsgraph, t->scene, pose_ob);
}
- /* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */
+ /* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */
if (!canceled && (t->mode != TFM_DUMMY)) {
transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL);
}
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index 7e5b80c2453..acef8a666e3 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -208,30 +208,18 @@ void createTransNlaData(bContext *C, TransInfo *t)
/* just set tdn to assume that it only has one handle for now */
tdn->handle = -1;
- /* now, link the transform data up to this data */
- if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
- td->loc = tdn->h1;
- copy_v3_v3(td->iloc, tdn->h1);
+ /* Now, link the transform data up to this data. */
+ td->loc = tdn->h1;
+ copy_v3_v3(td->iloc, tdn->h1);
- /* store all the other gunk that is required by transform */
+ if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
+ /* Store all the other gunk that is required by transform. */
copy_v3_v3(td->center, center);
- memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
-
- td->ext = NULL;
- td->val = NULL;
-
td->flag |= TD_SELECTED;
- td->dist = 0.0f;
-
unit_m3(td->mtx);
unit_m3(td->smtx);
}
- else {
- /* time scaling only needs single value */
- td->val = &tdn->h1[0];
- td->ival = tdn->h1[0];
- }
td->extra = tdn;
td++;
@@ -241,30 +229,18 @@ void createTransNlaData(bContext *C, TransInfo *t)
* then we're doing both, otherwise, only end */
tdn->handle = (tdn->handle) ? 2 : 1;
- /* now, link the transform data up to this data */
- if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
- td->loc = tdn->h2;
- copy_v3_v3(td->iloc, tdn->h2);
+ /* Now, link the transform data up to this data. */
+ td->loc = tdn->h2;
+ copy_v3_v3(td->iloc, tdn->h2);
- /* store all the other gunk that is required by transform */
+ if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
+ /* Store all the other gunk that is required by transform. */
copy_v3_v3(td->center, center);
- memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
-
- td->ext = NULL;
- td->val = NULL;
-
td->flag |= TD_SELECTED;
- td->dist = 0.0f;
-
unit_m3(td->mtx);
unit_m3(td->smtx);
}
- else {
- /* time scaling only needs single value */
- td->val = &tdn->h2[0];
- td->ival = tdn->h2[0];
- }
td->extra = tdn;
td++;
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index ad22b0fc444..09aa4314b32 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -958,25 +958,25 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t)
}
BLI_freelistN(&pidlist);
- /* pointcache refresh */
+ /* Point-cache refresh. */
if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
- /* Needed for proper updating of "quick cached" dynamics. */
- /* Creates troubles for moving animated objects without */
- /* autokey though, probably needed is an anim sys override? */
- /* Please remove if some other solution is found. -jahka */
+ /* Needed for proper updating of "quick cached" dynamics.
+ * Creates troubles for moving animated objects without
+ * auto-key though, probably needed is an animation-system override?
+ * NOTE(@jahka): Please remove if some other solution is found. */
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
- /* Set autokey if necessary */
+ /* Set auto-key if necessary. */
if (!canceled) {
ED_transform_autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode);
}
motionpath_update |= ED_transform_motionpath_need_update_object(t->scene, ob);
- /* restore rigid body transform */
+ /* Restore rigid body transform. */
if (ob->rigidbody_object && canceled) {
float ctime = BKE_scene_ctime_get(t->scene);
if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) {
diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c
index 5db9a2e092f..6e3f12de472 100644
--- a/source/blender/editors/transform/transform_convert_sequencer_image.c
+++ b/source/blender/editors/transform/transform_convert_sequencer_image.c
@@ -113,12 +113,17 @@ static void freeSeqData(TransInfo *UNUSED(t),
void createTransSeqImageData(TransInfo *t)
{
Editing *ed = SEQ_editing_get(t->scene);
+
+ if (ed == NULL) {
+ return;
+ }
+
ListBase *seqbase = SEQ_active_seqbase_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
const int count = SEQ_collection_len(strips);
- if (ed == NULL || count == 0) {
+ if (count == 0) {
SEQ_collection_free(strips);
return;
}
diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c
index 0d66db0d7e1..4f6556cd2a2 100644
--- a/source/blender/editors/transform/transform_gizmo_2d.c
+++ b/source/blender/editors/transform/transform_gizmo_2d.c
@@ -32,6 +32,7 @@
#include "DNA_view3d_types.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_layer.h"
#include "RNA_access.h"
@@ -70,6 +71,10 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt)
return false;
}
+ if (G.moving) {
+ return false;
+ }
+
ScrArea *area = CTX_wm_area(C);
switch (area->spacetype) {
case SPACE_IMAGE: {
diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c
index 50fd714727b..0a7ae54982e 100644
--- a/source/blender/editors/transform/transform_mode_timescale.c
+++ b/source/blender/editors/transform/transform_mode_timescale.c
@@ -87,7 +87,7 @@ static void applyTimeScaleValue(TransInfo *t, float value)
}
/* now, calculate the new value */
- *(td->val) = ((td->ival - startx) * fac) + startx;
+ td->loc[0] = ((td->iloc[0] - startx) * fac) + startx;
}
}
}
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 05a20a14477..39a70f5477e 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -590,6 +590,11 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.project = 0;
t->tsnap.mode = ts->snap_uv_mode;
+ if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) &&
+ (t->mode == TFM_TRANSLATION)) {
+ t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT;
+ t->tsnap.mode |= SCE_SNAP_MODE_GRID;
+ }
}
else if (t->spacetype == SPACE_SEQ) {
t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene);
@@ -1502,7 +1507,8 @@ bool transform_snap_grid(TransInfo *t, float *val)
return false;
}
- if (t->spacetype != SPACE_VIEW3D) {
+ /* Don't do grid snapping if not in 3D viewport or UV editor */
+ if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) {
return false;
}
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 891919fd46c..70297fad4ff 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -734,7 +734,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
+ BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index b396e348845..b339bfbdc47 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -103,8 +103,10 @@ set(SRC
../include/ED_view3d_offscreen.h
../include/UI_icons.h
../include/UI_interface.h
+ ../include/UI_interface.hh
../include/UI_interface_icons.h
../include/UI_resources.h
+ ../include/UI_tree_view.hh
../include/UI_view2d.h
)
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c
index 56bcbc63de1..6159758dbcd 100644
--- a/source/blender/editors/uvedit/uvedit_islands.c
+++ b/source/blender/editors/uvedit/uvedit_islands.c
@@ -29,6 +29,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
#include "BLI_boxpack_2d.h"
#include "BLI_convexhull_2d.h"
@@ -38,6 +39,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
+#include "BKE_image.h"
#include "DEG_depsgraph.h"
@@ -232,6 +234,101 @@ static void bm_face_array_uv_scale_y(BMFace **faces,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name UDIM packing helper functions
+ * \{ */
+
+/**
+ * Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image.
+ */
+bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2])
+{
+ const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+
+ if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) {
+ return true;
+ }
+ /* Check if selection lies on a valid UDIM image tile. */
+ if (is_tiled_image) {
+ LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
+ const int tile_index = tile->tile_number - 1001;
+ const int target_x = (tile_index % 10);
+ const int target_y = (tile_index / 10);
+ if (coords_floor[0] == target_x && coords_floor[1] == target_y) {
+ return true;
+ }
+ }
+ }
+ /* Probably not required since UDIM grid checks for 1001. */
+ else if (image && !is_tiled_image) {
+ if (is_zero_v2(coords_floor)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
+ */
+static float uv_nearest_image_tile_distance(const Image *image,
+ float coords[2],
+ float nearest_tile_co[2])
+{
+ int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords);
+ if (nearest_image_tile_index == -1) {
+ nearest_image_tile_index = 1001;
+ }
+
+ nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10;
+ nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10;
+ /* Add 0.5 to get tile center coordinates. */
+ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
+ add_v2_fl(nearest_tile_center_co, 0.5f);
+
+ return len_squared_v2v2(coords, nearest_tile_center_co);
+}
+
+/**
+ * Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number.
+ */
+static float uv_nearest_grid_tile_distance(const int udim_grid[2],
+ float coords[2],
+ float nearest_tile_co[2])
+{
+ const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
+
+ if (coords[0] > udim_grid[0]) {
+ nearest_tile_co[0] = udim_grid[0] - 1;
+ }
+ else if (coords[0] < 0) {
+ nearest_tile_co[0] = 0;
+ }
+ else {
+ nearest_tile_co[0] = coords_floor[0];
+ }
+
+ if (coords[1] > udim_grid[1]) {
+ nearest_tile_co[1] = udim_grid[1] - 1;
+ }
+ else if (coords[1] < 0) {
+ nearest_tile_co[1] = 0;
+ }
+ else {
+ nearest_tile_co[1] = coords_floor[1];
+ }
+
+ /* Add 0.5 to get tile center coordinates. */
+ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
+ add_v2_fl(nearest_tile_center_co, 0.5f);
+
+ return len_squared_v2v2(coords, nearest_tile_center_co);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Calculate UV Islands
*
* \note Currently this is a private API/type, it could be made public.
@@ -359,6 +456,7 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
void ED_uvedit_pack_islands_multi(const Scene *scene,
Object **objects,
const uint objects_len,
+ const struct UVMapUDIM_Params *udim_params,
const struct UVPackIsland_Params *params)
{
/* Align to the Y axis, could make this configurable. */
@@ -407,8 +505,27 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
int index;
+ /* Coordinates of bounding box containing all selected UVs. */
+ float selection_min_co[2], selection_max_co[2];
+ INIT_MINMAX2(selection_min_co, selection_max_co);
+
LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) {
+ /* Skip calculation if using specified UDIM option. */
+ if (udim_params && (udim_params->use_target_udim == false)) {
+ float bounds_min[2], bounds_max[2];
+ INIT_MINMAX2(bounds_min, bounds_max);
+ for (int i = 0; i < island->faces_len; i++) {
+ BMFace *f = island->faces[i];
+ BM_face_uv_minmax(f, bounds_min, bounds_max, island->cd_loop_uv_offset);
+ }
+
+ selection_min_co[0] = MIN2(bounds_min[0], selection_min_co[0]);
+ selection_min_co[1] = MIN2(bounds_min[1], selection_min_co[1]);
+ selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]);
+ selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]);
+ }
+
if (params->rotate) {
if (island->aspect_y != 1.0f) {
bm_face_array_uv_scale_y(
@@ -441,6 +558,13 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
}
}
+ /* Center of bounding box containing all selected UVs. */
+ float selection_center[2];
+ if (udim_params && (udim_params->use_target_udim == false)) {
+ selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f;
+ selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f;
+ }
+
if (margin > 0.0f) {
/* Logic matches behavior from #param_pack,
* use area so multiply the margin by the area to give
@@ -464,6 +588,53 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]};
+ /* Tile offset. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* CASE: ignore UDIM. */
+ if (udim_params == NULL) {
+ /* pass */
+ }
+ /* CASE: Active/specified(smart uv project) UDIM. */
+ else if (udim_params->use_target_udim) {
+
+ /* Calculate offset based on specified_tile_index. */
+ base_offset[0] = (udim_params->target_udim - 1001) % 10;
+ base_offset[1] = (udim_params->target_udim - 1001) / 10;
+ }
+
+ /* CASE: Closest UDIM. */
+ else {
+ const Image *image = udim_params->image;
+ const int *udim_grid = udim_params->grid_shape;
+ /* Check if selection lies on a valid UDIM grid tile. */
+ bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
+ if (is_valid_udim) {
+ base_offset[0] = floorf(selection_center[0]);
+ base_offset[1] = floorf(selection_center[1]);
+ }
+ /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */
+ else {
+ float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
+ float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX;
+ if (image) {
+ nearest_image_tile_dist = uv_nearest_image_tile_distance(
+ image, selection_center, nearest_image_tile_co);
+ }
+
+ float nearest_grid_tile_co[2] = {0.0f, 0.0f};
+ nearest_grid_tile_dist = uv_nearest_grid_tile_distance(
+ udim_grid, selection_center, nearest_grid_tile_co);
+
+ base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
+ nearest_image_tile_co[0] :
+ nearest_grid_tile_co[0];
+ base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
+ nearest_image_tile_co[1] :
+ nearest_grid_tile_co[1];
+ }
+ }
+
for (int i = 0; i < island_list_len; i++) {
struct FaceIsland *island = island_array[boxarray[i].index];
const float pivot[2] = {
@@ -471,8 +642,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
island->bounds_rect.ymin,
};
const float offset[2] = {
- (boxarray[i].x * scale[0]) - island->bounds_rect.xmin,
- (boxarray[i].y * scale[1]) - island->bounds_rect.ymin,
+ ((boxarray[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0],
+ ((boxarray[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1],
};
for (int j = 0; j < island->faces_len; j++) {
BMFace *efa = island->faces[j];
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3d5dabda23d..38233b55d15 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -37,6 +37,7 @@
#include "BLI_alloca.h"
#include "BLI_array.h"
#include "BLI_linklist.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_string.h"
@@ -64,6 +65,7 @@
#include "PIL_time.h"
#include "UI_interface.h"
+#include "UI_view2d.h"
#include "ED_image.h"
#include "ED_mesh.h"
@@ -143,6 +145,61 @@ static bool ED_uvedit_ensure_uvs(Object *obedit)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name UDIM Access
+ * \{ */
+
+bool ED_uvedit_udim_params_from_image_space(const SpaceImage *sima,
+ bool use_active,
+ struct UVMapUDIM_Params *udim_params)
+{
+ memset(udim_params, 0, sizeof(*udim_params));
+
+ udim_params->grid_shape[0] = 1;
+ udim_params->grid_shape[1] = 1;
+ udim_params->target_udim = 0;
+ udim_params->use_target_udim = false;
+
+ if (sima == NULL) {
+ return false;
+ }
+
+ udim_params->image = sima->image;
+ udim_params->grid_shape[0] = sima->tile_grid_shape[0];
+ udim_params->grid_shape[1] = sima->tile_grid_shape[1];
+
+ if (use_active) {
+ int active_udim = 1001;
+ /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for
+ * the tiled image is considered. */
+ Image *image = sima->image;
+ if (image && image->source == IMA_SRC_TILED) {
+ ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index);
+ if (active_tile) {
+ active_udim = active_tile->tile_number;
+ }
+ }
+ else {
+ /* TODO: Support storing an active UDIM when there are no tiles present.
+ * Until then, use 2D cursor to find the active tile index for the UDIM grid. */
+ const float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]};
+ if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) {
+ int tile_number = 1001;
+ tile_number += floorf(cursor_loc[1]) * 10;
+ tile_number += floorf(cursor_loc[0]);
+ active_udim = tile_number;
+ }
+ }
+
+ udim_params->target_udim = active_udim;
+ udim_params->use_target_udim = true;
+ }
+
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Parametrizer Conversion
* \{ */
@@ -1005,10 +1062,17 @@ static void uvedit_pack_islands_multi(const Scene *scene,
}
}
+/* Packing targets. */
+enum {
+ PACK_UDIM_SRC_CLOSEST = 0,
+ PACK_UDIM_SRC_ACTIVE = 1,
+};
+
static int pack_islands_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
const Scene *scene = CTX_data_scene(C);
+ const SpaceImage *sima = CTX_wm_space_image(C);
const UnwrapOptions options = {
.topology_from_uvs = true,
@@ -1018,17 +1082,19 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
.correct_aspect = true,
};
- bool rotate = RNA_boolean_get(op->ptr, "rotate");
-
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, CTX_wm_view3d(C), &objects_len);
+ /* Early exit in case no UVs are selected. */
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}
+ /* RNA props */
+ const bool rotate = RNA_boolean_get(op->ptr, "rotate");
+ const int udim_source = RNA_enum_get(op->ptr, "udim_source");
if (RNA_struct_property_is_set(op->ptr, "margin")) {
scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
}
@@ -1036,9 +1102,15 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
}
+ struct UVMapUDIM_Params udim_params;
+ const bool use_active = (udim_source == PACK_UDIM_SRC_ACTIVE);
+ const bool use_udim_params = ED_uvedit_udim_params_from_image_space(
+ sima, use_active, &udim_params);
+
ED_uvedit_pack_islands_multi(scene,
objects,
objects_len,
+ use_udim_params ? &udim_params : NULL,
&(struct UVPackIsland_Params){
.rotate = rotate,
.rotate_align_axis = -1,
@@ -1048,16 +1120,25 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
});
MEM_freeN(objects);
-
return OPERATOR_FINISHED;
}
void UV_OT_pack_islands(wmOperatorType *ot)
{
+ static const EnumPropertyItem pack_target[] = {
+ {PACK_UDIM_SRC_CLOSEST, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"},
+ {PACK_UDIM_SRC_ACTIVE,
+ "ACTIVE_UDIM",
+ 0,
+ "Active UDIM",
+ "Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"},
+ {0, NULL, 0, NULL, NULL},
+ };
/* identifiers */
ot->name = "Pack Islands";
ot->idname = "UV_OT_pack_islands";
- ot->description = "Transform all islands so that they fill up the UV space as much as possible";
+ ot->description =
+ "Transform all islands so that they fill up the UV/UDIM space as much as possible";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1066,6 +1147,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
ot->poll = ED_operator_uvedit;
/* properties */
+ RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
@@ -2206,6 +2288,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
ED_uvedit_pack_islands_multi(scene,
objects_changed,
object_changed_len,
+ NULL,
&(struct UVPackIsland_Params){
.rotate = true,
/* We could make this optional. */
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index c74fd60fe35..405deaf00b0 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -494,7 +494,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r
if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) {
// Create a blank render pass output.
RE_create_render_pass(
- re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname);
+ re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true);
}
return;
}
@@ -530,7 +530,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r
if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) {
RE_create_render_pass(
- re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname);
+ re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true);
dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_FREESTYLE, re->viewname);
}
else {
diff --git a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
index ab39b9ad883..bcb7af0e5a7 100644
--- a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
+++ b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
@@ -63,7 +63,7 @@ static char SmoothingShader___doc__[] =
"\n"
".. method:: shade(stroke)\n"
"\n"
- " Smoothes the stroke by moving the vertices to make the stroke\n"
+ " Smooths the stroke by moving the vertices to make the stroke\n"
" smoother. Uses curvature flow to converge towards a curve of\n"
" constant curvature. The diffusion method we use is anisotropic to\n"
" prevent the diffusion across corners.\n"
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 856668f01d7..309b92f1cb4 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -53,9 +53,9 @@ set(SRC
FN_multi_function_builder.hh
FN_multi_function_context.hh
FN_multi_function_data_type.hh
+ FN_multi_function_parallel.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
- FN_multi_function_parallel.hh
FN_multi_function_procedure.hh
FN_multi_function_procedure_builder.hh
FN_multi_function_procedure_executor.hh
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index d4375b625ce..3ce0993da59 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -484,4 +484,13 @@ template<typename T> Field<T> make_constant_field(T value)
GField make_field_constant_if_possible(GField field);
+class IndexFieldInput final : public FieldInput {
+ public:
+ IndexFieldInput();
+
+ const GVArray *get_varray_for_context(const FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const final;
+};
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 599e4d4595a..39688ef3daf 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -261,20 +261,6 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
}
/**
- * Utility class that destructs elements from a partially initialized array.
- */
-struct PartiallyInitializedArray : NonCopyable, NonMovable {
- void *buffer;
- IndexMask mask;
- const CPPType *type;
-
- ~PartiallyInitializedArray()
- {
- this->type->destruct_indices(this->buffer, this->mask);
- }
-};
-
-/**
* Evaluate fields in the given context. If possible, multiple fields should be evaluated together,
* because that can be more efficient when they share common sub-fields.
*
@@ -387,11 +373,11 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
/* Allocate a new buffer for the computed result. */
buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment());
- /* Make sure that elements in the buffer will be destructed. */
- PartiallyInitializedArray &destruct_helper = scope.construct<PartiallyInitializedArray>();
- destruct_helper.buffer = buffer;
- destruct_helper.mask = mask;
- destruct_helper.type = &type;
+ if (!type.is_trivially_destructible()) {
+ /* Destruct values in the end. */
+ scope.add_destruct_call(
+ [buffer, mask, &type]() { type.destruct_indices(buffer, mask); });
+ }
r_varrays[out_index] = &scope.construct<GVArray_For_GSpan>(
GSpan{type, buffer, array_size});
@@ -418,7 +404,10 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
build_multi_function_procedure_for_fields(
procedure, scope, field_tree_info, constant_fields_to_evaluate);
MFProcedureExecutor procedure_executor{"Procedure", procedure};
- MFParamsBuilder mf_params{procedure_executor, 1};
+ /* Run the code below even when the mask is empty, so that outputs are properly prepared.
+ * Higher level code can detect this as well and just skip evaluating the field. */
+ const int mask_size = mask.is_empty() ? 0 : 1;
+ MFParamsBuilder mf_params{procedure_executor, mask_size};
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
@@ -432,14 +421,14 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
/* Allocate memory where the computed value will be stored in. */
void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment());
- /* Use this to make sure that the value is destructed in the end. */
- PartiallyInitializedArray &destruct_helper = scope.construct<PartiallyInitializedArray>();
- destruct_helper.buffer = buffer;
- destruct_helper.mask = IndexRange(1);
- destruct_helper.type = &type;
+ if (!type.is_trivially_destructible() && mask_size > 0) {
+ BLI_assert(mask_size == 1);
+ /* Destruct value in the end. */
+ scope.add_destruct_call([buffer, &type]() { type.destruct(buffer); });
+ }
/* Pass output buffer to the procedure executor. */
- mf_params.add_uninitialized_single_output({type, buffer, 1});
+ mf_params.add_uninitialized_single_output({type, buffer, mask_size});
/* Create virtual array that can be used after the procedure has been executed below. */
const int out_index = constant_field_indices[i];
@@ -447,7 +436,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
type, array_size, buffer);
}
- procedure_executor.call(IndexRange(1), mf_params, mf_context);
+ procedure_executor.call(IndexRange(mask_size), mf_params, mf_context);
}
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has
@@ -526,6 +515,21 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
return field_input.get_varray_for_context(*this, mask, scope);
}
+IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
+{
+}
+
+const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context),
+ IndexMask mask,
+ ResourceScope &scope) const
+{
+ /* TODO: Investigate a similar method to IndexRange::as_span() */
+ auto index_func = [](int i) { return i; };
+ return &scope.construct<
+ fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
+ mask.min_array_size(), mask.min_array_size(), index_func);
+}
+
/* --------------------------------------------------------------------
* FieldOperation.
*/
diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc
index b97282accdd..6d2d121bafd 100644
--- a/source/blender/functions/intern/multi_function_procedure_executor.cc
+++ b/source/blender/functions/intern/multi_function_procedure_executor.cc
@@ -1022,7 +1022,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction,
}
}
- fn.call(IndexRange(1), params, context);
+ try {
+ fn.call(IndexRange(1), params, context);
+ }
+ catch (...) {
+ /* Multi-functions must not throw exceptions. */
+ BLI_assert_unreachable();
+ }
}
else {
MFParamsBuilder params(fn, &mask);
@@ -1038,7 +1044,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction,
}
}
- fn.call(mask, params, context);
+ try {
+ fn.call(mask, params, context);
+ }
+ catch (...) {
+ /* Multi-functions must not throw exceptions. */
+ BLI_assert_unreachable();
+ }
}
}
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index eb1f61b1862..afcd551d0af 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -69,8 +69,8 @@ set(SRC
intern/MOD_gpencilthick.c
intern/MOD_gpenciltime.c
intern/MOD_gpenciltint.c
- intern/MOD_gpencilweight_proximity.c
intern/MOD_gpencilweight_angle.c
+ intern/MOD_gpencilweight_proximity.c
MOD_gpencil_lineart.h
MOD_gpencil_modifiertypes.h
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 01488a8b2de..c5ccf1d8229 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -587,6 +587,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
is_geom ? IFACE_("Geometry Threshold") : NULL,
ICON_NONE);
+ uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
index 0885828a3a0..4079485de8d 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
@@ -83,13 +83,13 @@ static float calc_point_weight_by_distance(Object *ob,
float dist = len_v3v3(mmd->object->obmat[3], gvert);
if (dist > dist_max) {
- weight = 0.0f;
+ weight = 1.0f;
}
else if (dist <= dist_max && dist > dist_min) {
- weight = (dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f);
+ weight = 1.0f - ((dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f));
}
else {
- weight = 1.0f;
+ weight = 0.0f;
}
return weight;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 134d9707ade..c00f34185dd 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -317,6 +317,8 @@ typedef struct LineartRenderBuffer {
float chaining_image_threshold;
float angle_splitting_threshold;
+ float chain_smooth_tolerance;
+
/* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that
* are not in the selected source. This will not be needed after we have a proper scene-wise
* cache running because multiple modifiers can then select results from that without further
@@ -592,6 +594,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
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);
+void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance);
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index d86253e7fe0..8935bdd1870 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -924,6 +924,34 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
}
}
+void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
+{
+ LISTBASE_FOREACH (LineartEdgeChain *, rlc, &rb->chains) {
+ LineartEdgeChainItem *next_eci;
+ for (LineartEdgeChainItem *eci = rlc->chain.first; eci; eci = next_eci) {
+ next_eci = eci->next;
+ LineartEdgeChainItem *eci2, *eci3, *eci4;
+
+ /* Not enough point to do simplify. */
+ if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) {
+ continue;
+ }
+
+ /* No need to care for different line types/occlusion and so on, because at this stage they
+ * are all the same within a chain. */
+
+ /* If p3 is within the p1-p2 segment of a width of "tolerance" */
+ if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) {
+ /* And if p4 is on the extension of p1-p2 , we remove p3. */
+ if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) {
+ BLI_remlink(&rlc->chain, eci3);
+ next_eci = eci;
+ }
+ }
+ }
+ }
+}
+
/**
* This should always be the last stage!, see the end of
* #MOD_lineart_chain_split_for_fixed_occlusion().
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 725cc0741f0..7441c9a909c 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -2168,6 +2168,12 @@ static void lineart_main_load_geometries(
use_mesh = use_ob->data;
}
else {
+ /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, the curve objects are going to have a mesh
+ * equivalent already in the object list, so ignore converting the original curve in this
+ * case. */
+ if (allow_duplicates) {
+ continue;
+ }
use_mesh = BKE_mesh_new_from_object(depsgraph, use_ob, true, true);
}
@@ -3056,8 +3062,9 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->shift_y /= (1 + rb->overscan);
rb->crease_threshold = cos(M_PI - lmd->crease_threshold);
- rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
rb->chaining_image_threshold = lmd->chaining_image_threshold;
+ rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
+ rb->chain_smooth_tolerance = lmd->chain_smooth_tolerance;
rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0;
rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0;
@@ -4172,6 +4179,13 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* This configuration ensures there won't be accidental lost of short unchained segments. */
MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON);
+ if (rb->chain_smooth_tolerance > FLT_EPSILON) {
+ /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
+ * effective range in image-space (Coordinate only goes from -1 to 1). This value is somewhat
+ * arbitrary, but works best for the moment. */
+ MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50);
+ }
+
if (rb->angle_splitting_threshold > FLT_EPSILON) {
MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index b7dc3210c41..7a072900473 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -268,8 +268,8 @@ data_to_c_simple(shaders/gpu_shader_2D_edituvs_stretch_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_text_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_keyframe_shape_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_keyframe_shape_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC)
@@ -296,6 +296,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_diffuse.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_displacement.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_eevee_specular.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_emission.glsl SRC)
+data_to_c_simple(shaders/material/gpu_shader_material_float_curve.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_fractal_noise.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_fresnel.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_gamma.glsl SRC)
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 62b748b7edf..c6cfac79699 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -169,7 +169,7 @@ void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear);
typedef enum eGPUBuiltinShader {
/* specialized drawing */
GPU_SHADER_TEXT,
- GPU_SHADER_KEYFRAME_DIAMOND,
+ GPU_SHADER_KEYFRAME_SHAPE,
GPU_SHADER_SIMPLE_LIGHTING,
/* for simple 2D drawing */
/**
@@ -423,6 +423,19 @@ void GPU_shader_free_builtin_shaders(void);
/* Determined by the maximum uniform buffer size divided by chunk size. */
#define GPU_MAX_UNIFORM_ATTR 8
+typedef enum eGPUKeyframeShapes {
+ GPU_KEYFRAME_SHAPE_DIAMOND = (1 << 0),
+ GPU_KEYFRAME_SHAPE_CIRCLE = (1 << 1),
+ GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL = (1 << 2),
+ GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL = (1 << 3),
+ GPU_KEYFRAME_SHAPE_INNER_DOT = (1 << 4),
+ GPU_KEYFRAME_SHAPE_ARROW_END_MAX = (1 << 8),
+ GPU_KEYFRAME_SHAPE_ARROW_END_MIN = (1 << 9),
+ GPU_KEYFRAME_SHAPE_ARROW_END_MIXED = (1 << 10),
+} eGPUKeyframeShapes;
+#define GPU_KEYFRAME_SHAPE_SQUARE \
+ (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index bb1ebc0e85d..f0046e879a0 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -656,6 +656,8 @@ static const char *attr_prefix_get(CustomDataType type)
return "c";
case CD_AUTO_FROM_NAME:
return "a";
+ case CD_HAIRLENGTH:
+ return "hl";
default:
BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
return "";
@@ -675,7 +677,12 @@ static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
BLI_dynstr_append(ds, "\n");
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_appendf(ds, "float var%d;\n", attr->id);
+ }
+ else {
+ BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ }
}
if (builtins & GPU_BARYCENTRIC_TEXCO) {
BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
@@ -711,6 +718,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
}
+ else if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
+ BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n");
+ }
else if (attr->name[0] == '\0') {
BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
@@ -755,6 +766,9 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_appendf(
ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
}
+ else if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id);
+ }
else {
const char *type_str = gpu_data_type_to_string(attr->gputype);
BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 73a80c62bdc..74e0270c42a 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -62,6 +62,7 @@ extern char datatoc_gpu_shader_material_diffuse_glsl[];
extern char datatoc_gpu_shader_material_displacement_glsl[];
extern char datatoc_gpu_shader_material_eevee_specular_glsl[];
extern char datatoc_gpu_shader_material_emission_glsl[];
+extern char datatoc_gpu_shader_material_float_curve_glsl[];
extern char datatoc_gpu_shader_material_fractal_noise_glsl[];
extern char datatoc_gpu_shader_material_fresnel_glsl[];
extern char datatoc_gpu_shader_material_gamma_glsl[];
@@ -262,6 +263,11 @@ static GPUMaterialLibrary gpu_shader_material_emission_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_float_curve_library = {
+ .code = datatoc_gpu_shader_material_float_curve_glsl,
+ .dependencies = {NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_fresnel_library = {
.code = datatoc_gpu_shader_material_fresnel_glsl,
.dependencies = {NULL},
@@ -591,6 +597,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_color_util_library,
&gpu_shader_material_hash_library,
&gpu_shader_material_noise_library,
+ &gpu_shader_material_float_curve_library,
&gpu_shader_material_fractal_noise_library,
&gpu_shader_material_add_shader_library,
&gpu_shader_material_ambient_occlusion_library,
diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c
index 88b704a84a1..661c462f60d 100644
--- a/source/blender/gpu/intern/gpu_select.c
+++ b/source/blender/gpu/intern/gpu_select.c
@@ -20,8 +20,8 @@
/** \file
* \ingroup gpu
*
- * Interface for accessing gpu-related methods for selection. The semantics are
- * similar to glRenderMode(GL_SELECT) from older OpenGL versions.
+ * Interface for accessing GPU-related methods for selection. The semantics are
+ * similar to `glRenderMode(GL_SELECT)` from older OpenGL versions.
*/
#include <stdlib.h>
#include <string.h>
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc
index 7b9b3020639..047ce0cfb35 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.cc
+++ b/source/blender/gpu/intern/gpu_select_sample_query.cc
@@ -20,8 +20,8 @@
/** \file
* \ingroup gpu
*
- * Interface for accessing gpu-related methods for selection. The semantics will be
- * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility.
+ * Interface for accessing GPU-related methods for selection. The semantics will be
+ * similar to `glRenderMode(GL_SELECT)` since the goal is to maintain compatibility.
*/
#include <cstdlib>
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index c5122b76001..9ea46788f44 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -121,8 +121,8 @@ extern char datatoc_gpu_shader_3D_line_dashed_uniform_color_vert_glsl[];
extern char datatoc_gpu_shader_text_vert_glsl[];
extern char datatoc_gpu_shader_text_frag_glsl[];
-extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[];
-extern char datatoc_gpu_shader_keyframe_diamond_frag_glsl[];
+extern char datatoc_gpu_shader_keyframe_shape_vert_glsl[];
+extern char datatoc_gpu_shader_keyframe_shape_frag_glsl[];
extern char datatoc_gpu_shader_gpencil_stroke_vert_glsl[];
extern char datatoc_gpu_shader_gpencil_stroke_frag_glsl[];
@@ -166,11 +166,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.vert = datatoc_gpu_shader_text_vert_glsl,
.frag = datatoc_gpu_shader_text_frag_glsl,
},
- [GPU_SHADER_KEYFRAME_DIAMOND] =
+ [GPU_SHADER_KEYFRAME_SHAPE] =
{
- .name = "GPU_SHADER_KEYFRAME_DIAMOND",
- .vert = datatoc_gpu_shader_keyframe_diamond_vert_glsl,
- .frag = datatoc_gpu_shader_keyframe_diamond_frag_glsl,
+ .name = "GPU_SHADER_KEYFRAME_SHAPE",
+ .vert = datatoc_gpu_shader_keyframe_shape_vert_glsl,
+ .frag = datatoc_gpu_shader_keyframe_shape_frag_glsl,
},
[GPU_SHADER_SIMPLE_LIGHTING] =
{
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index d5d13ea269f..2744c0c5e17 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -461,7 +461,7 @@ void GPU_texture_generate_mipmap(GPUTexture *tex)
reinterpret_cast<Texture *>(tex)->generate_mipmap();
}
-/* Copy a texture content to a similar texture. Only Mip 0 is copied. */
+/* Copy a texture content to a similar texture. Only MIP 0 is copied. */
void GPU_texture_copy(GPUTexture *dst_, GPUTexture *src_)
{
Texture *src = reinterpret_cast<Texture *>(src_);
diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc
index 1106e3dab50..d737cf88a13 100644
--- a/source/blender/gpu/opengl/gl_state.cc
+++ b/source/blender/gpu/opengl/gl_state.cc
@@ -52,6 +52,7 @@ GLStateManager::GLStateManager()
glDisable(GL_DITHER);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPrimitiveRestartIndex((GLuint)0xFFFFFFFF);
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
index f07bd7f1d6f..55d5d941290 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
@@ -1,11 +1,41 @@
in float colorGradient;
in vec4 finalColor;
+in float lineU;
+flat in float lineLength;
+flat in float dashFactor;
+flat in int isMainLine;
out vec4 fragColor;
+#define DASH_WIDTH 20.0
+#define ANTIALIAS 1.0
+
void main()
{
fragColor = finalColor;
+
+ if ((isMainLine != 0) && (dashFactor < 1.0)) {
+ float distance_along_line = lineLength * lineU;
+ float normalized_distance = fract(distance_along_line / DASH_WIDTH);
+
+ /* Checking if `normalized_distance <= dashFactor` is already enough for a basic
+ * dash, however we want to handle a nice antialias. */
+
+ float dash_center = DASH_WIDTH * dashFactor * 0.5;
+ float normalized_distance_triangle =
+ 1.0 - abs((fract((distance_along_line - dash_center) / DASH_WIDTH)) * 2.0 - 1.0);
+ float t = ANTIALIAS / DASH_WIDTH;
+ float slope = 1.0 / (2.0 * t);
+
+ float alpha = min(1.0, max(0.0, slope * (normalized_distance_triangle - dashFactor + t)));
+
+ if (alpha < 0.0) {
+ discard;
+ }
+
+ fragColor.a *= 1.0 - alpha;
+ }
+
fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient));
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
index aae7f641af8..8f46c8eda4b 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
@@ -19,6 +19,8 @@ in vec2 P3;
in ivec4 colid_doarrow;
in ivec2 domuted;
in float dim_factor;
+in float thickness;
+in float dash_factor;
uniform vec4 colors[6];
@@ -41,6 +43,8 @@ uniform vec4 colors[3];
uniform bool doArrow;
uniform bool doMuted;
uniform float dim_factor;
+uniform float thickness;
+uniform float dash_factor;
# define colShadow colors[0]
# define colStart colors[1]
@@ -54,9 +58,21 @@ uniform mat4 ModelViewProjectionMatrix;
out float colorGradient;
out vec4 finalColor;
+out float lineU;
+flat out float lineLength;
+flat out float dashFactor;
+flat out int isMainLine;
void main(void)
{
+ /* Parameters for the dashed line. */
+ isMainLine = expand.y != 1.0 ? 0 : 1;
+ dashFactor = dash_factor;
+ /* Approximate line length, no need for real bezier length calculation. */
+ lineLength = distance(P0, P3);
+ /* TODO: Incorrect U, this leads to non-uniform dash distribution. */
+ lineU = uv.x;
+
float t = uv.x;
float t2 = t * t;
float t2_3 = 3.0 * t2;
@@ -103,7 +119,7 @@ void main(void)
finalColor[3] *= dim_factor;
/* Expand into a line */
- gl_Position.xy += exp_axis * expandSize * expand_dist;
+ gl_Position.xy += exp_axis * expandSize * expand_dist * thickness;
/* If the link is not muted or is not a reroute arrow the points are squashed to the center of
* the line. Magic numbers are defined in drawnode.c */
diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
index f7bf3d33361..193a4190cbf 100644
--- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -42,6 +42,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const sampler
return orco_madd[0].xyz + orco * orco_madd[1].xyz;
}
+float hair_len_get(int id, const samplerBuffer len)
+{
+ return texelFetch(len, id).x;
+}
+
vec4 tangent_get(const samplerBuffer attr, mat3 normalmat)
{
/* Unsupported */
@@ -71,6 +76,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco)
}
}
+float hair_len_get(int id, const float len)
+{
+ return len;
+}
+
vec4 tangent_get(vec4 attr, mat3 normalmat)
{
vec4 tangent;
diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl
index 1c4039bc590..a3b61dca8b4 100644
--- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl
@@ -1,3 +1,16 @@
+
+/* Values in GPU_shader.h. */
+#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0)
+#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1)
+#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2)
+#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3)
+#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10)
+#define GPU_KEYFRAME_SHAPE_SQUARE \
+ (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)
+
flat in vec4 radii;
flat in vec4 thresholds;
@@ -27,24 +40,24 @@ void main()
float outline_dist = -1.0;
/* Diamond outline */
- if (test(0x1)) {
+ if (test(GPU_KEYFRAME_SHAPE_DIAMOND)) {
outline_dist = max(outline_dist, radius - radii[0]);
}
/* Circle outline */
- if (test(0x2)) {
+ if (test(GPU_KEYFRAME_SHAPE_CIRCLE)) {
radius = length(absPos);
outline_dist = max(outline_dist, radius - radii[1]);
}
/* Top & Bottom clamp */
- if (test(0x4)) {
+ if (test(GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL)) {
outline_dist = max(outline_dist, absPos.y - radii[2]);
}
/* Left & Right clamp */
- if (test(0x8)) {
+ if (test(GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) {
outline_dist = max(outline_dist, absPos.x - radii[2]);
}
@@ -53,20 +66,20 @@ void main()
/* Inside the outline. */
if (outline_dist < 0) {
/* Middle dot */
- if (test(0x10)) {
- alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius));
+ if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) {
+ alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], length(absPos)));
}
/* Up and down arrow-like shading. */
- if (test(0x300)) {
+ if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX | GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) {
float ypos = -1.0;
/* Up arrow (maximum) */
- if (test(0x100)) {
+ if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX)) {
ypos = max(ypos, pos.y);
}
/* Down arrow (minimum) */
- if (test(0x200)) {
+ if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) {
ypos = max(ypos, -pos.y);
}
@@ -75,7 +88,7 @@ void main()
float minmax_step = smoothstep(thresholds[0], thresholds[1], minmax_dist * minmax_scale);
/* Reduced alpha for uncertain extremes. */
- float minmax_alpha = test(0x400) ? 0.55 : 0.85;
+ float minmax_alpha = test(GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) ? 0.55 : 0.85;
alpha = max(alpha, minmax_step * minmax_alpha);
}
diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl
index 2ba89230d80..18e8b76ba23 100644
--- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl
@@ -1,4 +1,16 @@
+/* Values in GPU_shader.h. */
+#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0)
+#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1)
+#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2)
+#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3)
+#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9)
+#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10)
+#define GPU_KEYFRAME_SHAPE_SQUARE \
+ (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)
+
uniform mat4 ModelViewProjectionMatrix;
uniform vec2 ViewportSize = vec2(-1, -1);
uniform float outline_scale = 1.0;
@@ -49,8 +61,9 @@ void main()
finalOutlineColor = outlineColor;
finalFlags = flags;
- if (!test(0xF)) {
- finalFlags |= 1;
+ if (!test(GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CIRCLE |
+ GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) {
+ finalFlags |= GPU_KEYFRAME_SHAPE_DIAMOND;
}
/* Size-dependent line thickness. */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl
new file mode 100644
index 00000000000..514409f7fdf
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl
@@ -0,0 +1,33 @@
+/* ext is vec4(in_x, in_dy, out_x, out_dy). */
+float curve_float_extrapolate(float x, float y, vec4 ext)
+{
+ if (x < 0.0) {
+ return y + x * ext.y;
+ }
+ else if (x > 1.0) {
+ return y + (x - 1.0) * ext.w;
+ }
+ else {
+ return y;
+ }
+}
+
+#define RANGE_RESCALE(x, min, range) ((x - min) * range)
+
+void curve_float(float fac,
+ float vec,
+ sampler1DArray curvemap,
+ float layer,
+ float range,
+ vec4 ext,
+ out float outvec)
+{
+ float xyz_min = ext.x;
+ vec = RANGE_RESCALE(vec, xyz_min, range);
+
+ outvec = texture(curvemap, vec2(vec, layer)).x;
+
+ outvec = curve_float_extrapolate(vec, outvec, ext);
+
+ outvec = mix(vec, outvec, fac);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 6330daa4391..6ffa6b59572 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -10,12 +10,15 @@ float wang_hash_noise(uint s)
return fract(float(s) / 4294967296.0);
}
-void node_hair_info(out float is_strand,
+void node_hair_info(float hair_length,
+ out float is_strand,
out float intercept,
+ out float length,
out float thickness,
out vec3 tangent,
out float random)
{
+ length = hair_length;
#ifdef HAIR_SHADER
is_strand = 1.0;
intercept = hairTime;
diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
index f0061a6bf5c..523a7e5b881 100644
--- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc
+++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
@@ -32,7 +32,7 @@ static void test_shader_builtin()
test_compile_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
test_compile_builtin_shader(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_KEYFRAME_DIAMOND, GPU_SHADER_CFG_DEFAULT);
+ test_compile_builtin_shader(GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT);
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index b9411f6dd2d..a9e1692ebf0 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -644,7 +644,7 @@ static bool base_callback(const iTaSC::Timestamp &timestamp,
ikscene->baseFrame = iTaSC::F_identity;
}
next.setValue(&rootmat[0][0]);
- /* if there is a polar target (only during solving otherwise we don't have end efffector) */
+ /* If there is a polar target (only during solving otherwise we don't have end effector). */
if (ikscene->polarConstraint && timestamp.update) {
/* compute additional rotation of base frame so that armature follows the polar target */
float imat[4][4]; /* IK tree base inverse matrix */
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
index 37309ccc13a..6c66b11df4f 100644
--- a/source/blender/imbuf/intern/IMB_indexer.h
+++ b/source/blender/imbuf/intern/IMB_indexer.h
@@ -33,7 +33,7 @@
* a) different time-codes within one file (like DTS/PTS, Time-code-Track,
* "implicit" time-codes within DV-files and HDV-files etc.)
* b) seeking difficulties within FFMPEG for files with timestamp holes
- * c) broken files that miss several frames / have varying framerates
+ * c) broken files that miss several frames / have varying frame-rates
* d) use proxies accordingly
*
* ... we need index files, that provide us with
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index cd323e72003..adf09f8dda8 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -555,7 +555,7 @@ static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flag
int xstride = sizeof(float) * channels;
int ystride = -xstride * width;
- /* last scanline, stride negative */
+ /* Last scan-line, stride negative. */
float *rect[4] = {nullptr, nullptr, nullptr, nullptr};
rect[0] = ibuf->rect_float + channels * (height - 1) * width;
rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
@@ -654,7 +654,7 @@ struct ExrChannel {
char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */
struct MultiViewChannelName *m; /* struct to store all multipart channel info */
- int xstride, ystride; /* step to next pixel, to next scanline */
+ int xstride, ystride; /* step to next pixel, to next scan-line. */
float *rect; /* first pointer to write in */
char chan_id; /* quick lookup of channel char */
int view_id; /* quick lookup of channel view */
@@ -681,6 +681,8 @@ struct ExrLayer {
ListBase passes;
};
+static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data);
+
/* ********************** */
void *IMB_exr_get_handle(void)
@@ -839,12 +841,12 @@ void IMB_exr_add_channel(void *handle,
}
/* used for output files (from RenderResult) (single and multilayer, single and multiview) */
-int IMB_exr_begin_write(void *handle,
- const char *filename,
- int width,
- int height,
- int compress,
- const StampData *stamp)
+bool IMB_exr_begin_write(void *handle,
+ const char *filename,
+ int width,
+ int height,
+ int compress,
+ const StampData *stamp)
{
ExrHandle *data = (ExrHandle *)handle;
Header header(width, height);
@@ -962,51 +964,64 @@ void IMB_exrtile_begin_write(
}
/* read from file */
-int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
+bool IMB_exr_begin_read(
+ void *handle, const char *filename, int *width, int *height, const bool parse_channels)
{
ExrHandle *data = (ExrHandle *)handle;
ExrChannel *echan;
/* 32 is arbitrary, but zero length files crashes exr. */
- if (BLI_exists(filename) && BLI_file_size(filename) > 32) {
- /* avoid crash/abort when we don't have permission to write here */
- try {
- data->ifile_stream = new IFileStream(filename);
- data->ifile = new MultiPartInputFile(*(data->ifile_stream));
- }
- catch (const std::exception &) {
- delete data->ifile;
- delete data->ifile_stream;
+ if (!(BLI_exists(filename) && BLI_file_size(filename) > 32)) {
+ return false;
+ }
- data->ifile = nullptr;
- data->ifile_stream = nullptr;
- }
+ /* avoid crash/abort when we don't have permission to write here */
+ try {
+ data->ifile_stream = new IFileStream(filename);
+ data->ifile = new MultiPartInputFile(*(data->ifile_stream));
+ }
+ catch (const std::exception &) {
+ delete data->ifile;
+ delete data->ifile_stream;
- if (data->ifile) {
- Box2i dw = data->ifile->header(0).dataWindow();
- data->width = *width = dw.max.x - dw.min.x + 1;
- data->height = *height = dw.max.y - dw.min.y + 1;
+ data->ifile = nullptr;
+ data->ifile_stream = nullptr;
+ }
- imb_exr_get_views(*data->ifile, *data->multiView);
+ if (!data->ifile) {
+ return false;
+ }
- std::vector<MultiViewChannelName> channels;
- GetChannelsInMultiPartFile(*data->ifile, channels);
+ Box2i dw = data->ifile->header(0).dataWindow();
+ data->width = *width = dw.max.x - dw.min.x + 1;
+ data->height = *height = dw.max.y - dw.min.y + 1;
- for (const MultiViewChannelName &channel : channels) {
- IMB_exr_add_channel(
- data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
+ if (parse_channels) {
+ /* Parse channels into view/layer/pass. */
+ if (!imb_exr_multilayer_parse_channels_from_file(data)) {
+ return false;
+ }
+ }
+ else {
+ /* Read view and channels without parsing. */
+ imb_exr_get_views(*data->ifile, *data->multiView);
- echan = (ExrChannel *)data->channels.last;
- echan->m->name = channel.name;
- echan->m->view = channel.view;
- echan->m->part_number = channel.part_number;
- echan->m->internal_name = channel.internal_name;
- }
+ std::vector<MultiViewChannelName> channels;
+ GetChannelsInMultiPartFile(*data->ifile, channels);
+
+ for (const MultiViewChannelName &channel : channels) {
+ IMB_exr_add_channel(
+ data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
- return 1;
+ echan = (ExrChannel *)data->channels.last;
+ echan->m->name = channel.name;
+ echan->m->view = channel.view;
+ echan->m->part_number = channel.part_number;
+ echan->m->internal_name = channel.internal_name;
}
}
- return 0;
+
+ return true;
}
/* still clumsy name handling, layers/channels can be ordered as list in list later */
@@ -1112,7 +1127,7 @@ void IMB_exr_write_channels(void *handle)
}
for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
- /* Writing starts from last scanline, stride negative. */
+ /* Writing starts from last scan-line, stride negative. */
if (echan->use_half_float) {
float *rect = echan->rect;
half *cur = current_rect_half;
@@ -1254,7 +1269,7 @@ void IMB_exr_read_channels(void *handle)
if (!flip) {
/* Inverse correct first pixel for data-window coordinates. */
rect -= echan->xstride * (dw.min.x - dw.min.y * data->width);
- /* move to last scanline to flip to Blender convention */
+ /* Move to last scan-line to flip to Blender convention. */
rect += echan->xstride * (data->height - 1) * data->width;
ystride = -ystride;
}
@@ -1524,25 +1539,8 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
return pass;
}
-/* creates channels, makes a hierarchy and assigns memory to channels */
-static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
- MultiPartInputFile &file,
- int width,
- int height)
+static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
{
- ExrLayer *lay;
- ExrPass *pass;
- ExrChannel *echan;
- ExrHandle *data = (ExrHandle *)IMB_exr_get_handle();
- int a;
- char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
-
- data->ifile_stream = &file_stream;
- data->ifile = &file;
-
- data->width = width;
- data->height = height;
-
std::vector<MultiViewChannelName> channels;
GetChannelsInMultiPartFile(*data->ifile, channels);
@@ -1552,7 +1550,7 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
IMB_exr_add_channel(
data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
- echan = (ExrChannel *)data->channels.last;
+ ExrChannel *echan = (ExrChannel *)data->channels.last;
echan->m->name = channel.name;
echan->m->view = channel.view;
echan->m->part_number = channel.part_number;
@@ -1561,7 +1559,9 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
/* now try to sort out how to assign memory to the channels */
/* first build hierarchical layer list */
- for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
+ ExrChannel *echan = (ExrChannel *)data->channels.first;
+ for (; echan; echan = echan->next) {
+ char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
if (imb_exr_split_channel_name(echan, layname, passname)) {
const char *view = echan->m->view.c_str();
@@ -1591,21 +1591,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
}
if (echan) {
printf("error, too many channels in one pass: %s\n", echan->m->name.c_str());
- IMB_exr_close(data);
- return nullptr;
+ return false;
}
/* with some heuristics, try to merge the channels in buffers */
- for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
- for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
+ for (ExrLayer *lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
+ for (ExrPass *pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
if (pass->totchan) {
- pass->rect = (float *)MEM_callocN(width * height * pass->totchan * sizeof(float),
- "pass rect");
+ pass->rect = (float *)MEM_callocN(
+ data->width * data->height * pass->totchan * sizeof(float), "pass rect");
if (pass->totchan == 1) {
- echan = pass->chan[0];
+ ExrChannel *echan = pass->chan[0];
echan->rect = pass->rect;
echan->xstride = 1;
- echan->ystride = width;
+ echan->ystride = data->width;
pass->chan_id[0] = echan->chan_id;
}
else {
@@ -1634,20 +1633,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
lookup[(unsigned int)'V'] = 1;
lookup[(unsigned int)'A'] = 2;
}
- for (a = 0; a < pass->totchan; a++) {
+ for (int a = 0; a < pass->totchan; a++) {
echan = pass->chan[a];
echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
echan->xstride = pass->totchan;
- echan->ystride = width * pass->totchan;
+ echan->ystride = data->width * pass->totchan;
pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
}
}
else { /* unknown */
- for (a = 0; a < pass->totchan; a++) {
- echan = pass->chan[a];
+ for (int a = 0; a < pass->totchan; a++) {
+ ExrChannel *echan = pass->chan[a];
echan->rect = pass->rect + a;
echan->xstride = pass->totchan;
- echan->ystride = width * pass->totchan;
+ echan->ystride = data->width * pass->totchan;
pass->chan_id[a] = echan->chan_id;
}
}
@@ -1656,6 +1655,28 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
}
}
+ return true;
+}
+
+/* creates channels, makes a hierarchy and assigns memory to channels */
+static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
+ MultiPartInputFile &file,
+ int width,
+ int height)
+{
+ ExrHandle *data = (ExrHandle *)IMB_exr_get_handle();
+
+ data->ifile_stream = &file_stream;
+ data->ifile = &file;
+
+ data->width = width;
+ data->height = height;
+
+ if (!imb_exr_multilayer_parse_channels_from_file(data)) {
+ IMB_exr_close(data);
+ return nullptr;
+ }
+
return data;
}
@@ -1988,7 +2009,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
/* Inverse correct first pixel for data-window
* coordinates (- dw.min.y because of y flip). */
first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
- /* but, since we read y-flipped (negative y stride) we move to last scanline */
+ /* But, since we read y-flipped (negative y stride) we move to last scan-line. */
first += 4 * (height - 1) * width;
if (num_rgb_channels > 0) {
diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h
index 556717ad618..82a5d161ded 100644
--- a/source/blender/imbuf/intern/openexr/openexr_multi.h
+++ b/source/blender/imbuf/intern/openexr/openexr_multi.h
@@ -50,13 +50,14 @@ void IMB_exr_add_channel(void *handle,
float *rect,
bool use_half_float);
-int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height);
-int IMB_exr_begin_write(void *handle,
- const char *filename,
- int width,
- int height,
- int compress,
- const struct StampData *stamp);
+bool IMB_exr_begin_read(
+ void *handle, const char *filename, int *width, int *height, const bool parse_channels);
+bool IMB_exr_begin_write(void *handle,
+ const char *filename,
+ int width,
+ int height,
+ int compress,
+ const struct StampData *stamp);
void IMB_exrtile_begin_write(
void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley);
diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp
index 51bc2094053..9b4d6178613 100644
--- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp
@@ -43,21 +43,22 @@ void IMB_exr_add_channel(void * /*handle*/,
{
}
-int IMB_exr_begin_read(void * /*handle*/,
- const char * /*filename*/,
- int * /*width*/,
- int * /*height*/)
+bool IMB_exr_begin_read(void * /*handle*/,
+ const char * /*filename*/,
+ int * /*width*/,
+ int * /*height*/,
+ const bool /*add_channels*/)
{
- return 0;
+ return false;
}
-int IMB_exr_begin_write(void * /*handle*/,
- const char * /*filename*/,
- int /*width*/,
- int /*height*/,
- int /*compress*/,
- const struct StampData * /*stamp*/)
+bool IMB_exr_begin_write(void * /*handle*/,
+ const char * /*filename*/,
+ int /*width*/,
+ int /*height*/,
+ int /*compress*/,
+ const struct StampData * /*stamp*/)
{
- return 0;
+ return false;
}
void IMB_exrtile_begin_write(void * /*handle*/,
const char * /*filename*/,
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index 94b2a62aa26..7f4e4dd31df 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -294,7 +294,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
break;
}
for (x = 0; x < width; x++) {
- /* convert to ldr */
+ /* Convert to LDR. */
RGBE2FLOAT(sline[x], fcol);
*rect_float++ = fcol[RED];
*rect_float++ = fcol[GRN];
@@ -328,7 +328,7 @@ static int fwritecolrs(
rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
- /* convert scanline */
+ /* Convert scan-line. */
for (size_t i = 0, j = 0; i < width; i++) {
if (fpscan) {
fcol[RED] = fpscan[j];
diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c
index 50210650f05..c75bdfa375c 100644
--- a/source/blender/imbuf/intern/readimage.c
+++ b/source/blender/imbuf/intern/readimage.c
@@ -126,7 +126,7 @@ ImBuf *IMB_ibImageFromMemory(const unsigned char *mem,
}
if ((flags & IB_test) == 0) {
- fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr);
+ fprintf(stderr, "%s: unknown file-format (%s)\n", __func__, descr);
}
return NULL;
diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c
index c2fc2190ce5..83dc29aa107 100644
--- a/source/blender/imbuf/intern/rotate.c
+++ b/source/blender/imbuf/intern/rotate.c
@@ -69,7 +69,7 @@ void IMB_flipy(struct ImBuf *ibuf)
topf = ibuf->rect_float;
bottomf = topf + 4 * ((y - 1) * x);
- linef = MEM_mallocN(4 * x * sizeof(float), "linebuff");
+ linef = MEM_mallocN(4 * x * sizeof(float), "linebuf");
y >>= 1;
diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c
index 8ed0b8b535c..333e29e6d97 100644
--- a/source/blender/imbuf/intern/targa.c
+++ b/source/blender/imbuf/intern/targa.c
@@ -192,7 +192,7 @@ static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(unsigned int, FILE
else {
while (*rect++ == this) { /* seek for first different byte */
if (--bytes == 0) {
- break; /* oor end of line */
+ break; /* Or end of line. */
}
}
rect--;
@@ -470,7 +470,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem
if (psize & 2) {
if (psize & 1) {
- /* order = bgra */
+ /* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
@@ -512,7 +512,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem
while (count > 0) {
if (psize & 2) {
if (psize & 1) {
- /* order = bgra */
+ /* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
@@ -589,7 +589,7 @@ static void ldtarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_siz
if (psize & 2) {
if (psize & 1) {
- /* order = bgra */
+ /* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index e3ed897458d..087d60f8896 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -247,13 +247,13 @@ void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &
coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]);
}
- if (!config.abc_ocro.valid()) {
+ if (!config.abc_orco.valid()) {
/* Create the Alembic property and keep a reference so future frames can reuse it. */
- config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
+ config.abc_orco = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
}
OV3fGeomParam::Sample sample(coords, kVertexScope);
- config.abc_ocro.set(sample);
+ config.abc_orco.set(sample);
}
void write_custom_data(const OCompoundProperty &prop,
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 4fba6a2f0f7..03e6f697f0c 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -79,8 +79,8 @@ struct CDStreamConfig {
* UV map is kept alive by the Alembic mesh sample itself. */
std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps;
- /* OCRO coordinates, aka Generated Coordinates. */
- Alembic::AbcGeom::OV3fGeomParam abc_ocro;
+ /* ORCO coordinates, aka Generated Coordinates. */
+ Alembic::AbcGeom::OV3fGeomParam abc_orco;
CDStreamConfig()
: mloop(NULL),
@@ -122,12 +122,6 @@ void read_custom_data(const std::string &iobject_full_name,
const CDStreamConfig &config,
const Alembic::Abc::ISampleSelector &iss);
-void read_velocity(const Alembic::Abc::ICompoundProperty &prop,
- const Alembic::Abc::PropertyHeader *prop_header,
- const Alembic::Abc::ISampleSelector &selector,
- const CDStreamConfig &config,
- const char *velocity_name,
- const float velocity_scale);
typedef enum {
ABC_UV_SCOPE_NONE,
ABC_UV_SCOPE_LOOP,
diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h
index 408e9623970..ca8dee80c9d 100644
--- a/source/blender/io/alembic/intern/abc_reader_camera.h
+++ b/source/blender/io/alembic/intern/abc_reader_camera.h
@@ -23,18 +23,18 @@
namespace blender::io::alembic {
-class AbcCameraReader : public AbcObjectReader {
+class AbcCameraReader final : public AbcObjectReader {
Alembic::AbcGeom::ICameraSchema m_schema;
public:
AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
- const char **err_str) const;
+ const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h
index 11b23c8a8cf..df5d68d7850 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.h
+++ b/source/blender/io/alembic/intern/abc_reader_curves.h
@@ -31,24 +31,24 @@ struct Curve;
namespace blender::io::alembic {
-class AbcCurveReader : public AbcObjectReader {
+class AbcCurveReader final : public AbcObjectReader {
Alembic::AbcGeom::ICurvesSchema m_curves_schema;
public:
AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
- const char **err_str) const;
+ const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
struct Mesh *read_mesh(struct Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel,
const int read_flag,
const char *velocity_name,
const float velocity_scale,
- const char **err_str);
+ const char **err_str) override;
void read_curve_sample(Curve *cu,
const Alembic::AbcGeom::ICurvesSchema &schema,
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h
index d9f89cc8085..2e34ca8ded0 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.h
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.h
@@ -26,7 +26,7 @@ struct Mesh;
namespace blender::io::alembic {
-class AbcMeshReader : public AbcObjectReader {
+class AbcMeshReader final : public AbcObjectReader {
Alembic::AbcGeom::IPolyMeshSchema m_schema;
CDStreamConfig m_mesh_data;
@@ -60,7 +60,7 @@ class AbcMeshReader : public AbcObjectReader {
std::map<std::string, int> &r_mat_map);
};
-class AbcSubDReader : public AbcObjectReader {
+class AbcSubDReader final : public AbcObjectReader {
Alembic::AbcGeom::ISubDSchema m_schema;
CDStreamConfig m_mesh_data;
@@ -68,17 +68,17 @@ class AbcSubDReader : public AbcObjectReader {
public:
AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
- const char **err_str) const;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ const char **err_str) const override;
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
struct Mesh *read_mesh(struct Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel,
const int read_flag,
const char *velocity_name,
const float velocity_scale,
- const char **err_str);
+ const char **err_str) override;
};
void read_mverts(MVert *mverts,
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
index 25567aa8c24..4492d1e1ca0 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
@@ -71,6 +71,26 @@ bool AbcNurbsReader::valid() const
return true;
}
+bool AbcNurbsReader::accepts_object_type(
+ const Alembic::AbcCoreAbstract::v12::ObjectHeader &alembic_header,
+ const Object *const ob,
+ const char **err_str) const
+{
+ if (!Alembic::AbcGeom::INuPatch::matches(alembic_header)) {
+ *err_str =
+ "Object type mismatch, Alembic object path pointed to NURBS when importing, but not any "
+ "more.";
+ return false;
+ }
+
+ if (ob->type != OB_CURVE) {
+ *err_str = "Object type mismatch, Alembic object path points to NURBS.";
+ return false;
+ }
+
+ return true;
+}
+
static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
{
if (!knots || knots->size() < 2) {
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h
index e8be2efba9f..66e68cf6942 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.h
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h
@@ -23,15 +23,19 @@
namespace blender::io::alembic {
-class AbcNurbsReader : public AbcObjectReader {
+class AbcNurbsReader final : public AbcObjectReader {
std::vector<std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject>> m_schemas;
public:
AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
+ const Object *const ob,
+ const char **err_str) const override;
+
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
private:
void getNurbsPatches(const Alembic::Abc::IObject &obj);
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index 3aeacbd14fe..75ed18f57f2 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -82,7 +82,7 @@ bool AbcPointsReader::accepts_object_type(
void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
{
Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
- Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, nullptr);
+ Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr);
if (read_mesh != mesh) {
BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
@@ -127,6 +127,8 @@ void read_points_sample(const IPointsSchema &schema,
struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh,
const ISampleSelector &sample_sel,
int read_flag,
+ const char * /*velocity_name*/,
+ const float /*velocity_scale*/,
const char **err_str)
{
IPointsSchema::Sample sample;
diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h
index aed66699c75..105d1276f7a 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.h
+++ b/source/blender/io/alembic/intern/abc_reader_points.h
@@ -27,24 +27,26 @@
namespace blender::io::alembic {
-class AbcPointsReader : public AbcObjectReader {
+class AbcPointsReader final : public AbcObjectReader {
Alembic::AbcGeom::IPointsSchema m_schema;
Alembic::AbcGeom::IPointsSchema::Sample m_sample;
public:
AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
- const char **err_str) const;
+ const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
struct Mesh *read_mesh(struct Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel,
int read_flag,
- const char **err_str);
+ const char *velocity_name,
+ const float velocity_scale,
+ const char **err_str) override;
};
void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h
index e515560912f..6810cc214b7 100644
--- a/source/blender/io/alembic/intern/abc_reader_transform.h
+++ b/source/blender/io/alembic/intern/abc_reader_transform.h
@@ -25,18 +25,18 @@
namespace blender::io::alembic {
-class AbcEmptyReader : public AbcObjectReader {
+class AbcEmptyReader final : public AbcObjectReader {
Alembic::AbcGeom::IXformSchema m_schema;
public:
AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- bool valid() const;
+ bool valid() const override;
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
- const char **err_str) const;
+ const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel);
+ void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index 2975915eccd..f5bdad3e79e 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -22,6 +22,7 @@
#include "DNA_defs.h"
#include "DNA_listBase.h"
+#include "DNA_uuid_types.h"
#ifdef __cplusplus
extern "C" {
@@ -58,6 +59,19 @@ typedef struct AssetMetaData {
/** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */
struct IDProperty *properties;
+ /**
+ * Asset Catalog identifier. Should not contain spaces.
+ * Mapped to a path in the asset catalog hierarchy by an #AssetCatalogService.
+ * Use #BKE_asset_metadata_catalog_id_set() to ensure a valid ID is set.
+ */
+ struct bUUID catalog_id;
+ /**
+ * Short name of the asset's catalog. This is for debugging purposes only, to allow (partial)
+ * reconstruction of asset catalogs in the unfortunate case that the mapping from catalog UUID to
+ * catalog path is lost. The catalog's simple name is copied to #catalog_simple_name whenever
+ * #catalog_id is updated. */
+ char catalog_simple_name[64]; /* MAX_NAME */
+
/** Optional description of this asset for display in the UI. Dynamic length. */
char *description;
/** User defined tags for this asset. The asset manager uses these for filtering, but how they
diff --git a/source/blender/makesdna/DNA_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h
index e2eea5e5422..49b2862032f 100644
--- a/source/blender/makesdna/DNA_cloth_types.h
+++ b/source/blender/makesdna/DNA_cloth_types.h
@@ -42,7 +42,7 @@ extern "C" {
*/
typedef struct ClothSimSettings {
- /** UNUSED atm. */
+ /** UNUSED. */
struct LinkNode *cache;
/** See SB. */
float mingoal;
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 4b4c24a7a4e..28756395f7d 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -81,8 +81,8 @@ typedef struct bConstraint {
/** Local influence ipo or driver */
struct Ipo *ipo DNA_DEPRECATED;
- /* below are readonly fields that are set at runtime
- * by the solver for use in the GE (only IK atm) */
+ /* Below are read-only fields that are set at runtime
+ * by the solver for use in the GE (only IK at the moment). */
/** Residual error on constraint expressed in blender unit. */
float lin_error;
/** Residual error on constraint expressed in radiant. */
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 6acea8da15f..36bdd4915ff 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -84,7 +84,8 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we can't use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
- int typemap[51];
+ int typemap[52];
+ char _pad[4];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -166,7 +167,9 @@ typedef enum CustomDataType {
CD_PROP_BOOL = 50,
- CD_NUMTYPES = 51,
+ CD_HAIRLENGTH = 51,
+
+ CD_NUMTYPES = 52,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -220,6 +223,8 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
+#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
+
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 2a3c6f4e3db..11299ae9717 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -318,7 +318,7 @@
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
- .overscan = 0.1f,\
+ .chain_smooth_tolerance = 0.2f,\
}
#define _DNA_DEFAULT_LengthGpencilModifierData \
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 8d967a38808..ea5c81761c6 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -1040,7 +1040,11 @@ typedef struct LineartGpencilModifierData {
/** `0..PI` angle, for splitting strokes at sharp points. */
float angle_splitting_threshold;
- /* Doubles as geometry threshold when geometry space chaining is enabled */
+ /** Strength for smoothing jagged chains. */
+ float chain_smooth_tolerance;
+ int _pad1;
+
+ /* CPU mode */
float chaining_image_threshold;
/* Ported from SceneLineArt flags. */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 68bd2961f23..0f570f8603d 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -49,6 +49,8 @@ struct MDeformVert;
#define GPENCIL_MIN_FILL_FAC 0.05f
#define GPENCIL_MAX_FILL_FAC 8.0f
+#define GPENCIL_MAX_THICKNESS 5000
+
/* ***************************************** */
/* GP Stroke Points */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 31daa778b03..631db64ddd3 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -848,7 +848,7 @@ typedef struct CollisionModifierData {
struct MVert *x;
/** Position at the end of the frame. */
struct MVert *xnew;
- /** Unused atm, but was discussed during sprint. */
+ /** Unused at the moment, but was discussed during sprint. */
struct MVert *xold;
/** New position at the actual inter-frame step. */
struct MVert *current_xnew;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 18545666796..ea87cef1118 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -120,7 +120,10 @@ typedef struct bNodeSocket {
/* XXX deprecated, kept for forward compatibility */
short stack_type DNA_DEPRECATED;
char display_shape;
- char _pad[1];
+
+ /* #AttributeDomain used when the geometry nodes modifier creates an attribute for a group
+ * output. */
+ char attribute_domain;
/* Runtime-only cache of the number of input links, for multi-input sockets. */
short total_inputs;
@@ -445,6 +448,7 @@ typedef struct bNodeLink {
#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
#define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */
#define NODE_LINK_MUTED (1 << 4) /* Link is muted. */
+#define NODE_LINK_DRAGGED (1 << 5) /* Node link is being dragged by the user. */
/* tree->edit_quality/tree->render_quality */
#define NTREE_QUALITY_HIGH 0
@@ -459,6 +463,16 @@ typedef struct bNodeLink {
#define NTREE_CHUNKSIZE_512 512
#define NTREE_CHUNKSIZE_1024 1024
+/** Workaround to forward-declare C++ type in C header. */
+#ifdef __cplusplus
+namespace blender::nodes {
+struct FieldInferencingInterface;
+}
+using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface;
+#else
+typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle;
+#endif
+
/* the basis for a Node tree, all links and nodes reside internal here */
/* only re-usable node trees are in the library though,
* materials and textures allocate own tree struct */
@@ -481,6 +495,8 @@ typedef struct bNodeTree {
float view_center[2];
ListBase nodes, links;
+ /** Information about how inputs and outputs of the node group interact with fields. */
+ FieldInferencingInterfaceHandle *field_inferencing_interface;
/** Set init on fileread. */
int type, init;
@@ -575,6 +591,9 @@ typedef enum eNodeTreeUpdate {
NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */
NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */
NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */
+ /* The field interface has changed. So e.g. an output that was always a field before is not
+ * anymore. This implies that the field type inferencing has to be done again. */
+ NTREE_UPDATE_FIELD_INFERENCING = (1 << 6),
/* group has changed (generic flag including all other group flags) */
NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT),
} eNodeTreeUpdate;
@@ -836,7 +855,7 @@ typedef struct NodeVertexCol {
char name[64];
} NodeVertexCol;
-/* qdn: Defocus blur node */
+/** Defocus blur node. */
typedef struct NodeDefocus {
char bktype, _pad0, preview, gamco;
short samples, no_zbuf;
@@ -852,7 +871,7 @@ typedef struct NodeScriptDict {
void *node;
} NodeScriptDict;
-/* qdn: glare node */
+/** glare node. */
typedef struct NodeGlare {
char quality, type, iter;
/* XXX angle is only kept for backward/forward compatibility,
@@ -863,14 +882,14 @@ typedef struct NodeGlare {
char _pad1[4];
} NodeGlare;
-/* qdn: tonemap node */
+/** Tonemap node. */
typedef struct NodeTonemap {
float key, offset, gamma;
float f, m, a, c;
int type;
} NodeTonemap;
-/* qdn: lens distortion node */
+/** Lens distortion node. */
typedef struct NodeLensDist {
short jit, proj, fit;
char _pad[2];
@@ -1222,6 +1241,11 @@ typedef struct NodeAttributeMix {
uint8_t input_type_b;
} NodeAttributeMix;
+typedef struct NodeRandomValue {
+ /* CustomDataType. */
+ uint8_t data_type;
+} NodeRandomValue;
+
typedef struct NodeAttributeRandomize {
/* CustomDataType. */
uint8_t data_type;
@@ -1338,6 +1362,11 @@ typedef struct NodeGeometryAttributeProximity {
uint8_t target_geometry_element;
} NodeGeometryAttributeProximity;
+typedef struct NodeGeometryProximity {
+ /* GeometryNodeProximityTargetType. */
+ uint8_t target_element;
+} NodeGeometryProximity;
+
typedef struct NodeGeometryVolumeToMesh {
/* VolumeToMeshResolutionMode */
uint8_t resolution_mode;
@@ -1487,6 +1516,11 @@ typedef struct NodeGeometryCurveFill {
uint8_t mode;
} NodeGeometryCurveFill;
+typedef struct NodeGeometryMeshToPoints {
+ /* GeometryNodeMeshToPointsMode */
+ uint8_t mode;
+} NodeGeometryMeshToPoints;
+
typedef struct NodeGeometryAttributeCapture {
/* CustomDataType. */
int8_t data_type;
@@ -1494,6 +1528,16 @@ typedef struct NodeGeometryAttributeCapture {
int8_t domain;
} NodeGeometryAttributeCapture;
+typedef struct NodeGeometryStringToCurves {
+ /* GeometryNodeStringToCurvesOverflowMode */
+ uint8_t overflow;
+ /* GeometryNodeStringToCurvesAlignXMode */
+ uint8_t align_x;
+ /* GeometryNodeStringToCurvesAlignYMode */
+ uint8_t align_y;
+ char _pad[1];
+} NodeGeometryStringToCurves;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1907,6 +1951,12 @@ typedef enum GeometryNodeAttributeProximityTargetType {
GEO_NODE_PROXIMITY_TARGET_FACES = 2,
} GeometryNodeAttributeProximityTargetType;
+typedef enum GeometryNodeProximityTargetType {
+ GEO_NODE_PROX_TARGET_POINTS = 0,
+ GEO_NODE_PROX_TARGET_EDGES = 1,
+ GEO_NODE_PROX_TARGET_FACES = 2,
+} GeometryNodeProximityTargetType;
+
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,
GEO_NODE_BOOLEAN_UNION = 1,
@@ -1972,11 +2022,21 @@ typedef enum GeometryNodePointDistributeMode {
GEO_NODE_POINT_DISTRIBUTE_POISSON = 1,
} GeometryNodePointDistributeMode;
+typedef enum GeometryNodeDistributePointsOnFacesMode {
+ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0,
+ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
+} GeometryNodeDistributePointsOnFacesMode;
+
typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_EULER = 0,
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
} GeometryNodeRotatePointsType;
+typedef enum FunctionNodeRotatePointsType {
+ FN_NODE_ROTATE_EULER_TYPE_EULER = 0,
+ FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE = 1,
+} FunctionNodeRotatePointsType;
+
typedef enum GeometryNodeAttributeVectorRotateMode {
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
@@ -1997,6 +2057,11 @@ typedef enum GeometryNodeRotatePointsSpace {
GEO_NODE_POINT_ROTATE_SPACE_POINT = 1,
} GeometryNodeRotatePointsSpace;
+typedef enum FunctionNodeRotateEulerSpace {
+ FN_NODE_ROTATE_EULER_SPACE_OBJECT = 0,
+ FN_NODE_ROTATE_EULER_SPACE_POINT = 1,
+} FunctionNodeRotateEulerSpace;
+
typedef enum GeometryNodeAlignRotationToVectorAxis {
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X = 0,
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y = 1,
@@ -2085,6 +2150,35 @@ typedef enum GeometryNodeCurveFillMode {
GEO_NODE_CURVE_FILL_MODE_NGONS = 1,
} GeometryNodeCurveFillMode;
+typedef enum GeometryNodeMeshToPointsMode {
+ GEO_NODE_MESH_TO_POINTS_VERTICES = 0,
+ GEO_NODE_MESH_TO_POINTS_EDGES = 1,
+ GEO_NODE_MESH_TO_POINTS_FACES = 2,
+ GEO_NODE_MESH_TO_POINTS_CORNERS = 3,
+} GeometryNodeMeshToPointsMode;
+
+typedef enum GeometryNodeStringToCurvesOverflowMode {
+ GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0,
+ GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1,
+ GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE = 2,
+} GeometryNodeStringToCurvesOverflowMode;
+
+typedef enum GeometryNodeStringToCurvesAlignXMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH = 4,
+} GeometryNodeStringToCurvesAlignXMode;
+
+typedef enum GeometryNodeStringToCurvesAlignYMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4,
+} GeometryNodeStringToCurvesAlignYMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index b28c3ac2b85..9a5c51825e1 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1463,14 +1463,15 @@ typedef struct ToolSettings {
char edge_mode_live_unwrap;
- char _pad1[1];
-
/* Transform */
char transform_pivot_point;
char transform_flag;
- char snap_mode, snap_node_mode;
+ char snap_mode;
+ char snap_node_mode;
char snap_uv_mode;
char snap_flag;
+ /** UV equivalent of `snap_flag`, limited to: #SCE_SNAP_ABS_GRID. */
+ char snap_uv_flag;
char snap_target;
char snap_transform_mode_flag;
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 25330acd486..a71f86eae9f 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -79,9 +79,13 @@ typedef struct StripTransform {
} StripTransform;
typedef struct StripColorBalance {
+ int method;
float lift[3];
float gamma[3];
float gain[3];
+ float slope[3];
+ float offset[3];
+ float power[3];
int flag;
char _pad[4];
/* float exposure; */
@@ -232,15 +236,21 @@ typedef struct Sequence {
int blend_mode;
float blend_opacity;
+ /* Tag color showed if `SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG` is set. */
+ int8_t color_tag;
+
+ char alpha_mode;
+ char _pad4[2];
+
+ int cache_flag;
+
/* is sfra needed anymore? - it looks like its only used in one place */
/** Starting frame according to the timeline of the scene. */
int sfra;
- char alpha_mode;
- char _pad[2];
-
/* Multiview */
char views_format;
+ char _pad[3];
struct Stereo3dFormat *stereo3d_format;
struct IDProperty *prop;
@@ -248,9 +258,6 @@ typedef struct Sequence {
/* modifiers */
ListBase modifiers;
- int cache_flag;
- int _pad2[3];
-
SequenceRuntime runtime;
} Sequence;
@@ -431,6 +438,11 @@ typedef struct ColorBalanceModifierData {
float color_multiply;
} ColorBalanceModifierData;
+enum {
+ SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN = 0,
+ SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER = 1,
+};
+
typedef struct CurvesModifierData {
SequenceModifierData modifier;
@@ -486,7 +498,7 @@ typedef struct SequencerScopes {
struct ImBuf *histogram_ibuf;
} SequencerScopes;
-#define MAXSEQ 32
+#define MAXSEQ 128
#define SELECT 1
@@ -568,6 +580,9 @@ enum {
#define SEQ_COLOR_BALANCE_INVERSE_GAIN 1
#define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2
#define SEQ_COLOR_BALANCE_INVERSE_LIFT 4
+#define SEQ_COLOR_BALANCE_INVERSE_SLOPE 8
+#define SEQ_COLOR_BALANCE_INVERSE_OFFSET 16
+#define SEQ_COLOR_BALANCE_INVERSE_POWER 32
/* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */
@@ -727,6 +742,22 @@ enum {
SEQ_CACHE_STORE_THUMBNAIL = (1 << 12),
};
+/* Sequence->color_tag. */
+typedef enum SequenceColorTag {
+ SEQUENCE_COLOR_NONE = -1,
+ SEQUENCE_COLOR_01,
+ SEQUENCE_COLOR_02,
+ SEQUENCE_COLOR_03,
+ SEQUENCE_COLOR_04,
+ SEQUENCE_COLOR_05,
+ SEQUENCE_COLOR_06,
+ SEQUENCE_COLOR_07,
+ SEQUENCE_COLOR_08,
+ SEQUENCE_COLOR_09,
+
+ SEQUENCE_COLOR_TOT,
+} SequenceColorTag;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index e849039fa93..aa74e7712c0 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -599,6 +599,7 @@ typedef struct SequencerTimelineOverlay {
typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag {
SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1),
SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2),
+ SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3), /* use Sequence->color_tag */
SEQ_TIMELINE_SHOW_FCURVES = (1 << 5),
SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */
SEQ_TIMELINE_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */
@@ -805,14 +806,25 @@ typedef struct FileAssetSelectParams {
FileSelectParams base_params;
AssetLibraryReference asset_library_ref;
+ short asset_catalog_visibility; /* eFileSel_Params_AssetCatalogVisibility */
+ char _pad[6];
+ /** If #asset_catalog_visibility is #FILE_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the
+ * catalog to show. */
+ bUUID catalog_id;
short import_type; /* eFileAssetImportType */
- char _pad[6];
+ char _pad2[6];
} FileAssetSelectParams;
typedef enum eFileAssetImportType {
+ /** Regular data-block linking. */
FILE_ASSET_IMPORT_LINK = 0,
+ /** Regular data-block appending (basically linking + "Make Local"). */
FILE_ASSET_IMPORT_APPEND = 1,
+ /** Append data-block with the #BLO_LIBLINK_APPEND_LOCAL_ID_REUSE flag enabled. Some typically
+ * heavy data dependencies (e.g. the image data-blocks of a material, the mesh of an object) may
+ * be reused from an earlier append. */
+ FILE_ASSET_IMPORT_APPEND_REUSE = 2,
} eFileAssetImportType;
/**
@@ -965,7 +977,10 @@ enum eFileDetails {
typedef enum eFileSelectType {
FILE_LOADLIB = 1,
FILE_MAIN = 2,
+ /** Load assets from #Main. */
FILE_MAIN_ASSET = 3,
+ /** Load assets of an asset library containing external files. */
+ FILE_ASSET_LIBRARY = 4,
FILE_UNIX = 8,
FILE_BLENDER = 8, /* don't display relative paths */
@@ -984,23 +999,31 @@ typedef enum eFileSel_Action {
* (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h).
*/
typedef enum eFileSel_Params_Flag {
- FILE_APPEND_SET_FAKEUSER = (1 << 0),
+ FILE_PARAMS_FLAG_UNUSED_1 = (1 << 0),
FILE_RELPATH = (1 << 1),
FILE_LINK = (1 << 2),
FILE_HIDE_DOT = (1 << 3),
FILE_AUTOSELECT = (1 << 4),
FILE_ACTIVE_COLLECTION = (1 << 5),
- FILE_APPEND_RECURSIVE = (1 << 6),
+ FILE_PARAMS_FLAG_UNUSED_2 = (1 << 6),
FILE_DIRSEL_ONLY = (1 << 7),
FILE_FILTER = (1 << 8),
- FILE_OBDATA_INSTANCE = (1 << 9),
- FILE_COLLECTION_INSTANCE = (1 << 10),
+ FILE_PARAMS_FLAG_UNUSED_3 = (1 << 9),
+ FILE_PARAMS_FLAG_UNUSED_4 = (1 << 10),
FILE_SORT_INVERT = (1 << 11),
FILE_HIDE_TOOL_PROPS = (1 << 12),
FILE_CHECK_EXISTING = (1 << 13),
FILE_ASSETS_ONLY = (1 << 14),
+ /** Enables filtering by asset catalog. */
+ FILE_FILTER_ASSET_CATALOG = (1 << 15),
} eFileSel_Params_Flag;
+typedef enum eFileSel_Params_AssetCatalogVisibility {
+ FILE_SHOW_ASSETS_ALL_CATALOGS,
+ FILE_SHOW_ASSETS_FROM_CATALOG,
+ FILE_SHOW_ASSETS_WITHOUT_CATALOG,
+} eFileSel_Params_AssetCatalogVisibility;
+
/* sfile->params->rename_flag */
/* NOTE: short flag. Defined as bitflags, but currently only used as exclusive status markers... */
typedef enum eFileSel_Params_RenameFlag {
@@ -1175,13 +1198,10 @@ typedef struct SpaceImage {
char mode_prev;
char pin;
- char _pad1;
- /**
- * The currently active tile of the image when tile is enabled,
- * is kept in sync with the active faces tile.
- */
- short curtile;
- short lock;
+
+ char pixel_snap_mode;
+
+ char lock;
/** UV draw type. */
char dt_uv;
/** Sticky selection type. */
@@ -1189,14 +1209,19 @@ typedef struct SpaceImage {
char dt_uvstretch;
char around;
- int flag;
+ char _pad1[3];
- char pixel_snap_mode;
- char _pad2[7];
+ int flag;
float uv_opacity;
int tile_grid_shape[2];
+ /**
+ * UV editor custom-grid. Value of `N` will produce `NxN` grid.
+ * Use when #SI_CUSTOM_GRID is set.
+ */
+ int custom_grid_subdiv;
+ char _pad3[4];
MaskSpaceInfo mask_info;
SpaceImageOverlay overlay;
@@ -1252,6 +1277,7 @@ typedef enum eSpaceImage_Flag {
SI_FLAG_UNUSED_7 = (1 << 7), /* cleared */
SI_FLAG_UNUSED_8 = (1 << 8), /* cleared */
SI_COORDFLOATS = (1 << 9),
+
SI_FLAG_UNUSED_10 = (1 << 10),
SI_LIVE_UNWRAP = (1 << 11),
SI_USE_ALPHA = (1 << 12),
@@ -1263,7 +1289,7 @@ typedef enum eSpaceImage_Flag {
SI_FULLWINDOW = (1 << 16),
SI_FLAG_UNUSED_17 = (1 << 17),
- SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */
+ SI_CUSTOM_GRID = (1 << 18),
/**
* This means that the image is drawn until it reaches the view edge,
@@ -1289,6 +1315,9 @@ typedef enum eSpaceImageOverlay_Flag {
SI_OVERLAY_SHOW_OVERLAYS = (1 << 0),
} eSpaceImageOverlay_Flag;
+/** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */
+#define SI_GRID_STEPS_LEN 8
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h
index fb2d985d353..0e313183300 100644
--- a/source/blender/makesdna/DNA_tracking_types.h
+++ b/source/blender/makesdna/DNA_tracking_types.h
@@ -398,6 +398,8 @@ typedef struct MovieTrackingDopesheetChannel {
int *segments;
/** Longest segment length and total number of tracked frames. */
int max_segment, total_frames;
+ /** These numbers are valid only if tot_segment > 0. */
+ int first_not_disabled_marker_framenr, last_not_disabled_marker_framenr;
} MovieTrackingDopesheetChannel;
typedef struct MovieTrackingDopesheetCoverageSegment {
@@ -592,6 +594,8 @@ enum {
TRACKING_DOPE_SORT_LONGEST = 1,
TRACKING_DOPE_SORT_TOTAL = 2,
TRACKING_DOPE_SORT_AVERAGE_ERROR = 3,
+ TRACKING_DOPE_SORT_START = 4,
+ TRACKING_DOPE_SORT_END = 5,
};
/* MovieTrackingDopesheet->flag */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4f86201ced2..291f6de5ba2 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -458,6 +458,10 @@ typedef struct ThemeCollectionColor {
unsigned char color[4];
} ThemeCollectionColor;
+typedef struct ThemeStripColor {
+ unsigned char color[4];
+} ThemeStripColor;
+
/**
* A theme.
*
@@ -500,8 +504,10 @@ typedef struct bTheme {
/* See COLLECTION_COLOR_TOT for the number of collection colors. */
ThemeCollectionColor collection_color[8];
+ /* See SEQUENCE_COLOR_TOT for the total number of strip colors. */
+ ThemeStripColor strip_color[9];
+
int active_theme_area;
- char _pad0[4];
} bTheme;
#define UI_THEMESPACE_START(btheme) \
@@ -635,7 +641,9 @@ typedef struct UserDef_Experimental {
/* Debug options, always available. */
char use_undo_legacy;
char no_override_auto_resync;
+ char no_proxy_to_override_conversion;
char use_cycles_debug;
+ char use_geometry_nodes_legacy;
char SANITIZE_AFTER_HERE;
/* The following options are automatically sanitized (set to 0)
* when the release cycle is not alpha. */
@@ -646,8 +654,7 @@ typedef struct UserDef_Experimental {
char use_sculpt_tools_tilt;
char use_extended_asset_browser;
char use_override_templates;
- char use_geometry_nodes_fields;
- char _pad[4];
+ char _pad[3];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/DNA_uuid_types.h b/source/blender/makesdna/DNA_uuid_types.h
index fa0a78f074b..dcebfed6be7 100644
--- a/source/blender/makesdna/DNA_uuid_types.h
+++ b/source/blender/makesdna/DNA_uuid_types.h
@@ -40,6 +40,12 @@ typedef struct bUUID {
uint8_t node[6];
} bUUID;
+/**
+ * Memory required for a string representation of a UUID according to RFC4122.
+ * This is 36 characters for the string + a trailing zero byte.
+ */
+#define UUID_STRING_LEN 37
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index ce53e3390e1..188f933dba5 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -558,6 +558,7 @@ extern StructRNA RNA_ShaderFxWave;
extern StructRNA RNA_ShaderNode;
extern StructRNA RNA_ShaderNodeCameraData;
extern StructRNA RNA_ShaderNodeCombineRGB;
+extern StructRNA RNA_ShaderNodeFloatCurve;
extern StructRNA RNA_ShaderNodeGamma;
extern StructRNA RNA_ShaderNodeHueSaturation;
extern StructRNA RNA_ShaderNodeInvert;
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index c8f44262020..03d371be1f7 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -211,6 +211,7 @@ DEF_ENUM(rna_enum_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_with_auto_items)
DEF_ENUM(rna_enum_collection_color_items)
+DEF_ENUM(rna_enum_strip_color_items)
DEF_ENUM(rna_enum_subdivision_uv_smooth_items)
DEF_ENUM(rna_enum_subdivision_boundary_smooth_items)
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index fceb6d045c3..f1980eed811 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -2206,6 +2206,7 @@ void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop);
}
+/* NOTE: `scene` pointer may be NULL. */
void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
{
rna_property_update(NULL, bmain, scene, ptr, prop);
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index f8a36c1b2e6..be8972dbff3 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -614,20 +614,25 @@ static bool rna_property_override_operation_apply(Main *bmain,
}
/* get and set the default values as appropriate for the various types */
- return override_apply(bmain,
- ptr_dst,
- ptr_src,
- ptr_storage,
- prop_dst,
- prop_src,
- prop_storage,
- len_dst,
- len_src,
- len_storage,
- ptr_item_dst,
- ptr_item_src,
- ptr_item_storage,
- opop);
+ const bool sucess = override_apply(bmain,
+ ptr_dst,
+ ptr_src,
+ ptr_storage,
+ prop_dst,
+ prop_src,
+ prop_storage,
+ len_dst,
+ len_src,
+ len_storage,
+ ptr_item_dst,
+ ptr_item_src,
+ ptr_item_storage,
+ opop);
+ if (sucess) {
+ RNA_property_update_main(bmain, NULL, ptr_dst, prop_dst);
+ }
+
+ return sucess;
}
/**
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index 1e583f4ca52..80824df1bc8 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -32,19 +32,67 @@
#ifdef RNA_RUNTIME
# include "BKE_asset.h"
+# include "BKE_asset_library.h"
+# include "BKE_context.h"
# include "BKE_idprop.h"
# include "BLI_listbase.h"
+# include "BLI_uuid.h"
# include "ED_asset.h"
+# include "ED_fileselect.h"
# include "RNA_access.h"
-static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data,
- ReportList *reports,
- const char *name,
- bool skip_if_exists)
+static bool rna_AssetMetaData_editable_from_owner_id(const ID *owner_id,
+ const AssetMetaData *asset_data,
+ const char **r_info)
{
+ if (owner_id && asset_data && (owner_id->asset_data == asset_data)) {
+ return true;
+ }
+
+ if (r_info) {
+ *r_info =
+ "Asset metadata from external asset libraries can't be edited, only assets stored in the "
+ "current file can";
+ }
+ return false;
+}
+
+int rna_AssetMetaData_editable(PointerRNA *ptr, const char **r_info)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, asset_data, r_info) ?
+ PROP_EDITABLE :
+ 0;
+}
+
+static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info)
+{
+ AssetTag *asset_tag = ptr->data;
+ ID *owner_id = ptr->owner_id;
+ if (owner_id && owner_id->asset_data) {
+ BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1,
+ "The owner of the asset tag pointer is not the asset ID containing the tag");
+ UNUSED_VARS_NDEBUG(asset_tag);
+ }
+
+ return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ?
+ PROP_EDITABLE :
+ 0;
+}
+
+static AssetTag *rna_AssetMetaData_tag_new(
+ ID *id, AssetMetaData *asset_data, ReportList *reports, const char *name, bool skip_if_exists)
+{
+ const char *disabled_info = NULL;
+ if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) {
+ BKE_report(reports, RPT_WARNING, disabled_info);
+ return NULL;
+ }
+
AssetTag *tag = NULL;
if (skip_if_exists) {
@@ -64,10 +112,17 @@ static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data,
return tag;
}
-static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data,
+static void rna_AssetMetaData_tag_remove(ID *id,
+ AssetMetaData *asset_data,
ReportList *reports,
PointerRNA *tag_ptr)
{
+ const char *disabled_info = NULL;
+ if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) {
+ BKE_report(reports, RPT_WARNING, disabled_info);
+ return;
+ }
+
AssetTag *tag = tag_ptr->data;
if (BLI_findindex(&asset_data->tags, tag) == -1) {
BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name);
@@ -126,6 +181,40 @@ static void rna_AssetMetaData_active_tag_range(
*max = *softmax = MAX2(asset_data->tot_tags - 1, 0);
}
+static void rna_AssetMetaData_catalog_id_get(PointerRNA *ptr, char *value)
+{
+ const AssetMetaData *asset_data = ptr->data;
+ BLI_uuid_format(value, asset_data->catalog_id);
+}
+
+static int rna_AssetMetaData_catalog_id_length(PointerRNA *UNUSED(ptr))
+{
+ return UUID_STRING_LEN - 1;
+}
+
+static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value)
+{
+ AssetMetaData *asset_data = ptr->data;
+ bUUID new_uuid;
+
+ if (value[0] == '\0') {
+ BKE_asset_metadata_catalog_id_clear(asset_data);
+ return;
+ }
+
+ if (!BLI_uuid_parse_string(&new_uuid, value)) {
+ // TODO(Sybren): raise ValueError exception once that's possible from an RNA setter.
+ printf("UUID %s not formatted correctly, ignoring new value\n", value);
+ return;
+ }
+
+ /* This just sets the new UUID and clears the catalog simple name. The actual
+ * catalog simple name will be updated by some update function, as it
+ * needs the asset library from the context. */
+ /* TODO(Sybren): write that update function. */
+ BKE_asset_metadata_catalog_id_set(asset_data, new_uuid, "");
+}
+
static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr)
{
AssetHandle *asset_handle = ptr->data;
@@ -185,6 +274,7 @@ static void rna_def_asset_tag(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_editable_func(prop, "rna_AssetTag_editable");
RNA_def_property_string_maxlength(prop, MAX_NAME);
RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag");
RNA_def_struct_name_property(srna, prop);
@@ -205,7 +295,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop)
/* Tag collection */
func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new");
RNA_def_function_ui_description(func, "Add a new tag to this asset");
- RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_boolean(func,
@@ -219,7 +309,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove");
RNA_def_function_ui_description(func, "Remove an existing tag from this asset");
- RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
/* tag to remove */
parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
@@ -239,6 +329,7 @@ static void rna_def_asset_data(BlenderRNA *brna)
RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */
prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable");
RNA_def_property_string_funcs(prop,
"rna_AssetMetaData_description_get",
"rna_AssetMetaData_description_length",
@@ -248,7 +339,7 @@ static void rna_def_asset_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "AssetTag");
- RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable");
RNA_def_property_ui_text(prop,
"Tags",
"Custom tags (name tokens) for the asset, used for filtering and "
@@ -258,6 +349,24 @@ static void rna_def_asset_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range");
RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing");
+
+ prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop,
+ "rna_AssetMetaData_catalog_id_get",
+ "rna_AssetMetaData_catalog_id_length",
+ "rna_AssetMetaData_catalog_id_set");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_ui_text(prop,
+ "Catalog UUID",
+ "Identifier for the asset's catalog, used by Blender to look up the "
+ "asset's catalog path. Must be a UUID according to RFC4122");
+
+ prop = RNA_def_property(srna, "catalog_simple_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Catalog Simple Name",
+ "Simple name of the asset's catalog, for debugging and "
+ "data recovery purposes");
}
static void rna_def_asset_handle_api(StructRNA *srna)
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 25caa411979..1d3b8cd9f9c 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -734,6 +734,12 @@ static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi
WM_main_add_notifier(NC_BRUSH | NA_EDITED, br);
}
+static bool rna_Brush_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ Image *image = (Image *)value.owner_id;
+ return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE;
+}
+
static void rna_TextureSlot_brush_angle_update(bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
@@ -3434,6 +3440,7 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Clone Image", "Image for clone tool");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_Brush_update");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Brush_imagetype_poll");
prop = RNA_def_property(srna, "clone_alpha", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "clone.alpha");
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 6480d16ccd2..675cff3e58c 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -26,6 +26,7 @@
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
@@ -59,6 +60,12 @@
const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
+ {eGpencilModifierType_Texture,
+ "GP_TEXTURE",
+ ICON_MOD_UVPROJECT,
+ "Texture Mapping",
+ "Change stroke uv texture values"},
+ {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"},
{eGpencilModifierType_WeightAngle,
"GP_WEIGHT_ANGLE",
ICON_MOD_VERTEX_WEIGHT,
@@ -143,7 +150,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_THICKNESS,
"Thickness",
"Change stroke thickness"},
- {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"},
{0, "", 0, N_("Color"), ""},
{eGpencilModifierType_Color,
"GP_COLOR",
@@ -155,11 +161,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_OPACITY,
"Opacity",
"Opacity of the strokes"},
- {eGpencilModifierType_Texture,
- "GP_TEXTURE",
- ICON_TEXTURE,
- "Texture Mapping",
- "Change stroke uv texture values"},
{eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"},
{0, NULL, 0, NULL, NULL},
};
@@ -2685,7 +2686,7 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "Texture Modifier", "Transform stroke texture coordinates Modifier");
RNA_def_struct_sdna(srna, "TextureGpencilModifierData");
- RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
+ RNA_def_struct_ui_icon(srna, ICON_MOD_UVPROJECT);
RNA_define_lib_overridable(true);
@@ -2864,10 +2865,11 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
- prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "dist_start");
- RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
- RNA_def_property_ui_text(prop, "Lowest", "Start value for distance calculation");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Lowest", "Distance mapping to 0.0 weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR);
@@ -2875,10 +2877,11 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "dist_end");
- RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
- RNA_def_property_ui_text(prop, "Highest", "Max value for distance calculation");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Highest", "Distance mapping to 1.0 weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
@@ -3123,6 +3126,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f));
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "smooth_tolerance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "chain_smooth_tolerance");
+ RNA_def_property_ui_text(
+ prop, "Smooth Tolerance", "Strength of smoothing applied on jagged chains");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05f, 4);
+ RNA_def_property_range(prop, 0.0f, 30.0f);
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 4a013dc9bd7..2f42e521b52 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -161,7 +161,9 @@ static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr)
ImageUser *iuser = ptr->data;
ID *id = ptr->owner_id;
- BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra);
+ if (scene != NULL) {
+ BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra);
+ }
if (id) {
if (GS(id->name) == ID_NT) {
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 0bb76fd933a..fd6664ff0de 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -267,6 +267,7 @@ void rna_def_mtex_common(struct BlenderRNA *brna,
void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna);
void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene);
+int rna_AssetMetaData_editable(struct PointerRNA *ptr, const char **r_info);
PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
const char *get,
const char *set);
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 22a9b9d930a..29df7a53c44 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -44,11 +44,20 @@ typedef struct IDProperty IDProperty;
/* Function Callbacks */
-typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr);
+/** Update callback for an RNA property.
+ *
+ * \note This is NOT called automatically when writing into the property, it needs to be called
+ * manually (through #RNA_property_update or #RNA_property_update_main) when needed.
+ *
+ * \param bmain: the Main data-base to which `ptr` data belongs.
+ * \param active_scene: The current active scene (may be NULL in some cases).
+ * \param ptr: The RNA pointer data to update. */
+typedef void (*UpdateFunc)(struct Main *bmain, struct Scene *active_scene, struct PointerRNA *ptr);
typedef void (*ContextPropUpdateFunc)(struct bContext *C,
struct PointerRNA *ptr,
struct PropertyRNA *prop);
typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr);
+
typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info);
typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index);
typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr);
diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c
index f5b13fdbc81..5f89664458c 100644
--- a/source/blender/makesrna/intern/rna_material_api.c
+++ b/source/blender/makesrna/intern/rna_material_api.c
@@ -38,8 +38,8 @@
void RNA_api_material(StructRNA *UNUSED(srna))
{
- /* FunctionRNA *func; */
- /* PropertyRNA *parm; */
+ // FunctionRNA *func;
+ // PropertyRNA *parm;
}
#endif
diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c
index 17c7b331c88..d0711f28a6e 100644
--- a/source/blender/makesrna/intern/rna_nla.c
+++ b/source/blender/makesrna/intern/rna_nla.c
@@ -751,14 +751,14 @@ static void rna_def_nlastrip(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Influence", "Amount the strip contributes to the current result");
/* XXX: Update temporarily disabled so that the property can be edited at all!
- * Even autokey only applies after the curves have been re-evaluated,
+ * Even auto-key only applies after the curves have been re-evaluated,
* causing the unkeyed values to be lost. */
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL);
prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME);
RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate");
/* XXX: Update temporarily disabled so that the property can be edited at all!
- * Even autokey only applies after the curves have been re-evaluated,
+ * Even auto-key only applies after the curves have been re-evaluated,
* causing the unkeyed values to be lost. */
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index b631e76c094..df1be412bc4 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2092,6 +2092,19 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported);
}
+static bool random_value_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32);
+}
+static const EnumPropertyItem *rna_FunctionNodeRandomValue_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, random_value_type_supported);
+}
+
static const EnumPropertyItem *rna_GeometryNodeAttributeRandomize_operation_itemf(
bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
@@ -3729,7 +3742,7 @@ static void rna_Node_image_layer_update(Main *bmain, Scene *scene, PointerRNA *p
rna_Node_update(bmain, scene, ptr);
- if (scene->nodetree != NULL) {
+ if (scene != NULL && scene->nodetree != NULL) {
ntreeCompositUpdateRLayers(scene->nodetree);
}
}
@@ -3901,7 +3914,7 @@ static const EnumPropertyItem *rna_Node_view_layer_itemf(bContext *UNUSED(C),
static void rna_Node_view_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
rna_Node_update(bmain, scene, ptr);
- if (scene->nodetree != NULL) {
+ if (scene != NULL && scene->nodetree != NULL) {
ntreeCompositUpdateRLayers(scene->nodetree);
}
}
@@ -4336,7 +4349,7 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
- RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
+ RenderEngineType *engine_type = (scene != NULL) ? RE_engines_find(scene->r.engine) : NULL;
if (engine_type && engine_type->update_script_node) {
/* auto update node */
@@ -4887,6 +4900,17 @@ static void def_vector_curve(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_float_curve(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "mapping", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "storage");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_time(StructRNA *srna)
{
PropertyRNA *prop;
@@ -6532,11 +6556,11 @@ static void def_cmp_levels(StructRNA *srna)
PropertyRNA *prop;
static const EnumPropertyItem channel_items[] = {
- {1, "COMBINED_RGB", 0, "C", "Combined RGB"},
- {2, "RED", 0, "R", "Red Channel"},
- {3, "GREEN", 0, "G", "Green Channel"},
- {4, "BLUE", 0, "B", "Blue Channel"},
- {5, "LUMINANCE", 0, "L", "Luminance Channel"},
+ {1, "COMBINED_RGB", 0, "Combined", "Combined RGB"},
+ {2, "RED", 0, "Red", "Red Channel"},
+ {3, "GREEN", 0, "Green", "Green Channel"},
+ {4, "BLUE", 0, "Blue", "Blue Channel"},
+ {5, "LUMINANCE", 0, "Luminance", "Luminance Channel"},
{0, NULL, 0, NULL, NULL},
};
@@ -9168,6 +9192,21 @@ static void def_geo_subdivision_surface(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_fn_random_value(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeRandomValue", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FunctionNodeRandomValue_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_attribute_randomize(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9479,6 +9518,33 @@ static void def_geo_point_distribute(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_distribute_points_on_faces(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rna_node_geometry_distribute_points_on_faces_mode_items[] = {
+ {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM,
+ "RANDOM",
+ 0,
+ "Random",
+ "Distribute points randomly on the surface"},
+ {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON,
+ "POISSON",
+ 0,
+ "Poisson Disk",
+ "Distribute the points randomly on the surface while taking a minimum distance between "
+ "points into account"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_distribute_points_on_faces_mode_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_attribute_color_ramp(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9601,6 +9667,23 @@ static void def_geo_curve_set_handles(StructRNA *srna)
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items);
RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_legacy_curve_set_handles(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage");
+
+ prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "handle_type");
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items);
+ RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles");
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
@@ -9735,6 +9818,51 @@ static void def_geo_point_rotate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_fn_rotate_euler(StructRNA *srna)
+{
+ static const EnumPropertyItem type_items[] = {
+ {FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE,
+ "AXIS_ANGLE",
+ ICON_NONE,
+ "Axis Angle",
+ "Rotate around an axis by an angle"},
+ {FN_NODE_ROTATE_EULER_TYPE_EULER,
+ "EULER",
+ ICON_NONE,
+ "Euler",
+ "Rotate around the X, Y, and Z axes"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem space_items[] = {
+ {FN_NODE_ROTATE_EULER_SPACE_OBJECT,
+ "OBJECT",
+ ICON_NONE,
+ "Object",
+ "Rotate the input rotation in the local space of the object"},
+ {FN_NODE_ROTATE_EULER_SPACE_POINT,
+ "POINT",
+ ICON_NONE,
+ "Point",
+ "Rotate the input rotation in its local space"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_ui_text(prop, "Type", "Method used to describe the rotation");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, space_items);
+ RNA_def_property_ui_text(prop, "Space", "Base orientation of the points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_align_rotation_to_vector(StructRNA *srna)
{
static const EnumPropertyItem axis_items[] = {
@@ -9917,7 +10045,7 @@ static void def_geo_collection_info(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
-static void def_geo_attribute_proximity(StructRNA *srna)
+static void def_geo_legacy_attribute_proximity(StructRNA *srna)
{
static const EnumPropertyItem target_geometry_element[] = {
{GEO_NODE_PROXIMITY_TARGET_POINTS,
@@ -9950,6 +10078,39 @@ static void def_geo_attribute_proximity(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_proximity(StructRNA *srna)
+{
+ static const EnumPropertyItem target_element_items[] = {
+ {GEO_NODE_PROX_TARGET_POINTS,
+ "POINTS",
+ ICON_NONE,
+ "Points",
+ "Calculate the proximity to the target's points (faster than the other modes)"},
+ {GEO_NODE_PROX_TARGET_EDGES,
+ "EDGES",
+ ICON_NONE,
+ "Edges",
+ "Calculate the proximity to the target's edges"},
+ {GEO_NODE_PROX_TARGET_FACES,
+ "FACES",
+ ICON_NONE,
+ "Faces",
+ "Calculate the proximity to the target's faces"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryProximity", "storage");
+
+ prop = RNA_def_property(srna, "target_element", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, target_element_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_PROX_TARGET_FACES);
+ RNA_def_property_ui_text(
+ prop, "Target Geometry", "Element of the target geometry to calculate the distance from");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_volume_to_mesh(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10036,7 +10197,7 @@ static void def_geo_mesh_cylinder(StructRNA *srna)
prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items);
RNA_def_property_ui_text(prop, "Fill Type", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_cone(StructRNA *srna)
@@ -10048,7 +10209,7 @@ static void def_geo_mesh_cone(StructRNA *srna)
prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items);
RNA_def_property_ui_text(prop, "Fill Type", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_line(StructRNA *srna)
@@ -10181,7 +10342,7 @@ static void def_geo_curve_resample(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_curve_subdivide(StructRNA *srna)
+static void def_geo_legacy_curve_subdivide(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10251,6 +10412,42 @@ static void def_geo_curve_to_points(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_mesh_to_points(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_MESH_TO_POINTS_VERTICES,
+ "VERTICES",
+ 0,
+ "Vertices",
+ "Create a point in the point cloud for each selected vertex"},
+ {GEO_NODE_MESH_TO_POINTS_EDGES,
+ "EDGES",
+ 0,
+ "Edges",
+ "Create a point in the point cloud for each selected edge"},
+ {GEO_NODE_MESH_TO_POINTS_FACES,
+ "FACES",
+ 0,
+ "Faces",
+ "Create a point in the point cloud for each selected face"},
+ {GEO_NODE_MESH_TO_POINTS_CORNERS,
+ "CORNERS",
+ 0,
+ "Corners",
+ "Create a point in the point cloud for each selected face corner"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToPoints", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_curve_trim(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10398,6 +10595,120 @@ static void def_geo_attribute_capture(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_string_to_curves(StructRNA *srna)
+{
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW,
+ "OVERFLOW",
+ ICON_NONE,
+ "Overflow",
+ "Let the text use more space than the specified height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT,
+ "SCALE_TO_FIT",
+ ICON_NONE,
+ "Scale To Fit",
+ "Scale the text size to fit inside the width and height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE,
+ "TRUNCATE",
+ ICON_NONE,
+ "Truncate",
+ "Only output curves that fit within the width and height. Output the remainder to the "
+ "\"Remainder\" output"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_x_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT,
+ "LEFT",
+ ICON_ALIGN_LEFT,
+ "Left",
+ "Align text to the left"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER,
+ "CENTER",
+ ICON_ALIGN_CENTER,
+ "Center",
+ "Align text to the center"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT,
+ "RIGHT",
+ ICON_ALIGN_RIGHT,
+ "Right",
+ "Align text to the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY,
+ "JUSTIFY",
+ ICON_ALIGN_JUSTIFY,
+ "Justify",
+ "Align text to the left and the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH,
+ "FLUSH",
+ ICON_ALIGN_FLUSH,
+ "Flush",
+ "Align text to the left and the right, with equal character spacing"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_y_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE,
+ "TOP_BASELINE",
+ ICON_ALIGN_TOP,
+ "Top Baseline",
+ "Align text to the top baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP,
+ "TOP",
+ ICON_ALIGN_TOP,
+ "Top",
+ "Align text to the top"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE,
+ "MIDDLE",
+ ICON_ALIGN_MIDDLE,
+ "Middle",
+ "Align text to the middle"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE,
+ "BOTTOM_BASELINE",
+ ICON_ALIGN_BOTTOM,
+ "Bottom Baseline",
+ "Align text to the bottom baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM,
+ "BOTTOM",
+ ICON_ALIGN_BOTTOM,
+ "Bottom",
+ "Align text to the bottom"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "VectorFont");
+ RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryStringToCurves", "storage");
+
+ prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "overflow");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_overflow_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ RNA_def_property_ui_text(prop, "Overflow", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_x");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_x_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT);
+ RNA_def_property_ui_text(prop, "Align X", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_y");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_y_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE);
+ RNA_def_property_ui_text(prop, "Align Y", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -10664,6 +10975,14 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
prop, "Hide Value", "Hide the socket input value even when the socket is not connected");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+ prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
+ RNA_def_property_ui_text(
+ prop,
+ "Attribute Domain",
+ "Attribute domain used by the geometry nodes modifier to create an attribute output");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
/* registration */
prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "typeinfo->idname");
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 98d59bf3a1a..7f997109920 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1056,8 +1056,8 @@ static void rna_def_ptcache_point_caches(BlenderRNA *brna, PropertyRNA *cprop)
StructRNA *srna;
PropertyRNA *prop;
- /* FunctionRNA *func; */
- /* PropertyRNA *parm; */
+ // FunctionRNA *func;
+ // PropertyRNA *parm;
RNA_def_property_srna(cprop, "PointCaches");
srna = RNA_def_struct(brna, "PointCaches", NULL);
diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c
index 0b56a73efa2..c51931d0d1a 100644
--- a/source/blender/makesrna/intern/rna_rigidbody.c
+++ b/source/blender/makesrna/intern/rna_rigidbody.c
@@ -215,9 +215,10 @@ static void rna_RigidBodyWorld_constraints_collection_update(Main *bmain,
static void rna_RigidBodyOb_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
- RigidBodyWorld *rbw = scene->rigidbody_world;
-
- BKE_rigidbody_cache_reset(rbw);
+ if (scene != NULL) {
+ RigidBodyWorld *rbw = scene->rigidbody_world;
+ BKE_rigidbody_cache_reset(rbw);
+ }
}
static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr)
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 80fc13faab4..5b46dfa8210 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -676,7 +676,9 @@ static void rna_ToolSettings_snap_mode_set(struct PointerRNA *ptr, int value)
/* Grease Pencil update cache */
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
- ED_gpencil_tag_scene_gpencil(scene);
+ if (scene != NULL) {
+ ED_gpencil_tag_scene_gpencil(scene);
+ }
}
static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr))
@@ -848,8 +850,9 @@ static void rna_Scene_camera_update(Main *bmain, Scene *UNUSED(scene_unused), Po
DEG_relations_tag_update(bmain);
}
-static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Scene_fps_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr)
{
+ Scene *scene = (Scene *)ptr->owner_id;
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS);
/* NOTE: Tag via dependency graph will take care of all the updates ion the evaluated domain,
* however, changes in FPS actually modifies an original skip length,
@@ -857,9 +860,9 @@ static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(p
SEQ_sound_update_length(bmain, scene);
}
-static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_LISTENER);
+ DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_LISTENER);
}
static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
@@ -885,8 +888,11 @@ static const char *rna_Scene_statistics_string_get(Scene *scene,
return ED_info_statistics_string(bmain, scene, view_layer);
}
-static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Scene_framelen_update(Main *UNUSED(bmain),
+ Scene *UNUSED(active_scene),
+ PointerRNA *ptr)
{
+ Scene *scene = (Scene *)ptr->owner_id;
scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images;
}
@@ -1940,9 +1946,9 @@ static void rna_Scene_use_audio_set(PointerRNA *ptr, bool value)
}
}
-static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_MUTE);
+ DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_MUTE);
}
static int rna_Scene_sync_mode_get(PointerRNA *ptr)
@@ -2199,9 +2205,9 @@ static void rna_SceneCamera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po
}
}
-static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
- SEQ_cache_cleanup(scene);
+ SEQ_cache_cleanup((Scene *)ptr->owner_id);
}
static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr))
@@ -3156,6 +3162,14 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+ prop = RNA_def_property(srna, "use_snap_uv_grid_absolute", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "snap_uv_flag", SCE_SNAP_ABS_GRID);
+ RNA_def_property_ui_text(
+ prop,
+ "Absolute Grid Snap",
+ "Absolute grid alignment while translating (based on the pivot center)");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+
prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "snap_target");
RNA_def_property_enum_items(prop, rna_enum_snap_target_items);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 1da08448c5b..a2db1536f55 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -130,7 +130,9 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
/* mark all grease pencil datablocks of the scene */
- ED_gpencil_tag_scene_gpencil(scene);
+ if (scene != NULL) {
+ ED_gpencil_tag_scene_gpencil(scene);
+ }
}
const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[] = {
@@ -501,6 +503,12 @@ static void rna_ImaPaint_stencil_update(bContext *C, PointerRNA *UNUSED(ptr))
}
}
+static bool rna_ImaPaint_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ Image *image = (Image *)value.owner_id;
+ return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE;
+}
+
static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr))
{
Main *bmain = CTX_data_main(C);
@@ -1033,17 +1041,20 @@ static void rna_def_image_paint(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
RNA_def_property_ui_text(prop, "Stencil Image", "Image used as stencil");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll");
prop = RNA_def_property(srna, "canvas", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
RNA_def_property_ui_text(prop, "Canvas", "Image used as canvas");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_canvas_update");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll");
prop = RNA_def_property(srna, "clone_image", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "clone");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Clone Image", "Image used as clone source");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll");
prop = RNA_def_property(srna, "stencil_color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_range(prop, 0.0, 1.0);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index b713ffb68b4..e519740259c 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -81,6 +81,20 @@ const EnumPropertyItem rna_enum_sequence_modifier_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_strip_color_items[] = {
+ {SEQUENCE_COLOR_NONE, "NONE", ICON_X, "None", "Assign no color tag to the collection"},
+ {SEQUENCE_COLOR_01, "COLOR_01", ICON_SEQUENCE_COLOR_01, "Color 01", ""},
+ {SEQUENCE_COLOR_02, "COLOR_02", ICON_SEQUENCE_COLOR_02, "Color 02", ""},
+ {SEQUENCE_COLOR_03, "COLOR_03", ICON_SEQUENCE_COLOR_03, "Color 03", ""},
+ {SEQUENCE_COLOR_04, "COLOR_04", ICON_SEQUENCE_COLOR_04, "Color 04", ""},
+ {SEQUENCE_COLOR_05, "COLOR_05", ICON_SEQUENCE_COLOR_05, "Color 05", ""},
+ {SEQUENCE_COLOR_06, "COLOR_06", ICON_SEQUENCE_COLOR_06, "Color 06", ""},
+ {SEQUENCE_COLOR_07, "COLOR_07", ICON_SEQUENCE_COLOR_07, "Color 07", ""},
+ {SEQUENCE_COLOR_08, "COLOR_08", ICON_SEQUENCE_COLOR_08, "Color 08", ""},
+ {SEQUENCE_COLOR_09, "COLOR_09", ICON_SEQUENCE_COLOR_09, "Color 09", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "BKE_global.h"
@@ -840,9 +854,9 @@ static int rna_Sequence_proxy_filepath_length(PointerRNA *ptr)
return strlen(path);
}
-static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
- DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS);
}
static void rna_Sequence_pan_range(
@@ -940,8 +954,9 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *UNUSED(scene), Poin
rna_Sequence_invalidate_raw_update(bmain, scene, ptr);
}
-static void rna_Sequence_sound_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
+static void rna_Sequence_sound_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr)
{
+ Scene *scene = (Scene *)ptr->owner_id;
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO);
DEG_relations_tag_update(bmain);
}
@@ -999,6 +1014,18 @@ static void rna_Sequence_opacity_set(PointerRNA *ptr, float value)
seq->blend_opacity = value * 100.0f;
}
+static int rna_Sequence_color_tag_get(PointerRNA *ptr)
+{
+ Sequence *seq = (Sequence *)(ptr->data);
+ return seq->color_tag;
+}
+
+static void rna_Sequence_color_tag_set(PointerRNA *ptr, int value)
+{
+ Sequence *seq = (Sequence *)(ptr->data);
+ seq->color_tag = value;
+}
+
static bool colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt)
{
SequenceSearchData *data = arg_pt;
@@ -1564,12 +1591,28 @@ static void rna_def_color_balance(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ static const EnumPropertyItem method_items[] = {
+ {SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN, "LIFT_GAMMA_GAIN", 0, "Lift/Gamma/Gain", ""},
+ {SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER,
+ "OFFSET_POWER_SLOPE",
+ 0,
+ "Offset/Power/Slope (ASC-CDL)",
+ "ASC-CDL standard color correction"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "SequenceColorBalanceData", NULL);
RNA_def_struct_ui_text(srna,
"Sequence Color Balance Data",
"Color balance parameters for a sequence strip and its modifiers");
RNA_def_struct_sdna(srna, "StripColorBalance");
+ prop = RNA_def_property(srna, "correction_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "method");
+ RNA_def_property_enum_items(prop, method_items);
+ RNA_def_property_ui_text(prop, "Correction Method", "");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
prop = RNA_def_property(srna, "lift", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_ui_text(prop, "Lift", "Color balance lift (shadows)");
RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
@@ -1588,14 +1631,22 @@ static void rna_def_color_balance(BlenderRNA *brna)
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
- prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN);
- RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`");
+ prop = RNA_def_property(srna, "slope", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_ui_text(prop, "Slope", "Correction for highlights");
+ RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
+ RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
- prop = RNA_def_property(srna, "invert_gamma", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAMMA);
- RNA_def_property_ui_text(prop, "Inverse Gamma", "Invert the gamma color");
+ prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_ui_text(prop, "Offset", "Correction for entire tonal range");
+ RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
+ prop = RNA_def_property(srna, "power", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_ui_text(prop, "Power", "Correction for midtones");
+ RNA_def_property_ui_range(prop, 0, 2, 0.1, 3);
+ RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_lift", PROP_BOOLEAN, PROP_NONE);
@@ -1603,6 +1654,31 @@ static void rna_def_color_balance(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Inverse Lift", "Invert the lift color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+ prop = RNA_def_property(srna, "invert_gamma", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAMMA);
+ RNA_def_property_ui_text(prop, "Inverse Gamma", "Invert the gamma color");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
+ prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN);
+ RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
+ prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE);
+ RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
+ prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_OFFSET);
+ RNA_def_property_ui_text(prop, "Inverse Offset", "Invert the offset color");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
+ prop = RNA_def_property(srna, "invert_power", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_POWER);
+ RNA_def_property_ui_text(prop, "Inverse Power", "Invert the power color");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
+
/* not yet used */
# if 0
prop = RNA_def_property(srna, "exposure", PROP_FLOAT, PROP_NONE);
@@ -1937,6 +2013,14 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
+ prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "color_tag");
+ RNA_def_property_enum_funcs(
+ prop, "rna_Sequence_color_tag_get", "rna_Sequence_color_tag_set", NULL);
+ RNA_def_property_enum_items(prop, rna_enum_strip_color_items);
+ RNA_def_property_ui_text(prop, "Strip Color", "Color tag for a strip");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
+
/* modifiers */
prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "SequenceModifier");
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index a05cef7a1cd..652d2545e67 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -39,6 +39,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_uuid.h"
#include "DNA_action_types.h"
#include "DNA_gpencil_types.h"
@@ -856,6 +857,14 @@ static void rna_Space_view2d_sync_set(PointerRNA *ptr, bool value)
ARegion *region;
area = rna_area_from_space(ptr); /* can be NULL */
+ if ((area != NULL) && !UI_view2d_area_supports_sync(area)) {
+ BKE_reportf(NULL,
+ RPT_ERROR,
+ "'show_locked_time' is not supported for the '%s' editor",
+ area->type->name);
+ return;
+ }
+
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region) {
View2D *v2d = &region->v2d;
@@ -906,7 +915,7 @@ static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UN
static void rna_SpaceView3D_camera_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
View3D *v3d = (View3D *)(ptr->data);
- if (v3d->scenelock) {
+ if (v3d->scenelock && scene != NULL) {
wmWindowManager *wm = bmain->wm.first;
scene->camera = v3d->camera;
@@ -1540,7 +1549,9 @@ static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr)
static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
{
- ED_space_image_paint_update(bmain, bmain->wm.first, scene);
+ if (scene != NULL) {
+ ED_space_image_paint_update(bmain, bmain->wm.first, scene);
+ }
}
static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value)
@@ -2604,16 +2615,38 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val
params->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
-static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
+static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr)
{
- FileSelectParams *params = ptr->data;
- params->filter_id = value;
+ const FileDirEntry *entry = ptr->data;
+
+ /* Note that the owning ID of the RNA pointer (`ptr->owner_id`) has to be set carefully:
+ * Local IDs (`entry->id`) own their asset metadata themselves. Asset metadata from other blend
+ * files are owned by the file browser (`entry`). Only if this is set correctly, we can tell from
+ * the metadata RNA pointer if the metadata is stored locally and can thus be edited or not. */
+
+ if (entry->id) {
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(entry->id, &id_ptr);
+ return rna_pointer_inherit_refine(&id_ptr, &RNA_AssetMetaData, entry->asset_data);
+ }
+
+ return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data);
}
-static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr)
+static int rna_FileBrowser_FileSelectEntry_name_editable(PointerRNA *ptr, const char **r_info)
{
- FileSelectParams *params = ptr->data;
- return params->filter_id;
+ const FileDirEntry *entry = ptr->data;
+
+ /* This actually always returns 0 (the name is never editable) but we want to get a disabled
+ * message returned to `r_info` in some cases. */
+
+ if (entry->asset_data) {
+ PointerRNA asset_data_ptr = rna_FileBrowser_FileSelectEntry_asset_data_get(ptr);
+ /* Get disabled hint from asset metadata polling. */
+ rna_AssetMetaData_editable(&asset_data_ptr, r_info);
+ }
+
+ return 0;
}
static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value)
@@ -2672,12 +2705,6 @@ static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr)
return ED_file_icon(entry);
}
-static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr)
-{
- const FileDirEntry *entry = ptr->data;
- return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data);
-}
-
static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr)
{
SpaceFile *sfile = ptr->data;
@@ -3153,6 +3180,17 @@ static void rna_SpaceSpreadsheet_context_path_guess(SpaceSpreadsheet *sspreadshe
WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
}
+static void rna_FileAssetSelectParams_catalog_id_get(PointerRNA *ptr, char *value)
+{
+ const FileAssetSelectParams *params = ptr->data;
+ BLI_uuid_format(value, params->catalog_id);
+}
+
+static int rna_FileAssetSelectParams_catalog_id_length(PointerRNA *UNUSED(ptr))
+{
+ return UUID_STRING_LEN - 1;
+}
+
#else
static const EnumPropertyItem dt_uv_items[] = {
@@ -3443,6 +3481,19 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
prop, "Tile Grid Shape", "How many tiles will be shown in the background");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ prop = RNA_def_property(srna, "use_custom_grid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
+ prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv");
+ RNA_def_property_range(prop, 1, 5000);
+ RNA_def_property_ui_text(
+ prop, "Dynamic Grid Size", "Number of grid units in UV space that make one UV Unit");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
prop = RNA_def_property(srna, "uv_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "uv_opacity");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -5449,6 +5500,12 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_THUMBNAILS);
RNA_def_property_ui_text(prop, "Show Thumbnails", "Show strip thumbnails");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_strip_tag_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG);
+ RNA_def_property_ui_text(
+ prop, "Show Color Tags", "Display the strip color tags in the sequencer");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
}
static void rna_def_space_sequencer(BlenderRNA *brna)
@@ -6260,12 +6317,13 @@ static void rna_def_fileselect_entry(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_editable_func(prop, "rna_FileBrowser_FileSelectEntry_name_editable");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_string_funcs(prop,
"rna_FileBrowser_FileSelectEntry_name_get",
"rna_FileBrowser_FileSelectEntry_name_length",
NULL);
RNA_def_property_ui_text(prop, "Name", "");
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_struct_name_property(srna, prop);
prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_NONE);
@@ -6537,47 +6595,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- /* XXX copied from rna_enum_id_type_filter_items. */
- static const EnumPropertyItem asset_category_items[] = {
- {FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"},
- {FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"},
- {FILTER_ID_OB | FILTER_ID_GR,
- "OBJECTS_AND_COLLECTIONS",
- ICON_GROUP,
- "Objects & Collections",
- "Show objects and collections"},
- {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME
- /* XXX avoid warning */
- // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO
- ,
- "GEOMETRY",
- ICON_MESH_DATA,
- "Geometry",
- "Show meshes, curves, lattice, armatures and metaballs data"},
- {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
- "SHADING",
- ICON_MATERIAL_DATA,
- "Shading",
- "Show materials, nodetrees, textures and Freestyle's linestyles"},
- {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
- "IMAGES_AND_SOUNDS",
- ICON_IMAGE_DATA,
- "Images & Sounds",
- "Show images, movie clips, sounds and masks"},
- {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
- "ENVIRONMENTS",
- ICON_WORLD_DATA,
- "Environment",
- "Show worlds, lights, cameras and speakers"},
- {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
- FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
- "MISC",
- ICON_GREASEPENCIL,
- "Miscellaneous",
- "Show other data types"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem asset_import_type_items[] = {
{FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"},
{FILE_ASSET_IMPORT_APPEND,
@@ -6585,6 +6602,14 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
0,
"Append",
"Import the assets as copied data-block, with no link to the original asset data-block"},
+ {FILE_ASSET_IMPORT_APPEND_REUSE,
+ "APPEND_REUSE",
+ 0,
+ "Append (Reuse Data)",
+ "Import the assets as copied data-block while avoiding multiple copies of nested, "
+ "typically heavy data. For example the textures of a material asset, or the mesh of an "
+ "object asset, don't have to be copied every time this asset is imported. The instances of "
+ "the asset share the data instead"},
{0, NULL, 0, NULL, NULL},
};
@@ -6598,14 +6623,13 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
- prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, asset_category_items);
- RNA_def_property_enum_funcs(prop,
- "rna_FileAssetSelectParams_asset_category_get",
- "rna_FileAssetSelectParams_asset_category_set",
- NULL);
- RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+ prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop,
+ "rna_FileAssetSelectParams_catalog_id_get",
+ "rna_FileAssetSelectParams_catalog_id_length",
+ NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog shown in the browser");
prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, asset_import_type_items);
diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index 336359a9dc0..c81588aa8b5 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -2437,6 +2437,8 @@ static void rna_def_trackingDopesheet(BlenderRNA *brna)
0,
"Average Error",
"Sort channels by average reprojection error of tracks after solve"},
+ {TRACKING_DOPE_SORT_START, "START", 0, "Start Frame", "Sort channels by first frame number"},
+ {TRACKING_DOPE_SORT_END, "END", 0, "End Frame", "Sort channels by last frame number"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 563c6ea35e0..ccfc1222d4c 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -3680,6 +3680,23 @@ static void rna_def_userdef_theme_collection_color(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
}
+static void rna_def_userdef_theme_strip_color(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ThemeStripColor", NULL);
+ RNA_def_struct_sdna(srna, "ThemeStripColor");
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+ RNA_def_struct_ui_text(srna, "Theme Strip Color", "Theme settings for strip colors");
+
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "color");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Color", "Strip Color");
+ RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
+}
+
static void rna_def_userdef_theme_space_clip(BlenderRNA *brna)
{
StructRNA *srna;
@@ -4029,6 +4046,12 @@ static void rna_def_userdef_themes(BlenderRNA *brna)
RNA_def_property_collection_sdna(prop, NULL, "collection_color", "");
RNA_def_property_struct_type(prop, "ThemeCollectionColor");
RNA_def_property_ui_text(prop, "Collection Color", "");
+
+ prop = RNA_def_property(srna, "strip_color", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_collection_sdna(prop, NULL, "strip_color", "");
+ RNA_def_property_struct_type(prop, "ThemeStripColor");
+ RNA_def_property_ui_text(prop, "Strip Color", "");
}
static void rna_def_userdef_addon(BlenderRNA *brna)
@@ -4272,6 +4295,7 @@ static void rna_def_userdef_dothemes(BlenderRNA *brna)
rna_def_userdef_theme_space_spreadsheet(brna);
rna_def_userdef_theme_colorset(brna);
rna_def_userdef_theme_collection_color(brna);
+ rna_def_userdef_theme_strip_color(brna);
rna_def_userdef_themes(brna);
}
@@ -6285,6 +6309,13 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Enable library overrides automatic resync detection and process on file load. Disable when "
"dealing with older .blend files that need manual Resync (Enforce) handling");
+ prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1);
+ RNA_def_property_ui_text(
+ prop,
+ "Proxy to Override Auto Conversion",
+ "Enable automatic conversion of proxies to library overrides on file load");
+
prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1);
RNA_def_property_ui_text(
@@ -6328,9 +6359,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Override Templates", "Enable library override template in the python API");
- prop = RNA_def_property(srna, "use_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1);
- RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes");
+ prop = RNA_def_property(srna, "use_geometry_nodes_legacy", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_legacy", 1);
+ RNA_def_property_ui_text(
+ prop, "Geometry Nodes Legacy", "Enable legacy geometry nodes in the menu");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index 8ed53c9f70f..f6b8b55688c 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -41,6 +41,16 @@
# include "WM_api.h"
# include "WM_types.h"
+static char *rna_VolumeRender_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("render");
+}
+
+static char *rna_VolumeDisplay_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("display");
+}
+
/* Updates */
static void rna_Volume_update_display(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
@@ -371,6 +381,7 @@ static void rna_def_volume_display(BlenderRNA *brna)
srna = RNA_def_struct(brna, "VolumeDisplay", NULL);
RNA_def_struct_ui_text(srna, "Volume Display", "Volume object display settings for 3D viewport");
RNA_def_struct_sdna(srna, "VolumeDisplay");
+ RNA_def_struct_path_func(srna, "rna_VolumeDisplay_path");
prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -477,6 +488,7 @@ static void rna_def_volume_render(BlenderRNA *brna)
srna = RNA_def_struct(brna, "VolumeRender", NULL);
RNA_def_struct_ui_text(srna, "Volume Render", "Volume object render settings");
RNA_def_struct_sdna(srna, "VolumeRender");
+ RNA_def_struct_path_func(srna, "rna_VolumeRender_path");
static const EnumPropertyItem space_items[] = {
{VOLUME_SPACE_OBJECT,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index fa2f70e1a9c..cf0658d4b39 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -194,7 +194,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
}
BKE_ptcache_free_list(&tclmd->ptcaches);
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* Share the cache with the original object's modifier. */
tclmd->modifier.flag |= eModifierFlag_SharedCaches;
tclmd->ptcaches = clmd->ptcaches;
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index e2b2cc58d48..34bb93cbbbc 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -163,7 +163,6 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return !dtmd->ob_source || dtmd->ob_source->type != OB_MESH;
}
-#define HIGH_POLY_WARNING 10000
#define DT_TYPES_AFFECT_MESH \
(DT_TYPE_BWEIGHT_VERT | DT_TYPE_BWEIGHT_EDGE | DT_TYPE_CREASE | DT_TYPE_SHARP_EDGE | \
DT_TYPE_LNOR | DT_TYPE_SHARP_FACE)
@@ -239,13 +238,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BKE_modifier_set_error(
ctx->object, (ModifierData *)dtmd, "Enable 'Auto Smooth' in Object Data Properties");
}
- else if (result->totvert > HIGH_POLY_WARNING ||
- ((Mesh *)(ob_source->data))->totvert > HIGH_POLY_WARNING) {
- BKE_modifier_set_error(
- ctx->object,
- md,
- "Source or destination object has a high polygon count, computation might be slow");
- }
return result;
}
@@ -473,7 +465,6 @@ static void panelRegister(ARegionType *region_type)
region_type, "advanced", "Topology Mapping", NULL, advanced_panel_draw, panel_type);
}
-#undef HIGH_POLY_WARNING
#undef DT_TYPES_AFFECT_MESH
ModifierTypeInfo modifierType_DataTransfer = {
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 8c02c83d479..d294491d51c 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -88,6 +88,7 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_node_declaration.hh"
#include "FN_field.hh"
#include "FN_multi_function.hh"
@@ -103,9 +104,13 @@ using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
+using blender::bke::OutputAttribute;
+using blender::fn::GField;
using blender::fn::GMutablePointer;
using blender::fn::GPointer;
+using blender::nodes::FieldInferencingInterface;
using blender::nodes::GeoNodeExecParams;
+using blender::nodes::InputSocketFieldType;
using blender::threading::EnumerableThreadSpecific;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -306,6 +311,17 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket)
return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT);
}
+/**
+ * \return Whether using an attribute to input values of this type is supported, and the node
+ * group's input for this socket accepts a field rather than just single values.
+ */
+static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
+{
+ BLI_assert(node_tree.field_inferencing_interface != nullptr);
+ const FieldInferencingInterface &field_interface = *node_tree.field_inferencing_interface;
+ return field_interface.inputs[socket_index] != InputSocketFieldType::None;
+}
+
static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
{
switch (socket.type) {
@@ -531,7 +547,8 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
}
- LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
+ int socket_index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) {
IDProperty *new_prop = id_property_create_from_socket(*socket);
if (new_prop == nullptr) {
/* Out of the set of supported input sockets, only
@@ -562,7 +579,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
}
- if (socket_type_has_attribute_toggle(*socket)) {
+ if (input_has_attribute_toggle(*nmd->node_group, socket_index)) {
const std::string use_attribute_id = socket->identifier + use_attribute_suffix;
const std::string attribute_name_id = socket->identifier + attribute_name_suffix;
@@ -589,6 +606,35 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
}
+ LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
+ if (!socket_type_has_attribute_toggle(*socket)) {
+ continue;
+ }
+
+ const std::string idprop_name = socket->identifier + attribute_name_suffix;
+ IDProperty *new_prop = IDP_NewString("", idprop_name.c_str(), MAX_NAME);
+ if (socket->description[0] != '\0') {
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
+ ui_data->description = BLI_strdup(socket->description);
+ }
+ IDP_AddToGroup(nmd->settings.properties, new_prop);
+
+ if (old_properties != nullptr) {
+ IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, idprop_name.c_str());
+ if (old_prop != nullptr) {
+ /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
+ * want to replace the values). So release it temporarily and replace it after. */
+ IDPropertyUIData *ui_data = new_prop->ui_data;
+ new_prop->ui_data = nullptr;
+ IDP_CopyPropertyContent(new_prop, old_prop);
+ if (new_prop->ui_data != nullptr) {
+ IDP_ui_data_free(new_prop);
+ }
+ new_prop->ui_data = ui_data;
+ }
+ }
+ }
+
if (old_properties != nullptr) {
IDP_FreeProperty(old_properties);
}
@@ -624,37 +670,39 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const bNodeSocket &socket,
+ const OutputSocketRef &socket,
void *r_value)
{
+ const bNodeSocketType &socket_type = *socket.typeinfo();
+ const bNodeSocket &bsocket = *socket.bsocket();
if (nmd.settings.properties == nullptr) {
- socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
+ socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
return;
}
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
- socket.identifier);
+ socket.identifier().c_str());
if (property == nullptr) {
- socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
+ socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
return;
}
- if (!id_property_type_matches_socket(socket, *property)) {
- socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
+ if (!id_property_type_matches_socket(bsocket, *property)) {
+ socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
return;
}
- if (!socket_type_has_attribute_toggle(socket)) {
+ if (!input_has_attribute_toggle(*nmd.node_group, socket.index())) {
init_socket_cpp_value_from_property(
- *property, static_cast<eNodeSocketDatatype>(socket.type), r_value);
+ *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value);
return;
}
const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
- nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str());
+ nmd.settings.properties, (socket.identifier() + use_attribute_suffix).c_str());
const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
- nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str());
+ nmd.settings.properties, (socket.identifier() + attribute_name_suffix).c_str());
if (property_use_attribute == nullptr || property_attribute_name == nullptr) {
init_socket_cpp_value_from_property(
- *property, static_cast<eNodeSocketDatatype>(socket.type), r_value);
+ *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value);
return;
}
@@ -662,12 +710,12 @@ static void initialize_group_input(NodesModifierData &nmd,
if (use_attribute) {
const StringRef attribute_name{IDP_String(property_attribute_name)};
auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>(
- attribute_name, *socket.typeinfo->get_base_cpp_type());
+ attribute_name, *socket_type.get_base_cpp_type());
new (r_value) blender::fn::GField(std::move(attribute_input), 0);
}
else {
init_socket_cpp_value_from_property(
- *property, static_cast<eNodeSocketDatatype>(socket.type), r_value);
+ *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value);
}
}
@@ -778,14 +826,78 @@ static void clear_runtime_data(NodesModifierData *nmd)
}
}
+static void store_field_on_geometry_component(GeometryComponent &component,
+ const StringRef attribute_name,
+ AttributeDomain domain,
+ const GField &field)
+{
+ /* If the attribute name corresponds to a built-in attribute, use the domain of the built-in
+ * attribute instead. */
+ if (component.attribute_is_builtin(attribute_name)) {
+ component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_name);
+ if (meta_data.has_value()) {
+ domain = meta_data->domain;
+ }
+ else {
+ return;
+ }
+ }
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(field.cpp_type());
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
+ attribute_name, domain, data_type);
+ if (attribute) {
+ /* In the future we could also evaluate all output fields at once. */
+ const int domain_size = component.attribute_domain_size(domain);
+ blender::bke::GeometryComponentFieldContext field_context{component, domain};
+ blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ field_evaluator.add_with_destination(field, attribute.varray());
+ field_evaluator.evaluate();
+ attribute.save();
+ }
+}
+
+static void store_output_value_in_geometry(GeometrySet &geometry_set,
+ NodesModifierData *nmd,
+ const InputSocketRef &socket,
+ const GPointer value)
+{
+ if (!socket_type_has_attribute_toggle(*socket.bsocket())) {
+ return;
+ }
+ const std::string prop_name = socket.identifier() + attribute_name_suffix;
+ const IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, prop_name.c_str());
+ if (prop == nullptr) {
+ return;
+ }
+ const StringRefNull attribute_name = IDP_String(prop);
+ if (attribute_name.is_empty()) {
+ return;
+ }
+ const GField &field = *(const GField *)value.get();
+ const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs,
+ socket.index());
+ const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain;
+ if (geometry_set.has_mesh()) {
+ MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ store_field_on_geometry_component(component, attribute_name, domain, field);
+ }
+ if (geometry_set.has_pointcloud()) {
+ PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>();
+ store_field_on_geometry_component(component, attribute_name, domain, field);
+ }
+ if (geometry_set.has_curve()) {
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ store_field_on_geometry_component(component, attribute_name, domain, field);
+ }
+}
+
/**
* Evaluate a node group to compute the output geometry.
- * Currently, this uses a fairly basic and inefficient algorithm that might compute things more
- * often than necessary. It's going to be replaced soon.
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Span<const NodeRef *> group_input_nodes,
- const InputSocketRef &socket_to_compute,
+ const NodeRef &output_node,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
@@ -819,7 +931,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type();
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, *socket->bsocket(), value_in);
+ initialize_group_input(*nmd, *socket, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
@@ -828,7 +940,9 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
input_geometry_set.clear();
Vector<DInputSocket> group_outputs;
- group_outputs.append({root_context, &socket_to_compute});
+ for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) {
+ group_outputs.append({root_context, socket_ref});
+ }
std::optional<geo_log::GeoLogger> geo_logger;
@@ -856,9 +970,15 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger);
}
- BLI_assert(eval_params.r_output_values.size() == 1);
- GMutablePointer result = eval_params.r_output_values[0];
- return result.relocate_out<GeometrySet>();
+ GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out<GeometrySet>();
+
+ for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) {
+ GMutablePointer socket_value = eval_params.r_output_values[socket->index()];
+ store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value);
+ socket_value.destruct();
+ }
+
+ return output_geometry_set;
}
/**
@@ -928,24 +1048,23 @@ static void modifyGeometry(ModifierData *md,
const NodeTreeRef &root_tree_ref = tree.root_context().tree();
Span<const NodeRef *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput");
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
-
if (output_nodes.size() != 1) {
return;
}
- Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
-
- if (group_outputs.size() == 0) {
+ const NodeRef &output_node = *output_nodes[0];
+ Span<const InputSocketRef *> group_outputs = output_node.inputs().drop_back(1);
+ if (group_outputs.is_empty()) {
return;
}
- const InputSocketRef *group_output = group_outputs[0];
- if (group_output->idname() != "NodeSocketGeometry") {
+ const InputSocketRef *first_output_socket = group_outputs[0];
+ if (first_output_socket->idname() != "NodeSocketGeometry") {
return;
}
geometry_set = compute_geometry(
- tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx);
+ tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
@@ -981,7 +1100,8 @@ static void draw_property_for_socket(uiLayout *layout,
NodesModifierData *nmd,
PointerRNA *bmain_ptr,
PointerRNA *md_ptr,
- const bNodeSocket &socket)
+ const bNodeSocket &socket,
+ const int socket_index)
{
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier);
@@ -1026,8 +1146,7 @@ static void draw_property_for_socket(uiLayout *layout,
break;
}
default: {
- if (socket_type_has_attribute_toggle(socket) &&
- USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) {
+ if (input_has_attribute_toggle(*nmd->node_group, socket_index)) {
const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) +
use_attribute_suffix + "\"]";
const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) +
@@ -1060,6 +1179,18 @@ static void draw_property_for_socket(uiLayout *layout,
}
}
+static void draw_property_for_output_socket(uiLayout *layout,
+ PointerRNA *md_ptr,
+ const bNodeSocket &socket)
+{
+ char socket_id_esc[sizeof(socket.identifier) * 2];
+ BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+ const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) +
+ attribute_name_suffix + "\"]";
+
+ uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE);
+}
+
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -1086,8 +1217,9 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA bmain_ptr;
RNA_main_pointer_create(bmain, &bmain_ptr);
- LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
- draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket);
+ int socket_index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) {
+ draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket, socket_index);
}
}
@@ -1107,7 +1239,7 @@ static void panel_draw(const bContext *C, Panel *panel)
});
}
- if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields) && has_legacy_node) {
+ if (has_legacy_node) {
uiLayout *row = uiLayoutRow(layout, false);
uiItemL(row, IFACE_("Node tree has legacy node"), ICON_ERROR);
uiLayout *sub = uiLayoutRow(row, false);
@@ -1118,9 +1250,34 @@ static void panel_draw(const bContext *C, Panel *panel)
modifier_panel_end(layout, ptr);
}
+static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+ NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, true);
+
+ if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
+ if (socket_type_has_attribute_toggle(*socket)) {
+ draw_property_for_output_socket(layout, ptr, *socket);
+ }
+ }
+ }
+}
+
static void panelRegister(ARegionType *region_type)
{
- modifier_panel_register(region_type, eModifierType_Nodes, panel_draw);
+ PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw);
+ modifier_subpanel_register(region_type,
+ "output_attributes",
+ N_("Output Attributes"),
+ nullptr,
+ output_attribute_panel_draw,
+ panel_type);
}
static void blendWrite(BlendWriter *writer, const ModifierData *md)
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index e50c07ce6f2..c5213dc304b 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -328,13 +328,24 @@ static void get_socket_value(const SocketRef &socket, void *r_value)
* more complex defaults (other than just single values) in their socket declarations. */
if (bsocket.flag & SOCK_HIDE_VALUE) {
const bNode &bnode = *socket.bnode();
- if (bsocket.type == SOCK_VECTOR &&
- ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) {
- new (r_value) Field<float3>(
- std::make_shared<bke::AttributeFieldInput>("position", CPPType::get<float3>()));
- return;
+ if (bsocket.type == SOCK_VECTOR) {
+ if (ELEM(bnode.type,
+ GEO_NODE_SET_POSITION,
+ SH_NODE_TEX_NOISE,
+ GEO_NODE_MESH_TO_POINTS,
+ GEO_NODE_PROXIMITY)) {
+ new (r_value) Field<float3>(bke::AttributeFieldInput::Create<float3>("position"));
+ return;
+ }
+ }
+ else if (bsocket.type == SOCK_INT) {
+ if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
+ new (r_value) Field<int>(std::make_shared<fn::IndexFieldInput>());
+ return;
+ }
}
}
+
const bNodeSocketType *typeinfo = socket.typeinfo();
typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value);
}
@@ -870,11 +881,9 @@ class GeometryNodesEvaluator {
NodeParamsProvider params_provider{*this, node, node_state};
GeoNodeExecParams params{params_provider};
- if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) {
- if (node->idname().find("Legacy") != StringRef::not_found) {
- params.error_message_add(geo_log::NodeWarningType::Legacy,
- TIP_("Legacy node will be removed before Blender 4.0"));
- }
+ if (node->idname().find("Legacy") != StringRef::not_found) {
+ params.error_message_add(geo_log::NodeWarningType::Legacy,
+ TIP_("Legacy node will be removed before Blender 4.0"));
}
bnode.typeinfo->geometry_node_execute(params);
}
@@ -883,6 +892,14 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
+ if (node->idname().find("Legacy") != StringRef::not_found) {
+ /* Create geometry nodes params just for creating an error message. */
+ NodeParamsProvider params_provider{*this, node, node_state};
+ GeoNodeExecParams params{params_provider};
+ params.error_message_add(geo_log::NodeWarningType::Legacy,
+ TIP_("Legacy node will be removed before Blender 4.0"));
+ }
+
LinearAllocator<> &allocator = local_allocators_.local();
/* Prepare the inputs for the multi function. */
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 8f9aa86e561..54a508ff5e2 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -1043,8 +1043,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
#define SOLIDIFY_SIDE_NORMALS
#ifdef SOLIDIFY_SIDE_NORMALS
- /* Note that, due to the code setting cd_dirty_vert a few lines above,
- * do_side_normals is always false. - Sybren */
+ /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above,
+ * do_side_normals is always false. */
const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL);
/* annoying to allocate these since we only need the edge verts, */
float(*edge_vert_nos)[3] = do_side_normals ?
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index e6af3ecafbc..903a30dd383 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -45,137 +45,159 @@ set(INC
set(SRC
- composite/nodes/node_composite_alphaOver.c
- composite/nodes/node_composite_antialiasing.c
- composite/nodes/node_composite_bilateralblur.c
- composite/nodes/node_composite_blur.c
- composite/nodes/node_composite_bokehblur.c
- composite/nodes/node_composite_bokehimage.c
- composite/nodes/node_composite_boxmask.c
- composite/nodes/node_composite_brightness.c
- composite/nodes/node_composite_channelMatte.c
- composite/nodes/node_composite_chromaMatte.c
- composite/nodes/node_composite_colorMatte.c
- composite/nodes/node_composite_colorSpill.c
- composite/nodes/node_composite_colorbalance.c
- composite/nodes/node_composite_colorcorrection.c
- composite/nodes/node_composite_common.c
- composite/nodes/node_composite_composite.c
- composite/nodes/node_composite_cornerpin.c
- composite/nodes/node_composite_crop.c
+ composite/nodes/node_composite_alphaOver.cc
+ composite/nodes/node_composite_antialiasing.cc
+ composite/nodes/node_composite_bilateralblur.cc
+ composite/nodes/node_composite_blur.cc
+ composite/nodes/node_composite_bokehblur.cc
+ composite/nodes/node_composite_bokehimage.cc
+ composite/nodes/node_composite_boxmask.cc
+ composite/nodes/node_composite_brightness.cc
+ composite/nodes/node_composite_channelMatte.cc
+ composite/nodes/node_composite_chromaMatte.cc
+ composite/nodes/node_composite_colorMatte.cc
+ composite/nodes/node_composite_colorSpill.cc
+ composite/nodes/node_composite_colorbalance.cc
+ composite/nodes/node_composite_colorcorrection.cc
+ composite/nodes/node_composite_common.cc
+ composite/nodes/node_composite_composite.cc
+ composite/nodes/node_composite_cornerpin.cc
+ composite/nodes/node_composite_crop.cc
composite/nodes/node_composite_cryptomatte.cc
- composite/nodes/node_composite_curves.c
- composite/nodes/node_composite_defocus.c
- composite/nodes/node_composite_denoise.c
- composite/nodes/node_composite_despeckle.c
- composite/nodes/node_composite_diffMatte.c
- composite/nodes/node_composite_dilate.c
- composite/nodes/node_composite_directionalblur.c
- composite/nodes/node_composite_displace.c
- composite/nodes/node_composite_distanceMatte.c
- composite/nodes/node_composite_doubleEdgeMask.c
- composite/nodes/node_composite_ellipsemask.c
- composite/nodes/node_composite_exposure.c
- composite/nodes/node_composite_filter.c
- composite/nodes/node_composite_flip.c
- composite/nodes/node_composite_gamma.c
- composite/nodes/node_composite_glare.c
- composite/nodes/node_composite_hueSatVal.c
- composite/nodes/node_composite_huecorrect.c
- composite/nodes/node_composite_idMask.c
- composite/nodes/node_composite_image.c
- composite/nodes/node_composite_inpaint.c
- composite/nodes/node_composite_invert.c
- composite/nodes/node_composite_keying.c
- composite/nodes/node_composite_keyingscreen.c
- composite/nodes/node_composite_lensdist.c
- composite/nodes/node_composite_levels.c
- composite/nodes/node_composite_lummaMatte.c
- composite/nodes/node_composite_mapRange.c
- composite/nodes/node_composite_mapUV.c
- composite/nodes/node_composite_mapValue.c
- composite/nodes/node_composite_mask.c
- composite/nodes/node_composite_math.c
- composite/nodes/node_composite_mixrgb.c
- composite/nodes/node_composite_movieclip.c
- composite/nodes/node_composite_moviedistortion.c
- composite/nodes/node_composite_normal.c
- composite/nodes/node_composite_normalize.c
- composite/nodes/node_composite_outputFile.c
- composite/nodes/node_composite_pixelate.c
- composite/nodes/node_composite_planetrackdeform.c
- composite/nodes/node_composite_posterize.c
- composite/nodes/node_composite_premulkey.c
- composite/nodes/node_composite_rgb.c
- composite/nodes/node_composite_rotate.c
- composite/nodes/node_composite_scale.c
- composite/nodes/node_composite_sepcombHSVA.c
- composite/nodes/node_composite_sepcombRGBA.c
- composite/nodes/node_composite_sepcombYCCA.c
- composite/nodes/node_composite_sepcombYUVA.c
- composite/nodes/node_composite_setalpha.c
- composite/nodes/node_composite_splitViewer.c
- composite/nodes/node_composite_stabilize2d.c
- composite/nodes/node_composite_sunbeams.c
- composite/nodes/node_composite_switch.c
- composite/nodes/node_composite_switchview.c
- composite/nodes/node_composite_texture.c
- composite/nodes/node_composite_tonemap.c
- composite/nodes/node_composite_trackpos.c
- composite/nodes/node_composite_transform.c
- composite/nodes/node_composite_translate.c
- composite/nodes/node_composite_valToRgb.c
- composite/nodes/node_composite_value.c
- composite/nodes/node_composite_vecBlur.c
- composite/nodes/node_composite_viewer.c
- composite/nodes/node_composite_zcombine.c
+ composite/nodes/node_composite_curves.cc
+ composite/nodes/node_composite_defocus.cc
+ composite/nodes/node_composite_denoise.cc
+ composite/nodes/node_composite_despeckle.cc
+ composite/nodes/node_composite_diffMatte.cc
+ composite/nodes/node_composite_dilate.cc
+ composite/nodes/node_composite_directionalblur.cc
+ composite/nodes/node_composite_displace.cc
+ composite/nodes/node_composite_distanceMatte.cc
+ composite/nodes/node_composite_doubleEdgeMask.cc
+ composite/nodes/node_composite_ellipsemask.cc
+ composite/nodes/node_composite_exposure.cc
+ composite/nodes/node_composite_filter.cc
+ composite/nodes/node_composite_flip.cc
+ composite/nodes/node_composite_gamma.cc
+ composite/nodes/node_composite_glare.cc
+ composite/nodes/node_composite_hueSatVal.cc
+ composite/nodes/node_composite_huecorrect.cc
+ composite/nodes/node_composite_idMask.cc
+ composite/nodes/node_composite_image.cc
+ composite/nodes/node_composite_inpaint.cc
+ composite/nodes/node_composite_invert.cc
+ composite/nodes/node_composite_keying.cc
+ composite/nodes/node_composite_keyingscreen.cc
+ composite/nodes/node_composite_lensdist.cc
+ composite/nodes/node_composite_levels.cc
+ composite/nodes/node_composite_lummaMatte.cc
+ composite/nodes/node_composite_mapRange.cc
+ composite/nodes/node_composite_mapUV.cc
+ composite/nodes/node_composite_mapValue.cc
+ composite/nodes/node_composite_mask.cc
+ composite/nodes/node_composite_math.cc
+ composite/nodes/node_composite_mixrgb.cc
+ composite/nodes/node_composite_movieclip.cc
+ composite/nodes/node_composite_moviedistortion.cc
+ composite/nodes/node_composite_normal.cc
+ composite/nodes/node_composite_normalize.cc
+ composite/nodes/node_composite_outputFile.cc
+ composite/nodes/node_composite_pixelate.cc
+ composite/nodes/node_composite_planetrackdeform.cc
+ composite/nodes/node_composite_posterize.cc
+ composite/nodes/node_composite_premulkey.cc
+ composite/nodes/node_composite_rgb.cc
+ composite/nodes/node_composite_rotate.cc
+ composite/nodes/node_composite_scale.cc
+ composite/nodes/node_composite_sepcombHSVA.cc
+ composite/nodes/node_composite_sepcombRGBA.cc
+ composite/nodes/node_composite_sepcombYCCA.cc
+ composite/nodes/node_composite_sepcombYUVA.cc
+ composite/nodes/node_composite_setalpha.cc
+ composite/nodes/node_composite_splitViewer.cc
+ composite/nodes/node_composite_stabilize2d.cc
+ composite/nodes/node_composite_sunbeams.cc
+ composite/nodes/node_composite_switch.cc
+ composite/nodes/node_composite_switchview.cc
+ composite/nodes/node_composite_texture.cc
+ composite/nodes/node_composite_tonemap.cc
+ composite/nodes/node_composite_trackpos.cc
+ composite/nodes/node_composite_transform.cc
+ composite/nodes/node_composite_translate.cc
+ composite/nodes/node_composite_valToRgb.cc
+ composite/nodes/node_composite_value.cc
+ composite/nodes/node_composite_vecBlur.cc
+ composite/nodes/node_composite_viewer.cc
+ composite/nodes/node_composite_zcombine.cc
- composite/node_composite_tree.c
- composite/node_composite_util.c
+ composite/node_composite_tree.cc
+ composite/node_composite_util.cc
+
+ function/nodes/legacy/node_fn_random_float.cc
function/nodes/node_fn_boolean_math.cc
function/nodes/node_fn_float_compare.cc
function/nodes/node_fn_float_to_int.cc
+ function/nodes/node_fn_input_special_characters.cc
function/nodes/node_fn_input_string.cc
function/nodes/node_fn_input_vector.cc
- function/nodes/node_fn_random_float.cc
+ function/nodes/node_fn_random_value.cc
+ function/nodes/node_fn_rotate_euler.cc
function/nodes/node_fn_string_length.cc
function/nodes/node_fn_string_substring.cc
function/nodes/node_fn_value_to_string.cc
function/node_function_util.cc
+ geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
+ geometry/nodes/legacy/node_geo_attribute_clamp.cc
+ geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
+ geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
+ geometry/nodes/legacy/node_geo_attribute_compare.cc
+ geometry/nodes/legacy/node_geo_attribute_convert.cc
+ geometry/nodes/legacy/node_geo_attribute_curve_map.cc
+ geometry/nodes/legacy/node_geo_attribute_fill.cc
+ geometry/nodes/legacy/node_geo_attribute_map_range.cc
+ geometry/nodes/legacy/node_geo_attribute_math.cc
+ geometry/nodes/legacy/node_geo_attribute_mix.cc
+ geometry/nodes/legacy/node_geo_attribute_proximity.cc
+ geometry/nodes/legacy/node_geo_attribute_randomize.cc
+ geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
+ geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/legacy/node_geo_attribute_transfer.cc
+ geometry/nodes/legacy/node_geo_attribute_vector_math.cc
+ geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
+ geometry/nodes/legacy/node_geo_curve_endpoints.cc
+ geometry/nodes/legacy/node_geo_curve_reverse.cc
+ geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
+ geometry/nodes/legacy/node_geo_curve_set_handles.cc
+ geometry/nodes/legacy/node_geo_curve_spline_type.cc
+ geometry/nodes/legacy/node_geo_curve_subdivide.cc
+ geometry/nodes/legacy/node_geo_curve_to_points.cc
+ geometry/nodes/legacy/node_geo_delete_geometry.cc
+ geometry/nodes/legacy/node_geo_edge_split.cc
geometry/nodes/legacy/node_geo_material_assign.cc
+ geometry/nodes/legacy/node_geo_mesh_to_curve.cc
+ geometry/nodes/legacy/node_geo_point_distribute.cc
+ geometry/nodes/legacy/node_geo_point_instance.cc
+ geometry/nodes/legacy/node_geo_point_rotate.cc
+ geometry/nodes/legacy/node_geo_point_scale.cc
+ geometry/nodes/legacy/node_geo_point_separate.cc
+ geometry/nodes/legacy/node_geo_point_translate.cc
+ geometry/nodes/legacy/node_geo_points_to_volume.cc
+ geometry/nodes/legacy/node_geo_raycast.cc
geometry/nodes/legacy/node_geo_select_by_material.cc
+ geometry/nodes/legacy/node_geo_subdivision_surface.cc
- geometry/nodes/node_geo_align_rotation_to_vector.cc
geometry/nodes/node_geo_attribute_capture.cc
- geometry/nodes/node_geo_attribute_clamp.cc
- geometry/nodes/node_geo_attribute_color_ramp.cc
- geometry/nodes/node_geo_attribute_combine_xyz.cc
- geometry/nodes/node_geo_attribute_compare.cc
- geometry/nodes/node_geo_attribute_convert.cc
- geometry/nodes/node_geo_attribute_curve_map.cc
- geometry/nodes/node_geo_attribute_fill.cc
- geometry/nodes/node_geo_attribute_map_range.cc
- geometry/nodes/node_geo_attribute_math.cc
- geometry/nodes/node_geo_attribute_mix.cc
- geometry/nodes/node_geo_attribute_proximity.cc
- geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_remove.cc
- geometry/nodes/node_geo_attribute_sample_texture.cc
- geometry/nodes/node_geo_attribute_separate_xyz.cc
geometry/nodes/node_geo_attribute_statistic.cc
- geometry/nodes/node_geo_attribute_transfer.cc
- geometry/nodes/node_geo_attribute_vector_math.cc
- geometry/nodes/node_geo_attribute_vector_rotate.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_convex_hull.cc
- geometry/nodes/node_geo_curve_sample.cc
- geometry/nodes/node_geo_curve_endpoints.cc
geometry/nodes/node_geo_curve_fill.cc
+ geometry/nodes/node_geo_curve_fillet.cc
geometry/nodes/node_geo_curve_length.cc
geometry/nodes/node_geo_curve_parameter.cc
geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
@@ -187,21 +209,20 @@ set(SRC
geometry/nodes/node_geo_curve_primitive_star.cc
geometry/nodes/node_geo_curve_resample.cc
geometry/nodes/node_geo_curve_reverse.cc
- geometry/nodes/node_geo_curve_select_by_handle_type.cc
+ geometry/nodes/node_geo_curve_sample.cc
geometry/nodes/node_geo_curve_set_handles.cc
geometry/nodes/node_geo_curve_spline_type.cc
geometry/nodes/node_geo_curve_subdivide.cc
- geometry/nodes/node_geo_curve_fillet.cc
geometry/nodes/node_geo_curve_to_mesh.cc
- geometry/nodes/node_geo_curve_to_points.cc
geometry/nodes/node_geo_curve_trim.cc
- geometry/nodes/node_geo_delete_geometry.cc
- geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_distribute_points_on_faces.cc
+ geometry/nodes/node_geo_input_index.cc
geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_input_normal.cc
geometry/nodes/node_geo_input_position.cc
+ geometry/nodes/node_geo_input_spline_length.cc
geometry/nodes/node_geo_input_tangent.cc
- geometry/nodes/node_geo_input_index.cc
+ geometry/nodes/node_geo_instance_on_points.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
geometry/nodes/node_geo_material_assign.cc
@@ -216,26 +237,21 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_line.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
- geometry/nodes/node_geo_mesh_to_curve.cc
+ geometry/nodes/node_geo_mesh_to_points.cc
geometry/nodes/node_geo_object_info.cc
- geometry/nodes/node_geo_point_distribute.cc
- geometry/nodes/node_geo_point_instance.cc
- geometry/nodes/node_geo_point_rotate.cc
- geometry/nodes/node_geo_point_scale.cc
- geometry/nodes/node_geo_point_separate.cc
- geometry/nodes/node_geo_point_translate.cc
- geometry/nodes/node_geo_points_to_volume.cc
- geometry/nodes/node_geo_raycast.cc
+ geometry/nodes/node_geo_points_to_vertices.cc
+ geometry/nodes/node_geo_proximity.cc
geometry/nodes/node_geo_realize_instances.cc
geometry/nodes/node_geo_separate_components.cc
geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_string_join.cc
- geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_string_to_curves.cc
geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_viewer.cc
geometry/nodes/node_geo_volume_to_mesh.cc
+
geometry/node_geometry_exec.cc
geometry/node_geometry_tree.cc
geometry/node_geometry_util.cc
@@ -362,18 +378,18 @@ set(SRC
intern/derived_node_tree.cc
intern/geometry_nodes_eval_log.cc
intern/math_functions.cc
- intern/node_common.c
+ intern/node_common.cc
+ intern/node_declaration.cc
intern/node_exec.cc
intern/node_geometry_exec.cc
intern/node_multi_function.cc
- intern/node_declaration.cc
- intern/node_socket_declarations.cc
intern/node_socket.cc
+ intern/node_socket_declarations.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_conversions.cc
- composite/node_composite_util.h
+ composite/node_composite_util.hh
function/node_function_util.hh
shader/node_shader_util.h
geometry/node_geometry_util.hh
@@ -389,10 +405,10 @@ set(SRC
NOD_math_functions.hh
NOD_multi_function.hh
NOD_node_declaration.hh
- NOD_socket_declarations.hh
NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
+ NOD_socket_declarations.hh
NOD_static_types.h
NOD_texture.h
NOD_type_conversions.hh
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 2cbbd31c97a..d243577f68d 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -145,7 +145,7 @@ void node_cmp_rlayers_register_pass(struct bNodeTree *ntree,
struct Scene *scene,
struct ViewLayer *view_layer,
const char *name,
- int type);
+ eNodeSocketDatatype type);
const char *node_cmp_rlayers_sock_to_pass(int sock_index);
void register_node_type_cmp_custom_group(bNodeType *ntype);
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index a67458418f2..450e999bea4 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -20,12 +20,16 @@
extern "C" {
#endif
+void register_node_type_fn_legacy_random_float(void);
+
void register_node_type_fn_boolean_math(void);
void register_node_type_fn_float_compare(void);
void register_node_type_fn_float_to_int(void);
+void register_node_type_fn_input_special_characters(void);
void register_node_type_fn_input_string(void);
void register_node_type_fn_input_vector(void);
-void register_node_type_fn_random_float(void);
+void register_node_type_fn_random_value(void);
+void register_node_type_fn_rotate_euler(void);
void register_node_type_fn_string_length(void);
void register_node_type_fn_string_substring(void);
void register_node_type_fn_value_to_string(void);
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 47bc54132eb..bb90c7b6c51 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -29,10 +29,17 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_custom_group(bNodeType *ntype);
+void register_node_type_geo_legacy_curve_set_handles(void);
+void register_node_type_geo_legacy_attribute_proximity(void);
+void register_node_type_geo_legacy_attribute_randomize(void);
void register_node_type_geo_legacy_material_assign(void);
void register_node_type_geo_legacy_select_by_material(void);
+void register_node_type_geo_legacy_curve_spline_type(void);
+void register_node_type_geo_legacy_curve_reverse(void);
+void register_node_type_geo_legacy_curve_subdivide(void);
void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_attribute_capture(void);
void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
@@ -40,12 +47,9 @@ void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
-void register_node_type_geo_attribute_capture(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
-void register_node_type_geo_attribute_proximity(void);
-void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_statistic(void);
@@ -58,9 +62,9 @@ void register_node_type_geo_collection_info(void);
void register_node_type_geo_convex_hull(void);
void register_node_type_geo_curve_endpoints(void);
void register_node_type_geo_curve_fill(void);
+void register_node_type_geo_curve_fillet(void);
void register_node_type_geo_curve_length(void);
void register_node_type_geo_curve_parameter(void);
-void register_node_type_geo_curve_sample(void);
void register_node_type_geo_curve_primitive_bezier_segment(void);
void register_node_type_geo_curve_primitive_circle(void);
void register_node_type_geo_curve_primitive_line(void);
@@ -70,20 +74,23 @@ void register_node_type_geo_curve_primitive_spiral(void);
void register_node_type_geo_curve_primitive_star(void);
void register_node_type_geo_curve_resample(void);
void register_node_type_geo_curve_reverse(void);
+void register_node_type_geo_curve_sample(void);
void register_node_type_geo_curve_set_handles(void);
void register_node_type_geo_curve_spline_type(void);
void register_node_type_geo_curve_subdivide(void);
-void register_node_type_geo_curve_fillet(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
+void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_input_index(void);
void register_node_type_geo_input_material(void);
void register_node_type_geo_input_normal(void);
void register_node_type_geo_input_position(void);
+void register_node_type_geo_input_spline_length(void);
void register_node_type_geo_input_tangent(void);
+void register_node_type_geo_instance_on_points(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
void register_node_type_geo_material_assign(void);
@@ -99,6 +106,7 @@ 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_subdivide(void);
void register_node_type_geo_mesh_to_curve(void);
+void register_node_type_geo_mesh_to_points(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);
@@ -106,7 +114,9 @@ void register_node_type_geo_point_rotate(void);
void register_node_type_geo_point_scale(void);
void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
+void register_node_type_geo_points_to_vertices(void);
void register_node_type_geo_points_to_volume(void);
+void register_node_type_geo_proximity(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
void register_node_type_geo_sample_texture(void);
@@ -114,6 +124,7 @@ void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_separate_components(void);
void register_node_type_geo_set_position(void);
void register_node_type_geo_string_join(void);
+void register_node_type_geo_string_to_curves(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 6ce3d0f2ab5..962e1c3c48f 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -46,6 +46,8 @@ using bke::WeakAnonymousAttributeID;
using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::Field;
+using fn::FieldContext;
+using fn::FieldEvaluator;
using fn::FieldInput;
using fn::FieldOperation;
using fn::GField;
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 8e9e72bf4c8..07d4e05cda8 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -27,6 +27,109 @@ namespace blender::nodes {
class NodeDeclarationBuilder;
+enum class InputSocketFieldType {
+ /** The input is required to be a single value. */
+ None,
+ /** The input can be a field. */
+ IsSupported,
+ /** The input can be a field and is a field implicitly if nothing is connected. */
+ Implicit,
+};
+
+enum class OutputSocketFieldType {
+ /** The output is always a single value. */
+ None,
+ /** The output is always a field, independent of the inputs. */
+ FieldSource,
+ /** If any input is a field, this output will be a field as well. */
+ DependentField,
+ /** If any of a subset of inputs is a field, this out will be a field as well.
+ * The subset is defined by the vector of indices. */
+ PartiallyDependent,
+};
+
+/**
+ * Contains information about how a node output's field state depends on inputs of the same node.
+ */
+class OutputFieldDependency {
+ private:
+ OutputSocketFieldType type_ = OutputSocketFieldType::None;
+ Vector<int> linked_input_indices_;
+
+ public:
+ static OutputFieldDependency ForFieldSource()
+ {
+ OutputFieldDependency field_dependency;
+ field_dependency.type_ = OutputSocketFieldType::FieldSource;
+ return field_dependency;
+ }
+
+ static OutputFieldDependency ForDataSource()
+ {
+ OutputFieldDependency field_dependency;
+ field_dependency.type_ = OutputSocketFieldType::None;
+ return field_dependency;
+ }
+
+ static OutputFieldDependency ForPartiallyDependentField(Vector<int> indices)
+ {
+ OutputFieldDependency field_dependency;
+ if (indices.is_empty()) {
+ field_dependency.type_ = OutputSocketFieldType::None;
+ }
+ else {
+ field_dependency.type_ = OutputSocketFieldType::PartiallyDependent;
+ field_dependency.linked_input_indices_ = std::move(indices);
+ }
+ return field_dependency;
+ }
+
+ static OutputFieldDependency ForDependentField()
+ {
+ OutputFieldDependency field_dependency;
+ field_dependency.type_ = OutputSocketFieldType::DependentField;
+ return field_dependency;
+ }
+
+ OutputSocketFieldType field_type() const
+ {
+ return type_;
+ }
+
+ Span<int> linked_input_indices() const
+ {
+ return linked_input_indices_;
+ }
+
+ friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b)
+ {
+ return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_;
+ }
+
+ friend bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b)
+ {
+ return !(a == b);
+ }
+};
+
+/**
+ * Information about how a node interacts with fields.
+ */
+struct FieldInferencingInterface {
+ Vector<InputSocketFieldType> inputs;
+ Vector<OutputFieldDependency> outputs;
+
+ friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b)
+ {
+ return a.inputs == b.inputs && a.outputs == b.outputs;
+ }
+
+ friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b)
+ {
+ return !(a == b);
+ }
+};
+
/**
* Describes a single input or output socket. This is subclassed for different socket types.
*/
@@ -34,11 +137,15 @@ class SocketDeclaration {
protected:
std::string name_;
std::string identifier_;
+ std::string description_;
bool hide_label_ = false;
bool hide_value_ = false;
bool is_multi_input_ = false;
bool no_mute_links_ = false;
+ InputSocketFieldType input_field_type_ = InputSocketFieldType::None;
+ OutputFieldDependency output_field_dependency_;
+
friend NodeDeclarationBuilder;
template<typename SocketDecl> friend class SocketDeclarationBuilder;
@@ -50,8 +157,12 @@ class SocketDeclaration {
virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const;
StringRefNull name() const;
+ StringRefNull description() const;
StringRefNull identifier() const;
+ InputSocketFieldType input_field_type() const;
+ const OutputFieldDependency &output_field_dependency() const;
+
protected:
void set_common_flags(bNodeSocket &socket) const;
bool matches_common_data(const bNodeSocket &socket) const;
@@ -95,11 +206,52 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ Self &description(std::string value = "")
+ {
+ decl_->description_ = std::move(value);
+ return *(Self *)this;
+ }
Self &no_muted_links(bool value = true)
{
decl_->no_mute_links_ = value;
return *(Self *)this;
}
+
+ /** The input socket allows passing in a field. */
+ Self &supports_field()
+ {
+ decl_->input_field_type_ = InputSocketFieldType::IsSupported;
+ return *(Self *)this;
+ }
+
+ /** The input supports a field and is a field by default when nothing is connected. */
+ Self &implicit_field()
+ {
+ this->hide_value();
+ decl_->input_field_type_ = InputSocketFieldType::Implicit;
+ return *(Self *)this;
+ }
+
+ /** The output is always a field, regardless of any inputs. */
+ Self &field_source()
+ {
+ decl_->output_field_dependency_ = OutputFieldDependency::ForFieldSource();
+ return *(Self *)this;
+ }
+
+ /** The output is a field if any of the inputs is a field. */
+ Self &dependent_field()
+ {
+ decl_->output_field_dependency_ = OutputFieldDependency::ForDependentField();
+ return *(Self *)this;
+ }
+
+ /** The output is a field if any of the inputs with indices in the given list is a field. */
+ Self &dependent_field(Vector<int> input_dependencies)
+ {
+ decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField(
+ std::move(input_dependencies));
+ }
};
using SocketDeclarationPtr = std::unique_ptr<SocketDeclaration>;
@@ -108,6 +260,7 @@ class NodeDeclaration {
private:
Vector<SocketDeclarationPtr> inputs_;
Vector<SocketDeclarationPtr> outputs_;
+ bool is_function_node_ = false;
friend NodeDeclarationBuilder;
@@ -118,6 +271,11 @@ class NodeDeclaration {
Span<SocketDeclarationPtr> inputs() const;
Span<SocketDeclarationPtr> outputs() const;
+ bool is_function_node() const
+ {
+ return is_function_node_;
+ }
+
MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration")
};
@@ -129,6 +287,15 @@ class NodeDeclarationBuilder {
public:
NodeDeclarationBuilder(NodeDeclaration &declaration);
+ /**
+ * All inputs support fields, and all outputs are fields if any of the inputs is a field.
+ * Calling field status definitions on each socket is unnecessary.
+ */
+ void is_function_node(bool value = true)
+ {
+ declaration_.is_function_node_ = value;
+ }
+
template<typename DeclType>
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
template<typename DeclType>
@@ -155,6 +322,20 @@ inline StringRefNull SocketDeclaration::identifier() const
return identifier_;
}
+inline StringRefNull SocketDeclaration::description() const
+{
+ return description_;
+}
+inline InputSocketFieldType SocketDeclaration::input_field_type() const
+{
+ return input_field_type_;
+}
+
+inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() const
+{
+ return output_field_dependency_;
+}
+
/* --------------------------------------------------------------------
* NodeDeclarationBuilder inline methods.
*/
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 4f2565cbbaf..1da42fb6425 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -182,6 +182,7 @@ class NodeRef : NonCopyable, NonMovable {
Span<const InputSocketRef *> inputs() const;
Span<const OutputSocketRef *> outputs() const;
Span<const InternalLinkRef *> internal_links() const;
+ Span<const SocketRef *> sockets(eNodeSocketInOut in_out) const;
const InputSocketRef &input(int index) const;
const OutputSocketRef &output(int index) const;
@@ -189,6 +190,10 @@ class NodeRef : NonCopyable, NonMovable {
const InputSocketRef &input_by_identifier(StringRef identifier) const;
const OutputSocketRef &output_by_identifier(StringRef identifier) const;
+ bool any_input_is_directly_linked() const;
+ bool any_output_is_directly_linked() const;
+ bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const;
+
bNode *bnode() const;
bNodeTree *btree() const;
@@ -196,6 +201,7 @@ class NodeRef : NonCopyable, NonMovable {
StringRefNull idname() const;
StringRefNull name() const;
bNodeType *typeinfo() const;
+ const NodeDeclaration *declaration() const;
int id() const;
@@ -272,6 +278,13 @@ class NodeTreeRef : NonCopyable, NonMovable {
bool has_link_cycles() const;
bool has_undefined_nodes_or_sockets() const;
+ enum class ToposortDirection {
+ LeftToRight,
+ RightToLeft,
+ };
+
+ Vector<const NodeRef *> toposort(ToposortDirection direction) const;
+
bNodeTree *btree() const;
StringRefNull name() const;
@@ -496,6 +509,12 @@ inline Span<const OutputSocketRef *> NodeRef::outputs() const
return outputs_;
}
+inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const
+{
+ return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() :
+ outputs_.as_span().cast<const SocketRef *>();
+}
+
inline Span<const InternalLinkRef *> NodeRef::internal_links() const
{
return internal_links_;
@@ -553,6 +572,13 @@ inline bNodeType *NodeRef::typeinfo() const
return bnode_->typeinfo;
}
+/* Returns a pointer because not all nodes have declarations currently. */
+inline const NodeDeclaration *NodeRef::declaration() const
+{
+ nodeDeclarationEnsure(this->tree().btree(), bnode_);
+ return bnode_->declaration;
+}
+
inline int NodeRef::id() const
{
return id_;
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 2911e0fbea6..76c174201e8 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -49,6 +49,7 @@ void register_node_type_sh_normal(void);
void register_node_type_sh_gamma(void);
void register_node_type_sh_brightcontrast(void);
void register_node_type_sh_mapping(void);
+void register_node_type_sh_curve_float(void);
void register_node_type_sh_curve_vec(void);
void register_node_type_sh_curve_rgb(void);
void register_node_type_sh_map_range(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ab673d814bb..c13ab199691 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -132,6 +132,7 @@ DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"
DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" )
DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" )
+DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" )
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -262,18 +263,22 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
+DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "")
+
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
+DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "")
+DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "")
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "")
+DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
-DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "")
@@ -283,18 +288,22 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "L
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "")
-DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_legacy_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "")
-DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "")
@@ -306,18 +315,17 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate,
DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "")
DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
@@ -328,16 +336,21 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
-DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "")
DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "")
+DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
@@ -352,12 +365,15 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
+DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.cc
index cc657d6f91d..a596a85b748 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.cc
@@ -21,7 +21,7 @@
* \ingroup nodes
*/
-#include <stdio.h>
+#include <cstdio>
#include "DNA_color_types.h"
#include "DNA_node_types.h"
@@ -41,7 +41,7 @@
#include "RNA_access.h"
#include "NOD_composite.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#ifdef WITH_COMPOSITOR
# include "COM_compositor.h"
@@ -55,7 +55,7 @@ static void composite_get_from_context(const bContext *C,
{
Scene *scene = CTX_data_scene(C);
- *r_from = NULL;
+ *r_from = nullptr;
*r_id = &scene->id;
*r_ntree = scene->nodetree;
}
@@ -77,19 +77,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node)
{
- bNodeSocket *sock;
-
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (sock->cache) {
- sock->cache = NULL;
+ sock->cache = nullptr;
}
}
}
static void free_cache(bNodeTree *ntree)
{
- bNode *node;
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
free_node_cache(ntree, node);
}
}
@@ -98,16 +95,16 @@ static void free_cache(bNodeTree *ntree)
static void localize(bNodeTree *localtree, bNodeTree *ntree)
{
- bNode *node = ntree->nodes.first;
- bNode *local_node = localtree->nodes.first;
- while (node != NULL) {
+ bNode *node = (bNode *)ntree->nodes.first;
+ bNode *local_node = (bNode *)localtree->nodes.first;
+ while (node != nullptr) {
/* Ensure new user input gets handled ok. */
node->need_exec = 0;
local_node->original = node;
/* move over the compbufs */
- /* right after ntreeCopyTree() oldsock pointers are valid */
+ /* right after #ntreeCopyTree() `oldsock` pointers are valid */
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (node->id) {
@@ -115,16 +112,16 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree)
local_node->id = (ID *)node->id;
}
else {
- local_node->id = NULL;
+ local_node->id = nullptr;
}
}
}
- bNodeSocket *output_sock = node->outputs.first;
- bNodeSocket *local_output_sock = local_node->outputs.first;
- while (output_sock != NULL) {
+ bNodeSocket *output_sock = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *local_output_sock = (bNodeSocket *)local_node->outputs.first;
+ while (output_sock != nullptr) {
local_output_sock->cache = output_sock->cache;
- output_sock->cache = NULL;
+ output_sock->cache = nullptr;
/* This is actually link to original: someone was just lazy enough and tried to save few
* bytes in the cost of readability. */
local_output_sock->new_sock = output_sock;
@@ -151,7 +148,7 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
/* move over the compbufs and previews */
BKE_node_preview_merge_tree(ntree, localtree, true);
- for (lnode = localtree->nodes.first; lnode; lnode = lnode->next) {
+ for (lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) {
if (ntreeNodeExists(ntree, lnode->new_node)) {
if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) {
@@ -165,18 +162,19 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
* copied back to original node */
if (lnode->storage) {
if (lnode->new_node->storage) {
- BKE_tracking_distortion_free(lnode->new_node->storage);
+ BKE_tracking_distortion_free((MovieDistortion *)lnode->new_node->storage);
}
- lnode->new_node->storage = BKE_tracking_distortion_copy(lnode->storage);
+ lnode->new_node->storage = BKE_tracking_distortion_copy(
+ (MovieDistortion *)lnode->storage);
}
}
- for (lsock = lnode->outputs.first; lsock; lsock = lsock->next) {
+ for (lsock = (bNodeSocket *)lnode->outputs.first; lsock; lsock = lsock->next) {
if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) {
lsock->new_sock->cache = lsock->cache;
- lsock->cache = NULL;
- lsock->new_sock = NULL;
+ lsock->cache = nullptr;
+ lsock->new_sock = nullptr;
}
}
}
@@ -216,13 +214,13 @@ bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
{
- bNodeTreeType *tt = ntreeType_Composite = MEM_callocN(sizeof(bNodeTreeType),
- "compositor node tree type");
+ bNodeTreeType *tt = ntreeType_Composite = (bNodeTreeType *)MEM_callocN(
+ sizeof(bNodeTreeType), "compositor node tree type");
tt->type = NTREE_COMPOSIT;
strcpy(tt->idname, "CompositorNodeTree");
strcpy(tt->ui_name, N_("Compositor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = 0; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Compositing nodes"));
tt->free_cache = free_cache;
@@ -241,9 +239,6 @@ void register_node_tree_type_cmp(void)
ntreeTypeAdd(tt);
}
-extern void *COM_linker_hack; /* Quiet warning. */
-void *COM_linker_hack = NULL;
-
void ntreeCompositExecTree(Scene *scene,
bNodeTree *ntree,
RenderData *rd,
@@ -276,13 +271,11 @@ void ntreeCompositExecTree(Scene *scene,
*/
void ntreeCompositUpdateRLayers(bNodeTree *ntree)
{
- bNode *node;
-
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return;
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == CMP_NODE_R_LAYERS) {
node_cmp_rlayers_outputs(ntree, node);
}
@@ -295,13 +288,11 @@ void ntreeCompositRegisterPass(bNodeTree *ntree,
const char *name,
eNodeSocketDatatype type)
{
- bNode *node;
-
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return;
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == CMP_NODE_R_LAYERS) {
node_cmp_rlayers_register_pass(ntree, node, scene, view_layer, name, type);
}
@@ -317,15 +308,14 @@ void ntreeCompositTagRender(Scene *scene)
* This is still rather weak though,
* ideally render struct would store own main AND original G_MAIN. */
- for (Scene *sce_iter = G_MAIN->scenes.first; sce_iter; sce_iter = sce_iter->id.next) {
+ for (Scene *sce_iter = (Scene *)G_MAIN->scenes.first; sce_iter;
+ sce_iter = (Scene *)sce_iter->id.next) {
if (sce_iter->nodetree) {
- bNode *node;
-
- for (node = sce_iter->nodetree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) {
if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) {
nodeUpdate(sce_iter->nodetree, node);
}
- else if (node->type == CMP_NODE_TEXTURE) /* uses scene sizex/sizey */ {
+ else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ {
nodeUpdate(sce_iter->nodetree, node);
}
}
@@ -336,13 +326,11 @@ void ntreeCompositTagRender(Scene *scene)
/* XXX after render animation system gets a refresh, this call allows composite to end clean */
void ntreeCompositClearTags(bNodeTree *ntree)
{
- bNode *node;
-
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return;
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->need_exec = 0;
if (node->type == NODE_GROUP) {
ntreeCompositClearTags((bNodeTree *)node->id);
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.cc
index 6cc17d8c272..86aaec61bc3 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.cc
@@ -21,7 +21,7 @@
* \ingroup nodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
bool cmp_node_poll_default(bNodeType *UNUSED(ntype),
bNodeTree *ntree,
@@ -36,11 +36,10 @@ bool cmp_node_poll_default(bNodeType *UNUSED(ntype),
void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
{
- bNodeSocket *sock;
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (sock->cache) {
// free_compbuf(sock->cache);
- // sock->cache = NULL;
+ // sock->cache = nullptr;
}
}
node->need_exec = 1;
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.hh
index 4fcccbb79f0..6fd82ffc93f 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.hh
@@ -47,20 +47,13 @@
/* only for forward declarations */
#include "NOD_composite.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "NOD_socket_declarations.hh"
#define CMP_SCALE_MAX 12000
bool cmp_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
- const char **r_disabled_info);
+ const char **r_disabled_hint);
void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node);
void cmp_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc
index 7a08bd51575..6210d946bc7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c
+++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc
@@ -21,19 +21,21 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** ALPHAOVER ******************** */
-static bNodeSocketTemplate cmp_node_alphaover_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_alphaover_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_alphaover_init(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -45,7 +47,7 @@ void register_node_type_cmp_alphaover(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_ALPHAOVER, "Alpha Over", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_alphaover_in, cmp_node_alphaover_out);
+ ntype.declare = blender::nodes::cmp_node_alphaover_declare;
node_type_init(&ntype, node_alphaover_init);
node_type_storage(
&ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index a5906c31093..23e63b9a53b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -23,7 +23,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Anti-Aliasing (SMAA 1x) ******************** */
@@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image"
static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data");
+ NodeAntiAliasingData *data = (NodeAntiAliasingData *)MEM_callocN(sizeof(NodeAntiAliasingData),
+ "node antialiasing data");
data->threshold = CMP_DEFAULT_SMAA_THRESHOLD;
data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT;
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index 270a137280c..3e724d17a10 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** BILATERALBLUR ******************** */
static bNodeSocketTemplate cmp_node_bilateralblur_in[] = {
@@ -36,8 +36,8 @@ static bNodeSocketTemplate cmp_node_bilateralblur_out[] = {
static void node_composit_init_bilateralblur(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeBilateralBlurData *nbbd = MEM_callocN(sizeof(NodeBilateralBlurData),
- "node bilateral blur data");
+ NodeBilateralBlurData *nbbd = (NodeBilateralBlurData *)MEM_callocN(sizeof(NodeBilateralBlurData),
+ "node bilateral blur data");
node->storage = nbbd;
nbbd->iter = 1;
nbbd->sigma_color = 0.3;
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.c b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index 92379f4552b..c5c0c21929e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -22,7 +22,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** BLUR ******************** */
static bNodeSocketTemplate cmp_node_blur_in[] = {
@@ -33,7 +33,7 @@ static bNodeSocketTemplate cmp_node_blur_out[] = {{SOCK_RGBA, N_("Image")}, {-1,
static void node_composit_init_blur(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeBlurData *data = MEM_callocN(sizeof(NodeBlurData), "node blur data");
+ NodeBlurData *data = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data");
data->filtertype = R_FILTER_GAUSS;
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index d724a83e5a2..f130a642e20 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -22,7 +22,7 @@
* \ingroup cmpnodes
*/
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** BLUR ******************** */
static bNodeSocketTemplate cmp_node_bokehblur_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 744aba417be..3a4bf94d256 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -21,18 +21,22 @@
* \ingroup cmpnodes
*/
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** Bokeh image Tools ******************** */
-static bNodeSocketTemplate cmp_node_bokehimage_out[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeBokehImage *data = MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage");
+ NodeBokehImage *data = (NodeBokehImage *)MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage");
data->angle = 0.0f;
data->flaps = 5;
data->rounding = 0.0f;
@@ -46,7 +50,7 @@ void register_node_type_cmp_bokehimage(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, NULL, cmp_node_bokehimage_out);
+ ntype.declare = blender::nodes::cmp_node_bokehimage_declare;
node_type_init(&ntype, node_composit_init_bokehimage);
node_type_storage(
&ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.c b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index e646b9a9adf..cdf96065f97 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate cmp_node_boxmask_in[] = {
@@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_boxmask_out[] = {
static void node_composit_init_boxmask(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeBoxMask *data = MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask");
+ NodeBoxMask *data = (NodeBoxMask *)MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask");
data->x = 0.5;
data->y = 0.5;
data->width = 0.2;
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.c b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index 5beecb55665..ad4b09c69d0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.c
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -21,20 +21,21 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-/* **************** Brigh and contrsast ******************** */
+/* **************** Bright and Contrast ******************** */
-static bNodeSocketTemplate cmp_node_brightcontrast_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Bright"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Contrast"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_brightcontrast_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Bright").min(-100.0f).max(100.0f);
+ b.add_input<decl::Float>("Contrast").min(-100.0f).max(100.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_brightcontrast(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -46,7 +47,7 @@ void register_node_type_cmp_brightcontrast(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_brightcontrast_in, cmp_node_brightcontrast_out);
+ ntype.declare = blender::nodes::cmp_node_brightcontrast_declare;
node_type_init(&ntype, node_composit_init_brightcontrast);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc
index 9912c10b368..e211bc45b17 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Channel Matte Node ********************************* */
static bNodeSocketTemplate cmp_node_channel_matte_in[] = {
@@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_channel_matte_out[] = {
static void node_composit_init_channel_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma");
node->storage = c;
c->t1 = 1.0f;
c->t2 = 0.0f;
diff --git a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc
index 705566df35a..990778160df 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
static bNodeSocketTemplate cmp_node_chroma_in[] = {
@@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_chroma_out[] = {
static void node_composit_init_chroma_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma");
node->storage = c;
c->t1 = DEG2RADF(30.0f);
c->t2 = DEG2RADF(10.0f);
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc
index f5cf7bcbf22..fc9a0075b14 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Color Key ********************************************************** */
static bNodeSocketTemplate cmp_node_color_in[] = {
@@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_color_out[] = {
static void node_composit_init_color_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node color");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node color");
node->storage = c;
c->t1 = 0.01f;
c->t2 = 0.1f;
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc
index 8ff4bcdced3..7bdc2e8289e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Color Spill Suppression ********************************* */
static bNodeSocketTemplate cmp_node_color_spill_in[] = {
@@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_color_spill_out[] = {
static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeColorspill *ncs = MEM_callocN(sizeof(NodeColorspill), "node colorspill");
+ NodeColorspill *ncs = (NodeColorspill *)MEM_callocN(sizeof(NodeColorspill), "node colorspill");
node->storage = ncs;
node->custom1 = 2; /* green channel */
node->custom2 = 0; /* simple limit algorithm */
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index 0525229697a..440e37fe741 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -21,19 +21,20 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Color Balance ********************************* */
-static bNodeSocketTemplate cmp_node_colorbalance_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_colorbalance_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
/* Sync functions update formula parameters for other modes, such that the result is comparable.
* Note that the results are not exactly the same due to differences in color handling
@@ -43,10 +44,9 @@ static bNodeSocketTemplate cmp_node_colorbalance_out[] = {
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeColorBalance *n = node->storage;
- int c;
+ NodeColorBalance *n = (NodeColorBalance *)node->storage;
- for (c = 0; c < 3; c++) {
+ for (int c = 0; c < 3; c++) {
n->slope[c] = (2.0f - n->lift[c]) * n->gain[c];
n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c];
n->power[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f;
@@ -55,10 +55,9 @@ void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node)
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeColorBalance *n = node->storage;
- int c;
+ NodeColorBalance *n = (NodeColorBalance *)node->storage;
- for (c = 0; c < 3; c++) {
+ for (int c = 0; c < 3; c++) {
float d = n->slope[c] + n->offset[c];
n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f);
n->gain[c] = d;
@@ -68,7 +67,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeColorBalance *n = node->storage = MEM_callocN(sizeof(NodeColorBalance), "node colorbalance");
+ NodeColorBalance *n = (NodeColorBalance *)MEM_callocN(sizeof(NodeColorBalance),
+ "node colorbalance");
n->lift[0] = n->lift[1] = n->lift[2] = 1.0f;
n->gamma[0] = n->gamma[1] = n->gamma[2] = 1.0f;
@@ -77,6 +77,7 @@ static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *nod
n->slope[0] = n->slope[1] = n->slope[2] = 1.0f;
n->offset[0] = n->offset[1] = n->offset[2] = 0.0f;
n->power[0] = n->power[1] = n->power[2] = 1.0f;
+ node->storage = n;
}
void register_node_type_cmp_colorbalance(void)
@@ -84,7 +85,7 @@ void register_node_type_cmp_colorbalance(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COLORBALANCE, "Color Balance", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_colorbalance_in, cmp_node_colorbalance_out);
+ ntype.declare = blender::nodes::cmp_node_colorbalance_declare;
node_type_size(&ntype, 400, 200, 400);
node_type_init(&ntype, node_composit_init_colorbalance);
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 45d39f8be8d..0682c66f1e8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -21,24 +21,25 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-/* ******************* Color Balance ********************************* */
-static bNodeSocketTemplate cmp_node_colorcorrection_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Mask"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
+/* ******************* Color Correction ********************************* */
-static bNodeSocketTemplate cmp_node_colorcorrection_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Mask").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeColorCorrection *n = node->storage = MEM_callocN(sizeof(NodeColorCorrection),
- "node colorcorrection");
+ NodeColorCorrection *n = (NodeColorCorrection *)MEM_callocN(sizeof(NodeColorCorrection),
+ "node colorcorrection");
n->startmidtones = 0.2f;
n->endmidtones = 0.7f;
n->master.contrast = 1.0f;
@@ -62,6 +63,7 @@ static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *
n->highlights.lift = 0.0f;
n->highlights.saturation = 1.0f;
node->custom1 = 7; // red + green + blue enabled
+ node->storage = n;
}
void register_node_type_cmp_colorcorrection(void)
@@ -69,7 +71,7 @@ void register_node_type_cmp_colorcorrection(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COLORCORRECTION, "Color Correction", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_colorcorrection_in, cmp_node_colorcorrection_out);
+ ntype.declare = blender::nodes::cmp_node_colorcorrection_declare;
node_type_size(&ntype, 400, 200, 600);
node_type_init(&ntype, node_composit_init_colorcorrection);
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.cc
index 61abc80fe93..fecf6795ef7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_common.c
+++ b/source/blender/nodes/composite/nodes/node_composite_common.cc
@@ -26,7 +26,7 @@
#include "NOD_common.h"
#include "node_common.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_node.h"
@@ -46,10 +46,10 @@ void register_node_type_cmp_group(void)
ntype.insert_link = node_insert_link_default;
ntype.update_internal_links = node_update_internal_links_default;
ntype.rna_ext.srna = RNA_struct_find("CompositorNodeGroup");
- BLI_assert(ntype.rna_ext.srna != NULL);
+ BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
- node_type_socket_templates(&ntype, NULL, NULL);
+ node_type_socket_templates(&ntype, nullptr, nullptr);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_group_update(&ntype, node_group_update);
@@ -60,13 +60,13 @@ void register_node_type_cmp_group(void)
void register_node_type_cmp_custom_group(bNodeType *ntype)
{
/* These methods can be overridden but need a default implementation otherwise. */
- if (ntype->poll == NULL) {
+ if (ntype->poll == nullptr) {
ntype->poll = cmp_node_poll_default;
}
- if (ntype->insert_link == NULL) {
+ if (ntype->insert_link == nullptr) {
ntype->insert_link = node_insert_link_default;
}
- if (ntype->update_internal_links == NULL) {
+ if (ntype->update_internal_links == nullptr) {
ntype->update_internal_links = node_update_internal_links_default;
}
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.c b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index dee2ce6b3ec..170fecb251c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.c
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -21,25 +21,30 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** COMPOSITE ******************** */
-static bNodeSocketTemplate cmp_node_composite_in[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_composite_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f);
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_composite(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_composite_in, NULL);
+ ntype.declare = blender::nodes::cmp_node_composite_declare;
/* Do not allow muting for this node. */
- node_type_internal_links(&ntype, NULL);
+ node_type_internal_links(&ntype, nullptr);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
index 135120c45aa..b5ca1fb015e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c
+++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate inputs[] = {
{SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.c b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index 868df5367c4..f07dba8a74b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.c
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Crop ******************** */
@@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_crop_out[] = {
static void node_composit_init_crop(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeTwoXYs *nxy = MEM_callocN(sizeof(NodeTwoXYs), "node xy data");
+ NodeTwoXYs *nxy = (NodeTwoXYs *)MEM_callocN(sizeof(NodeTwoXYs), "node xy data");
node->storage = nxy;
nxy->x1 = 0;
nxy->x2 = 0;
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 51dd73a86af..6657267b016 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BLI_assert.h"
#include "BLI_dynstr.h"
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.c b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index 470540d3337..88d96e1ca4a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.c
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -21,16 +21,20 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** CURVE Time ******************** */
-/* custom1 = sfra, custom2 = efra */
-static bNodeSocketTemplate cmp_node_time_out[] = {
- {SOCK_FLOAT, N_("Fac")},
- {-1, ""},
-};
+namespace blender::nodes {
+static void cmp_node_time_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("Fac");
+}
+
+} // namespace blender::nodes
+
+/* custom1 = start_frame, custom2 = end_frame */
static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node)
{
node->custom1 = 1;
@@ -43,7 +47,7 @@ void register_node_type_cmp_curve_time(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time", NODE_CLASS_INPUT, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_time_out);
+ ntype.declare = blender::nodes::cmp_node_time_declare;
node_type_size(&ntype, 140, 100, 320);
node_type_init(&ntype, node_composit_init_curves_time);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
@@ -81,18 +85,19 @@ void register_node_type_cmp_curve_vec(void)
}
/* **************** CURVE RGB ******************** */
-static bNodeSocketTemplate cmp_node_curve_rgb_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_RGBA, N_("Black Level"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_RGBA, N_("White Level"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_curve_rgb_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Color>("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -104,7 +109,7 @@ void register_node_type_cmp_curve_rgb(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_curve_rgb_in, cmp_node_curve_rgb_out);
+ ntype.declare = blender::nodes::cmp_node_rgbcurves_declare;
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, node_composit_init_curve_rgb);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.c b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
index 3803f450f49..1103aff4366 100644
--- a/source/blender/nodes/composite/nodes/node_composite_defocus.c
+++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
@@ -21,11 +21,11 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-#include <limits.h>
+#include <climits>
-/* ************ qdn: Defocus node ****************** */
+/* ************ Defocus Node ****************** */
static bNodeSocketTemplate cmp_node_defocus_in[] = {
{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
{SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
@@ -38,8 +38,8 @@ static bNodeSocketTemplate cmp_node_defocus_out[] = {
static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node)
{
- /* qdn: defocus node */
- NodeDefocus *nbd = MEM_callocN(sizeof(NodeDefocus), "node defocus data");
+ /* defocus node */
+ NodeDefocus *nbd = (NodeDefocus *)MEM_callocN(sizeof(NodeDefocus), "node defocus data");
nbd->bktype = 0;
nbd->rotation = 0.0f;
nbd->preview = 1;
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.c b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index e2c7c7b995f..ec085794462 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.c
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -23,7 +23,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate cmp_node_denoise_in[] = {
{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f},
@@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_denoise_out[] = {{SOCK_RGBA, N_("Image")}, {
static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeDenoise *ndg = MEM_callocN(sizeof(NodeDenoise), "node denoise data");
+ NodeDenoise *ndg = (NodeDenoise *)MEM_callocN(sizeof(NodeDenoise), "node denoise data");
ndg->hdr = true;
ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE;
node->storage = ndg;
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.c b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 18567ee2006..52d91dabeb1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.c
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** FILTER ******************** */
static bNodeSocketTemplate cmp_node_despeckle_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc
index 7871a9e8b04..1e1a48381b7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* channel Difference Matte ********************************* */
static bNodeSocketTemplate cmp_node_diff_matte_in[] = {
@@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_diff_matte_out[] = {
static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma");
node->storage = c;
c->t1 = 0.1f;
c->t2 = 0.1f;
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.c b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 12f1f229258..57884a299da 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Dilate/Erode ******************** */
@@ -31,7 +31,8 @@ static bNodeSocketTemplate cmp_node_dilateerode_out[] = {{SOCK_FLOAT, N_("Mask")
static void node_composit_init_dilateerode(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeDilateErode *data = MEM_callocN(sizeof(NodeDilateErode), "NodeDilateErode");
+ NodeDilateErode *data = (NodeDilateErode *)MEM_callocN(sizeof(NodeDilateErode),
+ "NodeDilateErode");
data->falloff = PROP_SMOOTH;
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index 6dd60526edf..d9f82ba5009 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate cmp_node_dblur_in[] = {{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
{-1, ""}};
@@ -30,7 +30,7 @@ static bNodeSocketTemplate cmp_node_dblur_out[] = {{SOCK_RGBA, N_("Image")}, {-1
static void node_composit_init_dblur(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeDBlurData *ndbd = MEM_callocN(sizeof(NodeDBlurData), "node dblur data");
+ NodeDBlurData *ndbd = (NodeDBlurData *)MEM_callocN(sizeof(NodeDBlurData), "node dblur data");
node->storage = ndbd;
ndbd->iter = 1;
ndbd->center_x = 0.5;
diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.c b/source/blender/nodes/composite/nodes/node_composite_displace.cc
index 819a4f29b3a..b1ed7f05794 100644
--- a/source/blender/nodes/composite/nodes/node_composite_displace.c
+++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Displace ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc
index 3e659fe662b..3f8767ecd08 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* channel Distance Matte ********************************* */
static bNodeSocketTemplate cmp_node_distance_matte_in[] = {
@@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_distance_matte_out[] = {
static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma");
node->storage = c;
c->channel = 1;
c->t1 = 0.1f;
diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc
index 6f68b187775..7c9a48efc2d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc
@@ -20,7 +20,7 @@
/** \file
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Double Edge Mask ******************** */
static bNodeSocketTemplate cmp_node_doubleedgemask_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index d5e1d519a1c..67196fb0d35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate cmp_node_ellipsemask_in[] = {
@@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_ellipsemask_out[] = {
static void node_composit_init_ellipsemask(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeEllipseMask *data = MEM_callocN(sizeof(NodeEllipseMask), "NodeEllipseMask");
+ NodeEllipseMask *data = (NodeEllipseMask *)MEM_callocN(sizeof(NodeEllipseMask),
+ "NodeEllipseMask");
data->x = 0.5;
data->y = 0.5;
data->width = 0.2;
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.c b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index bd27e4a3d76..fd959376afe 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.c
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -21,26 +21,27 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Exposure ******************** */
-static bNodeSocketTemplate cmp_node_exposure_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Exposure"), 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 10.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_exposure_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Exposure").min(-10.0f).max(10.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_exposure(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_exposure_in, cmp_node_exposure_out);
+ ntype.declare = blender::nodes::cmp_node_exposure_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.c b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index d0ad090ece4..f07619877f4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.c
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** FILTER ******************** */
static bNodeSocketTemplate cmp_node_filter_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.c b/source/blender/nodes/composite/nodes/node_composite_flip.cc
index 91a91bb5f5f..42aa3141f5c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_flip.c
+++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Flip ******************** */
static bNodeSocketTemplate cmp_node_flip_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.c b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index ddcaf691fd2..a29a001688a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.c
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -21,26 +21,28 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Gamma Tools ******************** */
-static bNodeSocketTemplate cmp_node_gamma_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Gamma"), 1.0f, 0.0f, 0.0f, 0.0f, 0.001f, 10.0f, PROP_UNSIGNED},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_gamma_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype(
+ PROP_UNSIGNED);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_gamma(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_gamma_in, cmp_node_gamma_out);
+ ntype.declare = blender::nodes::cmp_node_gamma_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.c b/source/blender/nodes/composite/nodes/node_composite_glare.cc
index a792fcc86cd..8a2fd1e1584 100644
--- a/source/blender/nodes/composite/nodes/node_composite_glare.c
+++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate cmp_node_glare_in[] = {
{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
@@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_glare_out[] = {
static void node_composit_init_glare(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGlare *ndg = MEM_callocN(sizeof(NodeGlare), "node glare data");
+ NodeGlare *ndg = (NodeGlare *)MEM_callocN(sizeof(NodeGlare), "node glare data");
ndg->quality = 1;
ndg->type = 2;
ndg->iter = 3;
diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc
index 494b6136a6e..07746918a94 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c
+++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc
@@ -21,28 +21,34 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Hue Saturation ******************** */
-static bNodeSocketTemplate cmp_node_hue_sat_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Hue"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_FLOAT, N_("Saturation"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR},
- {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR},
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_hue_sat_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>("Saturation")
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(2.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_hue_sat(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_hue_sat_in, cmp_node_hue_sat_out);
+ ntype.declare = blender::nodes::cmp_node_huesatval_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index 6a5c918d9ae..39014896a7b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -21,27 +21,28 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-static bNodeSocketTemplate cmp_node_huecorrect_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
+namespace blender::nodes {
-static bNodeSocketTemplate cmp_node_huecorrect_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
{
- CurveMapping *cumapping = node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
- int c;
+ node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ CurveMapping *cumapping = (CurveMapping *)node->storage;
cumapping->preset = CURVE_PRESET_MID9;
- for (c = 0; c < 3; c++) {
+ for (int c = 0; c < 3; c++) {
CurveMap *cuma = &cumapping->cm[c];
BKE_curvemap_reset(cuma, &cumapping->clipr, cumapping->preset, CURVEMAP_SLOPE_POSITIVE);
}
@@ -55,7 +56,7 @@ void register_node_type_cmp_huecorrect(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_HUECORRECT, "Hue Correct", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_huecorrect_in, cmp_node_huecorrect_out);
+ ntype.declare = blender::nodes::cmp_node_huecorrect_declare;
node_type_size(&ntype, 320, 140, 500);
node_type_init(&ntype, node_composit_init_huecorrect);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.c b/source/blender/nodes/composite/nodes/node_composite_idMask.cc
index 84563e7560b..de011dd6274 100644
--- a/source/blender/nodes/composite/nodes/node_composite_idMask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc
@@ -21,25 +21,26 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** ID Mask ******************** */
-static bNodeSocketTemplate cmp_node_idmask_in[] = {
- {SOCK_FLOAT, N_("ID value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_idmask_out[] = {
- {SOCK_FLOAT, N_("Alpha")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_idmask_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("ID value").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Float>("Alpha");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_idmask(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out);
+ ntype.declare = blender::nodes::cmp_node_idmask_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.cc
index a56dfea9dbf..3cef3a7625f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BLI_linklist.h"
#include "BLI_utildefines.h"
@@ -84,16 +84,17 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree,
LinkNodePair *available_sockets,
int *prev_index)
{
- bNodeSocket *sock = BLI_findstring(&node->outputs, name, offsetof(bNodeSocket, name));
+ bNodeSocket *sock = (bNodeSocket *)BLI_findstring(
+ &node->outputs, name, offsetof(bNodeSocket, name));
/* Replace if types don't match. */
if (sock && sock->type != type) {
nodeRemoveSocket(ntree, node, sock);
- sock = NULL;
+ sock = nullptr;
}
/* Create socket if it doesn't exist yet. */
- if (sock == NULL) {
+ if (sock == nullptr) {
if (rres_index >= 0) {
sock = node_add_socket_from_template(
ntree, node, &cmp_node_rlayers_out[rres_index], SOCK_OUT);
@@ -102,18 +103,19 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree,
sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, name, name);
}
/* extra socket info */
- NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
+ NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer),
+ "node image layer");
sock->storage = sockdata;
}
- NodeImageLayer *sockdata = sock->storage;
+ NodeImageLayer *sockdata = (NodeImageLayer *)sock->storage;
if (sockdata) {
BLI_strncpy(sockdata->pass_name, passname, sizeof(sockdata->pass_name));
}
/* Reorder sockets according to order that passes are added. */
const int after_index = (*prev_index)++;
- bNodeSocket *after_sock = BLI_findlink(&node->outputs, after_index);
+ bNodeSocket *after_sock = (bNodeSocket *)BLI_findlink(&node->outputs, after_index);
BLI_remlink(&node->outputs, sock);
BLI_insertlinkafter(&node->outputs, after_sock, sock);
@@ -128,8 +130,8 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree,
ImBuf *ibuf;
int prev_index = -1;
if (ima) {
- ImageUser *iuser = node->storage;
- ImageUser load_iuser = {NULL};
+ ImageUser *iuser = (ImageUser *)node->storage;
+ ImageUser load_iuser = {nullptr};
int offset = BKE_image_sequence_guess_offset(ima);
/* It is possible that image user in this node is not
@@ -138,21 +140,19 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree,
*
* So we manually construct image user to be sure first
* image from sequence (that one which is set as filename
- * for image datablock) is used for sockets detection
- */
+ * for image data-block) is used for sockets detection. */
load_iuser.ok = 1;
load_iuser.framenr = offset;
/* make sure ima->type is correct */
- ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, NULL);
+ ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, nullptr);
if (ima->rr) {
- RenderLayer *rl = BLI_findlink(&ima->rr->layers, iuser->layer);
+ RenderLayer *rl = (RenderLayer *)BLI_findlink(&ima->rr->layers, iuser->layer);
if (rl) {
- RenderPass *rpass;
- for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
- int type;
+ LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
+ eNodeSocketDatatype type;
if (rpass->channels == 1) {
type = SOCK_FLOAT;
}
@@ -182,7 +182,7 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree,
&prev_index);
}
}
- BKE_image_release_ibuf(ima, ibuf, NULL);
+ BKE_image_release_ibuf(ima, ibuf, nullptr);
return;
}
}
@@ -219,14 +219,14 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree,
available_sockets,
&prev_index);
}
- BKE_image_release_ibuf(ima, ibuf, NULL);
+ BKE_image_release_ibuf(ima, ibuf, nullptr);
}
}
-typedef struct RLayerUpdateData {
+struct RLayerUpdateData {
LinkNodePair *available_sockets;
int prev_index;
-} RLayerUpdateData;
+};
void node_cmp_rlayers_register_pass(bNodeTree *ntree,
bNode *node,
@@ -235,13 +235,13 @@ void node_cmp_rlayers_register_pass(bNodeTree *ntree,
const char *name,
eNodeSocketDatatype type)
{
- RLayerUpdateData *data = node->storage;
+ RLayerUpdateData *data = (RLayerUpdateData *)node->storage;
- if (scene == NULL || view_layer == NULL || data == NULL || node->id != (ID *)scene) {
+ if (scene == nullptr || view_layer == nullptr || data == nullptr || node->id != (ID *)scene) {
return;
}
- ViewLayer *node_view_layer = BLI_findlink(&scene->view_layers, node->custom1);
+ ViewLayer *node_view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
if (node_view_layer != view_layer) {
return;
}
@@ -281,7 +281,7 @@ static void cmp_node_rlayer_create_outputs_cb(void *UNUSED(userdata),
* unless we want to register that for every other temp Main we could generate??? */
ntreeCompositRegisterPass(scene->nodetree, scene, view_layer, name, type);
- for (Scene *sce = G_MAIN->scenes.first; sce; sce = sce->id.next) {
+ for (Scene *sce = (Scene *)G_MAIN->scenes.first; sce; sce = (Scene *)sce->id.next) {
if (sce->nodetree && sce != scene) {
ntreeCompositRegisterPass(sce->nodetree, scene, view_layer, name, type);
}
@@ -297,16 +297,17 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree,
if (scene) {
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
if (engine_type && engine_type->update_render_passes) {
- ViewLayer *view_layer = BLI_findlink(&scene->view_layers, node->custom1);
+ ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
if (view_layer) {
- RLayerUpdateData *data = MEM_mallocN(sizeof(RLayerUpdateData), "render layer update data");
+ RLayerUpdateData *data = (RLayerUpdateData *)MEM_mallocN(sizeof(RLayerUpdateData),
+ "render layer update data");
data->available_sockets = available_sockets;
data->prev_index = -1;
node->storage = data;
RenderEngine *engine = RE_engine_create(engine_type);
RE_engine_update_render_passes(
- engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, NULL);
+ engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, nullptr);
RE_engine_free(engine);
if ((scene->r.mode & R_EDGE_FRS) &&
@@ -315,7 +316,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree,
}
MEM_freeN(data);
- node->storage = NULL;
+ node->storage = nullptr;
return;
}
@@ -348,8 +349,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree,
static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer)
{
bNodeSocket *sock, *sock_next;
- LinkNodePair available_sockets = {NULL, NULL};
- int sock_index;
+ LinkNodePair available_sockets = {nullptr, nullptr};
/* XXX make callback */
if (rlayer) {
@@ -369,15 +369,15 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl
* the first 31 passes to belong to a specific pass type.
* So, we keep those 31 always allocated before the others as well,
* even if they have no links attached. */
- sock_index = 0;
- for (sock = node->outputs.first; sock; sock = sock_next, sock_index++) {
+ int sock_index = 0;
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) {
sock_next = sock->next;
if (BLI_linklist_index(available_sockets.list, sock) >= 0) {
sock->flag &= ~(SOCK_UNAVAIL | SOCK_HIDDEN);
}
else {
bNodeLink *link;
- for (link = ntree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
if (link->fromsock == sock) {
break;
}
@@ -392,7 +392,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl
}
}
- BLI_linklist_free(available_sockets.list, NULL);
+ BLI_linklist_free(available_sockets.list, nullptr);
}
static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
@@ -407,7 +407,7 @@ static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
static void node_composit_init_image(bNodeTree *ntree, bNode *node)
{
- ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user");
+ ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user");
node->storage = iuser;
iuser->frames = 1;
iuser->sfra = 1;
@@ -420,10 +420,8 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node)
static void node_composit_free_image(bNode *node)
{
- bNodeSocket *sock;
-
/* free extra socket info */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
MEM_freeN(sock->storage);
}
@@ -436,9 +434,9 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
{
dest_node->storage = MEM_dupallocN(src_node->storage);
- const bNodeSocket *src_output_sock = src_node->outputs.first;
- bNodeSocket *dest_output_sock = dest_node->outputs.first;
- while (dest_output_sock != NULL) {
+ const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
+ bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
+ while (dest_output_sock != nullptr) {
dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
src_output_sock = src_output_sock->next;
@@ -469,7 +467,7 @@ void node_cmp_rlayers_outputs(bNodeTree *ntree, bNode *node)
const char *node_cmp_rlayers_sock_to_pass(int sock_index)
{
if (sock_index >= NUM_LEGACY_SOCKETS) {
- return NULL;
+ return nullptr;
}
const char *name = cmp_node_rlayers_out[sock_index].name;
/* Exception for alpha, which is derived from Combined. */
@@ -479,14 +477,16 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
int sock_index = 0;
node->id = &scene->id;
id_us_plus(node->id);
- for (bNodeSocket *sock = node->outputs.first; sock; sock = sock->next, sock_index++) {
- NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
+ for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock;
+ sock = sock->next, sock_index++) {
+ NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer),
+ "node image layer");
sock->storage = sockdata;
BLI_strncpy(sockdata->pass_name,
@@ -510,13 +510,13 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype),
* Render layers node can only be used in local scene->nodetree,
* since it directly links to the scene.
*/
- for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
+ for (scene = (Scene *)G.main->scenes.first; scene; scene = (Scene *)scene->id.next) {
if (scene->nodetree == ntree) {
break;
}
}
- if (scene == NULL) {
+ if (scene == nullptr) {
*r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file";
return false;
}
@@ -525,10 +525,8 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype),
static void node_composit_free_rlayers(bNode *node)
{
- bNodeSocket *sock;
-
/* free extra socket info */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (sock->storage) {
MEM_freeN(sock->storage);
}
@@ -540,9 +538,9 @@ static void node_composit_copy_rlayers(bNodeTree *UNUSED(dest_ntree),
const bNode *src_node)
{
/* copy extra socket info */
- const bNodeSocket *src_output_sock = src_node->outputs.first;
- bNodeSocket *dest_output_sock = dest_node->outputs.first;
- while (dest_output_sock != NULL) {
+ const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
+ bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
+ while (dest_output_sock != nullptr) {
dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
src_output_sock = src_output_sock->next;
@@ -562,10 +560,10 @@ void register_node_type_cmp_rlayers(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_R_LAYERS, "Render Layers", NODE_CLASS_INPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, NULL, cmp_node_rlayers_out);
+ node_type_socket_templates(&ntype, nullptr, cmp_node_rlayers_out);
ntype.initfunc_api = node_composit_init_rlayers;
ntype.poll = node_composit_poll_rlayers;
- node_type_storage(&ntype, NULL, node_composit_free_rlayers, node_composit_copy_rlayers);
+ node_type_storage(&ntype, nullptr, node_composit_free_rlayers, node_composit_copy_rlayers);
node_type_update(&ntype, cmp_node_rlayers_update);
node_type_init(&ntype, node_cmp_rlayers_outputs);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.c b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
index 6c02bca9b39..d0ff97a2ca0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.c
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Inpaint/ ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.c b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index d229261f208..57b7ed36ccd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.c
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -21,15 +21,20 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** INVERT ******************** */
-static bNodeSocketTemplate cmp_node_invert_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""}};
-static bNodeSocketTemplate cmp_node_invert_out[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}};
+namespace blender::nodes {
+
+static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Color");
+}
+
+} // namespace blender::nodes
static void node_composit_init_invert(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -42,7 +47,7 @@ void register_node_type_cmp_invert(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_invert_in, cmp_node_invert_out);
+ ntype.declare = blender::nodes::cmp_node_invert_declare;
node_type_init(&ntype, node_composit_init_invert);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.c b/source/blender/nodes/composite/nodes/node_composite_keying.cc
index 73e86a21ebe..d5547161069 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keying.c
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc
@@ -27,7 +27,7 @@
#include "BLI_math_base.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -48,9 +48,7 @@ static bNodeSocketTemplate cmp_node_keying_out[] = {
static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeKeyingData *data;
-
- data = MEM_callocN(sizeof(NodeKeyingData), "node keying data");
+ NodeKeyingData *data = (NodeKeyingData *)MEM_callocN(sizeof(NodeKeyingData), "node keying data");
data->screen_balance = 0.5f;
data->despill_balance = 0.5f;
@@ -59,7 +57,6 @@ static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node)
data->edge_kernel_tolerance = 0.1f;
data->clip_black = 0.0f;
data->clip_white = 1.0f;
-
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index e5e97cd72e4..c976a92b92d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -26,7 +26,7 @@
#include "BLI_math_base.h"
#include "BLI_math_color.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -37,10 +37,8 @@ static bNodeSocketTemplate cmp_node_keyingscreen_out[] = {
static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeKeyingScreenData *data;
-
- data = MEM_callocN(sizeof(NodeKeyingScreenData), "node keyingscreen data");
-
+ NodeKeyingScreenData *data = (NodeKeyingScreenData *)MEM_callocN(sizeof(NodeKeyingScreenData),
+ "node keyingscreen data");
node->storage = data;
}
@@ -49,7 +47,7 @@ void register_node_type_cmp_keyingscreen(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_keyingscreen_out);
+ node_type_socket_templates(&ntype, nullptr, cmp_node_keyingscreen_out);
node_type_init(&ntype, node_composit_init_keyingscreen);
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.c b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index ce8c8c00e24..2a8dc035792 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.c
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate cmp_node_lensdist_in[] = {
{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
@@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_lensdist_out[] = {
static void node_composit_init_lensdist(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeLensDist *nld = MEM_callocN(sizeof(NodeLensDist), "node lensdist data");
+ NodeLensDist *nld = (NodeLensDist *)MEM_callocN(sizeof(NodeLensDist), "node lensdist data");
nld->jit = nld->proj = nld->fit = 0;
node->storage = nld;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.c b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index 7c70ccf412a..aaab8dcc874 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.c
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -21,19 +21,20 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** LEVELS ******************** */
-static bNodeSocketTemplate cmp_node_view_levels_in[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_view_levels_out[] = {
- {SOCK_FLOAT, N_("Mean")},
- {SOCK_FLOAT, N_("Std Dev")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_levels_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_output<decl::Float>("Mean");
+ b.add_output<decl::Float>("Std Dev");
+}
+
+} // namespace blender::nodes
static void node_composit_init_view_levels(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -45,7 +46,7 @@ void register_node_type_cmp_view_levels(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_VIEW_LEVELS, "Levels", NODE_CLASS_OUTPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_view_levels_in, cmp_node_view_levels_out);
+ ntype.declare = blender::nodes::cmp_node_levels_declare;
node_type_init(&ntype, node_composit_init_view_levels);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc
index cb0f59c2f4a..600406d22b9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* ******************* Luma Matte Node ********************************* */
static bNodeSocketTemplate cmp_node_luma_matte_in[] = {
@@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_luma_matte_out[] = {
static void node_composit_init_luma_matte(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma");
+ NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma");
node->storage = c;
c->t1 = 1.0f;
c->t2 = 0.0f;
diff --git a/source/blender/nodes/composite/nodes/node_composite_mapRange.c b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc
index cd95e73ba5c..808ad538e55 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mapRange.c
+++ b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
static bNodeSocketTemplate cmp_node_map_range_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_mapUV.c b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc
index e88a303e449..99032bd042e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mapUV.c
+++ b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Map UV ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_mapValue.c b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc
index c93807c3801..25c00c2ba13 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mapValue.c
+++ b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
static bNodeSocketTemplate cmp_node_map_value_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.c b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index e6a5df6c24b..8b415bb8b63 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -23,15 +23,22 @@
#include "DNA_mask_types.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-/* **************** Translate ******************** */
+/* **************** Mask ******************** */
-static bNodeSocketTemplate cmp_node_mask_out[] = {{SOCK_FLOAT, "Mask"}, {-1, ""}};
+namespace blender::nodes {
+
+static void cmp_node_mask_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("Mask");
+}
+
+} // namespace blender::nodes
static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeMask *data = MEM_callocN(sizeof(NodeMask), "NodeMask");
+ NodeMask *data = (NodeMask *)MEM_callocN(sizeof(NodeMask), "NodeMask");
data->size_x = data->size_y = 256;
node->storage = data;
@@ -41,7 +48,7 @@ static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node)
static void node_mask_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen)
{
- if (node->id != NULL) {
+ if (node->id != nullptr) {
BLI_strncpy(label, node->id->name + 2, maxlen);
}
else {
@@ -54,7 +61,7 @@ void register_node_type_cmp_mask(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_mask_out);
+ ntype.declare = blender::nodes::cmp_node_mask_declare;
node_type_init(&ntype, node_composit_init_mask);
node_type_label(&ntype, node_mask_label);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.cc
index 2191c6bcdc3..a9859425e28 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.c
+++ b/source/blender/nodes/composite/nodes/node_composite_math.cc
@@ -21,23 +21,28 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
-static bNodeSocketTemplate cmp_node_math_in[] = {
- {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {-1, ""}};
-static bNodeSocketTemplate cmp_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}};
+namespace blender::nodes {
+
+static void cmp_node_math_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Float>("Value");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_math(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out);
+ ntype.declare = blender::nodes::cmp_node_math_declare;
node_type_label(&ntype, node_math_label);
node_type_update(&ntype, node_math_update);
diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
index 9d3751c7da3..4f2a9c2717c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c
+++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
@@ -21,19 +21,21 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** MIX RGB ******************** */
-static bNodeSocketTemplate cmp_node_mix_rgb_in[] = {
- {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_mix_rgb_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
/* custom1 = mix type */
void register_node_type_cmp_mix_rgb(void)
@@ -41,7 +43,7 @@ void register_node_type_cmp_mix_rgb(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_mix_rgb_in, cmp_node_mix_rgb_out);
+ ntype.declare = blender::nodes::cmp_node_mixrgb_declare;
node_type_label(&ntype, node_blend_label);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.c b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index 4f5aef05425..ae91212f811 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.c
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -21,26 +21,31 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_context.h"
#include "BKE_lib_id.h"
-static bNodeSocketTemplate cmp_node_movieclip_out[] = {
- {SOCK_RGBA, N_("Image")},
- {SOCK_FLOAT, N_("Alpha")},
- {SOCK_FLOAT, N_("Offset X")},
- {SOCK_FLOAT, N_("Offset Y")},
- {SOCK_FLOAT, N_("Scale")},
- {SOCK_FLOAT, N_("Angle")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Color>("Image");
+ b.add_output<decl::Float>("Alpha");
+ b.add_output<decl::Float>("Offset X");
+ b.add_output<decl::Float>("Offset Y");
+ b.add_output<decl::Float>("Scale");
+ b.add_output<decl::Float>("Angle");
+}
+
+} // namespace blender::nodes
static void init(const bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
Scene *scene = CTX_data_scene(C);
- MovieClipUser *user = MEM_callocN(sizeof(MovieClipUser), "node movie clip user");
+ MovieClipUser *user = (MovieClipUser *)MEM_callocN(sizeof(MovieClipUser),
+ "node movie clip user");
node->id = (ID *)scene->clip;
id_us_plus(node->id);
@@ -53,7 +58,7 @@ void register_node_type_cmp_movieclip(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, NULL, cmp_node_movieclip_out);
+ ntype.declare = blender::nodes::cmp_node_movieclip_declare;
ntype.initfunc_api = init;
node_type_storage(
&ntype, "MovieClipUser", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 7e30d004b45..2bac30cc152 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_context.h"
#include "BKE_lib_id.h"
@@ -50,7 +50,7 @@ static void label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen
static void init(const bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
Scene *scene = CTX_data_scene(C);
node->id = (ID *)scene->clip;
@@ -60,16 +60,16 @@ static void init(const bContext *C, PointerRNA *ptr)
static void storage_free(bNode *node)
{
if (node->storage) {
- BKE_tracking_distortion_free(node->storage);
+ BKE_tracking_distortion_free((MovieDistortion *)node->storage);
}
- node->storage = NULL;
+ node->storage = nullptr;
}
static void storage_copy(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
{
if (src_node->storage) {
- dest_node->storage = BKE_tracking_distortion_copy(src_node->storage);
+ dest_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)src_node->storage);
}
}
@@ -82,7 +82,7 @@ void register_node_type_cmp_moviedistortion(void)
node_type_label(&ntype, label);
ntype.initfunc_api = init;
- node_type_storage(&ntype, NULL, storage_free, storage_copy);
+ node_type_storage(&ntype, nullptr, storage_free, storage_copy);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.c b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index 91300e66339..7531025daa5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.c
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** NORMAL ******************** */
static bNodeSocketTemplate cmp_node_normal_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.c b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
index 26f2abc745f..7cc54e4eed6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.c
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** NORMALIZE single channel, useful for Z buffer ******************** */
static bNodeSocketTemplate cmp_node_normalize_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc
index c10edd8d5ad..a372d2f7419 100644
--- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c
+++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc
@@ -23,13 +23,13 @@
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
-#include <string.h>
+#include <cstring>
#include "BKE_context.h"
#include "RNA_access.h"
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "intern/openexr/openexr_multi.h"
@@ -38,14 +38,15 @@
/* find unique path */
static bool unique_path_unique_check(void *arg, const char *name)
{
- struct {
+ struct Args {
ListBase *lb;
bNodeSocket *sock;
- } *data = arg;
- bNodeSocket *sock;
- for (sock = data->lb->first; sock; sock = sock->next) {
+ };
+ Args *data = (Args *)arg;
+
+ LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) {
if (sock != data->sock) {
- NodeImageMultiFileSocket *sockdata = sock->storage;
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
if (STREQ(sockdata->path, name)) {
return true;
}
@@ -67,11 +68,11 @@ void ntreeCompositOutputFileUniquePath(ListBase *list,
data.sock = sock;
/* See if we are given an empty string */
- if (ELEM(NULL, sock, defname)) {
+ if (ELEM(nullptr, sock, defname)) {
return;
}
- sockdata = sock->storage;
+ sockdata = (NodeImageMultiFileSocket *)sock->storage;
BLI_uniquename_cb(
unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path));
}
@@ -79,14 +80,15 @@ void ntreeCompositOutputFileUniquePath(ListBase *list,
/* find unique EXR layer */
static bool unique_layer_unique_check(void *arg, const char *name)
{
- struct {
+ struct Args {
ListBase *lb;
bNodeSocket *sock;
- } *data = arg;
- bNodeSocket *sock;
- for (sock = data->lb->first; sock; sock = sock->next) {
+ };
+ Args *data = (Args *)arg;
+
+ LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) {
if (sock != data->sock) {
- NodeImageMultiFileSocket *sockdata = sock->storage;
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
if (STREQ(sockdata->layer, name)) {
return true;
}
@@ -99,7 +101,6 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
const char defname[],
char delim)
{
- NodeImageMultiFileSocket *sockdata;
struct {
ListBase *lb;
bNodeSocket *sock;
@@ -108,11 +109,11 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
data.sock = sock;
/* See if we are given an empty string */
- if (ELEM(NULL, sock, defname)) {
+ if (ELEM(nullptr, sock, defname)) {
return;
}
- sockdata = sock->storage;
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
BLI_uniquename_cb(
unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer));
}
@@ -122,12 +123,13 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
const char *name,
ImageFormatData *im_format)
{
- NodeImageMultiFile *nimf = node->storage;
- bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name);
+ NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
+ bNodeSocket *sock = nodeAddStaticSocket(
+ ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name);
/* create format data for the input socket */
- NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket),
- "socket image format");
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)MEM_callocN(
+ sizeof(NodeImageMultiFileSocket), "socket image format");
sock->storage = sockdata;
BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
@@ -155,8 +157,8 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
{
- NodeImageMultiFile *nimf = node->storage;
- bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
+ NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
+ bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input);
int totinputs = BLI_listbase_count(&node->inputs);
if (!sock) {
@@ -176,14 +178,14 @@ int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name)
{
- NodeImageMultiFileSocket *sockdata = sock->storage;
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
}
void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name)
{
- NodeImageMultiFileSocket *sockdata = sock->storage;
+ NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
}
@@ -193,9 +195,10 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNode *node = ptr->data;
- NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
- ImageFormatData *format = NULL;
+ bNode *node = (bNode *)ptr->data;
+ NodeImageMultiFile *nimf = (NodeImageMultiFile *)MEM_callocN(sizeof(NodeImageMultiFile),
+ "node image multi file");
+ ImageFormatData *format = nullptr;
node->storage = nimf;
if (scene) {
@@ -219,10 +222,8 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
static void free_output_file(bNode *node)
{
- bNodeSocket *sock;
-
/* free storage data in sockets */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
MEM_freeN(sock->storage);
}
@@ -238,37 +239,35 @@ static void copy_output_file(bNodeTree *UNUSED(dest_ntree),
dest_node->storage = MEM_dupallocN(src_node->storage);
/* duplicate storage data in sockets */
- for (src_sock = src_node->inputs.first, dest_sock = dest_node->inputs.first;
+ for (src_sock = (bNodeSocket *)src_node->inputs.first,
+ dest_sock = (bNodeSocket *)dest_node->inputs.first;
src_sock && dest_sock;
- src_sock = src_sock->next, dest_sock = dest_sock->next) {
+ src_sock = src_sock->next, dest_sock = (bNodeSocket *)dest_sock->next) {
dest_sock->storage = MEM_dupallocN(src_sock->storage);
}
}
static void update_output_file(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *sock, *sock_next;
PointerRNA ptr;
/* XXX fix for T36706: remove invalid sockets added with bpy API.
* This is not ideal, but prevents crashes from missing storage.
* FileOutput node needs a redesign to support this properly.
*/
- for (sock = node->inputs.first; sock; sock = sock_next) {
- sock_next = sock->next;
- if (sock->storage == NULL) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
+ if (sock->storage == nullptr) {
nodeRemoveSocket(ntree, node, sock);
}
}
- for (sock = node->outputs.first; sock; sock = sock_next) {
- sock_next = sock->next;
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
nodeRemoveSocket(ntree, node, sock);
}
cmp_node_update_default(ntree, node);
/* automatically update the socket type based on linked input */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (sock->link) {
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
RNA_enum_set(&ptr, "type", sock->link->fromsock->type);
@@ -281,7 +280,7 @@ void register_node_type_cmp_output_file(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, NULL, NULL);
+ node_type_socket_templates(&ntype, nullptr, nullptr);
ntype.initfunc_api = init_output_file;
node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file);
node_type_update(&ntype, update_output_file);
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.c b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 6e8a28df76f..19975c21a0b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index ab5db41e5b5..e122b710b7b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate cmp_node_planetrackdeform_in[] = {
{SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}};
@@ -34,8 +34,8 @@ static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = {
static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodePlaneTrackDeformData *data = MEM_callocN(sizeof(NodePlaneTrackDeformData),
- "node plane track deform data");
+ NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)MEM_callocN(
+ sizeof(NodePlaneTrackDeformData), "node plane track deform data");
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
node->storage = data;
diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.c b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
index 5093e581cdc..45a98e68b4b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_posterize.c
+++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Posterize ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.c b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index be76bbf01cf..e557854c611 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.c
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -21,25 +21,26 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Premul and Key Alpha Convert ******************** */
-static bNodeSocketTemplate cmp_node_premulkey_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_premulkey_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_premulkey(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out);
+ ntype.declare = blender::nodes::cmp_node_premulkey_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.c b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index dae63f7a702..332e56e26b1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.c
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -21,20 +21,25 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** RGB ******************** */
-static bNodeSocketTemplate cmp_node_rgb_out[] = {
- {SOCK_RGBA, N_("RGBA"), 0.5f, 0.5f, 0.5f, 1.0f},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Color>("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_rgb(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_rgb_out);
+ ntype.declare = blender::nodes::cmp_node_rgb_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.c b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
index 7dd39d5eaa1..d28b35ec9fb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rotate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Rotate ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.c b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index 963832de03a..3972fc0d949 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.c
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Scale ******************** */
@@ -38,7 +38,7 @@ static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node)
bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (STR_ELEM(sock->name, "X", "Y")) {
if (use_xy_scale) {
sock->flag &= ~SOCK_UNAVAIL;
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc
index 001b197e23a..aa719a99b36 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc
@@ -21,50 +21,53 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SEPARATE HSVA ******************** */
-static bNodeSocketTemplate cmp_node_sephsva_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_sephsva_out[] = {
- {SOCK_FLOAT, N_("H")},
- {SOCK_FLOAT, N_("S")},
- {SOCK_FLOAT, N_("V")},
- {SOCK_FLOAT, N_("A")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>("H");
+ b.add_output<decl::Float>("S");
+ b.add_output<decl::Float>("V");
+ b.add_output<decl::Float>("A");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_sephsva(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out);
-
+ ntype.declare = blender::nodes::cmp_node_sephsva_declare;
nodeRegisterType(&ntype);
}
/* **************** COMBINE HSVA ******************** */
-static bNodeSocketTemplate cmp_node_combhsva_in[] = {
- {SOCK_FLOAT, N_("H"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("S"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_combhsva_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("H").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("S").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("V").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_combhsva(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out);
+ ntype.declare = blender::nodes::cmp_node_combhsva_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc
index e08f27db254..b29af1359f5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc
@@ -21,50 +21,53 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SEPARATE RGBA ******************** */
-static bNodeSocketTemplate cmp_node_seprgba_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_seprgba_out[] = {
- {SOCK_FLOAT, N_("R")},
- {SOCK_FLOAT, N_("G")},
- {SOCK_FLOAT, N_("B")},
- {SOCK_FLOAT, N_("A")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>("R");
+ b.add_output<decl::Float>("G");
+ b.add_output<decl::Float>("B");
+ b.add_output<decl::Float>("A");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_seprgba(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out);
+ ntype.declare = blender::nodes::cmp_node_seprgba_declare;
nodeRegisterType(&ntype);
}
/* **************** COMBINE RGBA ******************** */
-static bNodeSocketTemplate cmp_node_combrgba_in[] = {
- {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_combrgba_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("R").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("G").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("B").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_combrgba(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out);
+ ntype.declare = blender::nodes::cmp_node_combrgba_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc
index b3884296600..526d6b4eb5b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc
@@ -21,18 +21,22 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SEPARATE YCCA ******************** */
-static bNodeSocketTemplate cmp_node_sepycca_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}};
-static bNodeSocketTemplate cmp_node_sepycca_out[] = {
- {SOCK_FLOAT, N_("Y")},
- {SOCK_FLOAT, N_("Cb")},
- {SOCK_FLOAT, N_("Cr")},
- {SOCK_FLOAT, N_("A")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>("Y");
+ b.add_output<decl::Float>("Cb");
+ b.add_output<decl::Float>("Cr");
+ b.add_output<decl::Float>("A");
+}
+
+} // namespace blender::nodes
static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -44,24 +48,26 @@ void register_node_type_cmp_sepycca(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out);
+ ntype.declare = blender::nodes::cmp_node_sepycca_declare;
node_type_init(&ntype, node_composit_init_mode_sepycca);
nodeRegisterType(&ntype);
}
/* **************** COMBINE YCCA ******************** */
-static bNodeSocketTemplate cmp_node_combycca_in[] = {
- {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Cb"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Cr"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_combycca_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Y").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("Cb").default_value(0.5f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("Cr").default_value(0.5f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -73,7 +79,7 @@ void register_node_type_cmp_combycca(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out);
+ ntype.declare = blender::nodes::cmp_node_combycca_declare;
node_type_init(&ntype, node_composit_init_mode_combycca);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc
index 4da79ec7981..4619b0c97f1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc
@@ -21,48 +21,54 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SEPARATE YUVA ******************** */
-static bNodeSocketTemplate cmp_node_sepyuva_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}};
-static bNodeSocketTemplate cmp_node_sepyuva_out[] = {
- {SOCK_FLOAT, N_("Y")},
- {SOCK_FLOAT, N_("U")},
- {SOCK_FLOAT, N_("V")},
- {SOCK_FLOAT, N_("A")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>("Y");
+ b.add_output<decl::Float>("U");
+ b.add_output<decl::Float>("V");
+ b.add_output<decl::Float>("A");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_sepyuva(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out);
+ ntype.declare = blender::nodes::cmp_node_sepyuva_declare;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YUVA ******************** */
-static bNodeSocketTemplate cmp_node_combyuva_in[] = {
- {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("U"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_combyuva_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Y").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("U").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("V").min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_combyuva(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out);
+ ntype.declare = blender::nodes::cmp_node_combyuva_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.c b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 1b44cc011e9..07a7ffcb426 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.c
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -21,22 +21,24 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** SET ALPHA ******************** */
-static bNodeSocketTemplate cmp_node_setalpha_in[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_setalpha_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_setalpha(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeSetAlpha *settings = MEM_callocN(sizeof(NodeSetAlpha), __func__);
+ NodeSetAlpha *settings = (NodeSetAlpha *)MEM_callocN(sizeof(NodeSetAlpha), __func__);
node->storage = settings;
settings->mode = CMP_NODE_SETALPHA_MODE_APPLY;
}
@@ -46,7 +48,7 @@ void register_node_type_cmp_setalpha(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out);
+ ntype.declare = blender::nodes::cmp_node_setalpha_declare;
node_type_init(&ntype, node_composit_init_setalpha);
node_type_storage(
&ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc
index 8afb3fd4841..f64abe87116 100644
--- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c
+++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc
@@ -21,21 +21,26 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_global.h"
#include "BKE_image.h"
/* **************** SPLIT VIEWER ******************** */
-static bNodeSocketTemplate cmp_node_splitviewer_in[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_splitviewer_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image");
+ b.add_input<decl::Color>("Image", "Image_001");
+}
+
+} // namespace blender::nodes
static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node)
{
- ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user");
+ ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user");
node->storage = iuser;
iuser->sfra = 1;
iuser->ok = 1;
@@ -50,12 +55,12 @@ void register_node_type_cmp_splitviewer(void)
cmp_node_type_base(
&ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_splitviewer_in, NULL);
+ ntype.declare = blender::nodes::cmp_node_splitviewer_declare;
node_type_init(&ntype, node_composit_init_splitviewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
/* Do not allow muting for this node. */
- node_type_internal_links(&ntype, NULL);
+ node_type_internal_links(&ntype, nullptr);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
index b89f245c542..e5ce2e8ceb9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_context.h"
#include "BKE_lib_id.h"
@@ -40,7 +40,7 @@ static bNodeSocketTemplate cmp_node_stabilize2d_out[] = {
static void init(const bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
Scene *scene = CTX_data_scene(C);
node->id = (ID *)scene->clip;
diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
index 84ab2d30d34..73907d2e27f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c
+++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
static bNodeSocketTemplate inputs[] = {
{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
@@ -34,11 +34,10 @@ static bNodeSocketTemplate outputs[] = {
static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeSunBeams *data = MEM_callocN(sizeof(NodeSunBeams), "sun beams node");
+ NodeSunBeams *data = (NodeSunBeams *)MEM_callocN(sizeof(NodeSunBeams), "sun beams node");
data->source[0] = 0.5f;
data->source[1] = 0.5f;
-
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.c b/source/blender/nodes/composite/nodes/node_composite_switch.cc
index efbb3390e06..71226a6da0b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switch.c
+++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** MIX RGB ******************** */
static bNodeSocketTemplate cmp_node_switch_in[] = {
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index b09d5119bc4..a61712f7f8d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.c
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -25,7 +25,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
-#include "../node_composite_util.h"
+#include "../node_composite_util.hh"
/* **************** SWITCH VIEW ******************** */
static bNodeSocketTemplate cmp_node_switch_view_out[] = {
@@ -37,26 +37,23 @@ static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree,
bNode *node,
const char *name)
{
- bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name);
+ bNodeSocket *sock = nodeAddStaticSocket(
+ ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name);
return sock;
}
static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *sock;
-
if (!BLI_listbase_is_empty(&node->inputs)) {
return;
}
- sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View");
+ bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View");
sock->flag |= SOCK_HIDDEN;
}
static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *sock;
- SceneRenderView *srv;
Scene *scene = (Scene *)node->id;
/* only update when called from the operator button */
@@ -64,7 +61,7 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node)
return;
}
- if (scene == NULL) {
+ if (scene == nullptr) {
nodeRemoveAllSockets(ntree, node);
/* make sure there is always one socket */
cmp_node_switch_view_sanitycheck(ntree, node);
@@ -72,11 +69,12 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node)
}
/* remove the views that were removed */
- sock = node->inputs.last;
+ bNodeSocket *sock = (bNodeSocket *)node->inputs.last;
while (sock) {
- srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name));
+ SceneRenderView *srv = (SceneRenderView *)BLI_findstring(
+ &scene->r.views, sock->name, offsetof(SceneRenderView, name));
- if (srv == NULL) {
+ if (srv == nullptr) {
bNodeSocket *sock_del = sock;
sock = sock->prev;
nodeRemoveSocket(ntree, node, sock_del);
@@ -94,10 +92,10 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node)
}
/* add the new views */
- for (srv = scene->r.views.first; srv; srv = srv->next) {
- sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name));
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
+ sock = (bNodeSocket *)BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name));
- if (sock == NULL) {
+ if (sock == nullptr) {
sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
}
@@ -117,10 +115,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNode *node = ptr->data;
- SceneRenderView *srv;
- bNodeSocket *sock;
- int nr;
+ bNode *node = (bNode *)ptr->data;
/* store scene for updates */
node->id = (ID *)scene;
@@ -129,8 +124,8 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr)
if (scene) {
RenderData *rd = &scene->r;
- for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) {
- sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
+ LISTBASE_FOREACH (SceneRenderView *, srv, &rd->views) {
+ bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
if (srv->viewflag & SCE_VIEW_DISABLE) {
sock->flag |= SOCK_HIDDEN;
@@ -147,7 +142,7 @@ void register_node_type_cmp_switch_view(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out);
+ node_type_socket_templates(&ntype, nullptr, cmp_node_switch_view_out);
ntype.initfunc_api = init_switch_view;
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.c b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index 50be05fe5a6..eff008b4b41 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.c
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -21,26 +21,32 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
-static bNodeSocketTemplate cmp_node_texture_in[] = {
- {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_TRANSLATION},
- {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10.0f, 10.0f, PROP_XYZ},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_texture_out[] = {
- {SOCK_FLOAT, N_("Value")},
- {SOCK_RGBA, N_("Color")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>("Scale")
+ .default_value({1.0f, 1.0f, 1.0f})
+ .min(-10.0f)
+ .max(10.0f)
+ .subtype(PROP_XYZ);
+ b.add_output<decl::Float>("Value");
+ b.add_output<decl::Color>("Color");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_texture(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_texture_in, cmp_node_texture_out);
+ ntype.declare = blender::nodes::cmp_node_texture_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.c b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index 5fc86c997f5..85fd240ce2e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.c
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -21,20 +21,21 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-static bNodeSocketTemplate cmp_node_tonemap_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_tonemap_out[] = {
- {SOCK_RGBA, N_("Image")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>("Image");
+}
+
+} // namespace blender::nodes
static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeTonemap *ntm = MEM_callocN(sizeof(NodeTonemap), "node tonemap data");
+ NodeTonemap *ntm = (NodeTonemap *)MEM_callocN(sizeof(NodeTonemap), "node tonemap data");
ntm->type = 1;
ntm->key = 0.18;
ntm->offset = 1;
@@ -53,7 +54,7 @@ void register_node_type_cmp_tonemap(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_TONEMAP, "Tonemap", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_tonemap_in, cmp_node_tonemap_out);
+ ntype.declare = blender::nodes::cmp_node_tonemap_declare;
node_type_init(&ntype, node_composit_init_tonemap);
node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.c b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index d59ce9b8b7a..cb5c9468daa 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.c
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -21,18 +21,23 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
-static bNodeSocketTemplate cmp_node_trackpos_out[] = {
- {SOCK_FLOAT, N_("X")},
- {SOCK_FLOAT, N_("Y")},
- {SOCK_VECTOR, N_("Speed"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_VELOCITY},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("X");
+ b.add_output<decl::Float>("Y");
+ b.add_output<decl::Vector>("Speed").subtype(PROP_VELOCITY);
+}
+
+} // namespace blender::nodes
static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeTrackPosData *data = MEM_callocN(sizeof(NodeTrackPosData), "node track position data");
+ NodeTrackPosData *data = (NodeTrackPosData *)MEM_callocN(sizeof(NodeTrackPosData),
+ "node track position data");
node->storage = data;
}
@@ -42,7 +47,7 @@ void register_node_type_cmp_trackpos(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_trackpos_out);
+ ntype.declare = blender::nodes::cmp_node_trackpos_declare;
node_type_init(&ntype, init);
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.c b/source/blender/nodes/composite/nodes/node_composite_transform.cc
index be526c1059c..1695101cdbf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_transform.c
+++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Transform ******************** */
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.c b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index 43337ec6f7e..0ee8a41a5ea 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -38,7 +38,8 @@ static bNodeSocketTemplate cmp_node_translate_out[] = {
static void node_composit_init_translate(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeTranslateData *data = MEM_callocN(sizeof(NodeTranslateData), "node translate data");
+ NodeTranslateData *data = (NodeTranslateData *)MEM_callocN(sizeof(NodeTranslateData),
+ "node translate data");
node->storage = data;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc
index ed6dbfa2bf3..ba98ee12f30 100644
--- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
+++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc
@@ -21,18 +21,20 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** VALTORGB ******************** */
-static bNodeSocketTemplate cmp_node_valtorgb_in[] = {
- {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_valtorgb_out[] = {
- {SOCK_RGBA, N_("Image")},
- {SOCK_FLOAT, N_("Alpha")},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Color>("Image");
+ b.add_output<decl::Color>("Alpha");
+}
+
+} // namespace blender::nodes
static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node)
{
@@ -44,7 +46,7 @@ void register_node_type_cmp_valtorgb(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out);
+ ntype.declare = blender::nodes::cmp_node_valtorgb_declare;
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, node_composit_init_valtorgb);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
@@ -53,21 +55,23 @@ void register_node_type_cmp_valtorgb(void)
}
/* **************** RGBTOBW ******************** */
-static bNodeSocketTemplate cmp_node_rgbtobw_in[] = {
- {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_rgbtobw_out[] = {
- {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_output<decl::Color>("Val");
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_rgbtobw(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0);
- node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out);
+ ntype.declare = blender::nodes::cmp_node_rgbtobw_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.c b/source/blender/nodes/composite/nodes/node_composite_value.cc
index 2ede2cb8c83..5459801bcc7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.c
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -21,20 +21,25 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** VALUE ******************** */
-static bNodeSocketTemplate cmp_node_value_out[] = {
- {SOCK_FLOAT, N_("Value"), 0.5f, 0, 0, 0, -FLT_MAX, FLT_MAX, PROP_NONE},
- {-1, ""},
-};
+
+namespace blender::nodes {
+
+static void cmp_node_value_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("Value").default_value(0.5f);
+}
+
+} // namespace blender::nodes
void register_node_type_cmp_value(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
- node_type_socket_templates(&ntype, NULL, cmp_node_value_out);
+ ntype.declare = blender::nodes::cmp_node_value_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc
index 7005ea42afe..ce6ba659609 100644
--- a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc
@@ -21,7 +21,7 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** VECTOR BLUR ******************** */
static bNodeSocketTemplate cmp_node_vecblur_in[] = {
@@ -33,13 +33,13 @@ static bNodeSocketTemplate cmp_node_vecblur_out[] = {{SOCK_RGBA, N_("Image")}, {
static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeBlurData *nbd = MEM_callocN(sizeof(NodeBlurData), "node blur data");
+ NodeBlurData *nbd = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data");
node->storage = nbd;
nbd->samples = 32;
nbd->fac = 1.0f;
}
-/* custom1: iterations, custom2: maxspeed (0 = nolimit) */
+/* custom1: iterations, custom2: max_speed (0 = no_limit). */
void register_node_type_cmp_vecblur(void)
{
static bNodeType ntype;
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.c b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index b5f74d5c937..7234d4d8eb2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.c
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -21,21 +21,27 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
#include "BKE_global.h"
#include "BKE_image.h"
/* **************** VIEWER ******************** */
-static bNodeSocketTemplate cmp_node_viewer_in[] = {
- {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
- {-1, ""}};
+
+namespace blender::nodes {
+
+static void cmp_node_viewer_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f);
+}
+
+} // namespace blender::nodes
static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node)
{
- ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user");
+ ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user");
node->storage = iuser;
iuser->sfra = 1;
iuser->ok = 1;
@@ -50,11 +56,11 @@ void register_node_type_cmp_viewer(void)
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW);
- node_type_socket_templates(&ntype, cmp_node_viewer_in, NULL);
+ ntype.declare = blender::nodes::cmp_node_viewer_declare;
node_type_init(&ntype, node_composit_init_viewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
- node_type_internal_links(&ntype, NULL);
+ node_type_internal_links(&ntype, nullptr);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.c b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
index 5041b16c303..79e4d449159 100644
--- a/source/blender/nodes/composite/nodes/node_composite_zcombine.c
+++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
@@ -21,29 +21,31 @@
* \ingroup cmpnodes
*/
-#include "node_composite_util.h"
+#include "node_composite_util.hh"
/* **************** Z COMBINE ******************** */
-/* lazy coder NOTE: node->custom2 is abused to send signal. */
-static bNodeSocketTemplate cmp_node_zcombine_in[] = {
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE},
- {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f},
- {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE},
- {-1, ""},
-};
-static bNodeSocketTemplate cmp_node_zcombine_out[] = {
- {SOCK_RGBA, N_("Image")},
- {SOCK_FLOAT, N_("Z")},
- {-1, ""},
-};
+namespace blender::nodes {
+
+static void cmp_node_zcombine_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(10000.0f);
+ b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>("Z", "Z_001").default_value(1.0f).min(0.0f).max(10000.0f);
+ b.add_output<decl::Color>("Image");
+ b.add_output<decl::Float>("Z");
+}
+
+} // namespace blender::nodes
+
+/* lazy coder NOTE: node->custom2 is abused to send signal. */
void register_node_type_cmp_zcombine(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR, 0);
- node_type_socket_templates(&ntype, cmp_node_zcombine_in, cmp_node_zcombine_out);
+ ntype.declare = blender::nodes::cmp_node_zcombine_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
index 1bd39aacdca..7f6f554ba93 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
@@ -20,8 +20,9 @@
namespace blender::nodes {
-static void fn_node_random_float_declare(NodeDeclarationBuilder &b)
+static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Int>("Seed").min(-10000).max(10000);
@@ -67,19 +68,19 @@ class RandomFloatFunction : public blender::fn::MultiFunction {
}
};
-static void fn_node_random_float_build_multi_function(
+static void fn_node_legacy_random_float_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
static RandomFloatFunction fn;
builder.set_matching_fn(fn);
}
-void register_node_type_fn_random_float()
+void register_node_type_fn_legacy_random_float()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0);
- ntype.declare = blender::nodes::fn_node_random_float_declare;
- ntype.build_multi_function = fn_node_random_float_build_multi_function;
+ fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0, 0);
+ ntype.declare = blender::nodes::fn_node_legacy_random_float_declare;
+ ntype.build_multi_function = fn_node_legacy_random_float_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index b71ee092de6..d10490bb2ee 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -28,6 +28,7 @@ namespace blender::nodes {
static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Bool>("Boolean", "Boolean");
b.add_input<decl::Bool>("Boolean", "Boolean_001");
b.add_output<decl::Bool>("Boolean");
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index 4f4830afabc..9736c52e895 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -30,6 +30,7 @@ namespace blender::nodes {
static void fn_node_float_compare_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f);
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index e59c78d2c04..8bb5dafff8a 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -29,6 +29,7 @@ namespace blender::nodes {
static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Float");
b.add_output<decl::Int>("Integer");
};
diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
new file mode 100644
index 00000000000..11c64d3f694
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
@@ -0,0 +1,74 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_function_util.hh"
+
+namespace blender::nodes {
+
+static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::String>("Line Break");
+ b.add_output<decl::String>("Tab");
+};
+
+class MF_SpecialCharacters : public fn::MultiFunction {
+ public:
+ MF_SpecialCharacters()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Special Characters"};
+ signature.single_output<std::string>("Line Break");
+ signature.single_output<std::string>("Tab");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ MutableSpan<std::string> lb = params.uninitialized_single_output<std::string>(0, "Line Break");
+ MutableSpan<std::string> tab = params.uninitialized_single_output<std::string>(1, "Tab");
+
+ for (const int i : mask) {
+ new (&lb[i]) std::string("\n");
+ new (&tab[i]) std::string("\t");
+ }
+ }
+};
+
+static void fn_node_input_special_characters_build_multi_function(
+ NodeMultiFunctionBuilder &builder)
+{
+ static MF_SpecialCharacters special_characters_fn;
+ builder.set_matching_fn(special_characters_fn);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_input_special_characters()
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(
+ &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT, 0);
+ ntype.declare = blender::nodes::fn_node_input_special_characters_declare;
+ ntype.build_multi_function =
+ blender::nodes::fn_node_input_special_characters_build_multi_function;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index 4a8e898fb9b..704ae9d900c 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -23,6 +23,7 @@ namespace blender::nodes {
static void fn_node_input_string_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_output<decl::String>("String");
};
diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc
new file mode 100644
index 00000000000..53ca77aab0c
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc
@@ -0,0 +1,299 @@
+/*
+ * 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_hash.h"
+#include "BLI_noise.hh"
+
+#include "node_function_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+static void fn_node_random_value_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>("Min").supports_field();
+ b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field();
+ b.add_input<decl::Float>("Min", "Min_001").supports_field();
+ b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field();
+ b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>("Max", "Max_002")
+ .default_value(100)
+ .min(-100000)
+ .max(100000)
+ .supports_field();
+ b.add_input<decl::Float>("Probability")
+ .min(0.0f)
+ .max(1.0f)
+ .default_value(0.5f)
+ .subtype(PROP_FACTOR)
+ .supports_field();
+ b.add_input<decl::Int>("ID").implicit_field();
+ b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field();
+
+ b.add_output<decl::Vector>("Value").dependent_field();
+ b.add_output<decl::Float>("Value", "Value_001").dependent_field();
+ b.add_output<decl::Int>("Value", "Value_002").dependent_field();
+ b.add_output<decl::Bool>("Value", "Value_003").dependent_field();
+}
+
+static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void fn_node_random_value_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeRandomValue *data = (NodeRandomValue *)MEM_callocN(sizeof(NodeRandomValue), __func__);
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+}
+
+static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeRandomValue &storage = *(const NodeRandomValue *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+ bNodeSocket *sock_min_int = sock_max_float->next;
+ bNodeSocket *sock_max_int = sock_min_int->next;
+ bNodeSocket *sock_probability = sock_max_int->next;
+
+ bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *sock_out_float = sock_out_vector->next;
+ bNodeSocket *sock_out_int = sock_out_float->next;
+ bNodeSocket *sock_out_bool = sock_out_int->next;
+
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_probability, data_type == CD_PROP_BOOL);
+
+ nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL);
+}
+
+class RandomVectorFunction : public fn::MultiFunction {
+ public:
+ RandomVectorFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float3>("Min");
+ signature.single_input<float3>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<float3>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &min_values = params.readonly_single_input<float3>(0, "Min");
+ const VArray<float3> &max_values = params.readonly_single_input<float3>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<float3> values = params.uninitialized_single_output<float3>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float3 min_value = min_values[i];
+ const float3 max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float x = noise::hash_to_float(seed, id, 0);
+ const float y = noise::hash_to_float(seed, id, 1);
+ const float z = noise::hash_to_float(seed, id, 2);
+
+ values[i] = float3(x, y, z) * (max_value - min_value) + min_value;
+ }
+ }
+};
+
+class RandomFloatFunction : public fn::MultiFunction {
+ public:
+ RandomFloatFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float>("Min");
+ signature.single_input<float>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<float>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &min_values = params.readonly_single_input<float>(0, "Min");
+ const VArray<float> &max_values = params.readonly_single_input<float>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<float> values = params.uninitialized_single_output<float>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float min_value = min_values[i];
+ const float max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float value = noise::hash_to_float(seed, id);
+ values[i] = value * (max_value - min_value) + min_value;
+ }
+ }
+};
+
+class RandomIntFunction : public fn::MultiFunction {
+ public:
+ RandomIntFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<int>("Min");
+ signature.single_input<int>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<int>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &min_values = params.readonly_single_input<int>(0, "Min");
+ const VArray<int> &max_values = params.readonly_single_input<int>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<int> values = params.uninitialized_single_output<int>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float min_value = min_values[i];
+ const float max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float value = noise::hash_to_float(id, seed);
+ values[i] = round_fl_to_int(value * (max_value - min_value) + min_value);
+ }
+ }
+};
+
+class RandomBoolFunction : public fn::MultiFunction {
+ public:
+ RandomBoolFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float>("Probability");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<bool>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &probabilities = params.readonly_single_input<float>(0, "Probability");
+ const VArray<int> &ids = params.readonly_single_input<int>(1, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(2, "Seed");
+ MutableSpan<bool> values = params.uninitialized_single_output<bool>(3, "Value");
+
+ for (int64_t i : mask) {
+ const int seed = seeds[i];
+ const int id = ids[i];
+ const float probability = probabilities[i];
+ values[i] = noise::hash_to_float(id, seed) <= probability;
+ }
+ }
+};
+
+static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeRandomValue &storage = *(const NodeRandomValue *)builder.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ switch (data_type) {
+ case CD_PROP_FLOAT3: {
+ static RandomVectorFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ static RandomFloatFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_INT32: {
+ static RandomIntFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_BOOL: {
+ static RandomBoolFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_random_value()
+{
+ static bNodeType ntype;
+ fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER, 0);
+ node_type_init(&ntype, blender::nodes::fn_node_random_value_init);
+ node_type_update(&ntype, blender::nodes::fn_node_random_value_update);
+ ntype.draw_buttons = blender::nodes::fn_node_random_value_layout;
+ ntype.declare = blender::nodes::fn_node_random_value_declare;
+ ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function;
+ node_type_storage(
+ &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
new file mode 100644
index 00000000000..cbae1648663
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
@@ -0,0 +1,138 @@
+/*
+ * 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_listbase.h"
+#include "BLI_string.h"
+
+#include "RNA_enum_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_function_util.hh"
+
+namespace blender::nodes {
+static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value();
+ b.add_input<decl::Vector>("Rotate By").subtype(PROP_EULER);
+ b.add_input<decl::Vector>("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
+ b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
+ b.add_output<decl::Vector>("Rotation");
+};
+
+static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *rotate_by_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 1));
+ bNodeSocket *axis_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 2));
+ bNodeSocket *angle_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 3));
+
+ nodeSetSocketAvailability(rotate_by_socket,
+ ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_EULER));
+ nodeSetSocketAvailability(axis_socket,
+ ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE));
+ nodeSetSocketAvailability(angle_socket,
+ ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE));
+}
+
+static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static const fn::MultiFunction *get_multi_function(bNode &bnode)
+{
+ static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{
+ "Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) {
+ float input_mat[3][3];
+ eul_to_mat3(input_mat, input);
+ float rot_mat[3][3];
+ eul_to_mat3(rot_mat, rotation);
+ float mat_res[3][3];
+ mul_m3_m3m3(mat_res, rot_mat, input_mat);
+ float3 result;
+ mat3_to_eul(result, mat_res);
+ return result;
+ }};
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> obj_AA_rot{
+ "Rotate Euler by AxisAngle/Object",
+ [](const float3 &input, const float3 &axis, float angle) {
+ float input_mat[3][3];
+ eul_to_mat3(input_mat, input);
+ float rot_mat[3][3];
+ axis_angle_to_mat3(rot_mat, axis, angle);
+ float mat_res[3][3];
+ mul_m3_m3m3(mat_res, rot_mat, input_mat);
+ float3 result;
+ mat3_to_eul(result, mat_res);
+ return result;
+ }};
+ static fn::CustomMF_SI_SI_SO<float3, float3, float3> point_euler_rot{
+ "Rotate Euler by Euler/Point", [](const float3 &input, const float3 &rotation) {
+ float input_mat[3][3];
+ eul_to_mat3(input_mat, input);
+ float rot_mat[3][3];
+ eul_to_mat3(rot_mat, rotation);
+ float mat_res[3][3];
+ mul_m3_m3m3(mat_res, input_mat, rot_mat);
+ float3 result;
+ mat3_to_eul(result, mat_res);
+ return result;
+ }};
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> point_AA_rot{
+ "Rotate Euler by AxisAngle/Point", [](const float3 &input, const float3 &axis, float angle) {
+ float input_mat[3][3];
+ eul_to_mat3(input_mat, input);
+ float rot_mat[3][3];
+ axis_angle_to_mat3(rot_mat, axis, angle);
+ float mat_res[3][3];
+ mul_m3_m3m3(mat_res, input_mat, rot_mat);
+ float3 result;
+ mat3_to_eul(result, mat_res);
+ return result;
+ }};
+ short type = bnode.custom1;
+ short space = bnode.custom2;
+ if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) {
+ return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &point_AA_rot;
+ }
+ if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) {
+ return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &point_euler_rot;
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void fn_node_rotate_euler_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const fn::MultiFunction *fn = get_multi_function(builder.node());
+ builder.set_matching_fn(fn);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_rotate_euler()
+{
+ static bNodeType ntype;
+ fn_node_type_base(&ntype, FN_NODE_ROTATE_EULER, "Rotate Euler", NODE_CLASS_CONVERTER, 0);
+ ntype.declare = blender::nodes::fn_node_rotate_euler_declare;
+ ntype.draw_buttons = blender::nodes::fn_node_rotate_euler_layout;
+ node_type_update(&ntype, blender::nodes::fn_node_rotate_euler_update);
+ ntype.build_multi_function = blender::nodes::fn_node_rotate_euler_build_multi_function;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc
index a0f85dfd2bf..89038629c3c 100644
--- a/source/blender/nodes/function/nodes/node_fn_string_length.cc
+++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc
@@ -24,6 +24,7 @@ namespace blender::nodes {
static void fn_node_string_length_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::String>("String");
b.add_output<decl::Int>("Length");
};
diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc
index 55a01093ae9..b91171923d6 100644
--- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc
+++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc
@@ -22,6 +22,7 @@ namespace blender::nodes {
static void fn_node_string_substring_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::String>("String");
b.add_input<decl::Int>("Position");
b.add_input<decl::Int>("Length").min(0);
diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
index c1e6373cb6d..56206af2eb2 100644
--- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
@@ -21,6 +21,7 @@ namespace blender::nodes {
static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Value");
b.add_input<decl::Int>("Decimals").min(0);
b.add_output<decl::String>("String");
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index d6b23c38ee4..20b610a4db9 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -119,7 +119,7 @@ void register_node_tree_type_geo(void)
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = 0; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
tt->update = geometry_node_tree_update;
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 015ac0de002..5896b5bd6cc 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -65,7 +65,9 @@ Mesh *create_grid_mesh(const int verts_x,
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const float radius_bottom,
const float depth,
- const int verts_num,
+ const int circle_segments,
+ const int side_segments,
+ const int fill_segments,
const GeometryNodeMeshCircleFillType fill_type);
Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
index d0bb906e8af..d0bb906e8af 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
index 97070184609..2e931a2da98 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
@@ -269,7 +269,7 @@ void register_node_type_geo_attribute_clamp()
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init);
node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update);
ntype.declare = blender::nodes::geo_node_attribute_clamp_declare;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
index aa054af3acd..aa054af3acd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
index 569d5a824ca..569d5a824ca 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
index 0b9708dae14..0b9708dae14 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
index a2382aa9d25..a2382aa9d25 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
index b9621b4ae92..b9621b4ae92 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
index 3c50ae5c837..3c50ae5c837 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
index 0ea3bbe1e45..0ea3bbe1e45 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
index efa09215b45..efa09215b45 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
index 74e05cb997d..74e05cb997d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
index 0cf411343cf..6120118f611 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
@@ -232,7 +232,7 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_attribute_proximity()
+void register_node_type_geo_legacy_attribute_proximity()
{
static bNodeType ntype;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
index 60b9910399c..2e6ba456725 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
@@ -25,7 +25,7 @@
namespace blender::nodes {
-static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
+static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::String>("Attribute");
@@ -39,15 +39,15 @@ static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_attribute_random_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void geo_node_legacy_attribute_random_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
+static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN(
sizeof(NodeAttributeRandomize), __func__);
@@ -57,7 +57,7 @@ static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *no
node->storage = data;
}
-static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -280,7 +280,7 @@ static void randomize_attribute_on_component(GeometryComponent &component,
attribute.save();
}
-static void geo_node_random_attribute_exec(GeoNodeExecParams params)
+static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.get_input<std::string>("Attribute");
@@ -326,18 +326,18 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_attribute_randomize()
+void register_node_type_geo_legacy_attribute_randomize()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update);
+ node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init);
+ node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update);
- ntype.declare = blender::nodes::geo_node_attribute_randomize_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout;
+ ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout;
node_type_storage(
&ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
index 52f97475941..52f97475941 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
index de0090406c6..de0090406c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
index 874350cd714..f187ee39b94 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
@@ -404,7 +404,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
data_type);
for (const int i : IndexRange(tot_samples)) {
if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
- /* Pointcloud point is closer. */
+ /* Point-cloud point is closer. */
const int index = pointcloud_indices[i];
pointcloud_src_attribute.varray->get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
index 59903050f88..59903050f88 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
index adaa4de3029..0c515fa63fb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
@@ -332,7 +332,7 @@ void register_node_type_geo_attribute_vector_rotate()
static bNodeType ntype;
geo_node_type_base(&ntype,
- GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE,
"Attribute Vector Rotate",
NODE_CLASS_ATTRIBUTE,
0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
index 7853c5aa04a..65d22eca39c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
@@ -212,7 +212,8 @@ void register_node_type_geo_curve_endpoints()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_endpoints_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec;
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
new file mode 100644
index 00000000000..d1c81333c30
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
@@ -0,0 +1,71 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the reversed data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_CURVE, true);
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ if (selection[i]) {
+ splines[i]->reverse();
+ }
+ }
+ });
+
+ params.set_output("Curve", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_reverse()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
index dfcae2e65b0..dfcae2e65b0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
new file mode 100644
index 00000000000..339029336d9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
@@ -0,0 +1,144 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_curve_set_handles_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN(
+ sizeof(NodeGeometryCurveSetHandles), __func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSetHandles *node_storage =
+ (NodeGeometryCurveSetHandles *)params.node().storage;
+ const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type;
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_POINT, true);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+ bool has_bezier_spline = false;
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+ has_bezier_spline = true;
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
+ }
+ point_index++;
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+
+ if (!has_bezier_spline) {
+ params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
+ }
+
+ params.set_output("Curve", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_set_handles()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre;
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSetHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
new file mode 100644
index 00000000000..44522e990d9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
@@ -0,0 +1,302 @@
+/*
+ * 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 "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_legacy_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN(
+ sizeof(NodeGeometryCurveSplineType), __func__);
+
+ data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ node->storage = data;
+}
+
+template<class T>
+static void scale_input_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> r_output)
+{
+ for (const int i : IndexRange(r_output.size())) {
+ r_output[i] = input[i * scale + offset];
+ }
+}
+
+template<class T>
+static void scale_output_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> &r_output)
+{
+ for (const int i : IndexRange(input.size())) {
+ r_output[i * scale + offset] = input[i];
+ }
+}
+
+template<typename CopyFn>
+static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+{
+ input_spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+ if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
+ if (!dst) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ copy_fn(*src, *dst);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr convert_to_poly_spline(const Spline &input)
+{
+ std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr poly_to_nurbs(const Spline &input)
+{
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ Spline::copy_base_settings(input, *output);
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr bezier_to_nurbs(const Spline &input)
+{
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.size() * 3);
+
+ scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
+ scale_output_assign(input.radii(), 3, 0, output->radii());
+ scale_output_assign(input.tilts(), 3, 0, output->tilts());
+
+ scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
+ scale_output_assign(input.radii(), 3, 1, output->radii());
+ scale_output_assign(input.tilts(), 3, 1, output->tilts());
+
+ scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
+ scale_output_assign(input.radii(), 3, 2, output->radii());
+ scale_output_assign(input.tilts(), 3, 2, output->tilts());
+
+ Spline::copy_base_settings(input, *output);
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ output->set_cyclic(input.is_cyclic());
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr poly_to_bezier(const Spline &input)
+{
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->set_resolution(12);
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr nurbs_to_bezier(const Spline &input)
+{
+ const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size() / 3);
+ scale_input_assign<float3>(input.positions(), 3, 1, output->positions());
+ scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left());
+ scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
+ scale_input_assign<float>(input.radii(), 3, 2, output->radii());
+ scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Align);
+ output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->set_resolution(nurbs_spline.resolution());
+ Spline::copy_base_settings(input, *output);
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+{
+ switch (input.type()) {
+ case Spline::Type::Bezier:
+ return input.copy();
+ case Spline::Type::Poly:
+ return poly_to_bezier(input);
+ case Spline::Type::NURBS:
+ if (input.size() < 6) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("NURBS must have minimum of 6 points for Bezier Conversion"));
+ return input.copy();
+ }
+ else {
+ if (input.size() % 3 != 0) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("NURBS must have multiples of 3 points for full Bezier "
+ "conversion, curve truncated"));
+ }
+ return nurbs_to_bezier(input);
+ }
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static SplinePtr convert_to_nurbs(const Spline &input)
+{
+ switch (input.type()) {
+ case Spline::Type::NURBS:
+ return input.copy();
+ case Spline::Type::Bezier:
+ return bezier_to_nurbs(input);
+ case Spline::Type::Poly:
+ return poly_to_nurbs(input);
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static void geo_node_legacy_curve_spline_type_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSplineType *storage =
+ (const NodeGeometryCurveSplineType *)params.node().storage;
+ const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_CURVE, true);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ for (const int i : curve.splines().index_range()) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ break;
+ }
+ }
+ else {
+ new_curve->add_spline(curve.splines()[i]->copy());
+ }
+ }
+
+ new_curve->attributes = curve.attributes;
+ params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_spline_type()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_legacy_curve_spline_type_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_curve_spline_type_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_legacy_curve_spline_type_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSplineType",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_legacy_curve_spline_type_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
new file mode 100644
index 00000000000..f32a68bc042
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
@@ -0,0 +1,392 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+namespace blender::nodes {
+
+static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::String>("Cuts");
+ b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_output<decl::Geometry>("Geometry");
+}
+
+static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
+}
+
+static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
+ sizeof(NodeGeometryCurveSubdivide), __func__);
+
+ data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
+ node->storage = data;
+}
+
+static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
+static Array<int> get_subdivided_offsets(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ Array<int> offsets(spline.segments_size() + 1);
+ int offset = 0;
+ for (const int i : IndexRange(spline.segments_size())) {
+ offsets[i] = offset;
+ offset = offset + std::max(cuts[spline_offset + i], 0) + 1;
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+template<typename T>
+static void subdivide_attribute(Span<T> src,
+ const Span<int> offsets,
+ const bool is_cyclic,
+ MutableSpan<T> dst)
+{
+ const int src_size = src.size();
+ threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src[i];
+ const float factor_delta = 1.0f / (cuts + 1.0f);
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = (cut + 1) * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]);
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int i = src_size - 1;
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src.last();
+ const float factor_delta = 1.0f / (cuts + 1.0f);
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = (cut + 1) * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first());
+ }
+ }
+ else {
+ dst.last() = src.last();
+ }
+}
+
+/**
+ * In order to generate a Bezier spline with the same shape as the input spline, apply the
+ * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the
+ * previous result point's right handle and the left handle at the end of the segment.
+ *
+ * \note Non-vector segments in the result spline are given free handles. This could possibly be
+ * improved with another pass that sets handles to aligned where possible, but currently that does
+ * not provide much benefit for the increased complexity.
+ */
+static void subdivide_bezier_segment(const BezierSpline &src,
+ const int index,
+ const int offset,
+ const int result_size,
+ Span<float3> src_positions,
+ Span<float3> src_handles_left,
+ Span<float3> src_handles_right,
+ MutableSpan<float3> dst_positions,
+ MutableSpan<float3> dst_handles_left,
+ MutableSpan<float3> dst_handles_right,
+ MutableSpan<BezierSpline::HandleType> dst_type_left,
+ MutableSpan<BezierSpline::HandleType> dst_type_right)
+{
+ const bool is_last_cyclic_segment = index == (src.size() - 1);
+ const int next_index = is_last_cyclic_segment ? 0 : index + 1;
+
+ /* The first point in the segment is always copied. */
+ dst_positions[offset] = src_positions[index];
+
+ if (src.segment_is_vector(index)) {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Vector;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
+
+ const float factor_delta = 1.0f / result_size;
+ for (const int cut : IndexRange(result_size)) {
+ const float factor = cut * factor_delta;
+ dst_positions[offset + cut] = attribute_math::mix2(
+ factor, src_positions[index], src_positions[next_index]);
+ }
+ }
+ else {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Free;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
+
+ const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
+
+ /* Create a Bezier segment to update iteratively for every subdivision
+ * and references to the meaningful values for ease of use. */
+ BezierSpline temp;
+ temp.resize(2);
+ float3 &segment_start = temp.positions().first();
+ float3 &segment_end = temp.positions().last();
+ float3 &handle_prev = temp.handle_positions_right().first();
+ float3 &handle_next = temp.handle_positions_left().last();
+ segment_start = src_positions[index];
+ segment_end = src_positions[next_index];
+ handle_prev = src_handles_right[index];
+ handle_next = src_handles_left[next_index];
+
+ for (const int cut : IndexRange(result_size - 1)) {
+ const float parameter = 1.0f / (result_size - cut);
+ const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter);
+
+ /* Copy relevant temporary data to the result. */
+ dst_handles_right[offset + cut] = insert.handle_prev;
+ dst_handles_left[offset + cut + 1] = insert.left_handle;
+ dst_positions[offset + cut + 1] = insert.position;
+
+ /* Update the segment to prepare it for the next subdivision. */
+ segment_start = insert.position;
+ handle_prev = insert.right_handle;
+ handle_next = insert.handle_next;
+ }
+
+ /* Copy the handles for the last segment from the temporary spline. */
+ dst_handles_right[offset + result_size - 1] = handle_prev;
+ dst_handles_left[i_segment_last] = handle_next;
+ }
+}
+
+static void subdivide_bezier_spline(const BezierSpline &src,
+ const Span<int> offsets,
+ BezierSpline &dst)
+{
+ Span<float3> src_positions = src.positions();
+ Span<float3> src_handles_left = src.handle_positions_left();
+ Span<float3> src_handles_right = src.handle_positions_right();
+ MutableSpan<float3> dst_positions = dst.positions();
+ MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
+ MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
+ MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
+ MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
+
+ threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ subdivide_bezier_segment(src,
+ i,
+ offsets[i],
+ offsets[i + 1] - offsets[i],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ });
+
+ if (src.is_cyclic()) {
+ const int i_last = src.size() - 1;
+ subdivide_bezier_segment(src,
+ i_last,
+ offsets[i_last],
+ offsets.last() - offsets[i_last],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ else {
+ dst_positions.last() = src_positions.last();
+ }
+}
+
+static void subdivide_builtin_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
+ subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
+ switch (src_spline.type()) {
+ case Spline::Type::Poly: {
+ const PolySpline &src = static_cast<const PolySpline &>(src_spline);
+ PolySpline &dst = static_cast<PolySpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ break;
+ }
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
+ subdivide_bezier_spline(src, offsets, dst);
+ dst.mark_cache_invalid();
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
+ break;
+ }
+ }
+}
+
+static void subdivide_dynamic_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ src_spline.attributes.foreach_attribute(
+ [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+
+ if (!dst_spline.attributes.create(attribute_id, 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> dst = dst_spline.attributes.get_for_write(attribute_id);
+ BLI_assert(dst);
+
+ attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr subdivide_spline(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ /* Since we expect to access each value many times, it should be worth it to make sure the
+ * attribute is a real span (especially considering the note below). Using the offset at each
+ * point facilitates subdividing in parallel later. */
+ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset);
+ const int result_size = offsets.last() + int(!spline.is_cyclic());
+ SplinePtr new_spline = spline.copy_only_settings();
+ new_spline->resize(result_size);
+ subdivide_builtin_attributes(spline, offsets, *new_spline);
+ subdivide_dynamic_attributes(spline, offsets, *new_spline);
+ return new_spline;
+}
+
+/**
+ * \note Passing the virtual array for the entire spline is possibly quite inefficient here when
+ * the attribute was on the point domain and stored separately for each spline already, and it
+ * prevents some other optimizations like skipping splines with a single attribute value of < 1.
+ * However, it allows the node to access builtin attribute easily, so it the makes most sense this
+ * way until the attribute API is refactored.
+ */
+static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
+ const VArray<int> &cuts)
+{
+ const Array<int> control_point_offsets = input_curve.control_point_offsets();
+ const Span<SplinePtr> input_splines = input_curve.splines();
+
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+ output_curve->resize(input_splines.size());
+ output_curve->attributes = input_curve.attributes;
+ MutableSpan<SplinePtr> output_splines = output_curve->splines();
+
+ threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]);
+ }
+ });
+
+ return output_curve;
+}
+
+static void geo_node_subdivide_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ GVArray_Typed<int> cuts = params.get_input_attribute<int>(
+ "Cuts", component, ATTR_DOMAIN_POINT, 0);
+ if (cuts->is_single() && cuts->get_internal_single() < 1) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_subdivide()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_subdivide_declare;
+ ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout;
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSubdivide",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init);
+ node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
index 1e66b340f5c..0c435d69991 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
@@ -358,7 +358,8 @@ void register_node_type_geo_curve_to_points()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_to_points_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec;
ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
index 1e2f652cd78..1e2f652cd78 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
index 867fecea251..2ea6516996d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
@@ -82,7 +82,7 @@ void register_node_type_geo_edge_split()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0);
ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec;
ntype.declare = blender::nodes::geo_node_edge_split_declare;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
index 11349dc7d42..11349dc7d42 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
index 04b4003daed..f95b0da86ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
@@ -36,7 +36,6 @@
#include "node_geometry_util.hh"
-using blender::bke::AttributeKind;
using blender::bke::GeometryInstanceGroup;
namespace blender::nodes {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
index fb45c22ced4..fb45c22ced4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
index 60c82360007..60c82360007 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
index 99adce149e9..99adce149e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
index 48b6676c1dd..48b6676c1dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
index f2fce45c57b..f2fce45c57b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
index d920c8de9f0..d920c8de9f0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
index 401a478f04c..401a478f04c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
index 4541bf3569f..07d3f89bdb7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
@@ -133,7 +133,7 @@ void register_node_type_geo_subdivision_surface()
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_subdivision_surface_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index c8a33205de4..43fb00a482c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -26,18 +26,18 @@ namespace blender::nodes {
static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Value");
- b.add_input<decl::Float>("Value", "Value_001");
- b.add_input<decl::Color>("Value", "Value_002");
- b.add_input<decl::Bool>("Value", "Value_003");
- b.add_input<decl::Int>("Value", "Value_004");
+ b.add_input<decl::Vector>("Value").supports_field();
+ b.add_input<decl::Float>("Value", "Value_001").supports_field();
+ b.add_input<decl::Color>("Value", "Value_002").supports_field();
+ b.add_input<decl::Bool>("Value", "Value_003").supports_field();
+ b.add_input<decl::Int>("Value", "Value_004").supports_field();
b.add_output<decl::Geometry>("Geometry");
- b.add_output<decl::Vector>("Attribute");
- b.add_output<decl::Float>("Attribute", "Attribute_001");
- b.add_output<decl::Color>("Attribute", "Attribute_002");
- b.add_output<decl::Bool>("Attribute", "Attribute_003");
- b.add_output<decl::Int>("Attribute", "Attribute_004");
+ b.add_output<decl::Vector>("Attribute").field_source();
+ b.add_output<decl::Float>("Attribute", "Attribute_001").field_source();
+ b.add_output<decl::Color>("Attribute", "Attribute_002").field_source();
+ b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source();
+ b.add_output<decl::Int>("Attribute", "Attribute_004").field_source();
}
static void geo_node_attribute_capture_layout(uiLayout *layout,
@@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
const bNode &node = params.node();
const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
node.storage;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 21a9a338857..f93ef6f1db3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
- geometry_set = geometry_set_realize_instances(geometry_set);
-
if (geometry_set.has<MeshComponent>()) {
remove_attribute(
geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 5001034518c..1b7d2fe28a1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -29,8 +29,8 @@ namespace blender::nodes {
static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Attribute").hide_value();
- b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value();
+ b.add_input<decl::Float>("Attribute").hide_value().supports_field();
+ b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field();
b.add_output<decl::Float>("Mean");
b.add_output<decl::Float>("Median");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 2a1c43a89fe..21b425c0ed4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -83,10 +83,14 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
GeometrySet set_a;
if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
set_a = params.extract_input<GeometrySet>("Geometry 1");
+ if (set_a.has_instances()) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Instances are not supported for the first geometry input, and will not be used"));
+ }
/* Note that it technically wouldn't be necessary to realize the instances for the first
* geometry input, but the boolean code expects the first shape for the difference operation
* to be a single mesh. */
- set_a = geometry_set_realize_instances(set_a);
const Mesh *mesh_in_a = set_a.get_mesh_for_read();
if (mesh_in_a != nullptr) {
meshes.append(mesh_in_a);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index f4c295b06fb..d03221703f0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -21,6 +21,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BKE_collection.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes {
@@ -28,6 +30,12 @@ namespace blender::nodes {
static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Collection>("Collection").hide_label();
+ b.add_input<decl::Bool>("Separate Children")
+ .description("Output each child of the collection as a separate instance");
+ b.add_input<decl::Bool>("Reset Children")
+ .description(
+ "Reset the transforms of every child instance in the output. Only used when Separate "
+ "Children is enabled");
b.add_output<decl::Geometry>("Geometry");
}
@@ -57,23 +65,66 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bNode &bnode = params.node();
NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
- const bool transform_space_relative = (node_storage->transform_space ==
- GEO_NODE_TRANSFORM_SPACE_RELATIVE);
+ const bool use_relative_transform = (node_storage->transform_space ==
+ GEO_NODE_TRANSFORM_SPACE_RELATIVE);
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- float transform_mat[4][4];
- unit_m4(transform_mat);
const Object *self_object = params.self_object();
- if (transform_space_relative) {
- copy_v3_v3(transform_mat[3], collection->instance_offset);
-
- mul_m4_m4_pre(transform_mat, self_object->imat);
+ const bool separate_children = params.get_input<bool>("Separate Children");
+ if (separate_children) {
+ const bool reset_children = params.get_input<bool>("Reset Children");
+ Vector<Collection *> children_collections;
+ LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
+ children_collections.append(collection_child->collection);
+ }
+ Vector<Object *> children_objects;
+ LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
+ children_objects.append(collection_object->ob);
+ }
+
+ instances.reserve(children_collections.size() + children_objects.size());
+
+ for (Collection *child_collection : children_collections) {
+ float4x4 transform = float4x4::identity();
+ if (!reset_children) {
+ add_v3_v3(transform.values[3], child_collection->instance_offset);
+ if (use_relative_transform) {
+ mul_m4_m4_pre(transform.values, self_object->imat);
+ }
+ else {
+ sub_v3_v3(transform.values[3], collection->instance_offset);
+ }
+ }
+ const int handle = instances.add_reference(*child_collection);
+ instances.add_instance(handle, transform);
+ }
+ for (Object *child_object : children_objects) {
+ const int handle = instances.add_reference(*child_object);
+ float4x4 transform = float4x4::identity();
+ if (!reset_children) {
+ if (use_relative_transform) {
+ transform = self_object->imat;
+ }
+ else {
+ sub_v3_v3(transform.values[3], collection->instance_offset);
+ }
+ mul_m4_m4_post(transform.values, child_object->obmat);
+ }
+ instances.add_instance(handle, transform);
+ }
+ }
+ else {
+ float4x4 transform = float4x4::identity();
+ if (use_relative_transform) {
+ copy_v3_v3(transform.values[3], collection->instance_offset);
+ mul_m4_m4_pre(transform.values, self_object->imat);
+ }
+
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform);
}
-
- const int handle = instances.add_reference(*collection);
- instances.add_instance(handle, transform_mat, -1);
params.set_output("Geometry", geometry_set_out);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index f1e10e3d276..4377d32210d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
}
+/* Since only positions are read from the instances, this can be used as an internal optimization
+ * to avoid the cost of realizing instances before the node. But disable this for now, since
+ * re-enabling that optimization will be a separate step. */
+# if 0
static void read_positions(const GeometryComponent &component,
- Span<float4x4> transforms,
- Vector<float3> *r_coords)
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve,
}
}
+static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
+{
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ Vector<float3> coords;
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has_pointcloud()) {
+ read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
+ }
+ if (set.has_mesh()) {
+ read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
+ }
+ if (set.has_curve()) {
+ read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+ }
+ }
+ return hull_from_bullet(nullptr, coords);
+}
+# endif
+
#endif /* WITH_BULLET */
static void geo_node_convex_hull_exec(GeoNodeExecParams params)
@@ -272,33 +301,14 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
#ifdef WITH_BULLET
- Mesh *mesh = nullptr;
- if (geometry_set.has_instances()) {
- Vector<GeometryInstanceGroup> set_groups;
- bke::geometry_set_gather_instances(geometry_set, set_groups);
-
- Vector<float3> coords;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- Span<float4x4> transforms = set_group.transforms;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = compute_hull(geometry_set);
+ geometry_set.replace_mesh(mesh);
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
- if (set.has_pointcloud()) {
- read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
- }
- if (set.has_mesh()) {
- read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
- }
- if (set.has_curve()) {
- read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
- }
- }
- mesh = hull_from_bullet(nullptr, coords);
- }
- else {
- mesh = compute_hull(geometry_set);
- }
- params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Convex Hull", std::move(geometry_set));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without Bullet"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index 8de2975f9b0..c30741cf786 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -154,23 +154,8 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params)
const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
- if (geometry_set.has_instances()) {
- InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- instances.ensure_geometry_instances();
-
- threading::parallel_for(IndexRange(instances.references_amount()), 16, [&](IndexRange range) {
- for (int i : range) {
- GeometrySet &geometry_set = instances.geometry_set_from_reference(i);
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- curve_fill_calculate(geometry_set, mode);
- }
- });
-
- params.set_output("Mesh", std::move(geometry_set));
- return;
- }
-
- curve_fill_calculate(geometry_set, mode);
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode); });
params.set_output("Mesh", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 830cfcc8331..67ce20efd9d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -563,19 +563,16 @@ static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
return output_curve;
}
-static void geo_node_fillet_exec(GeoNodeExecParams params)
+static void calculate_curve_fillet(GeometrySet &geometry_set,
+ const GeometryNodeCurveFilletMode mode,
+ const Field<float> &radius_field,
+ const std::optional<Field<int>> &count_field,
+ const bool limit_radius)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
return;
}
- NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
- const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
FilletParam fillet_param;
fillet_param.mode = mode;
@@ -584,19 +581,16 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
fn::FieldEvaluator field_evaluator{field_context, domain_size};
- Field<float> radius_field = params.extract_input<Field<float>>("Radius");
- field_evaluator.add(std::move(radius_field));
+ field_evaluator.add(radius_field);
if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- Field<int> count_field = params.extract_input<Field<int>>("Count");
- field_evaluator.add(std::move(count_field));
+ field_evaluator.add(*count_field);
}
field_evaluator.evaluate();
fillet_param.radii = &field_evaluator.get_evaluated<float>(0);
if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) {
- params.set_output("Geometry", geometry_set);
return;
}
@@ -604,13 +598,36 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
fillet_param.counts = &field_evaluator.get_evaluated<int>(1);
}
- fillet_param.limit_radius = params.extract_input<bool>("Limit Radius");
+ fillet_param.limit_radius = limit_radius;
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param);
- params.set_output("Curve", GeometrySet::create_with_curve(output_curve.release()));
+ geometry_set.replace_curve(output_curve.release());
+}
+
+static void geo_node_fillet_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+
+ NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
+ const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
+
+ Field<float> radius_field = params.extract_input<Field<float>>("Radius");
+ const bool limit_radius = params.extract_input<bool>("Limit Radius");
+
+ std::optional<Field<int>> count_field;
+ if (mode == GEO_NODE_CURVE_FILLET_POLY) {
+ count_field.emplace(params.extract_input<Field<int>>("Count"));
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_curve_fillet()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
index 8fe054633a1..ac7df35bb72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -28,7 +28,6 @@ static void geo_node_curve_length_declare(NodeDeclarationBuilder &b)
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;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
index 2cde198e679..90853387ec7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Factor");
+ b.add_output<decl::Float>("Factor").field_source();
}
/**
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
index 0803d43e5c3..7292fafc8b0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
@@ -43,26 +43,18 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
const int totalpoints = std::max(int(resolution * rotations), 1);
const float delta_radius = (end_radius - start_radius) / (float)totalpoints;
- float radius = start_radius;
const float delta_height = height / (float)totalpoints;
- const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints;
- float theta = 0.0f;
+ const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints *
+ (direction ? 1.0f : -1.0f);
for (const int i : IndexRange(totalpoints + 1)) {
+ const float theta = i * delta_theta;
+ const float radius = start_radius + i * delta_radius;
const float x = radius * cos(theta);
const float y = radius * sin(theta);
const float z = delta_height * i;
spline->add_point(float3(x, y, z), 1.0f, 0.0f);
-
- radius += delta_radius;
-
- if (direction) {
- theta += delta_theta;
- }
- else {
- theta -= delta_theta;
- }
}
spline->attributes.reallocate(spline->size());
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 208525f17f6..e5be9b7a6f4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -35,8 +35,9 @@ namespace blender::nodes {
static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000).supports_field();
+ b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).supports_field().subtype(
+ PROP_DISTANCE);
b.add_output<decl::Geometry>("Geometry");
}
@@ -68,8 +69,8 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node
struct SampleModeParam {
GeometryNodeCurveResampleMode mode;
- std::optional<float> length;
- std::optional<int> count;
+ std::optional<Field<float>> length;
+ std::optional<Field<int>> count;
};
static SplinePtr resample_spline(const Spline &src, const int count)
@@ -163,28 +164,44 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
return dst;
}
-static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component,
const SampleModeParam &mode_param)
{
- Span<SplinePtr> input_splines = input_curve.splines();
+ const CurveEval *input_curve = component->get_for_read();
+ GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+
+ Span<SplinePtr> input_splines = input_curve->splines();
std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
output_curve->resize(input_splines.size());
MutableSpan<SplinePtr> output_splines = output_curve->splines();
if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
+ evaluator.add(*mode_param.count);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
BLI_assert(mode_param.count);
- output_splines[i] = resample_spline(*input_splines[i], *mode_param.count);
+ output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1));
}
});
}
else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
+ evaluator.add(*mode_param.length);
+ evaluator.evaluate();
+ const VArray<float> &lengths = evaluator.get_evaluated<float>(0);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
- const float length = input_splines[i]->length();
- const int count = std::max(int(length / *mode_param.length) + 1, 1);
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float divide_length = std::max(lengths[i], 0.0001f);
+ const float spline_length = input_splines[i]->length();
+ const int count = std::max(int(spline_length / divide_length) + 1, 1);
output_splines[i] = resample_spline(*input_splines[i], count);
}
});
@@ -197,29 +214,35 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
});
}
- output_curve->attributes = input_curve.attributes;
+ output_curve->attributes = input_curve->attributes;
return output_curve;
}
-static void geo_node_resample_exec(GeoNodeExecParams params)
+static void geometry_set_curve_resample(GeometrySet &geometry_set,
+ const SampleModeParam &mode_param)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
-
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
if (!geometry_set.has_curve()) {
- params.set_output("Geometry", GeometrySet());
return;
}
- const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ std::unique_ptr<CurveEval> output_curve = resample_curve(
+ geometry_set.get_component_for_read<CurveComponent>(), mode_param);
+
+ geometry_set.replace_curve(output_curve.release());
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
+
SampleModeParam mode_param;
mode_param.mode = mode;
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
- const int count = params.extract_input<int>("Count");
+ Field<int> count = params.extract_input<Field<int>>("Count");
if (count < 1) {
params.set_output("Geometry", GeometrySet());
return;
@@ -227,14 +250,14 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
mode_param.count.emplace(count);
}
else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
- /* Don't allow asymptotic count increase for low resolution values. */
- const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ Field<int> resolution = params.extract_input<Field<int>>("Length");
mode_param.length.emplace(resolution);
}
- std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); });
- params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+ params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index 32bcbe2c608..b644faabedb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -25,37 +25,39 @@ namespace blender::nodes {
static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
- /* Retrieve data for write access so we can avoid new allocations for the reversed data. */
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
+
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
- selection_name, ATTR_DOMAIN_CURVE, true);
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- if (selection[i]) {
- splines[i]->reverse();
+ CurveEval &curve = *component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+ threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ splines[selection[i]]->reverse();
}
- }
+ });
});
- params.set_output("Curve", geometry_set);
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -63,8 +65,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
void register_node_type_geo_curve_reverse()
{
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index ac0cd510ffa..1266f525861 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -28,12 +28,12 @@ namespace blender::nodes {
static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
+ b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field();
- b.add_output<decl::Vector>("Position");
- b.add_output<decl::Vector>("Tangent");
- b.add_output<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("Position").dependent_field();
+ b.add_output<decl::Vector>("Tangent").dependent_field();
+ b.add_output<decl::Vector>("Normal").dependent_field();
}
static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
index 31c13134f79..9e7ac60c29d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -23,10 +23,10 @@
namespace blender::nodes {
-static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
+static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
@@ -44,7 +44,7 @@ static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node
sizeof(NodeGeometryCurveSetHandles), __func__);
data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
- data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT;
node->storage = data;
}
@@ -72,57 +72,63 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
-
- /* Retrieve data for write access so we can avoid new allocations for the handles data. */
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
- selection_name, ATTR_DOMAIN_POINT, true);
-
- const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
- int point_index = 0;
bool has_bezier_spline = false;
- for (SplinePtr &spline : splines) {
- if (spline->type() != Spline::Type::Bezier) {
- point_index += spline->positions().size();
- continue;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
}
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
- if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
- /* In this case the automatically calculated handle types need to be "baked", because
- * they're possibly changing from a type that is calculated automatically to a type that
- * is positioned manually. */
- bezier_spline.ensure_auto_handles();
- }
- has_bezier_spline = true;
- for (int i_point : IndexRange(bezier_spline.size())) {
- if (selection[point_index]) {
- if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- bezier_spline.handle_types_left()[i_point] = new_handle_type;
- }
- if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
- bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
+ const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ has_bezier_spline = true;
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
}
+ point_index++;
}
- point_index++;
+ bezier_spline.mark_cache_invalid();
}
- bezier_spline.mark_cache_invalid();
- }
-
+ });
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
}
-
- params.set_output("Curve", geometry_set);
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -130,8 +136,8 @@ void register_node_type_geo_curve_set_handles()
{
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre;
+ &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_set_handles_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
node_type_storage(&ntype,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 0ef107fd8a4..ec72154db13 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -28,7 +28,7 @@ namespace blender::nodes {
static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
@@ -245,41 +245,47 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component->get_for_read();
-
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
- selection_name, ATTR_DOMAIN_CURVE, true);
-
- std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
- for (const int i : curve.splines().index_range()) {
- if (selection[i]) {
- switch (output_type) {
- case GEO_NODE_SPLINE_TYPE_POLY:
- new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
- break;
- case GEO_NODE_SPLINE_TYPE_BEZIER:
- new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
- break;
- case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
- break;
- }
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
}
- else {
- new_curve->add_spline(curve.splines()[i]->copy());
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+ GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
+ const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ for (const int i : curve.splines().index_range()) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ break;
+ }
+ }
+ else {
+ new_curve->add_spline(curve.splines()[i]->copy());
+ }
}
- }
+ new_curve->attributes = curve.attributes;
+ geometry_set.replace_curve(new_curve.release());
+ });
- new_curve->attributes = curve.attributes;
- params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -288,7 +294,7 @@ void register_node_type_geo_curve_spline_type()
{
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_spline_type_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec;
node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 0522f2b8981..34997c66cbb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -34,35 +34,10 @@ namespace blender::nodes {
static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Cuts");
- b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_input<decl::Int>("Cuts").default_value(1).min(0).max(1000).supports_field();
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
-}
-
-static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
- sizeof(NodeGeometryCurveSubdivide), __func__);
-
- data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
- node->storage = data;
-}
-
-static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
static Array<int> get_subdivided_offsets(const Spline &spline,
const VArray<int> &cuts,
const int spline_offset)
@@ -350,25 +325,30 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<int> cuts_field = params.extract_input<Field<int>>("Cuts");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
- if (!geometry_set.has_curve()) {
- params.set_output("Geometry", geometry_set);
- return;
- }
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GVArray_Typed<int> cuts = params.get_input_attribute<int>(
- "Cuts", component, ATTR_DOMAIN_POINT, 0);
- if (cuts->is_single() && cuts->get_internal_single() < 1) {
- params.set_output("Geometry", geometry_set);
- return;
- }
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(cuts_field);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts);
+ if (cuts.is_single() && cuts.get_internal_single() < 1) {
+ return;
+ }
- params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
+ geometry_set.replace_curve(output_curve.release());
+ });
+ params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
@@ -377,16 +357,8 @@ void register_node_type_geo_curve_subdivide()
{
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_subdivide_declare;
- ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout;
- node_type_storage(&ntype,
- "NodeGeometryCurveSubdivide",
- node_free_standard_storage,
- node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index 89ba635ff4b..00451946af9 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
@@ -32,39 +32,52 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Mesh");
}
+static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set)
+{
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ if (profile_curve == nullptr) {
+ Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read());
+ geometry_set.replace_mesh(mesh);
+ }
+ else {
+ Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve);
+ geometry_set.replace_mesh(mesh);
+ }
+}
+
static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
- curve_set = bke::geometry_set_realize_instances(curve_set);
- profile_set = bke::geometry_set_realize_instances(profile_set);
+ if (profile_set.has_instances()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Instances are not supported in the profile input"));
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
- /* NOTE: Theoretically an "is empty" check would be more correct for errors. */
- if (profile_set.has_mesh() && !profile_set.has_curve()) {
+ if (!profile_set.has_curve() && !profile_set.is_empty()) {
params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in profile input"));
+ TIP_("No curve data available in the profile input"));
}
- if (!curve_set.has_curve()) {
- if (curve_set.has_mesh()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in curve input"));
+ bool has_curve = false;
+ curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ has_curve = true;
+ geometry_set_curve_to_mesh(geometry_set, profile_set);
}
- params.set_output("Mesh", GeometrySet());
- return;
- }
-
- const CurveEval *profile_curve = profile_set.get_curve_for_read();
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
- if (profile_curve == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*curve_set.get_curve_for_read());
- params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
- }
- else {
- Mesh *mesh = bke::curve_to_mesh_sweep(*curve_set.get_curve_for_read(), *profile_curve);
- params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+ if (!has_curve && !curve_set.is_empty()) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("No curve data available in curve input"));
}
+
+ params.set_output("Mesh", std::move(curve_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index 2b6d25b6bf3..97043980899 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -320,28 +320,18 @@ static void trim_bezier_spline(Spline &spline,
bezier_spline.resize(size);
}
-static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+static void geometry_set_curve_trim(GeometrySet &geometry_set,
+ const GeometryNodeCurveSampleMode mode,
+ const float start,
+ const float end)
{
- const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
-
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
- params.set_output("Curve", std::move(geometry_set));
return;
}
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
+ CurveEval &curve = *geometry_set.get_curve_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
- const float start = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
- params.extract_input<float>("Start") :
- params.extract_input<float>("Start_001");
- const float end = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input<float>("End") :
- params.extract_input<float>("End_001");
-
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
Spline &spline = *splines[i];
@@ -382,6 +372,29 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params)
splines[i]->mark_cache_invalid();
}
});
+}
+
+static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+
+ if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
+ const float start = params.extract_input<float>("Start");
+ const float end = params.extract_input<float>("End");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_curve_trim(geometry_set, mode, start, end);
+ });
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ const float start = params.extract_input<float>("Start_001");
+ const float end = params.extract_input<float>("End_001");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_curve_trim(geometry_set, mode, start, end);
+ });
+ }
params.set_output("Curve", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
new file mode 100644
index 00000000000..1a4c5d84dbf
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -0,0 +1,594 @@
+/*
+ * 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_kdtree.h"
+#include "BLI_noise.hh"
+#include "BLI_rand.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::GeometryInstanceGroup;
+
+namespace blender::nodes {
+
+static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f);
+ b.add_input<decl::Float>("Density").default_value(10.0f).supports_field();
+ b.add_input<decl::Float>("Density Factor")
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .supports_field();
+ b.add_input<decl::Int>("Seed");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
+
+ b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Vector>("Normal").field_source();
+ b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
+ b.add_output<decl::Int>("Stable ID").field_source();
+}
+
+static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
+}
+
+static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next;
+ bNodeSocket *sock_density = sock_density_max->next;
+ bNodeSocket *sock_density_factor = sock_density->next;
+ nodeSetSocketAvailability(sock_distance_min,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density_max,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ nodeSetSocketAvailability(sock_density_factor,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+}
+
+/**
+ * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node.
+ */
+static float3 normal_to_euler_rotation(const float3 normal)
+{
+ float quat[4];
+ vec_to_quat(quat, normal, OB_NEGZ, OB_POSY);
+ float3 rotation;
+ quat_to_eul(rotation, quat);
+ return rotation;
+}
+
+static void sample_mesh_surface(const Mesh &mesh,
+ const float base_density,
+ const Span<float> density_factors,
+ const int seed,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_bary_coords,
+ Vector<int> &r_looptri_indices)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const int looptri_index : looptris.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+ const int v0_index = mesh.mloop[v0_loop].v;
+ const int v1_index = mesh.mloop[v1_loop].v;
+ const int v2_index = mesh.mloop[v2_loop].v;
+ const float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+
+ float looptri_density_factor = 1.0f;
+ if (!density_factors.is_empty()) {
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
+ looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
+ }
+ const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
+
+ const int looptri_seed = noise::hash(looptri_index, seed);
+ RandomNumberGenerator looptri_rng(looptri_seed);
+
+ const float points_amount_fl = area * base_density * looptri_density_factor;
+ const float add_point_probability = fractf(points_amount_fl);
+ const bool add_point = add_point_probability > looptri_rng.get_float();
+ const int point_amount = (int)points_amount_fl + (int)add_point;
+
+ for (int i = 0; i < point_amount; i++) {
+ const float3 bary_coord = looptri_rng.get_barycentric_coordinates();
+ float3 point_pos;
+ interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord);
+ r_positions.append(point_pos);
+ r_bary_coords.append(bary_coord);
+ r_looptri_indices.append(looptri_index);
+ }
+ }
+}
+
+BLI_NOINLINE static KDTree_3d *build_kdtree(Span<float3> positions)
+{
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size());
+
+ int i_point = 0;
+ for (const float3 position : positions) {
+ BLI_kdtree_3d_insert(kdtree, i_point, position);
+ i_point++;
+ }
+
+ BLI_kdtree_3d_balance(kdtree);
+ return kdtree;
+}
+
+BLI_NOINLINE static void update_elimination_mask_for_close_points(
+ Span<float3> positions, const float minimum_distance, MutableSpan<bool> elimination_mask)
+{
+ if (minimum_distance <= 0.0f) {
+ return;
+ }
+
+ KDTree_3d *kdtree = build_kdtree(positions);
+
+ for (const int i : positions.index_range()) {
+ if (elimination_mask[i]) {
+ continue;
+ }
+
+ struct CallbackData {
+ int index;
+ MutableSpan<bool> elimination_mask;
+ } callback_data = {i, elimination_mask};
+
+ BLI_kdtree_3d_range_search_cb(
+ kdtree,
+ positions[i],
+ minimum_distance,
+ [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
+ if (index != callback_data.index) {
+ callback_data.elimination_mask[index] = true;
+ }
+ return true;
+ },
+ &callback_data);
+ }
+
+ BLI_kdtree_3d_free(kdtree);
+}
+
+BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
+ const Mesh &mesh,
+ const Span<float> density_factors,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const MutableSpan<bool> elimination_mask)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+ for (const int i : bary_coords.index_range()) {
+ if (elimination_mask[i]) {
+ continue;
+ }
+
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ const float3 bary_coord = bary_coords[i];
+
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
+
+ const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
+
+ const float hash = noise::hash_float_to_float(bary_coord);
+ if (hash > probablity) {
+ elimination_mask[i] = true;
+ }
+ }
+}
+
+BLI_NOINLINE static void eliminate_points_based_on_mask(const Span<bool> elimination_mask,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ for (int i = positions.size() - 1; i >= 0; i--) {
+ if (elimination_mask[i]) {
+ positions.remove_and_reorder(i);
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ }
+ }
+}
+
+BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const AttributeDomain source_domain,
+ const GVArray &source_data,
+ GMutableSpan output_data)
+{
+ switch (source_domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
+ break;
+ }
+ default: {
+ /* Not supported currently. */
+ return;
+ }
+ }
+}
+
+BLI_NOINLINE static void propagate_existing_attributes(
+ const MeshComponent &mesh_component,
+ const Map<AttributeIDRef, AttributeKind> &attributes,
+ GeometryComponent &point_component,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType output_data_type = entry.value.data_type;
+ /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
+ OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, output_data_type);
+ if (!attribute_out) {
+ continue;
+ }
+
+ GMutableSpan out_span = attribute_out.as_span();
+
+ std::optional<AttributeMetaData> attribute_info = point_component.attribute_get_meta_data(
+ attribute_id);
+ if (!attribute_info) {
+ continue;
+ }
+
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = mesh_component.attribute_get_for_read(
+ attribute_id, source_domain, output_data_type, nullptr);
+ if (!source_attribute) {
+ continue;
+ }
+
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span);
+
+ attribute_out.save();
+ }
+}
+
+namespace {
+struct AttributeOutputs {
+ StrongAnonymousAttributeID normal_id;
+ StrongAnonymousAttributeID rotation_id;
+ StrongAnonymousAttributeID stable_id_id;
+};
+} // namespace
+
+BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component,
+ PointCloudComponent &point_component,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const AttributeOutputs &attribute_outputs)
+{
+ std::optional<OutputAttribute_Typed<int>> id_attribute;
+ std::optional<OutputAttribute_Typed<float3>> normal_attribute;
+ std::optional<OutputAttribute_Typed<float3>> rotation_attribute;
+
+ MutableSpan<int> ids;
+ MutableSpan<float3> normals;
+ MutableSpan<float3> rotations;
+
+ if (attribute_outputs.stable_id_id) {
+ id_attribute.emplace(point_component.attribute_try_get_for_output_only<int>(
+ attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT));
+ ids = id_attribute->as_span();
+ }
+ if (attribute_outputs.normal_id) {
+ normal_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT));
+ normals = normal_attribute->as_span();
+ }
+ if (attribute_outputs.rotation_id) {
+ rotation_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT));
+ rotations = rotation_attribute->as_span();
+ }
+
+ const Mesh &mesh = *mesh_component.get_for_read();
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+ const float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+
+ if (!ids.is_empty()) {
+ ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+ }
+ float3 normal;
+ if (!normals.is_empty() || !rotations.is_empty()) {
+ normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
+ }
+ if (!normals.is_empty()) {
+ normals[i] = normal;
+ }
+ if (!rotations.is_empty()) {
+ rotations[i] = normal_to_euler_rotation(normal);
+ }
+ }
+
+ if (id_attribute) {
+ id_attribute->save();
+ }
+ if (normal_attribute) {
+ normal_attribute->save();
+ }
+ if (rotation_attribute) {
+ rotation_attribute->save();
+ }
+}
+
+static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field)
+{
+ const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER;
+ GeometryComponentFieldContext field_context{component, attribute_domain};
+ const int domain_size = component.attribute_domain_size(attribute_domain);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0);
+
+ Array<float> densities(domain_size, 0.0f);
+
+ fn::FieldEvaluator density_evaluator{field_context, &selection_mask};
+ density_evaluator.add_with_destination(density_field, densities.as_mutable_span());
+ density_evaluator.evaluate();
+ return densities;
+}
+
+static void distribute_points_random(const MeshComponent &component,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ const Array<float> densities = calc_full_density_factors_with_selection(
+ component, density_field, selection_field);
+ const Mesh &mesh = *component.get_for_read();
+ sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices);
+}
+
+static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
+ const float minimum_distance,
+ const float max_density,
+ const Field<float> &density_factor_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+ sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices);
+
+ Array<bool> elimination_mask(positions.size(), false);
+ update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
+
+ const Array<float> density_factors = calc_full_density_factors_with_selection(
+ mesh_component, density_factor_field, selection_field);
+
+ update_elimination_mask_based_on_density_factors(
+ mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span());
+
+ eliminate_points_based_on_mask(
+ elimination_mask.as_span(), positions, bary_coords, looptri_indices);
+}
+
+static void point_distribution_calculate(GeometrySet &geometry_set,
+ const Field<bool> selection_field,
+ const GeometryNodeDistributePointsOnFacesMode method,
+ const int seed,
+ const AttributeOutputs &attribute_outputs,
+ const GeoNodeExecParams &params)
+{
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+
+ const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+
+ Vector<float3> positions;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+
+ switch (method) {
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
+ const Field<float> density_field = params.get_input<Field<float>>("Density");
+ distribute_points_random(mesh_component,
+ density_field,
+ selection_field,
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ break;
+ }
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
+ const float minimum_distance = params.get_input<float>("Distance Min");
+ const float density_max = params.get_input<float>("Density Max");
+ const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
+ distribute_points_poisson_disk(mesh_component,
+ minimum_distance,
+ density_max,
+ density_factors_field,
+ selection_field,
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ break;
+ }
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
+ memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ geometry_set.replace_pointcloud(pointcloud);
+
+ PointCloudComponent &point_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+
+ /* Position is set separately. */
+ attributes.remove("position");
+
+ propagate_existing_attributes(
+ mesh_component, attributes, point_component, bary_coords, looptri_indices);
+
+ compute_attribute_outputs(
+ mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
+}
+
+static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const GeometryNodeDistributePointsOnFacesMode method =
+ static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1);
+
+ const int seed = params.get_input<int>("Seed") * 5383843;
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ AttributeOutputs attribute_outputs;
+ if (params.output_is_required("Normal")) {
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("normal");
+ }
+ if (params.output_is_required("Rotation")) {
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
+ }
+ if (params.output_is_required("Stable ID")) {
+ attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id");
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ point_distribution_calculate(
+ geometry_set, selection_field, method, seed, attribute_outputs, params);
+ /* Keep instances because the original geometry set may contain instances that are processed as
+ * well. */
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+
+ if (attribute_outputs.normal_id) {
+ params.set_output(
+ "Normal",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ }
+ if (attribute_outputs.rotation_id) {
+ params.set_output(
+ "Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
+ }
+ if (attribute_outputs.stable_id_id) {
+ params.set_output(
+ "Stable ID",
+ AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id)));
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_distribute_points_on_faces()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_DISTRIBUTE_POINTS_ON_FACES,
+ "Distribute Points on Faces",
+ NODE_CLASS_GEOMETRY,
+ 0);
+ node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update);
+ node_type_size(&ntype, 170, 100, 320);
+ ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
index c52ff3d448e..7fcbaf429dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
@@ -20,30 +20,12 @@ namespace blender::nodes {
static void geo_node_input_index_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Index");
+ b.add_output<decl::Int>("Index").field_source();
}
-class IndexFieldInput final : public fn::FieldInput {
- public:
- IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
- {
- }
-
- const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context),
- IndexMask mask,
- ResourceScope &scope) const final
- {
- /* TODO: Investigate a similar method to IndexRange::as_span() */
- auto index_func = [](int i) { return i; };
- return &scope.construct<
- fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
- mask.min_array_size(), mask.min_array_size(), index_func);
- }
-};
-
static void geo_node_input_index_exec(GeoNodeExecParams params)
{
- Field<int> index_field{std::make_shared<IndexFieldInput>()};
+ Field<int> index_field{std::make_shared<fn::IndexFieldInput>()};
params.set_output("Index", std::move(index_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
index f92086acdf0..5a2495afb9e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -28,7 +28,7 @@ namespace blender::nodes {
static void geo_node_input_normal_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("Normal").field_source();
}
static GVArrayPtr mesh_face_normals(const Mesh &mesh,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
index 3f3457a3acb..44874259e20 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
@@ -20,13 +20,12 @@ namespace blender::nodes {
static void geo_node_input_position_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Position");
+ b.add_output<decl::Vector>("Position").field_source();
}
static void geo_node_input_position_exec(GeoNodeExecParams params)
{
- Field<float3> position_field{
- std::make_shared<AttributeFieldInput>("position", CPPType::get<float3>())};
+ Field<float3> position_field{AttributeFieldInput::Create<float3>("position")};
params.set_output("Position", std::move(position_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
new file mode 100644
index 00000000000..b5f3e1b0c28
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -0,0 +1,109 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BKE_spline.hh"
+
+namespace blender::nodes {
+
+static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("Length").field_source();
+}
+
+static const GVArray *construct_spline_length_gvarray(const CurveComponent &component,
+ const AttributeDomain domain,
+ ResourceScope &scope)
+{
+ const CurveEval *curve = component.get_for_read();
+ if (curve == nullptr) {
+ return nullptr;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ auto length_fn = [splines](int i) { return splines[i]->length(); };
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return &scope.construct<
+ fn::GVArray_For_EmbeddedVArray<float, VArray_For_Func<float, decltype(length_fn)>>>(
+ splines.size(), splines.size(), length_fn);
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ GVArrayPtr length = std::make_unique<
+ fn::GVArray_For_EmbeddedVArray<float, VArray_For_Func<float, decltype(length_fn)>>>(
+ splines.size(), splines.size(), length_fn);
+ return scope
+ .add_value(component.attribute_try_adapt_domain(
+ std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT))
+ .get();
+ }
+
+ return nullptr;
+}
+
+class SplineLengthFieldInput final : public fn::FieldInput {
+ public:
+ SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length")
+ {
+ }
+
+ const GVArray *get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &scope) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_spline_length_gvarray(curve_component, domain, scope);
+ }
+ }
+ return nullptr;
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 3549623580;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void geo_node_input_spline_length_exec(GeoNodeExecParams params)
+{
+ Field<float> length_field{std::make_shared<SplineLengthFieldInput>()};
+ params.set_output("Length", std::move(length_field));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_spline_length()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT, 0);
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_length_exec;
+ ntype.declare = blender::nodes::geo_node_input_spline_length_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index 68788709f1e..d690642373a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Tangent");
+ b.add_output<decl::Vector>("Tangent").field_source();
}
static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
new file mode 100644
index 00000000000..8c0c0763be8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -0,0 +1,207 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_collection_types.h"
+
+#include "BLI_hash.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Points").description("Points to instance on");
+ b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points");
+ b.add_input<decl::Bool>("Pick Instance")
+ .supports_field()
+ .description("Place different instances on different points");
+ b.add_input<decl::Int>("Instance Index")
+ .implicit_field()
+ .description(
+ "Index of the instance that used for each point. This is only used when Pick Instances "
+ "is on. By default the point index is used");
+ b.add_input<decl::Vector>("Rotation")
+ .subtype(PROP_EULER)
+ .supports_field()
+ .description("Rotation of the instances");
+ b.add_input<decl::Vector>("Scale")
+ .default_value({1.0f, 1.0f, 1.0f})
+ .supports_field()
+ .description("Scale of the instances");
+ b.add_input<decl::Int>("Stable ID")
+ .supports_field()
+ .description(
+ "ID for every instance that is used to identify it over time even when the number of "
+ "instances changes. Used for example for motion blur");
+
+ b.add_output<decl::Geometry>("Instances");
+}
+
+static void add_instances_from_component(InstancesComponent &dst_component,
+ const GeometryComponent &src_component,
+ const GeometrySet &instance,
+ const GeoNodeExecParams &params)
+{
+ const AttributeDomain domain = ATTR_DOMAIN_POINT;
+ const int domain_size = src_component.attribute_domain_size(domain);
+
+ /* The initial size of the component might be non-zero when this function is called for multiple
+ * component types. */
+ const int start_len = dst_component.instances_amount();
+ dst_component.resize(start_len + domain_size);
+ MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len,
+ domain_size);
+ MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
+ domain_size);
+ MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size);
+
+ GeometryComponentFieldContext field_context{src_component, domain};
+ FieldEvaluator field_evaluator{field_context, domain_size};
+
+ const VArray<bool> *pick_instance = nullptr;
+ const VArray<int> *indices = nullptr;
+ const VArray<float3> *rotations = nullptr;
+ const VArray<float3> *scales = nullptr;
+ field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
+ field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
+ field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
+ field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
+ field_evaluator.add_with_destination(params.get_input<Field<int>>("Stable ID"), dst_stable_ids);
+ field_evaluator.evaluate();
+
+ GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>(
+ "position", domain, {0, 0, 0});
+
+ const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
+
+ /* Maps handles from the source instances to handles on the new instance. */
+ Array<int> handle_mapping;
+ /* Only fill #handle_mapping when it may be used below. */
+ if (src_instances != nullptr &&
+ (!pick_instance->is_single() || pick_instance->get_internal_single())) {
+ Span<InstanceReference> src_references = src_instances->references();
+ handle_mapping.reinitialize(src_references.size());
+ for (const int src_instance_handle : src_references.index_range()) {
+ const InstanceReference &reference = src_references[src_instance_handle];
+ const int dst_instance_handle = dst_component.add_reference(reference);
+ handle_mapping[src_instance_handle] = dst_instance_handle;
+ }
+ }
+
+ const int full_instance_handle = dst_component.add_reference(instance);
+ /* Add this reference last, because it is the most likely one to be removed later on. */
+ const int empty_reference_handle = dst_component.add_reference(InstanceReference());
+
+ threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Compute base transform for every instances. */
+ float4x4 &dst_transform = dst_transforms[i];
+ dst_transform = float4x4::from_loc_eul_scale(
+ positions[i], rotations->get(i), scales->get(i));
+
+ /* Reference that will be used by this new instance. */
+ int dst_handle = empty_reference_handle;
+
+ const bool use_individual_instance = pick_instance->get(i);
+ if (use_individual_instance) {
+ if (src_instances != nullptr) {
+ const int src_instances_amount = src_instances->instances_amount();
+ const int original_index = indices->get(i);
+ /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to
+ * the last element. */
+ const int index = mod_i(original_index, std::max(src_instances_amount, 1));
+ if (index < src_instances_amount) {
+ /* Get the reference to the source instance. */
+ const int src_handle = src_instances->instance_reference_handles()[index];
+ dst_handle = handle_mapping[src_handle];
+
+ /* Take transforms of the source instance into account. */
+ mul_m4_m4_post(dst_transform.values,
+ src_instances->instance_transforms()[index].values);
+ }
+ }
+ }
+ else {
+ /* Use entire source geometry as instance. */
+ dst_handle = full_instance_handle;
+ }
+ /* Set properties of new instance. */
+ dst_handles[i] = dst_handle;
+ }
+ });
+
+ if (pick_instance->is_single()) {
+ if (pick_instance->get_internal_single()) {
+ if (instance.has_realized_data()) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Realized geometry is not used when pick instances is true"));
+ }
+ }
+ }
+}
+
+static void geo_node_instance_on_points_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ GeometrySet instance = params.get_input<GeometrySet>("Instance");
+ instance.ensure_owns_direct_data();
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+
+ if (geometry_set.has<MeshComponent>()) {
+ add_instances_from_component(
+ instances, *geometry_set.get_component_for_read<MeshComponent>(), instance, params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_MESH);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ instance,
+ params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(
+ instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_CURVE);
+ }
+ /* Unused references may have been added above. Remove those now so that other nodes don't
+ * process them needlessly. */
+ instances.remove_unused_references();
+ });
+
+ params.set_output("Instances", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_instance_on_points()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_instance_on_points_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 93643298f92..3e9b615f478 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -437,7 +437,7 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
/* Retrieve attribute info before moving the splines out of the input components. */
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(
{(const GeometryComponent **)src_components.data(), src_components.size()},
- {"position", "radius", "tilt", "cyclic", "resolution"});
+ {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"});
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
index 43818947272..780994996ae 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -30,7 +30,7 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Geometry");
}
@@ -64,23 +64,22 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
- if (geometry_set.has<MeshComponent>()) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- Mesh *mesh = mesh_component.get_for_write();
- if (mesh != nullptr) {
+ fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
-
- fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
-
- assign_material_to_faces(*mesh, selection, material);
+ assign_material_to_faces(*mesh, selection, material);
+ }
}
- }
+ });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
index a9c3bfc6ce0..a917434fa00 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -40,11 +40,9 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
Material *new_material = params.extract_input<Material *>("New");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
- if (geometry_set.has<MeshComponent>()) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- Mesh *mesh = mesh_component.get_for_write();
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = geometry_set.get_mesh_for_write();
if (mesh != nullptr) {
for (const int i : IndexRange(mesh->totcol)) {
if (mesh->mat[i] == old_material) {
@@ -52,7 +50,7 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
}
}
}
- }
+ });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index 22c24e34314..9d4533b9bda 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -31,7 +31,7 @@ namespace blender::nodes {
static void geo_node_material_selection_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Material>("Material").hide_label(true);
- b.add_output<decl::Bool>("Selection");
+ b.add_output<decl::Bool>("Selection").field_source();
}
static void select_mesh_by_material(const Mesh &mesh,
@@ -100,13 +100,16 @@ class MaterialSelectionFieldInput final : public fn::FieldInput {
uint64_t hash() const override
{
- /* Some random constant hash. */
- return 91619626;
+ return get_default_hash(material_);
}
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const MaterialSelectionFieldInput *>(&other) != nullptr;
+ if (const MaterialSelectionFieldInput *other_material_selection =
+ dynamic_cast<const MaterialSelectionFieldInput *>(&other)) {
+ return material_ == other_material_selection->material_;
+ }
+ return false;
}
};
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 0d58476fc58..059e6e8680c 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
@@ -29,13 +29,38 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3);
+ b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(512);
+ b.add_input<decl::Int>("Side Segments").default_value(1).min(1).max(512);
+ b.add_input<decl::Int>("Fill Segments").default_value(1).min(1).max(512);
b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>("Geometry");
}
+static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN(
+ sizeof(NodeGeometryMeshCone), __func__);
+
+ node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_mesh_primitive_cone_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage;
+ const GeometryNodeMeshCircleFillType fill_type =
+ static_cast<const GeometryNodeMeshCircleFillType>(storage.fill_type);
+ const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(fill_subdiv_socket, has_fill);
+}
+
static void geo_node_mesh_primitive_cone_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
@@ -45,493 +70,632 @@ static void geo_node_mesh_primitive_cone_layout(uiLayout *layout,
uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
}
-static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
+struct ConeConfig {
+ float radius_top;
+ float radius_bottom;
+ float height;
+ int circle_segments;
+ int side_segments;
+ int fill_segments;
+ GeometryNodeMeshCircleFillType fill_type;
+
+ bool top_is_point;
+ bool bottom_is_point;
+ /* The cone tip and a triangle fan filling are topologically identical.
+ * This simplifies the logic in some cases. */
+ bool top_has_center_vert;
+ bool bottom_has_center_vert;
+
+ /* Helpful quantities. */
+ int tot_quad_rings;
+ int tot_edge_rings;
+ int tot_verts;
+ int tot_edges;
+
+ /* Helpful vertex indices. */
+ int first_vert;
+ int first_ring_verts_start;
+ int last_ring_verts_start;
+ int last_vert;
+
+ /* Helpful edge indices. */
+ int first_ring_edges_start;
+ int last_ring_edges_start;
+ int last_fan_edges_start;
+ int last_edge;
+
+ ConeConfig(float radius_top,
+ float radius_bottom,
+ float depth,
+ int circle_segments,
+ int side_segments,
+ int fill_segments,
+ GeometryNodeMeshCircleFillType fill_type)
+ : radius_top(radius_top),
+ radius_bottom(radius_bottom),
+ height(0.5f * depth),
+ circle_segments(circle_segments),
+ side_segments(side_segments),
+ fill_segments(fill_segments),
+ fill_type(fill_type)
+ {
+ this->top_is_point = this->radius_top == 0.0f;
+ this->bottom_is_point = this->radius_bottom == 0.0f;
+ this->top_has_center_vert = this->top_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+ this->bottom_has_center_vert = this->bottom_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+
+ this->tot_quad_rings = this->calculate_total_quad_rings();
+ this->tot_edge_rings = this->calculate_total_edge_rings();
+ this->tot_verts = this->calculate_total_verts();
+ this->tot_edges = this->calculate_total_edges();
+
+ this->first_vert = 0;
+ this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert;
+ this->last_vert = this->tot_verts - 1;
+ this->last_ring_verts_start = this->last_vert - this->circle_segments;
+
+ this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0;
+ this->last_ring_edges_start = this->first_ring_edges_start +
+ this->tot_quad_rings * this->circle_segments * 2;
+ this->last_fan_edges_start = this->tot_edges - this->circle_segments;
+ this->last_edge = this->tot_edges - 1;
+ }
+
+ private:
+ int calculate_total_quad_rings();
+ int calculate_total_edge_rings();
+ int calculate_total_verts();
+ int calculate_total_edges();
+
+ public:
+ int get_tot_corners() const;
+ int get_tot_faces() const;
+};
+
+int ConeConfig::calculate_total_quad_rings()
{
- NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN(
- sizeof(NodeGeometryMeshCone), __func__);
+ if (top_is_point && bottom_is_point) {
+ return 0;
+ }
- node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+ int quad_rings = 0;
- node->storage = node_storage;
+ if (!top_is_point) {
+ quad_rings += fill_segments - 1;
+ }
+
+ quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1);
+
+ if (!bottom_is_point) {
+ quad_rings += fill_segments - 1;
+ }
+
+ return quad_rings;
}
-static int vert_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_edge_rings()
{
- int vert_total = 0;
+ if (top_is_point && bottom_is_point) {
+ return 0;
+ }
+
+ int edge_rings = 0;
+
if (!top_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
+ edge_rings += fill_segments;
}
- else {
+
+ edge_rings += side_segments - 1;
+
+ if (!bottom_is_point) {
+ edge_rings += fill_segments;
+ }
+
+ return edge_rings;
+}
+
+int ConeConfig::calculate_total_verts()
+{
+ if (top_is_point && bottom_is_point) {
+ return side_segments + 1;
+ }
+
+ int vert_total = 0;
+
+ if (top_has_center_vert) {
vert_total++;
}
+
+ if (!top_is_point) {
+ vert_total += circle_segments * fill_segments;
+ }
+
+ vert_total += circle_segments * (side_segments - 1);
+
if (!bottom_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
+ vert_total += circle_segments * fill_segments;
}
- else {
+
+ if (bottom_has_center_vert) {
vert_total++;
}
return vert_total;
}
-static int edge_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_edges()
{
if (top_is_point && bottom_is_point) {
- return 1;
+ return side_segments;
}
int edge_total = 0;
- if (!top_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ if (top_has_center_vert) {
+ edge_total += circle_segments;
}
- edge_total += verts_num;
+ edge_total += circle_segments * (tot_quad_rings * 2 + 1);
- if (!bottom_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ if (bottom_has_center_vert) {
+ edge_total += circle_segments;
}
return edge_total;
}
-static int corner_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::get_tot_corners() const
{
if (top_is_point && bottom_is_point) {
return 0;
}
int corner_total = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
- }
- if (!top_is_point && !bottom_is_point) {
- corner_total += verts_num * 4;
+ if (top_has_center_vert) {
+ corner_total += (circle_segments * 3);
}
- else {
- corner_total += verts_num * 3;
+ else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
+ corner_total += tot_quad_rings * (circle_segments * 4);
+
+ if (bottom_has_center_vert) {
+ corner_total += (circle_segments * 3);
+ }
+ else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
return corner_total;
}
-static int face_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::get_tot_faces() const
{
if (top_is_point && bottom_is_point) {
return 0;
}
int face_total = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ if (top_has_center_vert) {
+ face_total += circle_segments;
+ }
+ else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ face_total++;
}
- face_total += verts_num;
+ face_total += tot_quad_rings * circle_segments;
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ if (bottom_has_center_vert) {
+ face_total += circle_segments;
+ }
+ else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ face_total++;
}
return face_total;
}
-static void calculate_uvs(Mesh *mesh,
- const bool top_is_point,
- const bool bottom_is_point,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type)
+static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<float2> uv_attribute =
- mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
- MutableSpan<float2> uvs = uv_attribute.as_span();
-
- Array<float2> circle(verts_num);
+ Array<float2> circle(config.circle_segments);
+ const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
float angle = 0.0f;
- const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
- for (const int i : IndexRange(verts_num)) {
- circle[i].x = std::cos(angle) * 0.225f + 0.25f;
- circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle);
+ circle[i].y = std::sin(angle);
angle += angle_delta;
}
- int loop_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- }
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
- }
- }
- }
+ int vert_index = 0;
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
- const float vert = static_cast<float>(i);
- uvs[loop_index++] = float2(vert / verts_num, bottom);
- uvs[loop_index++] = float2(vert / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
- }
+ /* Top cone tip or triangle fan center. */
+ if (config.top_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height);
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
- }
- }
- else {
- BLI_assert(!bottom_is_point);
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
+
+ /* Top fill including the outer edge of the fill. */
+ if (!config.top_is_point) {
+ const float top_fill_radius_delta = config.radius_top /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float top_fill_radius = top_fill_radius_delta * (i + 1);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * top_fill_radius;
+ const float y = circle[j].y * top_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, config.height);
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards because of reversed face normal. */
- uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
- }
+ /* Rings along the side. */
+ const float side_radius_delta = (config.radius_bottom - config.radius_top) /
+ static_cast<float>(config.side_segments);
+ const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments);
+ for (const int i : IndexRange(config.side_segments - 1)) {
+ const float ring_radius = config.radius_top + (side_radius_delta * (i + 1));
+ const float ring_height = config.height - (height_delta * (i + 1));
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * ring_radius;
+ const float y = circle[j].y * ring_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, ring_height);
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+
+ /* Bottom fill including the outer edge of the fill. */
+ if (!config.bottom_is_point) {
+ const float bottom_fill_radius_delta = config.radius_bottom /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * bottom_fill_radius;
+ const float y = circle[j].y * bottom_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, -config.height);
}
}
}
- uv_attribute.save();
+ /* Bottom cone tip or triangle fan center. */
+ if (config.bottom_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height);
+ }
}
-Mesh *create_cylinder_or_cone_mesh(const float radius_top,
- const float radius_bottom,
- const float depth,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type)
+static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConfig &config)
{
- const bool top_is_point = radius_top == 0.0f;
- const bool bottom_is_point = radius_bottom == 0.0f;
- const float height = depth * 0.5f;
- /* Handle the case of a line / single point before everything else to avoid
- * the need to check for it later. */
- if (top_is_point && bottom_is_point) {
- const bool single_vertex = height == 0.0f;
- Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
- copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
- if (single_vertex) {
- const short up[3] = {0, 0, SHRT_MAX};
- copy_v3_v3_short(mesh->mvert[0].no, up);
- return mesh;
- }
- copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
- mesh->medge[0].v1 = 0;
- mesh->medge[0].v2 = 1;
- mesh->medge[0].flag |= ME_LOOSEEDGE;
- BKE_mesh_normals_tag_dirty(mesh);
- return mesh;
- }
-
- Mesh *mesh = BKE_mesh_new_nomain(
- vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
- edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
- 0,
- corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point));
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
-
- /* Calculate vertex positions. */
- const int top_verts_start = 0;
- const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
- const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
- for (const int i : IndexRange(verts_num)) {
- const float angle = i * angle_delta;
- const float x = std::cos(angle);
- const float y = std::sin(angle);
- if (!top_is_point) {
- copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
- }
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start + i].co,
- float3(x * radius_bottom, y * radius_bottom, -height));
+ int edge_index = 0;
+
+ /* Edges for top cone tip or triangle fan */
+ if (config.top_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.first_vert;
+ edge.v2 = config.first_ring_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
- if (top_is_point) {
- copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
- }
- if (bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
- }
- /* Add center vertices for the triangle fans at the end. */
- const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
- const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- if (!top_is_point) {
- copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ /* Rings and connecting edges between the rings. */
+ for (const int i : IndexRange(config.tot_edge_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+ /* Edge rings. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ if (i == config.tot_edge_rings - 1) {
+ /* There is one fewer ring of connecting edges. */
+ break;
}
- }
-
- /* Create top edges. */
- const int top_edges_start = 0;
- const int top_fan_edges_start = (!top_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- top_edges_start + verts_num :
- top_edges_start;
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_edges_start + i];
- edge.v1 = top_verts_start + i;
- edge.v2 = top_verts_start + (i + 1) % verts_num;
+ /* Connecting edges. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = next_ring_vert_start + j;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_fan_edges_start + i];
- edge.v1 = top_center_vert_index;
- edge.v2 = top_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
}
- /* Create connecting edges. */
- const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[connecting_edges_start + i];
- edge.v1 = top_verts_start + (!top_is_point ? i : 0);
- edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
-
- /* Create bottom edges. */
- const int bottom_edges_start = connecting_edges_start + verts_num;
- const int bottom_fan_edges_start = (!bottom_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- bottom_edges_start + verts_num :
- bottom_edges_start;
- if (!bottom_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_edges_start + i];
- edge.v1 = bottom_verts_start + i;
- edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ /* Edges for bottom triangle fan or tip. */
+ if (config.bottom_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.last_ring_verts_start + i;
+ edge.v2 = config.last_vert;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_fan_edges_start + i];
- edge.v1 = bottom_center_vert_index;
- edge.v2 = bottom_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
}
+}
- /* Create top corners and faces. */
+static void calculate_cone_faces(const MutableSpan<MLoop> &loops,
+ const MutableSpan<MPoly> &polys,
+ const ConeConfig &config)
+{
int loop_index = 0;
int poly_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+
+ if (config.top_has_center_vert) {
+ /* Top cone tip or center triangle fan in the fill. */
+ const int top_center_vert = 0;
+ const int top_fan_edges_start = 0;
+
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ poly.totloop = 3;
- for (const int i : IndexRange(verts_num)) {
- MLoop &loop = loops[loop_index++];
- loop.v = top_verts_start + i;
- loop.e = top_edges_start + i;
- }
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = config.first_ring_verts_start + i;
+ loop_a.e = config.first_ring_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments);
+ loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments);
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert;
+ loop_c.e = top_fan_edges_start + i;
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
+ }
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
+ for (const int i : IndexRange(config.circle_segments)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = i;
+ loop.e = i;
+ }
+ }
+
+ /* Quads connect one edge ring to the next one. */
+ if (config.tot_quad_rings > 0) {
+ for (const int i : IndexRange(config.tot_quad_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start +
+ (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+
+ const int this_ring_edges_start = config.first_ring_edges_start +
+ (i * 2 * config.circle_segments);
+ const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments);
+ const int ring_connections_start = this_ring_edges_start + config.circle_segments;
+
+ for (const int j : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 3;
+ poly.totloop = 4;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = top_edges_start + i;
+ loop_a.v = this_ring_vert_start + j;
+ loop_a.e = ring_connections_start + j;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = top_verts_start + (i + 1) % verts_num;
- loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ loop_b.v = next_ring_vert_start + j;
+ loop_b.e = next_ring_edges_start + j;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_center_vert_index;
- loop_c.e = top_fan_edges_start + i;
+ loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments);
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_d.e = this_ring_edges_start + j;
}
}
}
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
+ if (config.bottom_has_center_vert) {
+ /* Bottom cone tip or center triangle fan in the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 4;
+ poly.totloop = 3;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
+ loop_a.v = config.last_ring_verts_start + i;
+ loop_a.e = config.last_fan_edges_start + i;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start + i;
- loop_b.e = bottom_edges_start + i;
+ loop_b.v = config.last_vert;
+ loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_d = loops[loop_index++];
- loop_d.v = top_verts_start + (i + 1) % verts_num;
- loop_d.e = top_edges_start + i;
+ loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments;
+ loop_c.e = config.last_ring_edges_start + i;
}
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start;
- loop_b.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_verts_start + (i + 1) % verts_num;
- loop_c.e = top_edges_start + i;
- }
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = config.last_vert - i;
+ loop.e = config.last_edge - ((i + 1) % config.circle_segments);
}
- else {
- BLI_assert(!bottom_is_point);
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ }
+}
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = bottom_verts_start + i;
- loop_a.e = bottom_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start + (i + 1) % verts_num;
- loop_b.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_verts_start;
- loop_c.e = connecting_edges_start + i;
+/**
+ * If the top is the cone tip or has a fill, it is unwrapped into a circle in the
+ * lower left quadrant of the UV.
+ * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle
+ * in the lower right quadrant of the UV.
+ * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into
+ * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills).
+ */
+static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(config.circle_segments);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments);
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle) * 0.225f;
+ circle[i].y = std::sin(angle) * 0.225f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+
+ /* Left circle of the UV representing the top fill or top cone tip. */
+ if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_left(0.25f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.top_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int left_circle_segment_count = config.top_is_point ? config.side_segments :
+ config.fill_segments;
+
+ if (config.top_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = center_left;
+ }
+ }
+ else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ }
+ }
+ /* The rest of the top fill is made out of quad rings. */
+ for (const int i : IndexRange(1, left_circle_segment_count - 1)) {
+ const float inner_radius_factor = i * radius_factor_delta;
+ const float outer_radius_factor = (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ if (!config.top_is_point && !config.bottom_is_point) {
+ /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */
+ const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ const float x_delta = 1.0f / static_cast<float>(config.circle_segments);
+ const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments);
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards to reverse surface normal. */
- MLoop &loop = loops[loop_index++];
- loop.v = bottom_verts_start + verts_num - 1 - i;
- loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ for (const int i : IndexRange(config.side_segments)) {
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom);
+ uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom);
}
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ }
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = bottom_verts_start + i;
- loop_a.e = bottom_fan_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_center_vert_index;
- loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = bottom_edges_start + i;
+ /* Right circle of the UV representing the bottom fill or bottom cone tip. */
+ if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_right(0.75f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.bottom_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int right_circle_segment_count = config.bottom_is_point ? config.side_segments :
+ config.fill_segments;
+
+ /* The bottom circle has to be created outside in to match the loop order. */
+ for (const int i : IndexRange(right_circle_segment_count - 1)) {
+ const float outer_radius_factor = 1.0f - i * radius_factor_delta;
+ const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+
+ if (config.bottom_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_right;
+ uvs[loop_index++] = center_right;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+ else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] +
+ center_right;
}
}
}
+ uv_attribute.save();
+}
+
+static Mesh *create_vertex_mesh()
+{
+ /* Returns a mesh with a single vertex at the origin. */
+ Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
+ copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
+ const short up[3] = {0, 0, SHRT_MAX};
+ copy_v3_v3_short(mesh->mvert[0].no, up);
+ return mesh;
+}
+
+Mesh *create_cylinder_or_cone_mesh(const float radius_top,
+ const float radius_bottom,
+ const float depth,
+ const int circle_segments,
+ const int side_segments,
+ const int fill_segments,
+ const GeometryNodeMeshCircleFillType fill_type)
+{
+ const ConeConfig config(
+ radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type);
+
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (config.top_is_point && config.bottom_is_point) {
+ if (config.height == 0.0f) {
+ return create_vertex_mesh();
+ }
+ const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments);
+ const float3 start(0.0f, 0.0f, config.height);
+ const float3 delta(0.0f, 0.0f, z_delta);
+ return create_line_mesh(start, delta, config.tot_verts);
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ config.tot_verts, config.tot_edges, 0, config.get_tot_corners(), config.get_tot_faces());
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ calculate_cone_vertices(verts, config);
+ calculate_cone_edges(edges, config);
+ calculate_cone_faces(loops, polys, config);
+ calculate_cone_uvs(mesh, config);
+
BKE_mesh_normals_tag_dirty(mesh);
- calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+ calculate_cone_uvs(mesh, config);
return mesh;
}
@@ -540,23 +704,37 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage;
-
const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
storage.fill_type;
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
const float radius_top = params.extract_input<float>("Radius Top");
const float radius_bottom = params.extract_input<float>("Radius Bottom");
const float depth = params.extract_input<float>("Depth");
Mesh *mesh = create_cylinder_or_cone_mesh(
- radius_top, radius_bottom, depth, verts_num, fill_type);
+ radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type);
/* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
@@ -572,6 +750,7 @@ void register_node_type_geo_mesh_primitive_cone()
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0);
node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init);
+ node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cone_update);
node_type_storage(
&ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec;
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 ed701c921ca..287ea896ade 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
@@ -29,9 +29,31 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(4096);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>("Vertices")
+ .default_value(32)
+ .min(3)
+ .max(512)
+ .description("The number of vertices around the circumference");
+ b.add_input<decl::Int>("Side Segments")
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description("The number of segments along the side");
+ b.add_input<decl::Int>("Fill Segments")
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description("The number of concentric segments of the fill");
+ b.add_input<decl::Float>("Radius")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The radius of the cylinder");
+ b.add_input<decl::Float>("Depth")
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The height of the cylinder on the Z axis");
b.add_output<decl::Geometry>("Geometry");
}
@@ -54,6 +76,19 @@ static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNod
node->storage = node_storage;
}
+static void geo_node_mesh_primitive_cylinder_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage;
+ const GeometryNodeMeshCircleFillType fill_type =
+ static_cast<const GeometryNodeMeshCircleFillType>(storage.fill_type);
+ const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(fill_subdiv_socket, has_fill);
+}
+
static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
@@ -64,15 +99,31 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
const float depth = params.extract_input<float>("Depth");
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
/* 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);
+ Mesh *mesh = create_cylinder_or_cone_mesh(
+ radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
@@ -84,6 +135,7 @@ void register_node_type_geo_mesh_primitive_cylinder()
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0);
node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init);
+ node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_update);
node_type_storage(
&ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage);
ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
index 9ca74fed9a7..c436f5bd480 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
@@ -32,28 +32,9 @@ static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
+static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
-
if (!geometry_set.has_mesh()) {
- params.set_output("Geometry", geometry_set);
- return;
- }
-
-#ifndef WITH_OPENSUBDIV
- params.error_message_add(NodeWarningType::Error,
- TIP_("Disabled, Blender was compiled without OpenSubdiv"));
- params.set_output("Geometry", std::move(geometry_set));
- return;
-#endif
-
- /* See CCGSUBSURF_LEVEL_MAX for max limit. */
- const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
-
- if (subdiv_level == 0) {
- params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -61,7 +42,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
- mesh_settings.resolution = (1 << subdiv_level) + 1;
+ mesh_settings.resolution = (1 << level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
@@ -79,7 +60,6 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
- params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -90,6 +70,29 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
mesh_component.replace(mesh_out);
BKE_subdiv_free(subdiv);
+}
+
+static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+#ifndef WITH_OPENSUBDIV
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+#endif
+
+ /* See CCGSUBSURF_LEVEL_MAX for max limit. */
+ const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
+
+ if (subdiv_level == 0) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
new file mode 100644
index 00000000000..6863f685eae
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -0,0 +1,189 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh");
+ b.add_input<decl::Vector>("Position").implicit_field();
+ b.add_input<decl::Float>("Radius")
+ .default_value(0.05f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Points");
+}
+
+static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN(
+ sizeof(NodeGeometryMeshToPoints), __func__);
+ data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES;
+ node->storage = data;
+}
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &src,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
+ Field<float3> &position_field,
+ Field<float> &radius_field,
+ Field<bool> &selection_field,
+ const AttributeDomain domain)
+{
+ const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
+ if (mesh_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ GeometryComponentFieldContext field_context{*mesh_component, domain};
+ const int domain_size = mesh_component->attribute_domain_size(domain);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ geometry_set.replace_pointcloud(pointcloud);
+ PointCloudComponent &point_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+
+ /* Evaluating directly into the point cloud doesn't work because we are not using the full
+ * "min_array_size" array but compressing the selected elements into the final array with no
+ * gaps. */
+ fn::FieldEvaluator evaluator{field_context, &selection};
+ evaluator.add(position_field);
+ evaluator.add(radius_field);
+ evaluator.evaluate();
+ copy_attribute_to_points(evaluator.get_evaluated<float3>(0),
+ selection,
+ {(float3 *)pointcloud->co, pointcloud->totpoint});
+ copy_attribute_to_points(
+ evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint});
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+ attributes.remove("position");
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
+ OutputAttribute dst = point_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ copy_attribute_to_points(*src_typed, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<float3> position = params.extract_input<Field<float3>>("Position");
+ Field<float> radius = params.extract_input<Field<float>>("Radius");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+
+ /* Use another multi-function operation to make sure the input radius is greater than zero.
+ * TODO: Use mutable multi-function once that is supported. */
+ static fn::CustomMF_SI_SO<float, float> max_zero_fn(
+ __func__, [](float value) { return std::max(0.0f, value); });
+ auto max_zero_op = std::make_shared<FieldOperation>(
+ FieldOperation(max_zero_fn, {std::move(radius)}));
+ Field<float> positive_radius(std::move(max_zero_op), 0);
+
+ const NodeGeometryMeshToPoints &storage =
+ *(const NodeGeometryMeshToPoints *)params.node().storage;
+ const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ switch (mode) {
+ case GEO_NODE_MESH_TO_POINTS_VERTICES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_EDGES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_FACES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_CORNERS:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER);
+ break;
+ }
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_points()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_mesh_to_points_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init);
+ ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
new file mode 100644
index 00000000000..afd0ced6360
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -0,0 +1,118 @@
+/*
+ * 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_task.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Points");
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Mesh");
+}
+
+template<typename T>
+static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
+static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
+ Field<bool> &selection_field)
+{
+ const PointCloudComponent *point_component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ if (point_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
+ const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes);
+
+ Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
+ geometry_set.replace_mesh(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = point_component->attribute_get_for_read(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ VArray_Span<T> src_typed_span{*src_typed};
+ copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_points_to_vertices_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_points_to_vertices(geometry_set, selection_field);
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_points_to_vertices()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_points_to_vertices_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
new file mode 100644
index 00000000000..7062deff2f1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
@@ -0,0 +1,235 @@
+/*
+ * 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_task.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_geometry_set.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_proximity_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Target");
+ b.add_input<decl::Vector>("Source Position").implicit_field();
+ b.add_output<decl::Vector>("Position").dependent_field();
+ b.add_output<decl::Float>("Distance").dependent_field();
+}
+
+static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE);
+}
+
+static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN(
+ sizeof(NodeGeometryProximity), __func__);
+ node_storage->target_element = GEO_NODE_PROX_TARGET_FACES;
+ node->storage = node_storage;
+}
+
+static void calculate_mesh_proximity(const VArray<float3> &positions,
+ const IndexMask mask,
+ const Mesh &mesh,
+ const GeometryNodeProximityTargetType type,
+ const MutableSpan<float> r_distances,
+ const MutableSpan<float3> r_locations)
+{
+ BVHTreeFromMesh bvh_data;
+ switch (type) {
+ case GEO_NODE_PROX_TARGET_POINTS:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_VERTS, 2);
+ break;
+ case GEO_NODE_PROX_TARGET_EDGES:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_EDGES, 2);
+ break;
+ case GEO_NODE_PROX_TARGET_FACES:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_LOOPTRI, 2);
+ break;
+ }
+
+ if (bvh_data.tree == nullptr) {
+ return;
+ }
+
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
+ BVHTreeNearest nearest;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.index = -1;
+
+ for (int i : range) {
+ const int index = mask[i];
+ /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
+ nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]);
+
+ BLI_bvhtree_find_nearest(
+ bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data);
+
+ if (nearest.dist_sq < r_distances[index]) {
+ r_distances[index] = nearest.dist_sq;
+ if (!r_locations.is_empty()) {
+ r_locations[index] = nearest.co;
+ }
+ }
+ }
+ });
+
+ free_bvhtree_from_mesh(&bvh_data);
+}
+
+static void calculate_pointcloud_proximity(const VArray<float3> &positions,
+ const IndexMask mask,
+ const PointCloud &pointcloud,
+ const MutableSpan<float> r_distances,
+ const MutableSpan<float3> r_locations)
+{
+ BVHTreeFromPointCloud bvh_data;
+ BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2);
+ if (bvh_data.tree == nullptr) {
+ return;
+ }
+
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
+ BVHTreeNearest nearest;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.index = -1;
+
+ for (int i : range) {
+ const int index = mask[i];
+ /* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup.
+ * This is ok because we only need to find the closest point in the pointcloud if it's
+ * closer than the mesh. */
+ nearest.dist_sq = r_distances[index];
+
+ BLI_bvhtree_find_nearest(
+ bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data);
+
+ if (nearest.dist_sq < r_distances[index]) {
+ r_distances[index] = nearest.dist_sq;
+ if (!r_locations.is_empty()) {
+ r_locations[index] = nearest.co;
+ }
+ }
+ }
+ });
+
+ free_bvhtree_from_pointcloud(&bvh_data);
+}
+
+class ProximityFunction : public fn::MultiFunction {
+ private:
+ GeometrySet target_;
+ GeometryNodeProximityTargetType type_;
+
+ public:
+ ProximityFunction(GeometrySet target, GeometryNodeProximityTargetType type)
+ : target_(std::move(target)), type_(type)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Geometry Proximity"};
+ signature.single_input<float3>("Source Position");
+ signature.single_output<float3>("Position");
+ signature.single_output<float>("Distance");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &src_positions = params.readonly_single_input<float3>(0,
+ "Source Position");
+ MutableSpan<float3> positions = params.uninitialized_single_output_if_required<float3>(
+ 1, "Position");
+ /* Make sure there is a distance array, used for finding the smaller distance when there are
+ * multiple components. Theoretically it would be possible to avoid using the distance array
+ * when there is only one component. However, this only adds an allocation and a single float
+ * comparison per vertex, so it's likely not worth it. */
+ MutableSpan<float> distances = params.uninitialized_single_output<float>(2, "Distance");
+
+ distances.fill(FLT_MAX);
+
+ if (target_.has_mesh()) {
+ calculate_mesh_proximity(
+ src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions);
+ }
+
+ if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) {
+ calculate_pointcloud_proximity(
+ src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions);
+ }
+
+ if (params.single_output_is_required(2, "Distance")) {
+ threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ const int j = mask[i];
+ distances[j] = std::sqrt(distances[j]);
+ }
+ });
+ }
+ }
+};
+
+static void geo_node_proximity_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
+
+ if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
+ params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
+ params.set_output("Distance", fn::make_constant_field<float>({0.0f}));
+ return;
+ }
+
+ const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage;
+ Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
+
+ auto proximity_fn = std::make_unique<ProximityFunction>(
+ std::move(geometry_set_target),
+ static_cast<GeometryNodeProximityTargetType>(storage.target_element));
+ auto proximity_op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(proximity_fn), {std::move(position_field)}));
+
+ params.set_output("Position", Field<float3>(proximity_op, 0));
+ params.set_output("Distance", Field<float>(proximity_op, 1));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_proximity()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0);
+ node_type_init(&ntype, blender::nodes::geo_proximity_init);
+ node_type_storage(
+ &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage);
+ ntype.declare = blender::nodes::geo_node_proximity_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_proximity_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
index c63e4ec49d9..dafd10cee2d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
@@ -25,20 +25,18 @@ static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Point Cloud");
b.add_output<decl::Geometry>("Curve");
b.add_output<decl::Geometry>("Volume");
+ b.add_output<decl::Geometry>("Instances");
}
static void geo_node_separate_components_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- /* Note that it will be possible to skip realizing instances here when instancing
- * geometry directly is supported by creating corresponding geometry instances. */
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
GeometrySet meshes;
GeometrySet point_clouds;
GeometrySet volumes;
GeometrySet curves;
+ GeometrySet instances;
if (geometry_set.has<MeshComponent>()) {
meshes.add(*geometry_set.get_component_for_read<MeshComponent>());
@@ -52,11 +50,15 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params)
if (geometry_set.has<VolumeComponent>()) {
volumes.add(*geometry_set.get_component_for_read<VolumeComponent>());
}
+ if (geometry_set.has<InstancesComponent>()) {
+ instances.add(*geometry_set.get_component_for_read<InstancesComponent>());
+ }
params.set_output("Mesh", meshes);
params.set_output("Point Cloud", point_clouds);
params.set_output("Curve", curves);
params.set_output("Volume", volumes);
+ params.set_output("Instances", instances);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index c5e10b788ac..8caf961fc04 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -23,8 +23,8 @@ namespace blender::nodes {
static void geo_node_set_position_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Position").hide_value();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Vector>("Position").implicit_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Geometry");
}
@@ -34,6 +34,9 @@ static void set_position_in_component(GeometryComponent &component,
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
new file mode 100644
index 00000000000..5e2f03806c3
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -0,0 +1,306 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_curve_types.h"
+#include "DNA_vfont_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_font.h"
+#include "BKE_spline.hh"
+
+#include "BLI_hash.h"
+#include "BLI_string_utf8.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::String>("String");
+ b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Character Spacing")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>("Curves");
+ b.add_output<decl::String>("Remainder");
+}
+
+static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "font",
+ nullptr,
+ "FONT_OT_open",
+ "FONT_OT_unlink",
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
+}
+
+static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN(
+ sizeof(NodeGeometryStringToCurves), __func__);
+
+ data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
+ data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
+ data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE;
+ node->storage = data;
+ node->id = (ID *)BKE_vfont_builtin_get();
+}
+
+static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage->overflow;
+ bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next;
+ nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
+
+ bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last;
+ bNodeSocket *width_socket = height_socket->prev;
+ nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ node_sock_label(width_socket,
+ overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") :
+ N_("Text Box Width"));
+}
+
+struct TextLayout {
+ /* Position of each character. */
+ Vector<float2> positions;
+
+ /* The text that fit into the text box, with newline character sequences replaced. */
+ std::string text;
+
+ /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */
+ std::string truncated_text;
+
+ /* Font size could be modified if in 'Scale to fit'-mode. */
+ float final_font_size;
+};
+
+static TextLayout get_text_layout(GeoNodeExecParams &params)
+{
+ TextLayout layout;
+ layout.text = params.extract_input<std::string>("String");
+ if (layout.text.empty()) {
+ return {};
+ }
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage.overflow;
+ const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode)
+ storage.align_x;
+ const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode)
+ storage.align_y;
+
+ const float font_size = std::max(params.extract_input<float>("Size"), 0.0f);
+ const float char_spacing = params.extract_input<float>("Character Spacing");
+ const float word_spacing = params.extract_input<float>("Word Spacing");
+ const float line_spacing = params.extract_input<float>("Line Spacing");
+ const float textbox_w = params.extract_input<float>("Text Box Width");
+ const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
+ 0.0f :
+ params.extract_input<float>("Text Box Height");
+ VFont *vfont = (VFont *)params.node().id;
+
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ /* Set defaults */
+ cu.resolu = 12;
+ cu.smallcaps_scale = 0.75f;
+ cu.wordspace = 1.0f;
+ /* Set values from inputs */
+ cu.spacemode = align_x;
+ cu.align_y = align_y;
+ cu.fsize = font_size;
+ cu.spacing = char_spacing;
+ cu.wordspace = word_spacing;
+ cu.linedist = line_spacing;
+ cu.vfont = vfont;
+ cu.overflow = overflow;
+ cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__);
+ cu.tb->w = textbox_w;
+ cu.tb->h = textbox_h;
+ cu.totbox = 1;
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ cu.len_char32 = len_chars;
+ cu.len = len_bytes;
+ cu.pos = len_chars;
+ /* The reason for the additional character here is unknown, but reflects other code elsewhere. */
+ cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__);
+ cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__);
+ BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1);
+
+ struct CharTrans *chartransdata = nullptr;
+ int text_len;
+ bool text_free;
+ const char32_t *r_text = nullptr;
+ /* Mode FO_DUPLI used because it doesn't create curve splines. */
+ BKE_vfont_to_curve_ex(
+ nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata);
+
+ if (text_free) {
+ MEM_freeN((void *)r_text);
+ }
+
+ Span<CharInfo> info{cu.strinfo, text_len};
+ layout.final_font_size = cu.fsize_realtime;
+ layout.positions.reserve(text_len);
+
+ for (const int i : IndexRange(text_len)) {
+ CharTrans &ct = chartransdata[i];
+ layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size);
+
+ if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
+ const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1);
+ layout.truncated_text = layout.text.substr(offset);
+ layout.text = layout.text.substr(0, offset);
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(chartransdata);
+ MEM_SAFE_FREE(cu.str);
+ MEM_SAFE_FREE(cu.strinfo);
+ MEM_SAFE_FREE(cu.tb);
+
+ return layout;
+}
+
+/* Returns a mapping of UTF-32 character code to instance handle. */
+static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
+ const float fontsize,
+ const Span<char32_t> charcodes,
+ InstancesComponent &instance_component)
+{
+ VFont *vfont = (VFont *)params.node().id;
+ Map<int, int> handles;
+
+ for (int i : charcodes.index_range()) {
+ if (handles.contains(charcodes[i])) {
+ continue;
+ }
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ cu.resolu = 12;
+ cu.vfont = vfont;
+ CharInfo charinfo = {0};
+ charinfo.mat_nr = 1;
+
+ BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1);
+ std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
+ BKE_nurbList_free(&cu.nurb);
+ float4x4 size_matrix = float4x4::identity();
+ size_matrix.apply_scale(fontsize);
+ curve_eval->transform(size_matrix);
+
+ GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
+ handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve)));
+ }
+ return handles;
+}
+
+static void add_instances_from_handles(InstancesComponent &instances,
+ const Map<int, int> &char_handles,
+ const Span<char32_t> charcodes,
+ const Span<float2> positions)
+{
+ instances.resize(positions.size());
+ MutableSpan<int> handles = instances.instance_reference_handles();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
+ MutableSpan<int> instance_ids = instances.instance_ids();
+
+ threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = char_handles.lookup(charcodes[i]);
+ transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
+ instance_ids[i] = i;
+ }
+ });
+}
+
+static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
+{
+ TextLayout layout = get_text_layout(params);
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
+ params.set_output("Remainder", std::move(layout.truncated_text));
+ }
+
+ if (layout.positions.size() == 0) {
+ params.set_output("Curves", GeometrySet());
+ return;
+ }
+
+ /* Convert UTF-8 encoded string to UTF-32. */
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ Array<char32_t> char_codes(len_chars + 1);
+ BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1);
+
+ /* Create and add instances. */
+ GeometrySet geometry_set_out;
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Map<int, int> char_handles = create_curve_instances(
+ params, layout.final_font_size, char_codes, instances);
+ add_instances_from_handles(instances, char_handles, char_codes, layout.positions);
+
+ params.set_output("Curves", std::move(geometry_set_out));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_string_to_curves()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_string_to_curves_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init);
+ node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update);
+ node_type_size(&ntype, 190, 120, 700);
+ node_type_storage(&ntype,
+ "NodeGeometryStringToCurves",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index ca857c4d2e3..05df927fb39 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -19,24 +19,37 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+#include "FN_multi_function_signature.hh"
+
namespace blender::nodes {
static void geo_node_switch_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Bool>("Switch");
-
- b.add_input<decl::Float>("False");
- b.add_input<decl::Float>("True");
- b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000);
- b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000);
- b.add_input<decl::Bool>("False", "False_002");
- b.add_input<decl::Bool>("True", "True_002");
- b.add_input<decl::Vector>("False", "False_003");
- b.add_input<decl::Vector>("True", "True_003");
- b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::String>("False", "False_005");
- b.add_input<decl::String>("True", "True_005");
+ b.add_input<decl::Bool>("Switch").default_value(false).supports_field();
+ b.add_input<decl::Bool>("Switch", "Switch_001").default_value(false);
+
+ b.add_input<decl::Float>("False").supports_field();
+ b.add_input<decl::Float>("True").supports_field();
+ b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Bool>("False", "False_002").default_value(false).hide_value().supports_field();
+ b.add_input<decl::Bool>("True", "True_002").default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>("False", "False_003").supports_field();
+ b.add_input<decl::Vector>("True", "True_003").supports_field();
+ b.add_input<decl::Color>("False", "False_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::Color>("True", "True_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::String>("False", "False_005").supports_field();
+ b.add_input<decl::String>("True", "True_005").supports_field();
+
b.add_input<decl::Geometry>("False", "False_006");
b.add_input<decl::Geometry>("True", "True_006");
b.add_input<decl::Object>("False", "False_007");
@@ -48,12 +61,12 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Material>("False", "False_010");
b.add_input<decl::Material>("True", "True_010");
- b.add_output<decl::Float>("Output");
- b.add_output<decl::Int>("Output", "Output_001");
- b.add_output<decl::Bool>("Output", "Output_002");
- b.add_output<decl::Vector>("Output", "Output_003");
- b.add_output<decl::Color>("Output", "Output_004");
- b.add_output<decl::String>("Output", "Output_005");
+ b.add_output<decl::Float>("Output").dependent_field();
+ b.add_output<decl::Int>("Output", "Output_001").dependent_field();
+ b.add_output<decl::Bool>("Output", "Output_002").dependent_field();
+ b.add_output<decl::Vector>("Output", "Output_003").dependent_field();
+ b.add_output<decl::Color>("Output", "Output_004").dependent_field();
+ b.add_output<decl::String>("Output", "Output_005").dependent_field();
b.add_output<decl::Geometry>("Output", "Output_006");
b.add_output<decl::Object>("Output", "Output_007");
b.add_output<decl::Collection>("Output", "Output_008");
@@ -77,91 +90,168 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeSwitch *node_storage = (NodeSwitch *)node->storage;
int index = 0;
- LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
- nodeSetSocketAvailability(
- socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
- index++;
+ bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next;
+
+ const bool fields_type = ELEM((eNodeSocketDatatype)node_storage->input_type,
+ SOCK_FLOAT,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_STRING);
+
+ nodeSetSocketAvailability(field_switch, fields_type);
+ nodeSetSocketAvailability(non_field_switch, !fields_type);
+
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
+ if (index <= 1) {
+ continue;
+ }
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
}
+
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
nodeSetSocketAvailability(socket,
socket->type == (eNodeSocketDatatype)node_storage->input_type);
}
}
-template<typename T>
-static void output_input(GeoNodeExecParams &params,
- const bool input,
- const StringRef input_suffix,
- const StringRef output_identifier)
+template<typename T> class SwitchFieldsFunction : public fn::MultiFunction {
+ public:
+ SwitchFieldsFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Switch"};
+ signature.single_input<bool>("Switch");
+ signature.single_input<T>("False");
+ signature.single_input<T>("True");
+ signature.single_output<T>("Output");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch");
+ const VArray<T> &falses = params.readonly_single_input<T>(1, "False");
+ const VArray<T> &trues = params.readonly_single_input<T>(2, "True");
+ MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output");
+ for (int64_t i : mask) {
+ new (&values[i]) T(switches[i] ? trues[i] : falses[i]);
+ }
+ }
+};
+
+template<typename T> void switch_fields(GeoNodeExecParams &params, const StringRef suffix)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ /* TODO: Allow for Laziness when the switch field is constant. */
+ const bool require_false = params.lazy_require_input(name_false);
+ const bool require_true = params.lazy_require_input(name_true);
+ if (require_false | require_true) {
+ return;
+ }
+
+ Field<bool> switches_field = params.extract_input<Field<bool>>("Switch");
+ Field<T> falses_field = params.extract_input<Field<T>>(name_false);
+ Field<T> trues_field = params.extract_input<Field<T>>(name_true);
+
+ auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>();
+ auto switch_op = std::make_shared<FieldOperation>(FieldOperation(
+ std::move(switch_fn),
+ {std::move(switches_field), std::move(falses_field), std::move(trues_field)}));
+
+ params.set_output(name_output, Field<T>(switch_op, 0));
+}
+
+template<typename T> void switch_no_fields(GeoNodeExecParams &params, const StringRef 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)) {
+ if (params.lazy_require_input("Switch_001")) {
+ return;
+ }
+ bool switch_value = params.get_input<bool>("Switch_001");
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ if (switch_value) {
+ params.set_input_unused(name_false);
+ if (params.lazy_require_input(name_true)) {
return;
}
- params.set_output(output_identifier, params.extract_input<T>(name_b));
+ params.set_output(name_output, params.extract_input<T>(name_true));
}
else {
- params.set_input_unused(name_b);
- if (params.lazy_require_input(name_a)) {
+ params.set_input_unused(name_true);
+ if (params.lazy_require_input(name_false)) {
return;
}
- params.set_output(output_identifier, params.extract_input<T>(name_a));
+ params.set_output(name_output, params.extract_input<T>(name_false));
}
}
static void geo_node_switch_exec(GeoNodeExecParams params)
{
- if (params.lazy_require_input("Switch")) {
- return;
- }
const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
- const bool input = params.get_input<bool>("Switch");
- switch ((eNodeSocketDatatype)storage.input_type) {
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type);
+
+ switch (data_type) {
+
case SOCK_FLOAT: {
- output_input<float>(params, input, "", "Output");
+ switch_fields<float>(params, "");
break;
}
case SOCK_INT: {
- output_input<int>(params, input, "_001", "Output_001");
+ switch_fields<int>(params, "_001");
break;
}
case SOCK_BOOLEAN: {
- output_input<bool>(params, input, "_002", "Output_002");
+ switch_fields<bool>(params, "_002");
break;
}
case SOCK_VECTOR: {
- output_input<float3>(params, input, "_003", "Output_003");
+ switch_fields<float3>(params, "_003");
break;
}
case SOCK_RGBA: {
- output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ switch_fields<ColorGeometry4f>(params, "_004");
break;
}
case SOCK_STRING: {
- output_input<std::string>(params, input, "_005", "Output_005");
+ switch_fields<std::string>(params, "_005");
break;
}
case SOCK_GEOMETRY: {
- output_input<GeometrySet>(params, input, "_006", "Output_006");
+ switch_no_fields<GeometrySet>(params, "_006");
break;
}
case SOCK_OBJECT: {
- output_input<Object *>(params, input, "_007", "Output_007");
+ switch_no_fields<Object *>(params, "_007");
break;
}
case SOCK_COLLECTION: {
- output_input<Collection *>(params, input, "_008", "Output_008");
+ switch_no_fields<Collection *>(params, "_008");
break;
}
case SOCK_TEXTURE: {
- output_input<Tex *>(params, input, "_009", "Output_009");
+ switch_no_fields<Tex *>(params, "_009");
break;
}
case SOCK_MATERIAL: {
- output_input<Material *>(params, input, "_010", "Output_010");
+ switch_no_fields<Material *>(params, "_010");
break;
}
default:
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 8886c7194db..7ef0913622c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -58,14 +58,14 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params)
GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
params.node().custom2);
- geometry_set = geometry_set_realize_instances(geometry_set);
-
- /* #triangulate_mesh might modify the input mesh currently. */
- Mesh *mesh_in = geometry_set.get_mesh_for_write();
- if (mesh_in != nullptr) {
- Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
- geometry_set.replace_mesh(mesh_out);
- }
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ /* #triangulate_mesh might modify the input mesh currently. */
+ Mesh *mesh_in = geometry_set.get_mesh_for_write();
+ if (mesh_in != nullptr) {
+ Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
+ geometry_set.replace_mesh(mesh_out);
+ }
+ });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 3b3b643d0ae..fa9bf09d8d9 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -159,15 +159,22 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket
GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry)
{
- bke::geometry_set_instances_attribute_foreach(
- geometry_set,
- [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_VOLUME};
+ geometry_set.attribute_foreach(
+ all_component_types,
+ true,
+ [&](const bke::AttributeIDRef &attribute_id,
+ const AttributeMetaData &meta_data,
+ const GeometryComponent &UNUSED(component)) {
if (attribute_id.is_named()) {
this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
}
- return true;
- },
- 8);
+ });
+
for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
component_types_.append(component->type());
switch (component->type()) {
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.cc
index b8c89d1db37..f5e6e7640ad 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.cc
@@ -21,12 +21,16 @@
* \ingroup nodes
*/
-#include <stddef.h>
-#include <string.h>
+#include <cstddef>
+#include <cstring>
#include "DNA_node_types.h"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_multi_value_map.hh"
+#include "BLI_set.hh"
+#include "BLI_stack.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -42,10 +46,10 @@
#include "node_common.h"
#include "node_util.h"
-enum {
- REFINE_FORWARD = 1 << 0,
- REFINE_BACKWARD = 1 << 1,
-};
+using blender::Map;
+using blender::MultiValueMap;
+using blender::Set;
+using blender::Stack;
/* -------------------------------------------------------------------- */
/** \name Node Group
@@ -53,24 +57,22 @@ enum {
bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier)
{
- bNodeSocket *sock;
- for (sock = groupnode->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->inputs) {
if (STREQ(sock->identifier, identifier)) {
return sock;
}
}
- return NULL;
+ return nullptr;
}
bNodeSocket *node_group_find_output_socket(bNode *groupnode, const char *identifier)
{
- bNodeSocket *sock;
- for (sock = groupnode->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->outputs) {
if (STREQ(sock->identifier, identifier)) {
return sock;
}
}
- return NULL;
+ return nullptr;
}
/* groups display their internal tree name as label */
@@ -95,13 +97,12 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis
bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
{
- bNode *node;
bool valid = true;
/* unspecified node group, generally allowed
* (if anything, should be avoided on operator level)
*/
- if (grouptree == NULL) {
+ if (grouptree == nullptr) {
return true;
}
@@ -110,7 +111,7 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis
return false;
}
- for (node = grouptree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) {
if (node->typeinfo->poll_instance &&
!node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
valid = false;
@@ -121,12 +122,15 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis
}
/* used for both group nodes and interface nodes */
-static bNodeSocket *group_verify_socket(
- bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out)
+static bNodeSocket *group_verify_socket(bNodeTree *ntree,
+ bNode *gnode,
+ bNodeSocket *iosock,
+ ListBase *verify_lb,
+ eNodeSocketInOut in_out)
{
bNodeSocket *sock;
- for (sock = verify_lb->first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)verify_lb->first; sock; sock = sock->next) {
if (STREQ(sock->identifier, iosock->identifier)) {
break;
}
@@ -163,29 +167,32 @@ static bNodeSocket *group_verify_socket(
}
/* used for both group nodes and interface nodes */
-static void group_verify_socket_list(
- bNodeTree *ntree, bNode *gnode, ListBase *iosock_lb, ListBase *verify_lb, int in_out)
+static void group_verify_socket_list(bNodeTree *ntree,
+ bNode *gnode,
+ ListBase *iosock_lb,
+ ListBase *verify_lb,
+ eNodeSocketInOut in_out)
{
- bNodeSocket *iosock, *sock, *nextsock;
+ bNodeSocket *sock, *nextsock;
/* step by step compare */
- iosock = iosock_lb->first;
+ bNodeSocket *iosock = (bNodeSocket *)iosock_lb->first;
for (; iosock; iosock = iosock->next) {
/* abusing new_sock pointer for verification here! only used inside this function */
iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out);
}
/* leftovers are removed */
- for (sock = verify_lb->first; sock; sock = nextsock) {
+ for (sock = (bNodeSocket *)verify_lb->first; sock; sock = nextsock) {
nextsock = sock->next;
nodeRemoveSocket(ntree, gnode, sock);
}
/* and we put back the verified sockets */
- iosock = iosock_lb->first;
+ iosock = (bNodeSocket *)iosock_lb->first;
for (; iosock; iosock = iosock->next) {
if (iosock->new_sock) {
BLI_addtail(verify_lb, iosock->new_sock);
- iosock->new_sock = NULL;
+ iosock->new_sock = nullptr;
}
}
}
@@ -194,11 +201,11 @@ static void group_verify_socket_list(
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
{
/* check inputs and outputs, and remove or insert them */
- if (node->id == NULL) {
+ if (node->id == nullptr) {
nodeRemoveAllSockets(ntree, node);
}
else if ((ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING))) {
- /* Missing datablock, leave sockets unchanged so that when it comes back
+ /* Missing data-block, leave sockets unchanged so that when it comes back
* the links remain valid. */
}
else {
@@ -227,7 +234,7 @@ static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_frame(void)
{
/* frame type is used for all tree types, needs dynamic allocation */
- bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type");
+ bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type");
ntype->free_self = (void (*)(bNodeType *))MEM_freeN;
node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND);
@@ -254,11 +261,11 @@ static void node_reroute_update_internal_links(bNodeTree *ntree, bNode *node)
return;
}
- link = MEM_callocN(sizeof(bNodeLink), "internal node link");
+ link = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "internal node link");
link->fromnode = node;
- link->fromsock = node->inputs.first;
+ link->fromsock = (bNodeSocket *)node->inputs.first;
link->tonode = node;
- link->tosock = node->outputs.first;
+ link->tosock = (bNodeSocket *)node->outputs.first;
/* internal link is always valid */
link->flag |= NODE_LINK_VALID;
BLI_addtail(&node->internal_links, link);
@@ -276,7 +283,7 @@ static void node_reroute_init(bNodeTree *ntree, bNode *node)
void register_node_type_reroute(void)
{
/* frame type is used for all tree types, needs dynamic allocation */
- bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type");
+ bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type");
ntype->free_self = (void (*)(bNodeType *))MEM_freeN;
node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT, 0);
@@ -286,76 +293,37 @@ void register_node_type_reroute(void)
nodeRegisterType(ntype);
}
-static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, int flag)
+static void propagate_reroute_type_from_start_socket(
+ bNodeSocket *start_socket,
+ const MultiValueMap<bNodeSocket *, bNodeLink *> &links_map,
+ Map<bNode *, const bNodeSocketType *> &r_reroute_types)
{
- bNodeSocket *input = node->inputs.first;
- bNodeSocket *output = node->outputs.first;
- bNodeLink *link;
- int type = SOCK_FLOAT;
- const char *type_idname = nodeStaticSocketType(type, PROP_NONE);
-
- /* XXX it would be a little bit more efficient to restrict actual updates
- * to reroute nodes connected to an updated node, but there's no reliable flag
- * to indicate updated nodes (node->update is not set on linking).
- */
-
- node->done = 1;
-
- /* recursive update */
- for (link = ntree->links.first; link; link = link->next) {
- bNode *fromnode = link->fromnode;
- bNode *tonode = link->tonode;
- if (!tonode || !fromnode) {
- continue;
+ Stack<bNode *> nodes_to_check;
+ for (bNodeLink *link : links_map.lookup(start_socket)) {
+ if (link->tonode->type == NODE_REROUTE) {
+ nodes_to_check.push(link->tonode);
}
- if (nodeLinkIsHidden(link)) {
- continue;
- }
-
- if (flag & REFINE_FORWARD) {
- if (tonode == node && fromnode->type == NODE_REROUTE && !fromnode->done) {
- node_reroute_inherit_type_recursive(ntree, fromnode, REFINE_FORWARD);
- }
+ if (link->fromnode->type == NODE_REROUTE) {
+ nodes_to_check.push(link->fromnode);
}
- if (flag & REFINE_BACKWARD) {
- if (fromnode == node && tonode->type == NODE_REROUTE && !tonode->done) {
- node_reroute_inherit_type_recursive(ntree, tonode, REFINE_BACKWARD);
- }
- }
- }
-
- /* determine socket type from unambiguous input/output connection if possible */
- if (nodeSocketLinkLimit(input) == 1 && input->link) {
- type = input->link->fromsock->type;
- type_idname = nodeStaticSocketType(type, PROP_NONE);
- }
- else if (nodeSocketLinkLimit(output) == 1 && output->link) {
- type = output->link->tosock->type;
- type_idname = nodeStaticSocketType(type, PROP_NONE);
}
-
- if (input->type != type) {
- bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input");
- for (link = ntree->links.first; link; link = link->next) {
- if (link->tosock == input) {
- link->tosock = ninput;
- ninput->link = link;
+ const bNodeSocketType *current_type = start_socket->typeinfo;
+ while (!nodes_to_check.is_empty()) {
+ bNode *reroute_node = nodes_to_check.pop();
+ BLI_assert(reroute_node->type == NODE_REROUTE);
+ if (r_reroute_types.add(reroute_node, current_type)) {
+ for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->inputs.first)) {
+ if (link->fromnode->type == NODE_REROUTE) {
+ nodes_to_check.push(link->fromnode);
+ }
}
- }
- nodeRemoveSocket(ntree, node, input);
- }
-
- if (output->type != type) {
- bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output");
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromsock == output) {
- link->fromsock = noutput;
+ for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->outputs.first)) {
+ if (link->tonode->type == NODE_REROUTE) {
+ nodes_to_check.push(link->tonode);
+ }
}
}
- nodeRemoveSocket(ntree, node, output);
}
-
- nodeUpdateInternalLinks(ntree, node);
}
/* Global update function for Reroute node types.
@@ -363,16 +331,58 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
*/
void ntree_update_reroute_nodes(bNodeTree *ntree)
{
- bNode *node;
+ /* Contains nodes that are linked to at least one reroute node. */
+ Set<bNode *> nodes_linked_with_reroutes;
+ /* Contains all links that are linked to at least one reroute node. */
+ MultiValueMap<bNodeSocket *, bNodeLink *> links_map;
+ /* Build acceleration data structures for the algorithm below. */
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->fromsock == nullptr || link->tosock == nullptr) {
+ continue;
+ }
+ if (link->fromnode->type != NODE_REROUTE && link->tonode->type != NODE_REROUTE) {
+ continue;
+ }
+ if (link->fromnode->type != NODE_REROUTE) {
+ nodes_linked_with_reroutes.add(link->fromnode);
+ }
+ if (link->tonode->type != NODE_REROUTE) {
+ nodes_linked_with_reroutes.add(link->tonode);
+ }
+ links_map.add(link->fromsock, link);
+ links_map.add(link->tosock, link);
+ }
+
+ /* Will contain the socket type for every linked reroute node. */
+ Map<bNode *, const bNodeSocketType *> reroute_types;
+
+ /* Propagate socket types from left to right. */
+ for (bNode *start_node : nodes_linked_with_reroutes) {
+ LISTBASE_FOREACH (bNodeSocket *, output_socket, &start_node->outputs) {
+ propagate_reroute_type_from_start_socket(output_socket, links_map, reroute_types);
+ }
+ }
- /* clear tags */
- for (node = ntree->nodes.first; node; node = node->next) {
- node->done = 0;
+ /* Propagate socket types from right to left. This affects reroute nodes that haven't been
+ * changed in the the loop above. */
+ for (bNode *start_node : nodes_linked_with_reroutes) {
+ LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) {
+ propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types);
+ }
}
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node->type == NODE_REROUTE && !node->done) {
- node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD);
+ /* Actually update reroute nodes with changed types. */
+ for (const auto &item : reroute_types.items()) {
+ bNode *reroute_node = item.key;
+ const bNodeSocketType *socket_type = item.value;
+ bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first;
+ bNodeSocket *output_socket = (bNodeSocket *)reroute_node->outputs.first;
+
+ if (input_socket->typeinfo != socket_type) {
+ nodeModifySocketType(ntree, reroute_node, input_socket, socket_type->idname);
+ }
+ if (output_socket->typeinfo != socket_type) {
+ nodeModifySocketType(ntree, reroute_node, output_socket, socket_type->idname);
}
}
}
@@ -393,7 +403,7 @@ static bool node_is_connected_to_output_recursive(bNodeTree *ntree, bNode *node)
}
/* test all connected nodes, first positive find is sufficient to return true */
- for (link = ntree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
if (link->fromnode == node) {
if (node_is_connected_to_output_recursive(ntree, link->tonode)) {
return true;
@@ -408,7 +418,7 @@ bool BKE_node_is_connected_to_output(bNodeTree *ntree, bNode *node)
bNode *tnode;
/* clear flags */
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) {
tnode->done = 0;
}
@@ -419,9 +429,9 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree)
{
bNode *node;
- for (node = ntree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
if (node->id == id) {
- node->id = NULL;
+ node->id = nullptr;
}
}
}
@@ -440,17 +450,17 @@ static void node_group_input_init(bNodeTree *ntree, bNode *node)
bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
if (STREQ(sock->identifier, identifier)) {
return sock;
}
}
- return NULL;
+ return nullptr;
}
void node_group_input_update(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *extsock = node->outputs.last;
+ bNodeSocket *extsock = (bNodeSocket *)node->outputs.last;
bNodeLink *link, *linknext, *exposelink;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links from the extension socket
@@ -460,14 +470,14 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
/* find links from the extension socket and store them */
BLI_listbase_clear(&tmplinks);
- for (link = ntree->links.first; link; link = linknext) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = linknext) {
linknext = link->next;
if (nodeLinkIsHidden(link)) {
continue;
}
if (link->fromsock == extsock) {
- bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
+ bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
@@ -476,8 +486,8 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
}
/* find valid link to expose */
- exposelink = NULL;
- for (link = tmplinks.first; link; link = link->next) {
+ exposelink = nullptr;
+ for (link = (bNodeLink *)tmplinks.first; link; link = link->next) {
/* XXX Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
@@ -498,7 +508,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
newsock = node_group_input_find_socket(node, gsock->identifier);
/* redirect links from the extension socket */
- for (link = tmplinks.first; link; link = link->next) {
+ for (link = (bNodeLink *)tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, node, newsock, link->tonode, link->tosock);
}
}
@@ -518,7 +528,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
void register_node_type_group_input(void)
{
/* used for all tree types, needs dynamic allocation */
- bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type");
+ bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type");
ntype->free_self = (void (*)(bNodeType *))MEM_freeN;
node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0);
@@ -537,17 +547,17 @@ static void node_group_output_init(bNodeTree *ntree, bNode *node)
bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (STREQ(sock->identifier, identifier)) {
return sock;
}
}
- return NULL;
+ return nullptr;
}
void node_group_output_update(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *extsock = node->inputs.last;
+ bNodeSocket *extsock = (bNodeSocket *)node->inputs.last;
bNodeLink *link, *linknext, *exposelink;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links to the extension socket
@@ -557,14 +567,14 @@ void node_group_output_update(bNodeTree *ntree, bNode *node)
/* find links to the extension socket and store them */
BLI_listbase_clear(&tmplinks);
- for (link = ntree->links.first; link; link = linknext) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = linknext) {
linknext = link->next;
if (nodeLinkIsHidden(link)) {
continue;
}
if (link->tosock == extsock) {
- bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
+ bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
@@ -573,8 +583,8 @@ void node_group_output_update(bNodeTree *ntree, bNode *node)
}
/* find valid link to expose */
- exposelink = NULL;
- for (link = tmplinks.first; link; link = link->next) {
+ exposelink = nullptr;
+ for (link = (bNodeLink *)tmplinks.first; link; link = link->next) {
/* XXX Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
@@ -596,7 +606,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node)
newsock = node_group_output_find_socket(node, gsock->identifier);
/* redirect links to the extension socket */
- for (link = tmplinks.first; link; link = link->next) {
+ for (link = (bNodeLink *)tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock);
}
}
@@ -616,7 +626,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node)
void register_node_type_group_output(void)
{
/* used for all tree types, needs dynamic allocation */
- bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type");
+ bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type");
ntype->free_self = (void (*)(bNodeType *))MEM_freeN;
node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0);
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 31260f95242..4334f1b5030 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -872,7 +872,7 @@ static bNodeSocketType *make_socket_type_material()
void register_standard_node_socket_types(void)
{
- /* draw callbacks are set in drawnode.c to avoid bad-level calls */
+ /* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */
nodeRegisterSocketType(make_socket_type_float(PROP_NONE));
nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED));
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 641d02af902..43c7fbd2599 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -19,6 +19,7 @@
#include "NOD_node_tree_ref.hh"
#include "BLI_dot_export.hh"
+#include "BLI_stack.hh"
namespace blender::nodes {
@@ -473,6 +474,108 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const
return false;
}
+bool NodeRef::any_input_is_directly_linked() const
+{
+ for (const SocketRef *socket : inputs_) {
+ if (!socket->directly_linked_sockets().is_empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool NodeRef::any_output_is_directly_linked() const
+{
+ for (const SocketRef *socket : outputs_) {
+ if (!socket->directly_linked_sockets().is_empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const
+{
+ if (in_out == SOCK_IN) {
+ return this->any_input_is_directly_linked();
+ }
+ return this->any_output_is_directly_linked();
+}
+
+/**
+ * Sort nodes topologically from left to right or right to left.
+ * In the future the result if this could be cached on #NodeTreeRef.
+ */
+Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) const
+{
+ struct Item {
+ const NodeRef *node;
+ /* Index of the next socket that is checked in the depth-first search. */
+ int socket_index = 0;
+ /* Link index in the next socket that is checked in the depth-first search. */
+ int link_index = 0;
+ };
+
+ Vector<const NodeRef *> toposort;
+ toposort.reserve(nodes_by_id_.size());
+ Array<bool> node_is_done_by_id(nodes_by_id_.size(), false);
+ Stack<Item> nodes_to_check;
+
+ for (const NodeRef *start_node : nodes_by_id_) {
+ if (node_is_done_by_id[start_node->id()]) {
+ /* Ignore nodes that are done already. */
+ continue;
+ }
+ if (start_node->any_socket_is_directly_linked(
+ direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
+ /* Ignore non-start nodes. */
+ continue;
+ }
+
+ /* Do a depth-first search to sort nodes topologically. */
+ nodes_to_check.push({start_node});
+ while (!nodes_to_check.is_empty()) {
+ Item &item = nodes_to_check.peek();
+ const NodeRef &node = *item.node;
+ const Span<const SocketRef *> sockets = node.sockets(
+ direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
+
+ while (true) {
+ if (item.socket_index == sockets.size()) {
+ /* All sockets have already been visited. */
+ break;
+ }
+ const SocketRef &socket = *sockets[item.socket_index];
+ const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
+ if (item.link_index == linked_sockets.size()) {
+ /* All links connected to this socket have already been visited. */
+ item.socket_index++;
+ item.link_index = 0;
+ continue;
+ }
+ const SocketRef &linked_socket = *linked_sockets[item.link_index];
+ const NodeRef &linked_node = linked_socket.node();
+ if (node_is_done_by_id[linked_node.id()]) {
+ /* The linked node has already been visited. */
+ item.link_index++;
+ continue;
+ }
+ nodes_to_check.push({&linked_node});
+ break;
+ }
+
+ /* If no other element has been pushed, the current node can be pushed to the sorted list. */
+ if (&item == &nodes_to_check.peek()) {
+ node_is_done_by_id[node.id()] = true;
+ toposort.append(&node);
+ nodes_to_check.pop();
+ }
+ }
+ }
+
+ return toposort;
+}
+
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index c3a675fcd20..83ee0c2f411 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -201,7 +201,7 @@ void register_node_tree_type_sh(void)
tt->type = NTREE_SHADER;
strcpy(tt->idname, "ShaderNodeTree");
strcpy(tt->ui_name, N_("Shader Editor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = 0; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Shader nodes"));
tt->foreach_nodeclass = foreach_nodeclass;
diff --git a/source/blender/nodes/shader/nodes/node_shader_brightness.c b/source/blender/nodes/shader/nodes/node_shader_brightness.c
index d8f560277f2..4f375c666de 100644
--- a/source/blender/nodes/shader/nodes/node_shader_brightness.c
+++ b/source/blender/nodes/shader/nodes/node_shader_brightness.c
@@ -19,7 +19,7 @@
#include "node_shader_util.h"
-/* **************** Brigh and contrsast ******************** */
+/* **************** Bright and contrast ******************** */
static bNodeSocketTemplate sh_node_brightcontrast_in[] = {
{SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f},
diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
index 31d8f8ef15c..e8d4239937f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
@@ -27,6 +27,7 @@ namespace blender::nodes {
static void sh_node_clamp_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Value").min(0.0f).max(1.0f).default_value(1.0f);
b.add_input<decl::Float>("Min").default_value(0.0f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index e4ada06133e..8657d9e517d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -27,6 +27,7 @@ namespace blender::nodes {
static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Vector>("Vector").min(-1.0f).max(1.0f);
b.add_output<decl::Vector>("Vector");
@@ -342,3 +343,142 @@ void register_node_type_sh_curve_rgb(void)
nodeRegisterType(&ntype);
}
+
+/* **************** CURVE FLOAT ******************** */
+
+namespace blender::nodes {
+
+static void sh_node_curve_float_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>("Value").default_value(1.0f);
+ b.add_output<decl::Float>("Value");
+};
+
+} // namespace blender::nodes
+
+static void node_shader_exec_curve_float(void *UNUSED(data),
+ int UNUSED(thread),
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ bNodeStack **in,
+ bNodeStack **out)
+{
+ float value;
+ float fac;
+
+ nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
+ nodestack_get_vec(&value, SOCK_FLOAT, in[1]);
+ out[0]->vec[0] = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, value);
+ if (fac != 1.0f) {
+ out[0]->vec[0] = (1.0f - fac) * value + fac * out[0]->vec[0];
+ }
+}
+
+static void node_shader_init_curve_float(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+static int gpu_shader_curve_float(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ float *array, layer;
+ int size;
+
+ CurveMapping *cumap = (CurveMapping *)node->storage;
+
+ BKE_curvemapping_table_F(cumap, &array, &size);
+ GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
+
+ float ext_xyz[4];
+ float range_x;
+
+ const CurveMap *cm = &cumap->cm[0];
+ ext_xyz[0] = cm->mintable;
+ ext_xyz[2] = cm->maxtable;
+ range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
+ /* Compute extrapolation gradients. */
+ if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
+ ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f;
+ ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f;
+ }
+ else {
+ ext_xyz[1] = 0.0f;
+ ext_xyz[3] = 0.0f;
+ }
+ return GPU_stack_link(mat,
+ node,
+ "curve_float",
+ in,
+ out,
+ tex,
+ GPU_constant(&layer),
+ GPU_uniform(&range_x),
+ GPU_uniform(ext_xyz));
+}
+
+class CurveFloatFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveFloatFunction(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 Float"};
+ signature.single_input<float>("Factor");
+ signature.single_input<float>("Value");
+ signature.single_output<float>("Value");
+ 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, "Factor");
+ const blender::VArray<float> &val_in = params.readonly_single_input<float>(1, "Value");
+ blender::MutableSpan<float> val_out = params.uninitialized_single_output<float>(2, "Value");
+
+ for (int64_t i : mask) {
+ val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]);
+ if (fac[i] != 1.0f) {
+ val_out[i] = (1.0f - fac[i]) * val_in[i] + fac[i] * val_out[i];
+ }
+ }
+ }
+};
+
+static void sh_node_curve_float_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
+{
+ bNode &bnode = builder.node();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap);
+}
+
+void register_node_type_sh_curve_float(void)
+{
+ static bNodeType ntype;
+
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_FLOAT, "Float Curve", NODE_CLASS_CONVERTER, 0);
+ ntype.declare = blender::nodes::sh_node_curve_float_declare;
+ node_type_init(&ntype, node_shader_init_curve_float);
+ 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, nullptr, node_shader_exec_curve_float);
+ node_type_gpu(&ntype, gpu_shader_curve_float);
+ ntype.build_multi_function = sh_node_curve_float_build_multi_function;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.c
index 843185befb6..c721fb9c77a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c
+++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.c
@@ -22,6 +22,7 @@
static bNodeSocketTemplate outputs[] = {
{SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+ {SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
// { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
@@ -35,7 +36,11 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- return GPU_stack_link(mat, node, "node_hair_info", in, out);
+ /* Length: don't request length if not needed. */
+ static const float zero = 0;
+ GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) :
+ GPU_attribute(mat, CD_HAIRLENGTH, "");
+ return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
/* node type definition */
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index a5f9a24a728..5ea194ddc83 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -29,6 +29,7 @@ namespace blender::nodes {
static void sh_node_map_range_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f);
b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 80a27b8e6a1..96d1be49c04 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -31,6 +31,7 @@ namespace blender::nodes {
static void sh_node_math_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 860cc260d5d..d4d02e80ada 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -27,6 +27,7 @@ namespace blender::nodes {
static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Color>("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f});
b.add_input<decl::Color>("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f});
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 63be399366f..24c5dcf7ba3 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -27,6 +27,7 @@ namespace blender::nodes {
static void sh_node_seprgb_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_output<decl::Float>("R");
b.add_output<decl::Float>("G");
@@ -119,6 +120,7 @@ namespace blender::nodes {
static void sh_node_combrgb_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("R").min(0.0f).max(1.0f);
b.add_input<decl::Float>("G").min(0.0f).max(1.0f);
b.add_input<decl::Float>("B").min(0.0f).max(1.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index b4b3c48482f..8ca8fc19521 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -27,6 +27,7 @@ namespace blender::nodes {
static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_output<decl::Float>("X");
b.add_output<decl::Float>("Y");
@@ -103,6 +104,7 @@ namespace blender::nodes {
static void sh_node_combxyz_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
index d33d92f25fd..23f150d8135 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
@@ -23,6 +23,7 @@ namespace blender::nodes {
static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").hide_value();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
index 1ae6b3a616c..6ffc8979815 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
@@ -25,7 +25,8 @@ namespace blender::nodes {
static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Vector").hide_value();
+ b.is_function_node();
+ b.add_input<decl::Vector>("Vector").implicit_field();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
index cea7af247c1..e12e5724e8e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
@@ -23,6 +23,7 @@ namespace blender::nodes {
static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").hide_value();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
index bae16e10120..03543e5f7fe 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
@@ -23,6 +23,7 @@ namespace blender::nodes {
static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("W").min(-10000.0f).max(10000.0f);
b.add_output<decl::Float>("Value");
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 1870caffbb1..d4d08be5d49 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -33,6 +33,7 @@ namespace blender::nodes {
static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Color>("Color");
b.add_output<decl::Float>("Alpha");
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 5b24e8bb72d..f49ff06cef1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -29,6 +29,7 @@ namespace blender::nodes {
static void sh_node_vector_math_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_input<decl::Vector>("Vector", "Vector_001").min(-10000.0f).max(10000.0f);
b.add_input<decl::Vector>("Vector", "Vector_002").min(-10000.0f).max(10000.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index e9fd6c4f31e..c9b26fa5199 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -27,12 +27,13 @@ namespace blender::nodes {
static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b)
{
+ b.is_function_node();
b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value();
- b.add_input<decl::Vector>("Vector");
+ b.add_input<decl::Vector>("Center");
b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});
b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER);
- b.add_output<decl::Vector>("Value");
+ b.add_output<decl::Vector>("Vector");
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 7452007639c..14597050524 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -169,7 +169,7 @@ void register_node_tree_type_tex(void)
tt->type = NTREE_TEXTURE;
strcpy(tt->idname, "TextureNodeTree");
strcpy(tt->ui_name, N_("Texture Node Editor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = 0; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Texture nodes"));
tt->foreach_nodeclass = foreach_nodeclass;
diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c
index 70f7e731720..f61e3f36db5 100644
--- a/source/blender/nodes/texture/nodes/node_texture_curves.c
+++ b/source/blender/nodes/texture/nodes/node_texture_curves.c
@@ -26,7 +26,7 @@
/* **************** CURVE Time ******************** */
-/* custom1 = sfra, custom2 = efra */
+/* custom1 = start-frame, custom2 = end-frame. */
static bNodeSocketTemplate time_outputs[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}};
static void time_colorfn(
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index e5e601e0eb6..cbebe4746e9 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1950,7 +1950,7 @@ static PyObject *bpy_bmface_calc_tangent_edge_diagonal(BPy_BMFace *self)
PyDoc_STRVAR(bpy_bmface_calc_tangent_vert_diagonal_doc,
".. method:: calc_tangent_vert_diagonal()\n"
"\n"
- " Return face tangent based on the two most distent vertices.\n"
+ " Return face tangent based on the two most distant vertices.\n"
"\n"
" :return: a normalized vector.\n"
" :rtype: :class:`mathutils.Vector`\n");
@@ -3464,7 +3464,7 @@ PyDoc_STRVAR(bpy_bmelemseq_doc,
":class:`BMVert`, :class:`BMEdge`, :class:`BMFace`, :class:`BMLoop`.\n"
"\n"
"When accessed via :class:`BMesh.verts`, :class:`BMesh.edges`, :class:`BMesh.faces`\n"
- "there are also functions to create/remomove items.\n");
+ "there are also functions to create/remove items.\n");
PyDoc_STRVAR(bpy_bmiter_doc,
"Internal BMesh type for looping over verts/faces/edges,\n"
"used for iterating over :class:`BMElemSeq` types.\n");
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 7a93a076621..95affa9dba9 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -246,7 +246,7 @@ void BPY_modules_update(void)
#if 0 /* slow, this runs all the time poll, draw etc 100's of time a sec. */
PyObject *mod = PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
PyModule_AddObject(mod, "data", BPY_rna_module());
- PyModule_AddObject(mod, "types", BPY_rna_types()); /* atm this does not need updating */
+ PyModule_AddObject(mod, "types", BPY_rna_types()); /* This does not need updating. */
#endif
/* refreshes the main struct */
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index ac624d365fa..6d384ed9358 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -3750,7 +3750,7 @@ PyDoc_STRVAR(
" (e.g. returned by :class:`bpy.types.UILayout.icon`)\n"
" :number: Unique value used as the identifier for this item (stored in file data).\n"
" Use when the identifier may need to change. If the *ENUM_FLAG* option is used,\n"
- " the values are bitmasks and should be powers of two.\n"
+ " the values are bit-masks and should be powers of two.\n"
"\n"
" When an item only contains 4 items they define ``(identifier, name, description, "
"number)``.\n"
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 5868c76b28f..d47b59d0c76 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1210,7 +1210,7 @@ PyDoc_STRVAR(M_Geometry_tessellate_polygon_doc,
"\n"
" :arg veclist_list: list of polylines\n"
" :rtype: list\n");
-/* PolyFill function, uses Blenders scanfill to fill multiple poly lines */
+/* PolyFill function, uses Blenders scan-fill to fill multiple poly lines. */
static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject *polyLineSeq)
{
PyObject *tri_list; /* Return this list of tri's */
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 3237772dd80..0d2d93ae026 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -233,7 +233,8 @@ void RE_create_render_pass(struct RenderResult *rr,
int channels,
const char *chan_id,
const char *layername,
- const char *viewname);
+ const char *viewname,
+ const bool allocate);
/* obligatory initialize call, disprect is optional */
void RE_InitState(struct Render *re,
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index 389b821ca35..790c46dad0f 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -207,11 +207,10 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y,
/* Add render passes. */
RenderPass *result_pass = render_layer_add_pass(
- rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA");
- RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA");
- RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA");
-
- render_result_passes_allocated_ensure(rr);
+ rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA", true);
+ RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA", true);
+ RenderPass *differential_pass = render_layer_add_pass(
+ rr, rl, 4, "BakeDifferential", "", "RGBA", true);
/* Fill render passes from bake pixel array, to be read by the render engine. */
for (int ty = 0; ty < h; ty++) {
@@ -414,7 +413,7 @@ void RE_engine_add_pass(RenderEngine *engine,
return;
}
- RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL);
+ RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL, false);
}
void RE_engine_end_result(
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 72ff920561d..7c5259a1c5c 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -1015,10 +1015,10 @@ static void render_result_uncrop(Render *re)
render_result_disprect_to_full_resolution(re);
rres = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS);
- render_result_passes_allocated_ensure(rres);
rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data);
render_result_clone_passes(re, rres, NULL);
+ render_result_passes_allocated_ensure(rres);
render_result_merge(rres, re->result);
render_result_free(re->result);
@@ -2817,7 +2817,7 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha
BLI_freelinkN(&rl->passes, rp);
}
/* create a totally new pass */
- return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA");
+ return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA", true);
}
bool RE_allow_render_generic_object(Object *ob)
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index c308147fc5b..0681bcd9aa5 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -213,12 +213,37 @@ static void set_pass_full_name(
/********************************** New **************************************/
+static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp)
+{
+ if (rp->rect != NULL) {
+ return;
+ }
+
+ const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels;
+ rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name);
+
+ if (STREQ(rp->name, RE_PASSNAME_VECTOR)) {
+ /* initialize to max speed */
+ float *rect = rp->rect;
+ for (int x = rectsize - 1; x >= 0; x--) {
+ rect[x] = PASS_VECTOR_MAX;
+ }
+ }
+ else if (STREQ(rp->name, RE_PASSNAME_Z)) {
+ float *rect = rp->rect;
+ for (int x = rectsize - 1; x >= 0; x--) {
+ rect[x] = 10e10;
+ }
+ }
+}
+
RenderPass *render_layer_add_pass(RenderResult *rr,
RenderLayer *rl,
int channels,
const char *name,
const char *viewname,
- const char *chan_id)
+ const char *chan_id,
+ const bool allocate)
{
const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name));
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name);
@@ -250,8 +275,13 @@ RenderPass *render_layer_add_pass(RenderResult *rr,
BLI_addtail(&rl->passes, rpass);
- /* The result contains non-allocated pass now, so tag it as such. */
- rr->passes_allocated = false;
+ if (allocate) {
+ render_layer_allocate_pass(rr, rpass);
+ }
+ else {
+ /* The result contains non-allocated pass now, so tag it as such. */
+ rr->passes_allocated = false;
+ }
return rpass;
}
@@ -323,14 +353,14 @@ RenderResult *render_result_new(Render *re,
#define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \
do { \
- if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \
+ if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == NULL) { \
render_result_free(rr); \
return NULL; \
} \
} while (false)
/* A renderlayer should always have a Combined pass. */
- render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA");
+ render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA", false);
if (view_layer->passflag & SCE_PASS_Z) {
RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z");
@@ -427,7 +457,7 @@ RenderResult *render_result_new(Render *re,
}
/* a renderlayer should always have a Combined pass */
- render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA");
+ render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA", false);
}
/* NOTE: this has to be in sync with `scene.c`. */
@@ -453,26 +483,7 @@ void render_result_passes_allocated_ensure(RenderResult *rr)
continue;
}
- if (rp->rect != NULL) {
- continue;
- }
-
- const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels;
- rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name);
-
- if (STREQ(rp->name, RE_PASSNAME_VECTOR)) {
- /* initialize to max speed */
- float *rect = rp->rect;
- for (int x = rectsize - 1; x >= 0; x--) {
- rect[x] = PASS_VECTOR_MAX;
- }
- }
- else if (STREQ(rp->name, RE_PASSNAME_Z)) {
- float *rect = rp->rect;
- for (int x = rectsize - 1; x >= 0; x--) {
- rect[x] = 10e10;
- }
- }
+ render_layer_allocate_pass(rr, rp);
}
}
@@ -501,7 +512,7 @@ void render_result_clone_passes(Render *re, RenderResult *rr, const char *viewna
&rl->passes, main_rp->fullname, offsetof(RenderPass, fullname));
if (!rp) {
render_layer_add_pass(
- rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id);
+ rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id, false);
}
}
}
@@ -512,7 +523,8 @@ void RE_create_render_pass(RenderResult *rr,
int channels,
const char *chan_id,
const char *layername,
- const char *viewname)
+ const char *viewname,
+ const bool allocate)
{
RenderLayer *rl;
RenderPass *rp;
@@ -542,7 +554,7 @@ void RE_create_render_pass(RenderResult *rr,
}
if (!rp) {
- render_layer_add_pass(rr, rl, channels, name, view, chan_id);
+ render_layer_add_pass(rr, rl, channels, name, view, chan_id, allocate);
}
}
}
@@ -1083,7 +1095,7 @@ int render_result_exr_file_read_path(RenderResult *rr,
void *exrhandle = IMB_exr_get_handle();
int rectx, recty;
- if (IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty) == 0) {
+ if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, false)) {
printf("failed being read %s\n", filepath);
IMB_exr_close(exrhandle);
return 0;
@@ -1175,20 +1187,32 @@ void render_result_exr_file_cache_write(Render *re)
/* For cache, makes exact copy of render result */
bool render_result_exr_file_cache_read(Render *re)
{
- char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = "";
+ /* File path to cache. */
+ char filepath[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = "";
char *root = U.render_cachedir;
+ render_result_exr_file_cache_path(re->scene, root, filepath);
- RE_FreeRenderResult(re->result);
- re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ printf("read exr cache file: %s\n", filepath);
- /* First try cache. */
- render_result_exr_file_cache_path(re->scene, root, str);
+ /* Try opening the file. */
+ void *exrhandle = IMB_exr_get_handle();
+ int rectx, recty;
- printf("read exr cache file: %s\n", str);
- if (!render_result_exr_file_read_path(re->result, NULL, str)) {
- printf("cannot read: %s\n", str);
+ if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, true)) {
+ printf("cannot read: %s\n", filepath);
+ IMB_exr_close(exrhandle);
return false;
}
+
+ /* Read file contents into render result. */
+ const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
+ RE_FreeRenderResult(re->result);
+
+ IMB_exr_read_channels(exrhandle);
+ re->result = render_result_new_from_exr(exrhandle, colorspace, false, rectx, recty);
+
+ IMB_exr_close(exrhandle);
+
return true;
}
diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h
index 4145bb3b8ab..34b8143869c 100644
--- a/source/blender/render/intern/render_result.h
+++ b/source/blender/render/intern/render_result.h
@@ -83,7 +83,8 @@ struct RenderPass *render_layer_add_pass(struct RenderResult *rr,
int channels,
const char *name,
const char *viewname,
- const char *chan_id);
+ const char *chan_id,
+ const bool allocate);
int render_result_exr_file_read_path(struct RenderResult *rr,
struct RenderLayer *rl_single,
diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c
index 62aee564626..edfa284242c 100644
--- a/source/blender/render/intern/texture_image.c
+++ b/source/blender/render/intern/texture_image.c
@@ -1958,13 +1958,11 @@ int imagewraposa(Tex *tex,
}
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) {
- /* qdn: normal from color
- * The invert of the red channel is to make
- * the normal map compliant with the outside world.
- * It needs to be done because in Blender
- * the normal used in the renderer points inward. It is generated
- * this way in calc_vertexnormals(). Should this ever change
- * this negate must be removed. */
+ /* Normal from color:
+ * The invert of the red channel is to make the normal map compliant with the outside world.
+ * It needs to be done because in Blender the normal used in the renderer points inward.
+ * It is generated this way in #calc_vertexnormals().
+ * Should this ever change this negate must be removed. */
texres->nor[0] = -2.0f * (texres->tr - 0.5f);
texres->nor[1] = 2.0f * (texres->tg - 0.5f);
texres->nor[2] = 2.0f * (texres->tb - 0.5f);
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 7e733817630..1b8982da0d2 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -89,6 +89,7 @@ void SEQ_sequence_base_dupli_recursive(const struct Scene *scene_src,
const struct ListBase *seqbase,
int dupe_flag,
const int flag);
+bool SEQ_valid_strip_channel(struct Sequence *seq);
/* Read and Write functions for .blend file data */
void SEQ_blend_write(struct BlendWriter *writer, struct ListBase *seqbase);
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 4448db013fe..427a8835879 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -3154,6 +3154,7 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq)
float target_frame = 0;
for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
target_frame += evaluate_fcurve(fcu, seq->startdisp + frame_index);
+ CLAMP(target_frame, 0, seq->seq1->len);
v->frameMap[frame_index] = target_frame;
}
}
diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c
index 07d09f4ae17..1a63f4c4655 100644
--- a/source/blender/sequencer/intern/modifier.c
+++ b/source/blender/sequencer/intern/modifier.c
@@ -216,7 +216,7 @@ static void modifier_apply_threaded(ImBuf *ibuf,
/** \name Color Balance Modifier
* \{ */
-static StripColorBalance calc_cb(StripColorBalance *cb_)
+static StripColorBalance calc_cb_lgg(StripColorBalance *cb_)
{
StripColorBalance cb = *cb_;
int c;
@@ -262,8 +262,52 @@ static StripColorBalance calc_cb(StripColorBalance *cb_)
return cb;
}
+static StripColorBalance calc_cb_sop(StripColorBalance *cb_)
+{
+ StripColorBalance cb = *cb_;
+ int c;
+
+ for (c = 0; c < 3; c++) {
+ if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_SLOPE) {
+ if (cb.slope[c] != 0.0f) {
+ cb.slope[c] = 1.0f / cb.slope[c];
+ }
+ else {
+ cb.slope[c] = 1000000;
+ }
+ }
+
+ if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_OFFSET) {
+ cb.offset[c] = -1.0f * (cb.offset[c] - 1.0f);
+ }
+ else {
+ cb.offset[c] = cb.offset[c] - 1.0f;
+ }
+
+ if (!(cb.flag & SEQ_COLOR_BALANCE_INVERSE_POWER)) {
+ if (cb.power[c] != 0.0f) {
+ cb.power[c] = 1.0f / cb.power[c];
+ }
+ else {
+ cb.power[c] = 1000000;
+ }
+ }
+ }
+
+ return cb;
+}
+
+static StripColorBalance calc_cb(StripColorBalance *cb_)
+{
+ if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) {
+ return calc_cb_lgg(cb_);
+ }
+ /* `cb_->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER`. */
+ return calc_cb_sop(cb_);
+}
+
/* NOTE: lift is actually 2-lift. */
-MINLINE float color_balance_fl(
+MINLINE float color_balance_fl_lgg(
float in, const float lift, const float gain, const float gamma, const float mul)
{
float x = (((in - 1.0f) * lift) + 1.0f) * gain;
@@ -278,12 +322,40 @@ MINLINE float color_balance_fl(
return x;
}
-static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul)
+MINLINE float color_balance_fl_sop(float in,
+ const float slope,
+ const float offset,
+ const float power,
+ const float pivot,
+ float mul)
{
- int y;
+ float x = in * slope + offset;
+
+ /* prevent NaN */
+ if (x < 0.0f) {
+ x = 0.0f;
+ }
- for (y = 0; y < 256; y++) {
- float v = color_balance_fl((float)y * (1.0f / 255.0f), lift, gain, gamma, mul);
+ x = powf(x / pivot, power) * pivot;
+ x *= mul;
+ CLAMP(x, FLT_MIN, FLT_MAX);
+ return x;
+}
+
+static void make_cb_table_float_lgg(float lift, float gain, float gamma, float *table, float mul)
+{
+ for (int y = 0; y < 256; y++) {
+ float v = color_balance_fl_lgg((float)y * (1.0f / 255.0f), lift, gain, gamma, mul);
+
+ table[y] = v;
+ }
+}
+
+static void make_cb_table_float_sop(
+ float slope, float offset, float power, float pivot, float *table, float mul)
+{
+ for (int y = 0; y < 256; y++) {
+ float v = color_balance_fl_sop((float)y * (1.0f / 255.0f), slope, offset, power, pivot, mul);
table[y] = v;
}
@@ -310,7 +382,13 @@ static void color_balance_byte_byte(StripColorBalance *cb_,
straight_uchar_to_premul_float(p, cp);
for (c = 0; c < 3; c++) {
- float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
+ float t;
+ if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) {
+ t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
+ }
+ else {
+ t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul);
+ }
if (m) {
float m_normal = (float)m[c] / 255.0f;
@@ -352,7 +430,12 @@ static void color_balance_byte_float(StripColorBalance *cb_,
cb = calc_cb(cb_);
for (c = 0; c < 3; c++) {
- make_cb_table_float(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul);
+ if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) {
+ make_cb_table_float_lgg(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul);
+ }
+ else {
+ make_cb_table_float_sop(cb.slope[c], cb.offset[c], cb.power[c], 1.0, cb_tab[c], mul);
+ }
}
for (i = 0; i < 256; i++) {
@@ -397,7 +480,13 @@ static void color_balance_float_float(StripColorBalance *cb_,
while (p < e) {
int c;
for (c = 0; c < 3; c++) {
- float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
+ float t;
+ if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) {
+ t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
+ }
+ else {
+ t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul);
+ }
if (m) {
p[c] = p[c] * (1.0f - m[c]) + t * m[c];
@@ -507,11 +596,15 @@ static void colorBalance_init_data(SequenceModifierData *smd)
int c;
cbmd->color_multiply = 1.0f;
+ cbmd->color_balance.method = 0;
for (c = 0; c < 3; c++) {
cbmd->color_balance.lift[c] = 1.0f;
cbmd->color_balance.gamma[c] = 1.0f;
cbmd->color_balance.gain[c] = 1.0f;
+ cbmd->color_balance.slope[c] = 1.0f;
+ cbmd->color_balance.offset[c] = 1.0f;
+ cbmd->color_balance.power[c] = 1.0f;
}
}
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index 382bd51aae1..3478c2d4f97 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -144,6 +144,8 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int
seq->strip = seq_strip_alloc(type);
seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format");
+ seq->color_tag = SEQUENCE_COLOR_NONE;
+
SEQ_relations_session_uuid_generate(seq);
return seq;
@@ -636,6 +638,18 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src,
seq_new_fix_links_recursive(seq);
}
}
+
+bool SEQ_valid_strip_channel(Sequence *seq)
+{
+ if (seq->machine < 1) {
+ return false;
+ }
+ if (seq->machine > MAXSEQ) {
+ return false;
+ }
+ return true;
+}
+
/* r_prefix + [" + escaped_name + "] + \0 */
#define SEQ_RNAPATH_MAXSTR ((30 + 2 + (SEQ_NAME_MAXSTR * 2) + 2) + 1)
diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c
index 46fdd2c3d14..9822bfe38f9 100644
--- a/source/blender/sequencer/intern/strip_relations.c
+++ b/source/blender/sequencer/intern/strip_relations.c
@@ -526,4 +526,4 @@ struct Sequence *SEQ_find_metastrip_by_sequence(ListBase *seqbase, Sequence *met
}
return NULL;
-} \ No newline at end of file
+}
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index d5ff455c694..54ca4ef487f 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -278,7 +278,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep,
SEQ_time_update_sequence(evil_scene, test);
}
- if ((test->machine < 1) || (test->machine > MAXSEQ)) {
+ if (!SEQ_valid_strip_channel(test)) {
/* Blender 2.4x would remove the strip.
* nicer to move it to the end */
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 987fef570cb..cf90179f129 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -208,14 +208,16 @@ struct ID *WM_file_link_datablock(struct Main *bmain,
struct View3D *v3d,
const char *filepath,
const short id_code,
- const char *id_name);
+ const char *id_name,
+ int flag);
struct ID *WM_file_append_datablock(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct View3D *v3d,
const char *filepath,
const short id_code,
- const char *id_name);
+ const char *id_name,
+ int flag);
void WM_lib_reload(struct Library *lib, struct bContext *C, struct ReportList *reports);
/* mouse cursors */
@@ -906,6 +908,7 @@ int WM_event_modifier_flag(const struct wmEvent *event);
bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
bool WM_event_is_mouse_drag(const struct wmEvent *event);
+bool WM_event_is_mouse_drag_or_press(const wmEvent *event);
int WM_event_drag_threshold(const struct wmEvent *event);
bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 1b6e9bd2c1d..2f98834703c 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -998,6 +998,13 @@ typedef struct wmDragAsset {
const char *path;
int id_type;
int import_type; /* eFileAssetImportType */
+
+ /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the
+ * #wmDropBox.
+ * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its
+ * copy callback.
+ * */
+ struct bContext *evil_C;
} wmDragAsset;
typedef char *(*WMDropboxTooltipFunc)(struct bContext *,
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 6585349c83c..c5a89e3ad9f 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -42,6 +42,9 @@
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
+#include "BKE_main.h"
+
+#include "BLO_readfile.h"
#include "GPU_shader.h"
#include "GPU_state.h"
@@ -390,11 +393,43 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
const char *name = asset_drag->name;
ID_Type idtype = asset_drag->id_type;
+ /* FIXME: Link/Append should happens in the operator called at the end of drop process, not from
+ * here. */
+
+ Main *bmain = CTX_data_main(asset_drag->evil_C);
+ Scene *scene = CTX_data_scene(asset_drag->evil_C);
+ ViewLayer *view_layer = CTX_data_view_layer(asset_drag->evil_C);
+ View3D *view3d = CTX_wm_view3d(asset_drag->evil_C);
+
switch ((eFileAssetImportType)asset_drag->import_type) {
case FILE_ASSET_IMPORT_LINK:
- return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name);
+ return WM_file_link_datablock(bmain,
+ scene,
+ view_layer,
+ view3d,
+ asset_drag->path,
+ idtype,
+ name,
+ FILE_ACTIVE_COLLECTION);
case FILE_ASSET_IMPORT_APPEND:
- return WM_file_append_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name);
+ return WM_file_append_datablock(bmain,
+ scene,
+ view_layer,
+ view3d,
+ asset_drag->path,
+ idtype,
+ name,
+ BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION);
+ case FILE_ASSET_IMPORT_APPEND_REUSE:
+ return WM_file_append_datablock(G_MAIN,
+ scene,
+ view_layer,
+ view3d,
+ asset_drag->path,
+ idtype,
+ name,
+ BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION |
+ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE);
}
BLI_assert_unreachable();
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 562a1f83473..364aab9482a 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -270,6 +270,12 @@ bool WM_event_is_mouse_drag(const wmEvent *event)
return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
}
+bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
+{
+ return WM_event_is_mouse_drag(event) ||
+ (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS));
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 3b92d858c71..f025fd43b49 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3046,7 +3046,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
/* Other drop custom types allowed. */
if (event->custom == EVT_DATA_DRAGDROP) {
ListBase *lb = (ListBase *)event->customdata;
- LISTBASE_FOREACH (wmDrag *, drag, lb) {
+ LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) {
if (drop->poll(C, drag, event)) {
/* Optionally copy drag information to operator properties. Don't call it if the
* operator fails anyway, it might do more than just set properties (e.g.
@@ -3057,7 +3057,8 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
/* Pass single matched wmDrag onto the operator. */
BLI_remlink(lb, drag);
- ListBase single_lb = {drag, drag};
+ ListBase single_lb = {0};
+ BLI_addtail(&single_lb, drag);
event->customdata = &single_lb;
int op_retval = wm_operator_call_internal(
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 126f8abc69a..24a3b58fc26 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -869,6 +869,28 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
duration_lib_override_recursive_resync_seconds);
}
+ if (bf_reports->count.linked_proxies != 0 ||
+ bf_reports->count.proxies_to_lib_overrides_success != 0 ||
+ bf_reports->count.proxies_to_lib_overrides_failures != 0) {
+ BKE_reportf(bf_reports->reports,
+ RPT_WARNING,
+ "Proxies are deprecated (%d proxies were automatically converted to library "
+ "overrides, %d proxies could not be converted and %d linked proxies were kept "
+ "untouched). If you need to keep proxies for the time being, please disable the "
+ "`Proxy to Override Auto Conversion` in Experimental user preferences",
+ bf_reports->count.proxies_to_lib_overrides_success,
+ bf_reports->count.proxies_to_lib_overrides_failures,
+ bf_reports->count.linked_proxies);
+ }
+
+ if (bf_reports->count.vse_strips_skipped != 0) {
+ BKE_reportf(bf_reports->reports,
+ RPT_ERROR,
+ "%d sequence strips were not read because they were in a channel larger than %d",
+ bf_reports->count.vse_strips_skipped,
+ MAXSEQ);
+ }
+
BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL);
bf_reports->resynced_lib_overrides_libraries = NULL;
}
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 09567eca17f..a73bea31669 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -127,10 +127,10 @@ static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNU
return OPERATOR_RUNNING_MODAL;
}
-static short wm_link_append_flag(wmOperator *op)
+static int wm_link_append_flag(wmOperator *op)
{
PropertyRNA *prop;
- short flag = 0;
+ int flag = 0;
if (RNA_boolean_get(op->ptr, "autoselect")) {
flag |= FILE_AUTOSELECT;
@@ -147,17 +147,20 @@ static short wm_link_append_flag(wmOperator *op)
}
else {
if (RNA_boolean_get(op->ptr, "use_recursive")) {
- flag |= FILE_APPEND_RECURSIVE;
+ flag |= BLO_LIBLINK_APPEND_RECURSIVE;
}
if (RNA_boolean_get(op->ptr, "set_fake")) {
- flag |= FILE_APPEND_SET_FAKEUSER;
+ flag |= BLO_LIBLINK_APPEND_SET_FAKEUSER;
+ }
+ if (RNA_boolean_get(op->ptr, "do_reuse_local_id")) {
+ flag |= BLO_LIBLINK_APPEND_LOCAL_ID_REUSE;
}
}
if (RNA_boolean_get(op->ptr, "instance_collections")) {
- flag |= FILE_COLLECTION_INSTANCE;
+ flag |= BLO_LIBLINK_COLLECTION_INSTANCE;
}
if (RNA_boolean_get(op->ptr, "instance_object_data")) {
- flag |= FILE_OBDATA_INSTANCE;
+ flag |= BLO_LIBLINK_OBDATA_INSTANCE;
}
return flag;
@@ -396,7 +399,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data,
LinkNode *itemlink;
Collection *active_collection = NULL;
- const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0;
+ const bool do_obdata = (lapp_data->flag & BLO_LIBLINK_OBDATA_INSTANCE) != 0;
const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0;
/* Do NOT make base active here! screws up GUI stuff,
@@ -472,7 +475,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data,
/* In case user requested instantiation of collections as empties, we do so for the one they
* explicitly selected (originally directly linked IDs). */
- if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 &&
+ if ((lapp_data->flag & BLO_LIBLINK_COLLECTION_INSTANCE) != 0 &&
(item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) {
/* BKE_object_add(...) messes with the selection. */
Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2);
@@ -573,6 +576,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data,
ob, object_set_selected, object_set_active, view_layer, v3d);
copy_v3_v3(ob->loc, scene->cursor.location);
+
+ id->tag &= ~LIB_TAG_DOIT;
}
}
@@ -592,6 +597,11 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data)
}
if (!BKE_idtype_idcode_is_linkable(GS(id->name))) {
+ /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items,
+ * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be
+ * processed, so we need to recursively deal with them here. */
+ BKE_library_foreach_ID_link(
+ cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP);
return IDWALK_RET_NOP;
}
@@ -626,8 +636,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
{
BLI_assert((lapp_data->flag & FILE_LINK) == 0);
- const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0;
- const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0;
+ const bool do_recursive = (lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0;
+ const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0;
+ const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0;
LinkNode *itemlink;
@@ -642,7 +653,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
BLI_ghash_insert(lapp_data->new_id_to_item, id, item);
}
- const bool do_reuse_existing_id = false;
lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain);
/* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect
@@ -656,8 +666,8 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
}
BLI_assert(item->customdata == NULL);
- /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose
- * objects etc.). */
+ /* In Append case linked IDs should never be marked as needing post-processing (instantiation
+ * of loose objects etc.). */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ?
@@ -674,10 +684,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
item->append_action = WM_APPEND_ACT_KEEP_LINKED;
}
- /* Only re-use existing local ID for indirectly linked data, the ID explicitely selected by the
- * user we always fully append. */
- else if (do_reuse_existing_id && existing_local_id != NULL &&
- (item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) {
+ else if (do_reuse_local_id && existing_local_id != NULL) {
CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name);
item->append_action = WM_APPEND_ACT_REUSE_LOCAL;
item->customdata = existing_local_id;
@@ -806,6 +813,27 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
BKE_libblock_relink_to_newid_new(bmain, id);
}
+ /* Remove linked IDs when a local existing data has been reused instead. */
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+
+ if (item->append_action != WM_APPEND_ACT_REUSE_LOCAL) {
+ continue;
+ }
+
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+ BLI_assert(ID_IS_LINKED(id));
+ BLI_assert(id->newid != NULL);
+
+ id->tag |= LIB_TAG_DOIT;
+ item->new_id = id->newid;
+ }
+ BKE_id_multi_tagged_delete(bmain);
+
/* Instantiate newly created (duplicated) IDs as needed. */
wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d);
@@ -1057,7 +1085,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- short flag = wm_link_append_flag(op);
+ int flag = wm_link_append_flag(op);
const bool do_append = (flag & FILE_LINK) == 0;
/* sanity checks for flag */
@@ -1066,12 +1094,10 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
RPT_WARNING,
"Scene '%s' is linked, instantiation of objects is disabled",
scene->id.name + 2);
- flag &= ~(FILE_COLLECTION_INSTANCE | FILE_OBDATA_INSTANCE);
+ flag &= ~(BLO_LIBLINK_COLLECTION_INSTANCE | BLO_LIBLINK_OBDATA_INSTANCE);
scene = NULL;
}
- /* We need to add nothing from #eBLOLibLinkFlags to flag here. */
-
/* from here down, no error returns */
if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) {
@@ -1201,14 +1227,25 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
prop = RNA_def_boolean(
ot->srna, "link", is_link, "Link", "Link the objects or data-blocks rather than appending");
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+
+ prop = RNA_def_boolean(
+ ot->srna,
+ "do_reuse_local_id",
+ false,
+ "Re-Use Local Data",
+ "Try to re-use previously matching appended data-blocks instead of appending a new copy");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+
prop = RNA_def_boolean(ot->srna, "autoselect", true, "Select", "Select new objects");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
prop = RNA_def_boolean(ot->srna,
"active_collection",
true,
"Active Collection",
"Put new objects on the active collection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
prop = RNA_def_boolean(
ot->srna,
"instance_collections",
@@ -1299,13 +1336,14 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain,
const char *filepath,
const short id_code,
const char *id_name,
- const bool do_append)
+ const int flag)
{
+ const bool do_append = (flag & FILE_LINK) == 0;
/* Tag everything so we can make local only the new datablock. */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
/* Define working data, with just the one item we want to link. */
- WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0);
+ WMLinkAppendData *lapp_data = wm_link_append_data_new(flag);
wm_link_append_data_library_add(lapp_data, filepath);
WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL);
@@ -1314,13 +1352,13 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain,
/* Link datablock. */
wm_link_do(lapp_data, NULL, bmain, scene, view_layer, v3d);
- /* Get linked datablock and free working data. */
- ID *id = item->new_id;
-
if (do_append) {
wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d);
}
+ /* Get linked datablock and free working data. */
+ ID *id = item->new_id;
+
wm_link_append_data_free(lapp_data);
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
@@ -1338,10 +1376,12 @@ ID *WM_file_link_datablock(Main *bmain,
View3D *v3d,
const char *filepath,
const short id_code,
- const char *id_name)
+ const char *id_name,
+ int flag)
{
+ flag |= FILE_LINK;
return wm_file_link_append_datablock_ex(
- bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag);
}
/*
@@ -1354,10 +1394,12 @@ ID *WM_file_append_datablock(Main *bmain,
View3D *v3d,
const char *filepath,
const short id_code,
- const char *id_name)
+ const char *id_name,
+ int flag)
{
+ BLI_assert((flag & FILE_LINK) == 0);
ID *id = wm_file_link_append_datablock_ex(
- bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag);
return id;
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 06d5c16108f..b087bd9d668 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -184,7 +184,8 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input");
+ const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
+ RNA_boolean_get(op->ptr, "wait_for_input");
if (wait_for_input) {
op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT);
@@ -438,7 +439,8 @@ static void gesture_circle_apply(bContext *C, wmOperator *op);
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
- const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input");
+ const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) &&
+ RNA_boolean_get(op->ptr, "wait_for_input");
op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE);
wmGesture *gesture = op->customdata;
@@ -1009,7 +1011,7 @@ int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *e
op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_STRAIGHTLINE);
- if (ISTWEAK(event->type)) {
+ if (WM_event_is_mouse_drag_or_press(event)) {
wmGesture *gesture = op->customdata;
gesture->is_active = true;
}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index a8d2e000108..ad04416613a 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -373,13 +373,6 @@ void WM_init(bContext *C, int argc, const char **argv)
BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib));
-#ifdef WITH_COMPOSITOR
- if (1) {
- extern void *COM_linker_hack;
- COM_linker_hack = COM_execute;
- }
-#endif
-
wm_homefile_read_post(C, params_file_read_post);
}
diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c
index 29e12a96fe1..5604fb4c58d 100644
--- a/source/creator/creator_signals.c
+++ b/source/creator/creator_signals.c
@@ -79,7 +79,7 @@ static void sig_handle_fpe(int UNUSED(sig))
}
# endif
-/* handling ctrl-c event in console */
+/* Handling `Ctrl-C` event in the console. */
# if !defined(WITH_HEADLESS)
static void sig_handle_blender_esc(int sig)
{
diff --git a/source/tools b/source/tools
-Subproject 723b24841df1ed8478949bca20c73878fab00dc
+Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8
diff --git a/tests/performance/api/device.py b/tests/performance/api/device.py
index b61ae42be36..1e930a12352 100644
--- a/tests/performance/api/device.py
+++ b/tests/performance/api/device.py
@@ -11,7 +11,7 @@ def get_cpu_name() -> str:
return platform.processor()
elif platform.system() == "Darwin":
cmd = ['/usr/sbin/sysctl', "-n", "machdep.cpu.brand_string"]
- return subprocess.check_output(cmd).strip().decode('utf-8')
+ return subprocess.check_output(cmd).strip().decode('utf-8', 'ignore')
else:
with open('/proc/cpuinfo') as f:
for line in f:
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 75f00c3c5cc..2b31b6362e9 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -749,10 +749,26 @@ set(geo_node_tests
points
utilities
vector
- volume
-
)
+if(WITH_GMP)
+ list(APPEND geo_node_tests mesh/boolean)
+else()
+ MESSAGE(STATUS "Disabling mesh/boolean tests because WITH_GMP is off.")
+endif()
+
+if(WITH_OPENVDB)
+ list(APPEND geo_node_tests volume)
+else()
+ MESSAGE(STATUS "Disabling volume tests because WITH_OPENVDB is off.")
+endif()
+
+if(WITH_OPENSUBDIV)
+ list(APPEND geo_node_tests mesh/subdivision_tests)
+else()
+ MESSAGE(STATUS "Disabling mesh/subdivision_tests because WITH_OPENSUBDIV is off.")
+endif()
+
foreach(geo_node_test ${geo_node_tests})
if(EXISTS "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/")
file(GLOB files "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/*.blend")
diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py
index 992bf6b89d9..4545e0b846a 100644
--- a/tests/python/bl_blendfile_liblink.py
+++ b/tests/python/bl_blendfile_liblink.py
@@ -10,7 +10,7 @@ from bl_blendfile_utils import TestHelper
class TestBlendLibLinkHelper(TestHelper):
-
+
def __init__(self, args):
self.args = args
@@ -165,7 +165,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Mesh")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=False, set_fake=False, use_recursive=False)
+ instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False)
assert(len(bpy.data.meshes) == 1)
assert(bpy.data.meshes[0].library is None)
@@ -179,7 +179,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Mesh")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=True, set_fake=False, use_recursive=False)
+ instance_object_data=True, set_fake=False, use_recursive=False, do_reuse_local_id=False)
assert(len(bpy.data.meshes) == 1)
assert(bpy.data.meshes[0].library is None)
@@ -194,7 +194,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Mesh")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=False, set_fake=True, use_recursive=False)
+ instance_object_data=False, set_fake=True, use_recursive=False, do_reuse_local_id=False)
assert(len(bpy.data.meshes) == 1)
assert(bpy.data.meshes[0].library is None)
@@ -208,7 +208,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Object")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=False, set_fake=False, use_recursive=False)
+ instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False)
assert(len(bpy.data.meshes) == 1)
# This one fails currently, for unclear reasons.
@@ -224,7 +224,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Object")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=False, set_fake=False, use_recursive=True)
+ instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False)
assert(len(bpy.data.meshes) == 1)
assert(bpy.data.meshes[0].library is None)
@@ -239,7 +239,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
link_dir = os.path.join(output_lib_path, "Collection")
bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
- instance_object_data=False, set_fake=False, use_recursive=True)
+ instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False)
assert(bpy.data.meshes[0].library is None)
assert(bpy.data.meshes[0].users == 1)
@@ -251,9 +251,73 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper):
assert(bpy.data.collections[0].users == 1)
+class TestBlendLibAppendReuseID(TestBlendLibLinkHelper):
+
+ def __init__(self, args):
+ self.args = args
+
+ def test_append(self):
+ output_dir = self.args.output_dir
+ output_lib_path = self.init_lib_data_basic()
+
+ # Append of a single Object, and then append it again.
+ self.reset_blender()
+
+ link_dir = os.path.join(output_lib_path, "Object")
+ bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
+ instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False)
+
+ assert(len(bpy.data.meshes) == 1)
+ assert(bpy.data.meshes[0].library is None)
+ assert(bpy.data.meshes[0].use_fake_user is False)
+ assert(bpy.data.meshes[0].users == 1)
+ assert(bpy.data.meshes[0].library_weak_reference is not None)
+ assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path)
+ assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh")
+ assert(len(bpy.data.objects) == 1)
+ for ob in bpy.data.objects:
+ assert(ob.library is None)
+ assert(ob.library_weak_reference is None)
+ assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here
+
+ bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
+ instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=True)
+
+ assert(len(bpy.data.meshes) == 1)
+ assert(bpy.data.meshes[0].library is None)
+ assert(bpy.data.meshes[0].use_fake_user is False)
+ assert(bpy.data.meshes[0].users == 2)
+ assert(bpy.data.meshes[0].library_weak_reference is not None)
+ assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path)
+ assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh")
+ assert(len(bpy.data.objects) == 2)
+ for ob in bpy.data.objects:
+ assert(ob.library is None)
+ assert(ob.library_weak_reference is None)
+ assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here
+
+ bpy.ops.wm.append(directory=link_dir, filename="LibMesh",
+ instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False)
+
+ assert(len(bpy.data.meshes) == 2)
+ assert(bpy.data.meshes[0].library_weak_reference is None)
+ assert(bpy.data.meshes[1].library is None)
+ assert(bpy.data.meshes[1].use_fake_user is False)
+ assert(bpy.data.meshes[1].users == 1)
+ assert(bpy.data.meshes[1].library_weak_reference is not None)
+ assert(bpy.data.meshes[1].library_weak_reference.filepath == output_lib_path)
+ assert(bpy.data.meshes[1].library_weak_reference.id_name == "MELibMesh")
+ assert(len(bpy.data.objects) == 3)
+ for ob in bpy.data.objects:
+ assert(ob.library is None)
+ assert(ob.library_weak_reference is None)
+ assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here
+
+
TESTS = (
TestBlendLibLinkSaveLoadBasic,
TestBlendLibAppendBasic,
+ TestBlendLibAppendReuseID,
)
diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py
index 560f8e33585..90f16dc80fb 100755
--- a/tests/python/modules/render_report.py
+++ b/tests/python/modules/render_report.py
@@ -410,7 +410,7 @@ class Report:
failed = False
except subprocess.CalledProcessError as e:
if self.verbose:
- print_message(e.output.decode("utf-8"))
+ print_message(e.output.decode("utf-8", 'ignore'))
failed = e.returncode != 1
else:
if not self.update:
@@ -437,7 +437,7 @@ class Report:
subprocess.check_output(command)
except subprocess.CalledProcessError as e:
if self.verbose:
- print_message(e.output.decode("utf-8"))
+ print_message(e.output.decode("utf-8", 'ignore'))
return not failed
@@ -488,7 +488,7 @@ class Report:
if verbose:
print(" ".join(command))
if (verbose or crash) and output:
- print(output.decode("utf-8"))
+ print(output.decode("utf-8", 'ignore'))
# Detect missing filepaths and consider those errors
for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths):