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:
-rw-r--r--.clang-format1
-rw-r--r--build_files/build_environment/CMakeLists.txt2
-rw-r--r--build_files/build_environment/cmake/blosc.cmake2
-rw-r--r--build_files/build_environment/cmake/boost.cmake9
-rw-r--r--build_files/build_environment/cmake/embree.cmake2
-rw-r--r--build_files/build_environment/cmake/gmp.cmake26
-rw-r--r--build_files/build_environment/cmake/harvest.cmake6
-rw-r--r--build_files/build_environment/cmake/llvm.cmake2
-rw-r--r--build_files/build_environment/cmake/opencolorio.cmake2
-rw-r--r--build_files/build_environment/cmake/options.cmake8
-rw-r--r--build_files/build_environment/cmake/png.cmake4
-rw-r--r--build_files/build_environment/cmake/sse2neon.cmake22
-rw-r--r--build_files/build_environment/cmake/ssl.cmake4
-rw-r--r--build_files/build_environment/cmake/ssl.conf5
-rw-r--r--build_files/build_environment/cmake/tbb.cmake4
-rw-r--r--build_files/build_environment/cmake/versions.cmake23
-rw-r--r--build_files/build_environment/cmake/x264.cmake18
-rwxr-xr-xbuild_files/build_environment/install_deps.sh25
-rw-r--r--build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake2
-rw-r--r--build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake2
-rw-r--r--build_files/build_environment/patches/cmakelists_tbb.txt636
-rw-r--r--build_files/build_environment/patches/tbb.diff13
-rw-r--r--build_files/build_environment/patches/theora.diff4
-rw-r--r--build_files/build_environment/patches/usd.diff144
-rw-r--r--build_files/buildbot/buildbot_utils.py4
-rw-r--r--build_files/cmake/Modules/FindEmbree.cmake2
-rw-r--r--build_files/cmake/buildinfo.cmake2
-rw-r--r--build_files/cmake/macros.cmake4
-rw-r--r--doc/doxygen/Doxyfile2
-rw-r--r--doc/python_api/examples/bpy.types.RenderEngine.py72
-rw-r--r--doc/python_api/examples/gpu.4.py6
-rw-r--r--doc/python_api/examples/gpu.6.py11
-rw-r--r--doc/python_api/examples/gpu.7.py10
-rw-r--r--doc/python_api/examples/gpu.8.py13
-rw-r--r--doc/python_api/examples/gpu.9.py5
-rw-r--r--doc/python_api/examples/mathutils.Matrix.LocRotScale.py5
-rw-r--r--doc/python_api/examples/mathutils.Matrix.py6
-rw-r--r--doc/python_api/rst/info_gotcha.rst3
-rw-r--r--doc/python_api/sphinx_doc_gen.py5
-rw-r--r--intern/clog/clog.c4
-rw-r--r--intern/cycles/app/CMakeLists.txt10
-rw-r--r--intern/cycles/blender/addon/engine.py6
-rw-r--r--intern/cycles/blender/blender_object.cpp6
-rw-r--r--intern/cycles/blender/blender_sync.cpp21
-rw-r--r--intern/cycles/blender/blender_sync.h6
-rw-r--r--intern/cycles/device/device.h2
-rw-r--r--intern/cycles/device/device_optix.cpp38
-rw-r--r--intern/cycles/graph/node.cpp78
-rw-r--r--intern/cycles/graph/node.h36
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/bvh/bvh.h98
-rw-r--r--intern/cycles/kernel/bvh/bvh_shadow_all.h19
-rw-r--r--intern/cycles/kernel/bvh/bvh_util.h162
-rw-r--r--intern/cycles/kernel/kernel_path.h17
-rw-r--r--intern/cycles/kernel/kernel_types.h2
-rw-r--r--intern/cycles/kernel/shaders/node_noise_texture.osl2
-rw-r--r--intern/cycles/kernel/svm/svm_noisetex.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/alembic.cpp970
-rw-r--r--intern/cycles/render/alembic.h53
-rw-r--r--intern/cycles/render/alembic_read.cpp1007
-rw-r--r--intern/cycles/render/alembic_read.h134
-rw-r--r--intern/cycles/render/background.cpp1
-rw-r--r--intern/cycles/render/geometry.cpp1
-rw-r--r--intern/cycles/render/geometry.h2
-rw-r--r--intern/cycles/render/light.cpp1
-rw-r--r--intern/cycles/render/osl.cpp14
-rw-r--r--intern/cycles/render/osl.h11
-rw-r--r--intern/cycles/render/scene.cpp58
-rw-r--r--intern/cycles/render/shader.cpp62
-rw-r--r--intern/cycles/render/shader.h11
-rw-r--r--intern/cycles/render/svm.cpp10
-rw-r--r--intern/cycles/render/svm.h9
-rw-r--r--intern/cycles/util/util_math_fast.h2
-rw-r--r--intern/cycles/util/util_simd.h8
-rw-r--r--intern/cycles/util/util_sseb.h12
-rw-r--r--intern/cycles/util/util_ssef.h4
-rw-r--r--intern/cycles/util/util_ssei.h13
-rw-r--r--intern/cycles/util/util_texture.h12
-rw-r--r--intern/ffmpeg/ffmpeg_compat.h529
-rw-r--r--intern/ghost/CMakeLists.txt3
-rw-r--r--intern/ghost/GHOST_C-api.h107
-rw-r--r--intern/ghost/GHOST_IXrContext.h6
-rw-r--r--intern/ghost/GHOST_Types.h60
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp148
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp3
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_Util.h45
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp34
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_XrAction.cpp477
-rw-r--r--intern/ghost/intern/GHOST_XrAction.h145
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp20
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h5
-rw-r--r--intern/ghost/intern/GHOST_XrException.h5
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp365
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h37
-rw-r--r--intern/ghost/intern/GHOST_Xr_intern.h24
-rw-r--r--intern/ghost/test/gears/GHOST_C-Test.c2
-rw-r--r--intern/ghost/test/multitest/MultiTest.c2
-rw-r--r--intern/itasc/kdl/frames.hpp2
-rw-r--r--intern/itasc/kdl/utilities/error.h2
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/splash.pngbin858406 -> 737984 bytes
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/freestyle/styles/ignore_small_occlusions.py2
-rw-r--r--release/scripts/modules/addon_utils.py16
-rw-r--r--release/scripts/modules/bl_ui_utils/bug_report_url.py8
-rw-r--r--release/scripts/modules/bpy/path.py2
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py2
-rw-r--r--release/scripts/modules/bpy_types.py6
-rw-r--r--release/scripts/modules/gpu_extras/presets.py21
-rw-r--r--release/scripts/modules/keyingsets_utils.py34
-rw-r--r--release/scripts/modules/rna_keymap_ui.py2
-rw-r--r--release/scripts/modules/rna_manual_reference.py20
-rw-r--r--release/scripts/modules/rna_prop_ui.py2
-rw-r--r--release/scripts/modules/sys_info.py52
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py35
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py3
-rw-r--r--release/scripts/startup/bl_operators/clip.py61
-rw-r--r--release/scripts/startup/bl_operators/constraint.py15
-rw-r--r--release/scripts/startup/bl_operators/node.py8
-rw-r--r--release/scripts/startup/bl_operators/userpref.py11
-rw-r--r--release/scripts/startup/bl_operators/view3d.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_bone.py7
-rw-r--r--release/scripts/startup/bl_ui/properties_data_gpencil.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py12
-rw-r--r--release/scripts/startup/bl_ui/properties_material_gpencil.py14
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py20
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_info.py13
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py2
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py10
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py2
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py29
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py3
-rw-r--r--release/scripts/startup/keyingsets_builtins.py125
-rw-r--r--release/scripts/startup/nodeitems_builtins.py13
-rw-r--r--release/scripts/templates_py/gizmo_custom_geometry.py2
-rw-r--r--release/scripts/templates_py/operator_modal_draw.py9
-rw-r--r--source/blender/blenkernel/BKE_action.h4
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh411
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh82
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h6
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h1
-rw-r--r--source/blender/blenkernel/BKE_context.h19
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h14
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh353
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h7
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h6
-rw-r--r--source/blender/blenkernel/BKE_material.h9
-rw-r--r--source/blender/blenkernel/BKE_mesh.h6
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh55
-rw-r--r--source/blender/blenkernel/BKE_modifier.h4
-rw-r--r--source/blender/blenkernel/BKE_nla.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h13
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh8
-rw-r--r--source/blender/blenkernel/BKE_object.h1
-rw-r--r--source/blender/blenkernel/BKE_paint.h6
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh153
-rw-r--r--source/blender/blenkernel/BKE_softbody.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh517
-rw-r--r--source/blender/blenkernel/CMakeLists.txt17
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc8
-rw-r--r--source/blender/blenkernel/intern/action.c18
-rw-r--r--source/blender/blenkernel/intern/anim_data.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c528
-rw-r--r--source/blender/blenkernel/intern/appdir.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c7
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc846
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh230
-rw-r--r--source/blender/blenkernel/intern/blender.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c3
-rw-r--r--source/blender/blenkernel/intern/brush.c1
-rw-r--r--source/blender/blenkernel/intern/constraint.c2
-rw-r--r--source/blender/blenkernel/intern/context.c51
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc272
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c3
-rw-r--r--source/blender/blenkernel/intern/displist.cc (renamed from source/blender/blenkernel/intern/displist.c)226
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc1019
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc100
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc398
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc59
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc52
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc160
-rw-r--r--source/blender/blenkernel/intern/gpencil.c20
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c4
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c152
-rw-r--r--source/blender/blenkernel/intern/lib_id.c69
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc112
-rw-r--r--source/blender/blenkernel/intern/lib_override.c38
-rw-r--r--source/blender/blenkernel/intern/lib_query.c2
-rw-r--r--source/blender/blenkernel/intern/material.c130
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c9
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c62
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc158
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/nla.c50
-rw-r--r--source/blender/blenkernel/intern/node.cc97
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc2
-rw-r--r--source/blender/blenkernel/intern/object.c21
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc36
-rw-r--r--source/blender/blenkernel/intern/particle.c2
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h28
-rw-r--r--source/blender/blenkernel/intern/pointcache.c2
-rw-r--r--source/blender/blenkernel/intern/softbody.c33
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc343
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc584
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc434
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc117
-rw-r--r--source/blender/blenkernel/intern/volume.cc64
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c348
-rw-r--r--source/blender/blenkernel/nla_private.h11
-rw-r--r--source/blender/blenlib/BLI_compiler_attrs.h7
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh73
-rw-r--r--source/blender/blenlib/BLI_fileops.h2
-rw-r--r--source/blender/blenlib/BLI_float3.hh7
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh44
-rw-r--r--source/blender/blenlib/BLI_hash.hh22
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh15
-rw-r--r--source/blender/blenlib/BLI_map.hh195
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh12
-rw-r--r--source/blender/blenlib/BLI_math_color.h5
-rw-r--r--source/blender/blenlib/BLI_math_geom.h10
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h2
-rw-r--r--source/blender/blenlib/BLI_math_solvers.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h8
-rw-r--r--source/blender/blenlib/BLI_span.hh14
-rw-r--r--source/blender/blenlib/BLI_stack.hh5
-rw-r--r--source/blender/blenlib/BLI_vector.hh46
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh77
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh427
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/BLI_dial_2d.c2
-rw-r--r--source/blender/blenlib/intern/math_color.c72
-rw-r--r--source/blender/blenlib/intern/math_geom.c4
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc4
-rw-r--r--source/blender/blenlib/intern/storage.c3
-rw-r--r--source/blender/blenlib/intern/uvproject.c2
-rw-r--r--source/blender/blenlib/tests/BLI_linear_allocator_test.cc13
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc49
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc39
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_virtual_array_test.cc133
-rw-r--r--source/blender/blenloader/CMakeLists.txt1
-rw-r--r--source/blender/blenloader/intern/readfile.c5
-rw-r--r--source/blender/blenloader/intern/readfile.h2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c135
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c22
-rw-r--r--source/blender/bmesh/tools/bmesh_bisect_plane.c109
-rw-r--r--source/blender/compositor/COM_defines.h1
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.cc2
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc89
-rw-r--r--source/blender/compositor/intern/COM_Debug.h95
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc85
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h152
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc10
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc112
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h5
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc12
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc15
-rw-r--r--source/blender/draw/CMakeLists.txt1
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c8
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c3
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c10
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl4
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c22
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_particle.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c2
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c9
-rw-r--r--source/blender/draw/intern/draw_manager.c8
-rw-r--r--source/blender/draw/tests/draw_testing.cc18
-rw-r--r--source/blender/draw/tests/draw_testing.hh13
-rw-r--r--source/blender/draw/tests/shaders_test.cc12
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c2
-rw-r--r--source/blender/editors/armature/CMakeLists.txt1
-rw-r--r--source/blender/editors/armature/armature_add.c14
-rw-r--r--source/blender/editors/armature/pose_slide.c454
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c94
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c101
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c258
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c162
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h2
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c32
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c2
-rw-r--r--source/blender/editors/include/ED_fileselect.h2
-rw-r--r--source/blender/editors/include/ED_gizmo_library.h5
-rw-r--r--source/blender/editors/include/ED_gpencil.h1
-rw-r--r--source/blender/editors/include/ED_keyframing.h1
-rw-r--r--source/blender/editors/include/ED_screen.h8
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h17
-rw-r--r--source/blender/editors/include/ED_view3d.h4
-rw-r--r--source/blender/editors/include/UI_interface.h27
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/interface/interface.c8
-rw-r--r--source/blender/editors/interface/interface_handlers.c290
-rw-r--r--source/blender/editors/interface/interface_intern.h100
-rw-r--r--source/blender/editors/interface/interface_layout.c4
-rw-r--r--source/blender/editors/interface/interface_ops.c214
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c13
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c8
-rw-r--r--source/blender/editors/interface/interface_templates.c2
-rw-r--r--source/blender/editors/interface/interface_widgets.c35
-rw-r--r--source/blender/editors/interface/view2d_ops.c2
-rw-r--r--source/blender/editors/io/io_alembic.c31
-rw-r--r--source/blender/editors/io/io_collada.c16
-rw-r--r--source/blender/editors/io/io_gpencil_export.c18
-rw-r--r--source/blender/editors/io/io_gpencil_import.c8
-rw-r--r--source/blender/editors/mask/mask_shapekey.c4
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c49
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c34
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c31
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_add.c68
-rw-r--r--source/blender/editors/object/object_edit.c19
-rw-r--r--source/blender/editors/object/object_intern.h4
-rw-r--r--source/blender/editors/object/object_modes.c71
-rw-r--r--source/blender/editors/object/object_modifier.c13
-rw-r--r--source/blender/editors/object/object_relations.c2
-rw-r--r--source/blender/editors/render/render_preview.c35
-rw-r--r--source/blender/editors/screen/area.c25
-rw-r--r--source/blender/editors/screen/screen_draw.c313
-rw-r--r--source/blender/editors/screen/screen_edit.c245
-rw-r--r--source/blender/editors/screen/screen_geometry.c28
-rw-r--r--source/blender/editors/screen/screen_intern.h47
-rw-r--r--source/blender/editors/screen/screen_ops.c352
-rw-r--r--source/blender/editors/screen/screendump.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c24
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c6
-rw-r--r--source/blender/editors/sound/sound_ops.c3
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c3
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c50
-rw-r--r--source/blender/editors/space_file/file_draw.c9
-rw-r--r--source/blender/editors/space_file/file_intern.h1
-rw-r--r--source/blender/editors/space_file/file_ops.c116
-rw-r--r--source/blender/editors/space_file/filelist.c88
-rw-r--r--source/blender/editors/space_file/space_file.c10
-rw-r--r--source/blender/editors/space_image/image_ops.c66
-rw-r--r--source/blender/editors/space_info/info_ops.c90
-rw-r--r--source/blender/editors/space_info/info_stats.c3
-rw-r--r--source/blender/editors/space_info/space_info.c9
-rw-r--r--source/blender/editors/space_nla/nla_draw.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c18
-rw-r--r--source/blender/editors/space_node/node_edit.c4
-rw-r--r--source/blender/editors/space_node/node_relationships.c69
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c4
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c8
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c109
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c89
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc76
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c14
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c2
-rw-r--r--source/blender/editors/transform/transform.c4
-rw-r--r--source/blender/editors/transform/transform.h50
-rw-r--r--source/blender/editors/transform/transform_convert.c177
-rw-r--r--source/blender/editors/transform/transform_convert.h7
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c3
-rw-r--r--source/blender/editors/transform/transform_convert_cursor.c26
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c8
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c20
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c1200
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_skin.c30
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c2
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c4
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/editors/transform/transform_mode.c4
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c1
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_mirror.c3
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c1
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c1
-rw-r--r--source/blender/editors/transform/transform_orientations.c2
-rw-r--r--source/blender/editors/transform/transform_snap.c6
-rw-r--r--source/blender/editors/transform/transform_snap_object.c667
-rw-r--r--source/blender/editors/util/ed_transverts.c2
-rw-r--r--source/blender/functions/FN_cpp_type.hh2
-rw-r--r--source/blender/functions/FN_generic_pointer.hh10
-rw-r--r--source/blender/functions/FN_generic_span.hh20
-rw-r--r--source/blender/functions/FN_generic_value_map.hh5
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh6
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh729
-rw-r--r--source/blender/functions/FN_generic_virtual_vector_array.hh16
-rw-r--r--source/blender/functions/FN_multi_function_params.hh17
-rw-r--r--source/blender/functions/intern/generic_vector_array.cc6
-rw-r--r--source/blender/functions/intern/generic_virtual_array.cc309
-rw-r--r--source/blender/functions/intern/generic_virtual_vector_array.cc26
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc28
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc2
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt1
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c14
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c223
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c66
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c73
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c59
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h6
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c6
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c40
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c10
-rw-r--r--source/blender/gpu/CMakeLists.txt1
-rw-r--r--source/blender/gpu/GPU_capabilities.h9
-rw-r--r--source/blender/gpu/GPU_common.h1
-rw-r--r--source/blender/gpu/GPU_framebuffer.h5
-rw-r--r--source/blender/gpu/GPU_platform.h3
-rw-r--r--source/blender/gpu/GPU_texture.h6
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc40
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh9
-rw-r--r--source/blender/gpu/intern/gpu_context.cc2
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc4
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc19
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh14
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c7
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc2
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc73
-rw-r--r--source/blender/gpu/intern/gpu_platform_private.hh16
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc2
-rw-r--r--source/blender/gpu/intern/gpu_state.cc6
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc19
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh7
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc92
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc2
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh2
-rw-r--r--source/blender/gpu/opengl/gl_immediate.hh2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl15
-rw-r--r--source/blender/gpu/tests/gpu_testing.cc4
-rw-r--r--source/blender/imbuf/CMakeLists.txt6
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h6
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h2
-rw-r--r--source/blender/imbuf/intern/anim_movie.c165
-rw-r--r--source/blender/imbuf/intern/colormanagement.c2
-rw-r--r--source/blender/imbuf/intern/colormanagement_inline.c4
-rw-r--r--source/blender/imbuf/intern/indexer.c241
-rw-r--r--source/blender/imbuf/intern/tiff.c10
-rw-r--r--source/blender/imbuf/intern/util.c18
-rw-r--r--source/blender/io/alembic/ABC_alembic.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc13
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc72
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h18
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc5
-rw-r--r--source/blender/io/collada/Materials.cpp73
-rw-r--r--source/blender/io/collada/Materials.h1
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc2
-rw-r--r--source/blender/io/gpencil/nanosvg/nanosvg.h26
-rw-r--r--source/blender/makesdna/DNA_action_types.h13
-rw-r--r--source/blender/makesdna/DNA_boid_types.h5
-rw-r--r--source/blender/makesdna/DNA_brush_types.h3
-rw-r--r--source/blender/makesdna/DNA_effect_types.h9
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h14
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h61
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h4
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h5
-rw-r--r--source/blender/makesdna/DNA_node_types.h73
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h9
-rw-r--r--source/blender/makesdna/DNA_particle_types.h10
-rw-r--r--source/blender/makesdna/DNA_pointcache_types.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h6
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h6
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesdna/DNA_texture_types.h3
-rw-r--r--source/blender/makesdna/DNA_xr_types.h15
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_define.h1
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/RNA_types.h26
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/makesrna.c29
-rw-r--r--source/blender/makesrna/intern/rna_ID.c34
-rw-r--r--source/blender/makesrna/intern/rna_access.c18
-rw-r--r--source/blender/makesrna/intern/rna_armature.c2
-rw-r--r--source/blender/makesrna/intern/rna_armature_api.c20
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c16
-rw-r--r--source/blender/makesrna/intern/rna_brush.c9
-rw-r--r--source/blender/makesrna/intern/rna_curve_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c2
-rw-r--r--source/blender/makesrna/intern/rna_define.c22
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c53
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c4
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c188
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_lattice_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_main.c4
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_meta_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c4
-rw-r--r--source/blender/makesrna/intern/rna_movieclip.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c367
-rw-r--r--source/blender/makesrna/intern/rna_object.c104
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_particle.c12
-rw-r--r--source/blender/makesrna/intern/rna_pose.c21
-rw-r--r--source/blender/makesrna/intern/rna_pose_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c3
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c41
-rw-r--r--source/blender/makesrna/intern/rna_space.c14
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c2
-rw-r--r--source/blender/modifiers/CMakeLists.txt16
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c1
-rw-r--r--source/blender/modifiers/intern/MOD_array.c1
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c1
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_build.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c1
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c1
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c1
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c1
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c1
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c1
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c1
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc19
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c1
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c1
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc515
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc1567
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh52
-rw-r--r--source/blender/modifiers/intern/MOD_none.c1
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c1
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c1
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c1
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c190
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c1
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c1
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c1
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c1
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c1
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc20
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc1
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt9
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh13
-rw-r--r--source/blender/nodes/NOD_geometry.h9
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh236
-rw-r--r--source/blender/nodes/NOD_static_types.h81
-rw-r--r--source/blender/nodes/NOD_type_conversions.hh4
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c7
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc188
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc54
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc92
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc231
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc140
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc166
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc91
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc61
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc597
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc247
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc238
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc312
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc51
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc124
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc175
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc204
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc97
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc177
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc25
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc98
-rw-r--r--source/blender/nodes/intern/node_socket.cc113
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc10
-rw-r--r--source/blender/nodes/intern/node_util.c16
-rw-r--r--source/blender/nodes/intern/type_conversions.cc112
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c7
-rw-r--r--source/blender/nodes/shader/node_shader_util.c14
-rw-r--r--source/blender/nodes/shader/node_shader_util.h6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c19
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wavelength.c28
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c7
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c14
-rw-r--r--source/blender/python/generic/idprop_py_api.c578
-rw-r--r--source/blender/python/generic/idprop_py_api.h46
-rw-r--r--source/blender/python/gpu/CMakeLists.txt4
-rw-r--r--source/blender/python/gpu/gpu_py_api.c8
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c176
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c148
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.h23
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c158
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.h7
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c18
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c81
-rw-r--r--source/blender/python/gpu/gpu_py_platform.h23
-rw-r--r--source/blender/python/gpu/gpu_py_state.c16
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c32
-rw-r--r--source/blender/python/gpu/gpu_py_texture.h3
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c1
-rw-r--r--source/blender/python/intern/bpy_interface.c13
-rw-r--r--source/blender/python/intern/bpy_operator.c8
-rw-r--r--source/blender/python/intern/bpy_rna.c30
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c150
-rw-r--r--source/blender/python/intern/bpy_rna_operator.h31
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.c18
-rw-r--r--source/blender/python/mathutils/mathutils.c12
-rw-r--r--source/blender/python/mathutils/mathutils_Euler.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c146
-rw-r--r--source/blender/python/mathutils/mathutils_interpolate.c15
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c2
-rw-r--r--source/blender/render/RE_engine.h2
-rw-r--r--source/blender/render/intern/engine.c2
-rw-r--r--source/blender/render/intern/zbuf.c2
-rw-r--r--source/blender/sequencer/SEQ_iterator.h75
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h2
-rw-r--r--source/blender/sequencer/SEQ_utils.h10
-rw-r--r--source/blender/sequencer/intern/effects.c5
-rw-r--r--source/blender/sequencer/intern/iterator.c300
-rw-r--r--source/blender/sequencer/intern/render.c185
-rw-r--r--source/blender/sequencer/intern/render.h4
-rw-r--r--source/blender/sequencer/intern/sequencer.c4
-rw-r--r--source/blender/sequencer/intern/strip_add.c2
-rw-r--r--source/blender/sequencer/intern/strip_edit.c114
-rw-r--r--source/blender/sequencer/intern/strip_time.c26
-rw-r--r--source/blender/sequencer/intern/utils.c91
-rw-r--r--source/blender/shader_fx/intern/FX_shader_glow.c7
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h76
-rw-r--r--source/blender/windowmanager/WM_types.h29
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c15
-rw-r--r--source/blender/windowmanager/intern/wm_files.c71
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c74
-rw-r--r--source/blender/windowmanager/intern/wm_window.c25
-rw-r--r--source/blender/windowmanager/wm_files.h3
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actions.c480
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h68
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c148
-rw-r--r--source/creator/CMakeLists.txt11
-rw-r--r--source/creator/creator.c1
-rw-r--r--source/creator/creator_args.c8
-rw-r--r--source/creator/creator_intern.h2
-rw-r--r--tests/python/bl_blendfile_library_overrides.py2
-rw-r--r--tests/python/bl_pyapi_idprop.py46
-rw-r--r--tests/python/bl_pyapi_mathutils.py23
-rw-r--r--tests/python/compositor_render_tests.py1
-rw-r--r--tests/python/operators.py32
740 files changed, 29002 insertions, 10486 deletions
diff --git a/.clang-format b/.clang-format
index b61ce6018d3..9cc3cdeaaf8 100644
--- a/.clang-format
+++ b/.clang-format
@@ -255,6 +255,7 @@ ForEachMacros:
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN
- SEQ_ALL_BEGIN
+ - SEQ_ITERATOR_FOREACH
- SURFACE_QUAD_ITER_BEGIN
- foreach
- ED_screen_areas_iter
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index a3d694b4bc3..fb79eee62be 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -113,7 +113,7 @@ include(cmake/expat.cmake)
include(cmake/yamlcpp.cmake)
include(cmake/opencolorio.cmake)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
include(cmake/sse2neon.cmake)
endif()
diff --git a/build_files/build_environment/cmake/blosc.cmake b/build_files/build_environment/cmake/blosc.cmake
index 89df67b7d58..ce88f081722 100644
--- a/build_files/build_environment/cmake/blosc.cmake
+++ b/build_files/build_environment/cmake/blosc.cmake
@@ -29,7 +29,7 @@ set(BLOSC_EXTRA_ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
-# Prevent blosc from including it's own local copy of zlib in the object file
+# Prevent blosc from including its own local copy of zlib in the object file
# and cause linker errors with everybody else.
set(BLOSC_EXTRA_ARGS ${BLOSC_EXTRA_ARGS}
-DPREFER_EXTERNAL_ZLIB=ON
diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake
index 8b36af7dc41..5170a3a123e 100644
--- a/build_files/build_environment/cmake/boost.cmake
+++ b/build_files/build_environment/cmake/boost.cmake
@@ -18,6 +18,12 @@
set(BOOST_ADDRESS_MODEL 64)
+if(BLENDER_PLATFORM_ARM)
+ set(BOOST_ARCHITECTURE arm)
+else()
+ set(BOOST_ARCHITECTURE x86)
+endif()
+
if(WIN32)
set(BOOST_TOOLSET toolset=msvc-14.1)
set(BOOST_COMPILER_STRING -vc141)
@@ -29,7 +35,6 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-${BOOST_VERSION_NODOTS_SHORT}/ ${HARVEST_TARGET}/boost/include/)
endif()
-
elseif(APPLE)
set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh)
set(BOOST_BUILD_COMMAND ./b2)
@@ -93,7 +98,7 @@ ExternalProject_Add(external_boost
UPDATE_COMMAND ""
PATCH_COMMAND ${BOOST_PATCH_COMMAND}
CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND}
- BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=x86 address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
+ BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=${BOOST_ARCHITECTURE} address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
BUILD_IN_SOURCE 1
INSTALL_COMMAND "${BOOST_HARVEST_CMD}"
)
diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake
index 4830630def0..cd693d766dc 100644
--- a/build_files/build_environment/cmake/embree.cmake
+++ b/build_files/build_environment/cmake/embree.cmake
@@ -47,7 +47,7 @@ else()
set(EMBREE_BUILD_DIR)
endif()
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
ExternalProject_Add(external_embree
GIT_REPOSITORY ${EMBREE_ARM_GIT}
GIT_TAG "blender-arm"
diff --git a/build_files/build_environment/cmake/gmp.cmake b/build_files/build_environment/cmake/gmp.cmake
index 323630a63aa..6ca81678a32 100644
--- a/build_files/build_environment/cmake/gmp.cmake
+++ b/build_files/build_environment/cmake/gmp.cmake
@@ -25,19 +25,12 @@ else()
set(GMP_OPTIONS --enable-static --disable-shared )
endif()
-if(APPLE)
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- set(GMP_OPTIONS
- ${GMP_OPTIONS}
- --disable-assembly
- )
- else()
- set(GMP_OPTIONS
- ${GMP_OPTIONS}
- --with-pic
- )
- endif()
-elseif(UNIX)
+if(APPLE AND NOT BLENDER_PLATFORM_ARM)
+ set(GMP_OPTIONS
+ ${GMP_OPTIONS}
+ --with-pic
+ )
+elseif(UNIX AND NOT APPLE)
set(GMP_OPTIONS
${GMP_OPTIONS}
--with-pic
@@ -45,6 +38,13 @@ elseif(UNIX)
)
endif()
+if(BLENDER_PLATFORM_ARM)
+ set(GMP_OPTIONS
+ ${GMP_OPTIONS}
+ --disable-assembly
+ )
+endif()
+
ExternalProject_Add(external_gmp
URL file://${PACKAGE_DIR}/${GMP_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index 23d0dcbab7b..fc7e652a028 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -109,9 +109,9 @@ harvest(llvm/lib llvm/lib "libclang*.a")
if(APPLE)
harvest(openmp/lib openmp/lib "*")
harvest(openmp/include openmp/include "*.h")
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- harvest(sse2neon sse2neon "*.h")
- endif()
+endif()
+if(BLENDER_PLATFORM_ARM)
+ harvest(sse2neon sse2neon "*.h")
endif()
harvest(ogg/lib ffmpeg/lib "*.a")
harvest(openal/include openal/include "*.h")
diff --git a/build_files/build_environment/cmake/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake
index f067267a416..cbb986410aa 100644
--- a/build_files/build_environment/cmake/llvm.cmake
+++ b/build_files/build_environment/cmake/llvm.cmake
@@ -16,7 +16,7 @@
#
# ***** END GPL LICENSE BLOCK *****
-if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+if(BLENDER_PLATFORM_ARM)
set(LLVM_TARGETS AArch64$<SEMICOLON>ARM)
else()
set(LLVM_TARGETS X86)
diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake
index bd03be5ebff..28c93973cf5 100644
--- a/build_files/build_environment/cmake/opencolorio.cmake
+++ b/build_files/build_environment/cmake/opencolorio.cmake
@@ -36,7 +36,7 @@ set(OPENCOLORIO_EXTRA_ARGS
-Dyaml-cpp_ROOT=${LIBDIR}/yamlcpp
)
-if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
+if(BLENDER_PLATFORM_ARM)
set(OPENCOLORIO_EXTRA_ARGS
${OPENCOLORIO_EXTRA_ARGS}
-DOCIO_USE_SSE=OFF
diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake
index 486b3d1a802..8930bd70fab 100644
--- a/build_files/build_environment/cmake/options.cmake
+++ b/build_files/build_environment/cmake/options.cmake
@@ -137,6 +137,10 @@ else()
endif()
set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk)
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(BLENDER_PLATFORM_ARM ON)
+ endif()
+
set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
@@ -151,6 +155,10 @@ else()
-DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT}
)
else()
+ if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
+ set(BLENDER_PLATFORM_ARM ON)
+ endif()
+
set(PLATFORM_CFLAGS "-fPIC")
set(PLATFORM_CXXFLAGS "-std=c++11 -fPIC")
set(PLATFORM_LDFLAGS)
diff --git a/build_files/build_environment/cmake/png.cmake b/build_files/build_environment/cmake/png.cmake
index d9248b61c98..458d3a1fd98 100644
--- a/build_files/build_environment/cmake/png.cmake
+++ b/build_files/build_environment/cmake/png.cmake
@@ -22,8 +22,8 @@ set(PNG_EXTRA_ARGS
-DPNG_STATIC=ON
)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
- set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64")
+if(BLENDER_PLATFORM_ARM)
+ set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64")
endif()
ExternalProject_Add(external_png
diff --git a/build_files/build_environment/cmake/sse2neon.cmake b/build_files/build_environment/cmake/sse2neon.cmake
index dca2d94f913..d7987fdfd2f 100644
--- a/build_files/build_environment/cmake/sse2neon.cmake
+++ b/build_files/build_environment/cmake/sse2neon.cmake
@@ -16,15 +16,13 @@
#
# ***** END GPL LICENSE BLOCK *****
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
- ExternalProject_Add(external_sse2neon
- GIT_REPOSITORY ${SSE2NEON_GIT}
- GIT_TAG ${SSE2NEON_GIT_HASH}
- DOWNLOAD_DIR ${DOWNLOAD_DIR}
- PREFIX ${BUILD_DIR}/sse2neon
- CONFIGURE_COMMAND echo sse2neon - Nothing to configure
- BUILD_COMMAND echo sse2neon - nothing to build
- INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
- INSTALL_DIR ${LIBDIR}/sse2neon
- )
-endif()
+ExternalProject_Add(external_sse2neon
+ GIT_REPOSITORY ${SSE2NEON_GIT}
+ GIT_TAG ${SSE2NEON_GIT_HASH}
+ DOWNLOAD_DIR ${DOWNLOAD_DIR}
+ PREFIX ${BUILD_DIR}/sse2neon
+ CONFIGURE_COMMAND echo sse2neon - Nothing to configure
+ BUILD_COMMAND echo sse2neon - nothing to build
+ INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
+ INSTALL_DIR ${LIBDIR}/sse2neon
+)
diff --git a/build_files/build_environment/cmake/ssl.cmake b/build_files/build_environment/cmake/ssl.cmake
index 4426cc876c6..615b88167ec 100644
--- a/build_files/build_environment/cmake/ssl.cmake
+++ b/build_files/build_environment/cmake/ssl.cmake
@@ -22,7 +22,9 @@ set(SSL_PATCH_CMD echo .)
if(APPLE)
set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}")
else()
- if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
+ if(BLENDER_PLATFORM_ARM)
+ set(SSL_OS_COMPILER "blender-linux-aarch64")
+ elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128)
set(SSL_OS_COMPILER "blender-linux-x86_64")
else()
diff --git a/build_files/build_environment/cmake/ssl.conf b/build_files/build_environment/cmake/ssl.conf
index 8a9c9dcab4c..fa59bcf2825 100644
--- a/build_files/build_environment/cmake/ssl.conf
+++ b/build_files/build_environment/cmake/ssl.conf
@@ -8,6 +8,11 @@ my %targets = (
inherit_from => [ "linux-x86_64" ],
cflags => add("-fPIC"),
},
+ "blender-linux-aarch64" => {
+ inherit_from => [ "linux-aarch64" ],
+ cxxflags => add("-fPIC"),
+ cflags => add("-fPIC"),
+ },
"blender-darwin-x86_64" => {
inherit_from => [ "darwin64-x86_64-cc" ],
cflags => add("-fPIC"),
diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake
index b006898e6fd..44307cd2afb 100644
--- a/build_files/build_environment/cmake/tbb.cmake
+++ b/build_files/build_environment/cmake/tbb.cmake
@@ -21,6 +21,7 @@ if(WIN32)
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=On
-DTBB_BUILD_STATIC=Off
+ -DTBB_BUILD_TESTS=Off
)
set(TBB_LIBRARY tbb)
set(TBB_STATIC_LIBRARY Off)
@@ -30,6 +31,7 @@ else()
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=Off
-DTBB_BUILD_STATIC=On
+ -DTBB_BUILD_TESTS=Off
)
set(TBB_LIBRARY tbb_static)
set(TBB_STATIC_LIBRARY On)
@@ -42,7 +44,7 @@ ExternalProject_Add(external_tbb
URL_HASH ${TBB_HASH_TYPE}=${TBB_HASH}
PREFIX ${BUILD_DIR}/tbb
PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_tbb.txt ${BUILD_DIR}/tbb/src/external_tbb/CMakeLists.txt &&
- ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/src/tbb/version_string.ver &&
+ ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/build/version_string.ver.in &&
${PATCH_CMD} -p 1 -d ${BUILD_DIR}/tbb/src/external_tbb < ${PATCH_DIR}/tbb.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/tbb ${DEFAULT_CMAKE_FLAGS} ${TBB_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/tbb
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index 97da5d54d48..83e438614b6 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -152,7 +152,7 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0)
set(OPENCOLORIO_HASH_TYPE MD5)
set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
# Newer version required by ISPC with arm support.
set(LLVM_VERSION 11.0.1)
set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
@@ -398,11 +398,20 @@ set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df)
set(LZMA_HASH_TYPE SHA256)
set(LZMA_FILE xz-${LZMA_VERSION}.tar.bz2)
-set(SSL_VERSION 1.1.1g)
-set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
-set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
-set(SSL_HASH_TYPE SHA256)
-set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+if(BLENDER_PLATFORM_ARM)
+ # Need at least 1.1.1i for aarch64 support (https://github.com/openssl/openssl/pull/13218)
+ set(SSL_VERSION 1.1.1i)
+ set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
+ set(SSL_HASH e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242)
+ set(SSL_HASH_TYPE SHA256)
+ set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+else()
+ set(SSL_VERSION 1.1.1g)
+ set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
+ set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
+ set(SSL_HASH_TYPE SHA256)
+ set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+endif()
set(SQLITE_VERSION 3.31.1)
set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip)
@@ -453,7 +462,7 @@ set(XR_OPENXR_SDK_HASH 0df6b2fd6045423451a77ff6bc3e1a75)
set(XR_OPENXR_SDK_HASH_TYPE MD5)
set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
# Unreleased version with macOS arm support.
set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d)
diff --git a/build_files/build_environment/cmake/x264.cmake b/build_files/build_environment/cmake/x264.cmake
index a32f119d184..08d698cc3ad 100644
--- a/build_files/build_environment/cmake/x264.cmake
+++ b/build_files/build_environment/cmake/x264.cmake
@@ -20,24 +20,16 @@ if(WIN32)
set(X264_EXTRA_ARGS --enable-win32thread --cross-prefix=${MINGW_HOST}- --host=${MINGW_HOST})
endif()
-
-if(APPLE)
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
- set(X264_CONFIGURE_ENV echo .)
- else()
- set(X264_CONFIGURE_ENV
- export AS=${LIBDIR}/nasm/bin/nasm
- )
- endif()
-else()
- set(X264_CONFIGURE_ENV echo .)
+if(BLENDER_PLATFORM_ARM)
+ set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
endif()
-if(UNIX AND NOT APPLE)
+if((APPLE AND NOT BLENDER_PLATFORM_ARM) OR (UNIX AND NOT APPLE))
set(X264_CONFIGURE_ENV
export AS=${LIBDIR}/nasm/bin/nasm
)
+else()
+ set(X264_CONFIGURE_ENV echo .)
endif()
ExternalProject_Add(external_x264
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 7cd21b2885c..18922799d08 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -37,7 +37,7 @@ if [ $USE_DEBUG_TRAP -ne 0 ]; then
trap 'err_report $LINENO' ERR
fi
-# Noisy, show every line that runs with it's line number.
+# Noisy, show every line that runs with its line number.
if [ $USE_DEBUG_LOG -ne 0 ]; then
PS4='\e[0;33m$(printf %4d ${LINENO}):\e\033[0m '
set -x
@@ -1797,6 +1797,10 @@ compile_OCIO() {
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
+ if [ $(uname -m) == "aarch64" ]; then
+ cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
+ fi
+
if file /bin/cp | grep -q '32-bit'; then
cflags="-fPIC -m32 -march=i686"
else
@@ -2059,7 +2063,10 @@ compile_OIIO() {
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D LINKSTATIC=OFF"
- cmake_d="$cmake_d -D USE_SIMD=sse2"
+
+ if [ $(uname -m) != "aarch64" ]; then
+ cmake_d="$cmake_d -D USE_SIMD=sse2"
+ fi
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
@@ -2079,7 +2086,7 @@ compile_OIIO() {
cmake_d="$cmake_d -D USE_OPENCV=OFF"
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
- cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
+ cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
cmake_d="$cmake_d -D TXT2MAN="
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
@@ -2209,10 +2216,15 @@ compile_LLVM() {
mkdir build
cd build
+ LLVM_TARGETS="X86"
+ if [ $(uname -m) == "aarch64" ]; then
+ LLVM_TARGETS="AArch64"
+ fi
+
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D LLVM_ENABLE_FFI=ON"
- cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=X86"
+ cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=$LLVM_TARGETS"
cmake_d="$cmake_d -D LLVM_ENABLE_TERMINFO=OFF"
if [ -d $_FFI_INCLUDE_DIR ]; then
@@ -2329,13 +2341,16 @@ compile_OSL() {
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
- cmake_d="$cmake_d -D USE_SIMD=sse2"
cmake_d="$cmake_d -D USE_LLVM_BITCODE=OFF"
cmake_d="$cmake_d -D USE_PARTIO=OFF"
cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF"
cmake_d="$cmake_d -D USE_QT=OFF"
cmake_d="$cmake_d -D USE_PYTHON=OFF"
+ if [ $(uname -m) != "aarch64" ]; then
+ cmake_d="$cmake_d -D USE_SIMD=sse2"
+ fi
+
cmake_d="$cmake_d -D CMAKE_CXX_STANDARD=14"
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
diff --git a/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake b/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
index 731cf514a91..ffd70ac155c 100644
--- a/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
+++ b/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
@@ -20,7 +20,7 @@
# ILMBASE_LIBRARIES - list of libraries to link against when using IlmBase.
# ILMBASE_FOUND - True if IlmBase was found.
-# Other standarnd issue macros
+# Other standard issue macros
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)
include(SelectLibraryConfigurations)
diff --git a/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake b/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
index a0390e3311a..330038f022d 100644
--- a/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
+++ b/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
@@ -22,7 +22,7 @@
# These are defined by the FindIlmBase module.
# OPENEXR_FOUND - True if OpenEXR was found.
-# Other standarnd issue macros
+# Other standard issue macros
include(SelectLibraryConfigurations)
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)
diff --git a/build_files/build_environment/patches/cmakelists_tbb.txt b/build_files/build_environment/patches/cmakelists_tbb.txt
index 4032e5d6f83..e05f27afdd6 100644
--- a/build_files/build_environment/patches/cmakelists_tbb.txt
+++ b/build_files/build_environment/patches/cmakelists_tbb.txt
@@ -1,5 +1,32 @@
-cmake_minimum_required (VERSION 2.8)
-project(tbb CXX)
+cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+
+if (POLICY CMP0048)
+ # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning:
+ cmake_policy(SET CMP0048 NEW)
+endif()
+
+project (tbb CXX)
+
+include(CheckCXXCompilerFlag)
+include(CheckCXXSourceRuns)
+
+if(POLICY CMP0058)
+ cmake_policy(SET CMP0058 NEW)
+endif()
+
+if(POLICY CMP0068)
+ cmake_policy(SET CMP0068 NEW)
+endif()
+
+if (POLICY CMP0078)
+ # swig standard target names
+ cmake_policy(SET CMP0078 NEW)
+endif ()
+
+if (POLICY CMP0086)
+ # UseSWIG honors SWIG_MODULE_NAME via -module flag
+ cmake_policy(SET CMP0086 NEW)
+endif ()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Release' as none was specified.")
@@ -8,12 +35,36 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
"MinSizeRel" "RelWithDebInfo")
endif()
-include_directories(include src src/rml/include )
+if(NOT TBB_INSTALL_RUNTIME_DIR)
+ set(TBB_INSTALL_RUNTIME_DIR bin)
+endif()
+if(NOT TBB_INSTALL_LIBRARY_DIR)
+ set(TBB_INSTALL_LIBRARY_DIR lib)
+endif()
+if(NOT TBB_INSTALL_ARCHIVE_DIR)
+ set(TBB_INSTALL_ARCHIVE_DIR lib)
+endif()
+if(NOT TBB_INSTALL_INCLUDE_DIR)
+ set(TBB_INSTALL_INCLUDE_DIR include)
+endif()
+if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR)
+ set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb)
+endif()
+
+include_directories(include src src/rml/include ${CMAKE_CURRENT_BINARY_DIR})
option(TBB_BUILD_SHARED "Build TBB shared library" ON)
option(TBB_BUILD_STATIC "Build TBB static library" ON)
option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON)
option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" ON)
+option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" ON)
+option(TBB_NO_DATE "Do not save the configure date in the version string" OFF)
+option(TBB_BUILD_PYTHON "Build TBB Python bindings" OFF)
+option(TBB_SET_SOVERSION "Set the SOVERSION (shared library build version suffix)?" OFF)
+
+# When this repository is part of a larger build system of a parent project
+# we may not want TBB to set up default installation targets
+option(TBB_INSTALL_TARGETS "Include build targets for 'make install'" ON)
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
@@ -39,66 +90,143 @@ set(tbbmalloc_proxy_src
src/tbbmalloc/proxy.cpp
src/tbbmalloc/tbb_function_replacement.cpp)
-if (NOT APPLE)
- add_definitions(-DDO_ITT_NOTIFY)
-else()
+add_library (tbb_interface INTERFACE)
+add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
+
+if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i386|x86_64)")
+ if (NOT APPLE AND NOT MINGW)
+ target_compile_definitions(tbb_interface INTERFACE DO_ITT_NOTIFY)
+ endif()
+endif()
+
+if (APPLE)
# Disable annoying "has no symbols" warnings
- set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
- set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
- set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
- set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
+ set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
+ set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
endif()
-if (UNIX)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DUSE_PTHREAD")
- if(NOT CMAKE_CXX_FLAGS MATCHES "-mno-rtm")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mrtm")
- endif()
- if (APPLE)
- set(ARCH_PREFIX "mac")
- else()
- set(ARCH_PREFIX "lin")
- endif()
- if (CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(ARCH_PREFIX "${ARCH_PREFIX}64")
+macro(CHECK_CXX_COMPILER_AND_LINKER_FLAGS _RESULT _CXX_FLAGS _LINKER_FLAGS)
+ set(CMAKE_REQUIRED_FLAGS ${_CXX_FLAGS})
+ set(CMAKE_REQUIRED_LIBRARIES ${_LINKER_FLAGS})
+ set(CMAKE_REQUIRED_QUIET TRUE)
+ check_cxx_source_runs("#include <iostream>\nint main(int argc, char **argv) { std::cout << \"test\"; return 0; }" ${_RESULT})
+ set(CMAKE_REQUIRED_FLAGS "")
+ set(CMAKE_REQUIRED_LIBRARIES "")
+endmacro()
+
+# Prefer libc++ in conjunction with Clang
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ if (CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+")
+ message(STATUS "TBB: using libc++.")
else()
- set(ARCH_PREFIX "${ARCH_PREFIX}32")
+ CHECK_CXX_COMPILER_AND_LINKER_FLAGS(HAS_LIBCPP "-stdlib=libc++" "-stdlib=libc++")
+ if (HAS_LIBCPP)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -D_LIBCPP_VERSION")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
+ message(STATUS "TBB: using libc++.")
+ else()
+ message(STATUS "TBB: NOT using libc++.")
+ endif()
endif()
- set(ENABLE_RTTI "-frtti -fexceptions ")
- set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
+endif()
+
+set (CMAKE_CXX_STANDARD 11)
+
+if (UNIX)
+ target_compile_definitions(tbb_interface INTERFACE USE_PTHREAD)
+
+ check_cxx_compiler_flag ("-mrtm -Werror" SUPPORTS_MRTM)
+ if (SUPPORTS_MRTM)
+ target_compile_options(tbb_interface INTERFACE "-mrtm")
+ endif ()
+
elseif(WIN32)
- cmake_minimum_required (VERSION 3.1)
- enable_language(ASM_MASM)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS- /Zc:wchar_t /Zc:forScope /DUSE_WINTHREAD")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_DEPRECATE /D_WIN32_WINNT=0x0600 /volatile:iso")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4800 /wd4146 /wd4244 /wd4018")
-
- if (CMAKE_SIZEOF_VOID_P EQUAL 8)
- list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
- src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
- list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
- set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1")
- set(ARCH_PREFIX "win64")
- else()
- list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
- src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
- list(APPEND tbbmalloc_src src/tbb/ia32-masm/atomic_support.asm)
- set(ARCH_PREFIX "win32")
- endif()
+ target_compile_definitions(tbb_interface INTERFACE USE_WINTHREAD _WIN32_WINNT=0x0600)
+ if (MSVC)
+ enable_language(ASM_MASM)
+ target_compile_options(tbb_interface INTERFACE /GS- /Zc:wchar_t /Zc:forScope)
+ check_cxx_compiler_flag ("/volatile:iso" SUPPORTS_VOLATILE_FLAG)
+ if (SUPPORTS_VOLATILE_FLAG)
+ target_compile_options(tbb_interface INTERFACE /volatile:iso)
+ endif ()
+ target_compile_options(tbb_interface INTERFACE $<$<COMPILE_LANGUAGE:CXX>:/wd4267 /wd4800 /wd4146 /wd4244 /wd4577 /wd4018>)
+ if (NOT CMAKE_SIZEOF_VOID_P)
+ message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Please delete your build directory and rerun CMake again!")
+ endif()
+
+ if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
+ src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
+ list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
+ set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1 ${CMAKE_ASM_MASM_FLAGS}")
+ else()
+ list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
+ src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
+ # Enable SAFESEH feature for assembly (x86 builds only).
+ set(CMAKE_ASM_MASM_FLAGS "/safeseh ${CMAKE_ASM_MASM_FLAGS}")
+ endif()
+ elseif (MINGW)
+ target_compile_options(tbb_interface INTERFACE "-mthreads")
+ endif ()
+endif()
+
+if (MSVC)
set(ENABLE_RTTI "/EHsc /GR ")
set(DISABLE_RTTI "/EHs- /GR- ")
+elseif (UNIX)
+ set(ENABLE_RTTI "-frtti -fexceptions ")
+ set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
+endif ()
+
+##--------
+# - Added TBB_USE_GLIBCXX_VERSION macro to specify the version of GNU
+# libstdc++ when it cannot be properly recognized, e.g. when used
+# with Clang on Linux* OS. Inspired by a contribution from David A.
+if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE)
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ # using Clang
+ string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION})
+ endif()
endif()
+if (TBB_USE_GLIBCXX_VERSION)
+ target_compile_definitions(tbb_interface INTERFACE TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION})
+endif()
+
+##-------
+
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
- include(CheckCXXCompilerFlag)
- check_cxx_compiler_flag("-flifetime-dse=1" SUPPORTS_FLIFETIME)
- if (SUPPORTS_FLIFETIME)
- add_definitions(-flifetime-dse=1)
- endif()
+ check_cxx_compiler_flag ("-flifetime-dse=1" SUPPORTS_FLIFETIME)
+ if (SUPPORTS_FLIFETIME)
+ target_compile_options(tbb_interface INTERFACE -flifetime-dse=1)
+ endif()
endif()
# Linker export definitions
-if (WIN32)
+if (APPLE)
+ set (ARCH_PREFIX "mac")
+elseif(WIN32)
+ set (ARCH_PREFIX "win")
+else()
+ set (ARCH_PREFIX "lin")
+endif()
+
+if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(ARCH_PREFIX "${ARCH_PREFIX}64")
+else()
+ set(ARCH_PREFIX "${ARCH_PREFIX}32")
+endif()
+
+if (MINGW)
+ set (ARCH_PREFIX "${ARCH_PREFIX}-gcc")
+ # there's no win32-gcc-tbb-export.def, use lin32-tbb-export.def
+ execute_process (COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/lin32-tbb-export.def ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/win32-gcc-tbb-export.def)
+endif()
+
+if (MSVC)
add_custom_command(OUTPUT tbb.def
COMMAND ${CMAKE_CXX_COMPILER} /TC /EP ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include > tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
@@ -110,18 +238,15 @@ if (WIN32)
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
- list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc)
- list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
- list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
else()
add_custom_command(OUTPUT tbb.def
- COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
+ COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
COMMENT "Preprocessing tbb.def"
)
add_custom_command(OUTPUT tbbmalloc.def
- COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
+ COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
@@ -132,34 +257,80 @@ add_custom_target(tbb_def_files DEPENDS tbb.def tbbmalloc.def)
# TBB library
if (TBB_BUILD_STATIC)
add_library(tbb_static STATIC ${tbb_src})
- set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
- set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
+ target_link_libraries(tbb_static PRIVATE tbb_interface)
+ target_include_directories(tbb_static INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb_static APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
- install(TARGETS tbb_static ARCHIVE DESTINATION lib)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbb_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
+
+ target_compile_definitions(tbb_static
+ PRIVATE
+ -D__TBB_BUILD=1
+ -D__TBB_DYNAMIC_LOAD_ENABLED=0
+ -D__TBB_SOURCE_DIRECTLY_INCLUDED=1)
+
+ if (MSVC)
+ target_compile_definitions(tbb_static
+ PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
+ PRIVATE -D_CRT_SECURE_NO_WARNINGS)
+ endif()
+
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbb_static PUBLIC pthread dl)
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbb SHARED ${tbb_src})
- set_property(TARGET tbb APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
+ target_link_libraries(tbb PRIVATE tbb_interface)
+ target_include_directories(tbb INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbb PROPERTY SOVERSION 2)
+ endif ()
+
+ target_compile_definitions(tbb
+ PRIVATE -D__TBB_BUILD=1)
+
+ if (MSVC)
+ target_compile_definitions(tbb
+ PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
+ PRIVATE -D_CRT_SECURE_NO_WARNINGS)
+ endif()
+
add_dependencies(tbb tbb_def_files)
+
if (APPLE)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
- elseif(UNIX)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
- elseif(WIN32)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
-
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
+ elseif (MSVC)
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
+ else ()
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
endif()
- install(TARGETS tbb DESTINATION lib)
- if(WIN32)
- set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>")
+
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbb EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbb> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbb PUBLIC pthread dl)
endif()
endif()
-if(CMAKE_COMPILER_IS_GNUCC)
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Quench a warning on GCC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/governor.cpp COMPILE_FLAGS "-Wno-missing-field-initializers ")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ # Quench a warning on Clang
+ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/itt_notify.cpp COMPILE_FLAGS "-Wno-varargs ")
elseif(MSVC)
# Quench a warning on MSVC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/scheduler.cpp COMPILE_FLAGS "/wd4458 ")
@@ -169,24 +340,50 @@ if(TBB_BUILD_TBBMALLOC)
# TBB malloc library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_static STATIC ${tbbmalloc_static_src})
+ target_link_libraries(tbbmalloc_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
+ set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
+ set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- install(TARGETS tbbmalloc_static ARCHIVE DESTINATION lib)
+ if (MSVC)
+ target_compile_definitions(tbbmalloc_static PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
+ endif()
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc SHARED ${tbbmalloc_src})
+ target_link_libraries(tbbmalloc PRIVATE tbb_interface)
set_property(TARGET tbbmalloc APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbbmalloc PROPERTY SOVERSION 2)
+ endif ()
add_dependencies(tbbmalloc tbb_def_files)
if (APPLE)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
- elseif(UNIX)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
- elseif(WIN32)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ elseif (MSVC)
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ else ()
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ endif()
+ if (MSVC)
+ target_compile_definitions(tbbmalloc PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
+ endif()
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbbmalloc> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbbmalloc PUBLIC pthread dl)
endif()
- install(TARGETS tbbmalloc DESTINATION lib)
endif()
endif()
@@ -194,19 +391,298 @@ if(TBB_BUILD_TBBMALLOC_PROXY)
# TBB malloc proxy library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_proxy_static STATIC ${tbbmalloc_proxy_src})
+ target_link_libraries(tbbmalloc_proxy_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
+ set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
+ set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_proxy_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- link_libraries(tbbmalloc_proxy_static tbbmalloc)
- install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION lib)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src})
+ target_link_libraries(tbbmalloc_proxy PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- target_link_libraries(tbbmalloc_proxy tbbmalloc)
- install(TARGETS tbbmalloc_proxy DESTINATION lib)
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbbmalloc_proxy PROPERTY SOVERSION 2)
+ endif ()
+ target_link_libraries(tbbmalloc_proxy PUBLIC tbbmalloc)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_proxy EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbbmalloc_proxy> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbbmalloc_proxy PUBLIC pthread dl)
+ endif()
endif()
endif()
-install(DIRECTORY include/tbb DESTINATION include)
+if (TBB_INSTALL_TARGETS)
+ install(DIRECTORY include/tbb DESTINATION ${TBB_INSTALL_INCLUDE_DIR})
+ if (TBB_BUILD_SHARED)
+ install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake)
+ endif()
+endif()
+
+# version file
+if (TBB_INSTALL_TARGETS)
+ set (_VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/include/tbb/tbb_stddef.h)
+ file (STRINGS ${_VERSION_FILE} _VERSION_MAJOR_STRING REGEX ".*define[ ]+TBB_VERSION_MAJOR[ ]+[0-9]+.*")
+ file (STRINGS ${_VERSION_FILE} _VERSION_MINOR_STRING REGEX ".*define[ ]+TBB_VERSION_MINOR[ ]+[0-9]+.*")
+ string (REGEX REPLACE ".*TBB_VERSION_MAJOR[ ]+([0-9]+)" "\\1" TBB_MAJOR_VERSION ${_VERSION_MAJOR_STRING})
+ string (REGEX REPLACE ".*TBB_VERSION_MINOR[ ]+([0-9]+)" "\\1" TBB_MINOR_VERSION ${_VERSION_MINOR_STRING})
+ set (TBB_VERSION_STRING "${TBB_MAJOR_VERSION}.${TBB_MINOR_VERSION}")
+ include (CMakePackageConfigHelpers)
+ write_basic_package_version_file (TBBConfigVersion.cmake VERSION "${TBB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion)
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/TBBConfigVersion.cmake DESTINATION "${TBB_CMAKE_PACKAGE_INSTALL_DIR}")
+endif()
+
+# version_string.ver
+if (UNIX AND NOT TBB_NO_DATE)
+ execute_process (COMMAND date "+%a, %d %b %Y %H:%M:%S %z"
+ OUTPUT_VARIABLE _configure_date
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+elseif (WIN32 AND NOT TBB_NO_DATE)
+ execute_process (COMMAND cmd " /C date /T"
+ OUTPUT_VARIABLE _configure_date
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+else ()
+ set (_configure_date "Unknown")
+endif()
+set (TBB_CONFIG_DATE "${_configure_date}" CACHE STRING "First time that TBB was configured")
+set (_configure_date "${TBB_CONFIG_DATE}")
+include_directories (${CMAKE_BINARY_DIR})
+configure_file (build/version_string.ver.in version_string.ver @ONLY)
+
+if (TBB_BUILD_TESTS)
+ enable_language (C)
+ enable_testing ()
+
+ find_library (LIBRT_LIBRARIES rt)
+ find_library (LIDL_LIBRARIES dl)
+ find_package (Threads)
+ if (NOT APPLE)
+ find_package (OpenMP)
+ endif()
+
+ macro (tbb_add_test testname)
+ set (full_testname tbb_test_${testname})
+ add_executable (${full_testname} src/test/test_${testname}.cpp)
+ target_link_libraries(${full_testname} PRIVATE tbb_interface)
+ if (TBB_BUILD_SHARED)
+ target_link_libraries (${full_testname} PRIVATE tbb tbbmalloc)
+ target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb)
+ else ()
+ target_link_libraries (${full_testname} PRIVATE tbb_static tbbmalloc_static)
+ target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb_static)
+ endif ()
+ if (LIBRT_LIBRARIES)
+ target_link_libraries (${full_testname} PRIVATE ${LIBRT_LIBRARIES})
+ endif ()
+ if (LIDL_LIBRARIES)
+ target_link_libraries (${full_testname} PRIVATE ${LIDL_LIBRARIES})
+ endif ()
+ if (Threads_FOUND)
+ target_link_libraries (${full_testname} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
+ endif ()
+ if (OPENMP_FOUND AND "${testname}" MATCHES "openmp")
+ set_target_properties (${full_testname} PROPERTIES COMPILE_FLAGS "${OpenMP_CXX_FLAGS}")
+ set_target_properties (${full_testname} PROPERTIES LINK_FLAGS "${OpenMP_CXX_FLAGS}")
+ endif()
+ if (MINGW)
+ target_link_libraries (${full_testname} PRIVATE psapi)
+ endif ()
+ add_test (NAME ${full_testname} COMMAND ${full_testname})
+ endmacro ()
+
+ tbb_add_test (aggregator)
+ tbb_add_test (aligned_space)
+ tbb_add_test (assembly)
+ if (NOT WIN32)
+ tbb_add_test (async_msg) # msvc64/debug timeouts
+ endif()
+ tbb_add_test (async_node)
+ # tbb_add_test (atomic) # msvc64/debug timeouts: Compile-time initialization fails for static tbb::atomic variables
+ tbb_add_test (blocked_range2d)
+ tbb_add_test (blocked_range3d)
+ tbb_add_test (blocked_range)
+ tbb_add_test (broadcast_node)
+ tbb_add_test (buffer_node)
+ tbb_add_test (cache_aligned_allocator)
+ if (NOT WIN32)
+ tbb_add_test (cache_aligned_allocator_STL)
+ endif()
+ tbb_add_test (cilk_dynamic_load)
+ tbb_add_test (cilk_interop)
+ tbb_add_test (combinable)
+ tbb_add_test (composite_node)
+ tbb_add_test (concurrent_hash_map)
+ tbb_add_test (concurrent_lru_cache)
+ # tbb_add_test (concurrent_monitor) # too long
+ # tbb_add_test (concurrent_priority_queue)
+ if (NOT WIN32)
+ tbb_add_test (concurrent_queue) # msvc64/debug timeouts
+ endif()
+ # tbb_add_test (concurrent_queue_whitebox)
+ tbb_add_test (concurrent_unordered_map)
+ # tbb_add_test (concurrent_unordered_set)
+ tbb_add_test (concurrent_vector)
+ tbb_add_test (continue_node)
+ tbb_add_test (critical_section)
+ tbb_add_test (dynamic_link)
+ # tbb_add_test (eh_algorithms)
+ tbb_add_test (eh_flow_graph)
+ # tbb_add_test (eh_tasks)
+ tbb_add_test (enumerable_thread_specific)
+ tbb_add_test (examples_common_utility)
+ # tbb_add_test (fast_random)
+ tbb_add_test (flow_graph)
+ tbb_add_test (flow_graph_whitebox)
+ # tbb_add_test (fp) # mingw: harness_fp.h:66, assertion !checkConsistency || (ctl.mxcsr & SSE_RND_MODE_MASK) >> 3 == (ctl.x87cw & FE_RND_MODE_MASK): failed
+ # tbb_add_test (function_node) # mingw:random timeout
+ # tbb_add_test (global_control)
+ # tbb_add_test (global_control_whitebox)
+ tbb_add_test (halt)
+ tbb_add_test (handle_perror)
+ # tbb_add_test (hw_concurrency)
+ tbb_add_test (indexer_node)
+ tbb_add_test (inits_loop)
+ tbb_add_test (intrusive_list)
+ tbb_add_test (ittnotify)
+ # tbb_add_test (join_node) #msvc/64: fatal error C1128: number of sections exceeded object file format limit: compile with /bigob
+ tbb_add_test (lambda)
+ tbb_add_test (limiter_node)
+ # tbb_add_test (malloc_atexit)
+ # tbb_add_test (malloc_compliance) #mingw: Limits should be decreased for the test to work
+ tbb_add_test (malloc_init_shutdown)
+ # tbb_add_test (malloc_lib_unload)
+ # tbb_add_test (malloc_overload)
+ tbb_add_test (malloc_pools)
+ tbb_add_test (malloc_regression)
+ # tbb_add_test (malloc_used_by_lib)
+ # tbb_add_test (malloc_whitebox)
+ tbb_add_test (model_plugin)
+ # tbb_add_test (multifunction_node) # too long
+ tbb_add_test (mutex)
+ tbb_add_test (mutex_native_threads)
+ # tbb_add_test (opencl_node)
+ if (OPENMP_FOUND)
+ tbb_add_test (openmp)
+ endif ()
+ tbb_add_test (overwrite_node)
+ # tbb_add_test (parallel_do)
+ # This seems to fail on CI platforms (AppVeyor/Travis), perhaps because the VM exposes just 1 core?
+ tbb_add_test (parallel_for)
+ tbb_add_test (parallel_for_each)
+ tbb_add_test (parallel_for_vectorization)
+ tbb_add_test (parallel_invoke)
+ tbb_add_test (parallel_pipeline)
+ tbb_add_test (parallel_reduce)
+ tbb_add_test (parallel_scan)
+ tbb_add_test (parallel_sort)
+ tbb_add_test (parallel_while)
+ # tbb_add_test (partitioner_whitebox) # too long
+ tbb_add_test (pipeline)
+ # tbb_add_test (pipeline_with_tbf) # takes forever on appveyor
+ tbb_add_test (priority_queue_node)
+ tbb_add_test (queue_node)
+ tbb_add_test (reader_writer_lock)
+ # tbb_add_test (runtime_loader) # LINK : fatal error LNK1104: cannot open file 'tbbproxy.lib' [C:\projects\tbb\test_runtime_loader.vcxproj]
+ tbb_add_test (rwm_upgrade_downgrade)
+ # tbb_add_test (ScalableAllocator)
+ if (NOT WIN32)
+ tbb_add_test (ScalableAllocator_STL)
+ endif()
+ tbb_add_test (semaphore)
+ # tbb_add_test (sequencer_node) # msvc: timeout
+ tbb_add_test (source_node)
+ tbb_add_test (split_node)
+ tbb_add_test (static_assert)
+ tbb_add_test (std_thread)
+ tbb_add_test (tagged_msg)
+ # tbb_add_test (task_arena) # LINK : fatal error LNK1104: cannot open file '__TBB_LIB_NAME.lib' [C:\projects\tbb\test_task_arena.vcxproj]
+ # tbb_add_test (task_assertions)
+ tbb_add_test (task_auto_init)
+ tbb_add_test (task)
+ # tbb_add_test (task_enqueue) # too long
+ tbb_add_test (task_group)
+ # tbb_add_test (task_leaks)
+ # tbb_add_test (task_priority)
+ # tbb_add_test (task_scheduler_init) # msvc: test_task_scheduler_init.cpp:68, assertion !test_mandatory_parallelism || Harness::CanReachConcurrencyLevel(threads): failed
+ tbb_add_test (task_scheduler_observer)
+ tbb_add_test (task_steal_limit)
+ tbb_add_test (tbb_condition_variable)
+ tbb_add_test (tbb_fork)
+ # tbb_add_test (tbb_header)
+ tbb_add_test (tbb_thread)
+ # tbb_add_test (tbb_version)
+ tbb_add_test (tick_count)
+ tbb_add_test (tuple)
+ tbb_add_test (write_once_node)
+ tbb_add_test (yield)
+endif ()
+
+if (TBB_BUILD_PYTHON)
+ find_package(PythonInterp)
+ find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT)
+ find_package(SWIG 3)
+ if (PythonLibs_FOUND AND SWIG_FOUND AND TBB_BUILD_SHARED)
+ include (${SWIG_USE_FILE})
+ set_source_files_properties (python/tbb/api.i PROPERTIES CPLUSPLUS ON)
+ set (CMAKE_SWIG_FLAGS "-threads")
+
+ # swig_add_module is deprecated
+ if (CMAKE_VERSION VERSION_LESS 3.8)
+ swig_add_module (api python python/tbb/api.i)
+ else ()
+ swig_add_library (api LANGUAGE python SOURCES python/tbb/api.i)
+ endif ()
+
+ # UseSWIG generates now standard target names
+ if (CMAKE_VERSION VERSION_LESS 3.13)
+ set (module_target ${SWIG_MODULE_api_REAL_NAME})
+ else ()
+ set (module_target api)
+ endif ()
+
+ target_include_directories(${module_target} PRIVATE ${PYTHON_INCLUDE_DIRS})
+ target_link_libraries(${module_target} PRIVATE tbb)
+ if(WIN32)
+ target_link_libraries(${module_target} ${PYTHON_LIBRARIES})
+ elseif(APPLE)
+ set_target_properties(${module_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
+ endif()
+
+ if (WIN32)
+ set (PYTHON_SITE_PACKAGES Lib/site-packages)
+ else ()
+ set (PYTHON_SITE_PACKAGES lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
+ endif ()
+ if (TBB_INSTALL_TARGETS)
+ install(FILES python/TBB.py
+ DESTINATION ${PYTHON_SITE_PACKAGES})
+ install(FILES python/tbb/__init__.py python/tbb/pool.py python/tbb/test.py python/tbb/__main__.py ${CMAKE_CURRENT_BINARY_DIR}/api.py
+ DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
+ install(TARGETS ${module_target}
+ DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
+ endif()
+
+ if(UNIX AND NOT APPLE)
+ add_library(irml SHARED python/rml/ipc_server.cpp python/rml/ipc_utils.cpp src/tbb/cache_aligned_allocator.cpp src/tbb/dynamic_link.cpp src/tbb/tbb_misc_ex.cpp src/tbb/tbb_misc.cpp)
+ target_compile_definitions(irml PRIVATE DO_ITT_NOTIFY=0 USE_PTHREAD=1)
+ target_link_libraries(irml PRIVATE tbb)
+ set_target_properties(irml PROPERTIES VERSION 1)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS irml DESTINATION ${TBB_INSTALL_LIBRARY_DIR})
+ endif()
+ endif ()
+ endif ()
+endif ()
diff --git a/build_files/build_environment/patches/tbb.diff b/build_files/build_environment/patches/tbb.diff
index c05c35bca95..07f70aa7007 100644
--- a/build_files/build_environment/patches/tbb.diff
+++ b/build_files/build_environment/patches/tbb.diff
@@ -10,4 +10,15 @@ index 7a8d06a0..886699d8 100644
+ #if (__cplusplus >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920))
#define __TBB_DEPRECATED [[deprecated]]
#define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]]
- #elif _MSC_VER \ No newline at end of file
+ #elif _MSC_VER
+--- a/src/tbb/tools_api/ittnotify_config.h
++++ b/src/tbb/tools_api/ittnotify_config.h
+@@ -162,7 +162,7 @@
+ # define ITT_ARCH ITT_ARCH_IA32E
+ # elif defined _M_IA64 || defined __ia64__
+ # define ITT_ARCH ITT_ARCH_IA64
+-# elif defined _M_ARM || defined __arm__
++# elif defined _M_ARM || defined __arm__ || defined __aarch64__
+ # define ITT_ARCH ITT_ARCH_ARM
+ # elif defined __powerpc64__
+ # define ITT_ARCH ITT_ARCH_PPC64
diff --git a/build_files/build_environment/patches/theora.diff b/build_files/build_environment/patches/theora.diff
index 3abadb66be9..4436577816e 100644
--- a/build_files/build_environment/patches/theora.diff
+++ b/build_files/build_environment/patches/theora.diff
@@ -4,7 +4,7 @@
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
-+ | aarch64 \
++ | aarch64 \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
@@ -12,7 +12,7 @@
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
-+ | aarch64-* \
++ | aarch64-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff
index 42527a4f443..dc4982ad114 100644
--- a/build_files/build_environment/patches/usd.diff
+++ b/build_files/build_environment/patches/usd.diff
@@ -53,3 +53,147 @@ diff -ru USD-20.11/pxr/base/tf/pxrLZ4/lz4.cpp external_usd/pxr/base/tf/pxrLZ4/lz
/*-******************************
* Compression functions
+
+From 442d087962f762deeb8b6e49a0955753fcf9aeb9 Mon Sep 17 00:00:00 2001
+From: Tsahi Zidenberg <tsahee@amazon.com>
+Date: Sun, 15 Nov 2020 15:18:24 +0000
+Subject: [PATCH 1/2] stackTrace: support aarch64/linux
+
+stacktrace calls syscall directly via assembler. Create compatible
+aarch64 code.
+---
+ pxr/base/arch/stackTrace.cpp | 30 ++++++++++++++++++++++++------
+ 1 file changed, 24 insertions(+), 6 deletions(-)
+
+diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp
+index dcc1dfd46..c11aabeb1 100644
+--- a/pxr/base/arch/stackTrace.cpp
++++ b/pxr/base/arch/stackTrace.cpp
+@@ -583,7 +583,6 @@ nonLockingLinux__execve (const char *file,
+ char *const argv[],
+ char *const envp[])
+ {
+-#if defined(ARCH_BITS_64)
+ /*
+ * We make a direct system call here, because we can't find an
+ * execve which corresponds with the non-locking fork we call
+@@ -594,7 +593,27 @@ nonLockingLinux__execve (const char *file,
+ * hangs in a threaded app. (We use the non-locking fork to get
+ * around problems with forking when we have had memory
+ * corruption.) whew.
+- *
++ */
++
++ unsigned long result;
++
++#if defined (__aarch64__)
++ {
++ register long __file_result asm ("x0") = (long)file;
++ register char* const* __argv asm ("x1") = argv;
++ register char* const* __envp asm ("x2") = envp;
++ register long __num_execve asm ("x8") = 221;
++ __asm__ __volatile__ (
++ "svc 0"
++ : "=r" (__file_result)
++ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp)
++ : "memory"
++ );
++ result = __file_result;
++ }
++#elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64)
++
++ /*
+ * %rdi, %rsi, %rdx, %rcx, %r8, %r9 are args 0-5
+ * syscall clobbers %rcx and %r11
+ *
+@@ -603,7 +622,6 @@ nonLockingLinux__execve (const char *file,
+ * constraints to gcc.
+ */
+
+- unsigned long result;
+ __asm__ __volatile__ (
+ "mov %0, %%rdi \n\t"
+ "mov %%rcx, %%rsi \n\t"
+@@ -614,6 +632,9 @@ nonLockingLinux__execve (const char *file,
+ : "0" (file), "c" (argv), "d" (envp)
+ : "memory", "cc", "r11"
+ );
++#else
++#error Unknown architecture
++#endif
+
+ if (result >= 0xfffffffffffff000) {
+ errno = -result;
+@@ -621,9 +642,6 @@ nonLockingLinux__execve (const char *file,
+ }
+
+ return result;
+-#else
+-#error Unknown architecture
+-#endif
+ }
+
+ #endif
+
+From a1dffe02519bb3c6ccbbe8c6c58304da5db98995 Mon Sep 17 00:00:00 2001
+From: Tsahi Zidenberg <tsahee@amazon.com>
+Date: Sun, 15 Nov 2020 15:22:52 +0000
+Subject: [PATCH 2/2] timing: support aarch64/linux
+
+The aarch64 arch-timer is directly accessible to userspace via two
+registers:
+CNTVCT_EL0 - holds the current counter value
+CNTFRQ_EL0 - holds the counter frequency (in Hz)
+---
+ pxr/base/arch/timing.cpp | 6 ++++++
+ pxr/base/arch/timing.h | 6 +++++-
+ 2 files changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/pxr/base/arch/timing.cpp b/pxr/base/arch/timing.cpp
+index 27ad58fed..9022950c1 100644
+--- a/pxr/base/arch/timing.cpp
++++ b/pxr/base/arch/timing.cpp
+@@ -59,6 +59,11 @@ ARCH_HIDDEN
+ void
+ Arch_InitTickTimer()
+ {
++#ifdef __aarch64__
++ uint64_t counter_hz;
++ __asm __volatile("mrs %0, CNTFRQ_EL0" : "=&r" (counter_hz));
++ Arch_NanosecondsPerTick = double(1e9) / double(counter_hz);
++#else
+ // NOTE: Normally ifstream would be cleaner, but it causes crashes when
+ // used in conjunction with DSOs and the Intel Compiler.
+ FILE *in;
+@@ -135,6 +140,7 @@ Arch_InitTickTimer()
+ }
+
+ Arch_NanosecondsPerTick = double(1e9) / double(cpuHz);
++#endif
+ }
+ #elif defined(ARCH_OS_WINDOWS)
+
+diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h
+index 67ec0d15f..6dc3e85a0 100644
+--- a/pxr/base/arch/timing.h
++++ b/pxr/base/arch/timing.h
+@@ -36,7 +36,7 @@
+ /// \addtogroup group_arch_SystemFunctions
+ ///@{
+
+-#if defined(ARCH_OS_LINUX)
++#if defined(ARCH_OS_LINUX) && defined(ARCH_CPU_INTEL)
+ #include <x86intrin.h>
+ #elif defined(ARCH_OS_DARWIN)
+ #include <mach/mach_time.h>
+@@ -69,6 +69,10 @@ ArchGetTickTime()
+ #elif defined(ARCH_CPU_INTEL)
+ // On Intel we'll use the rdtsc instruction.
+ return __rdtsc();
++#elif defined (__aarch64__)
++ uint64_t result;
++ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result));
++ return result;
+ #else
+ #error Unknown architecture.
+ #endif
diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py
index 057c2155fb6..7e9858d9268 100644
--- a/build_files/buildbot/buildbot_utils.py
+++ b/build_files/buildbot/buildbot_utils.py
@@ -85,8 +85,8 @@ class VersionInfo:
version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION'))
version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH'))
version_numbers = (version_number // 100, version_number % 100, version_number_patch)
- self.short_version = "%d.%02d" % (version_numbers[0], version_numbers[1])
- self.version = "%d.%02d.%d" % version_numbers
+ self.short_version = "%d.%d" % (version_numbers[0], version_numbers[1])
+ self.version = "%d.%d.%d" % version_numbers
self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE')
self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1]
diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake
index 7f7f2ae0fb3..e6105a885f3 100644
--- a/build_files/cmake/Modules/FindEmbree.cmake
+++ b/build_files/cmake/Modules/FindEmbree.cmake
@@ -34,7 +34,7 @@ FIND_PATH(EMBREE_INCLUDE_DIR
include
)
-IF(NOT (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")))
+IF(NOT (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") OR (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))))
SET(_embree_SIMD_COMPONENTS
embree_sse42
embree_avx
diff --git a/build_files/cmake/buildinfo.cmake b/build_files/cmake/buildinfo.cmake
index a349ffc1b56..2b39c36a4e9 100644
--- a/build_files/cmake/buildinfo.cmake
+++ b/build_files/cmake/buildinfo.cmake
@@ -79,7 +79,7 @@ if(EXISTS ${SOURCE_DIR}/.git)
ERROR_QUIET)
if(NOT _git_below_check STREQUAL "")
# If there're commits between HEAD and upstream this means
- # that we're reset-ed to older revision. Use it's hash then.
+ # that we're reset-ed to older revision. Use its hash then.
execute_process(COMMAND git rev-parse --short=12 HEAD
WORKING_DIRECTORY ${SOURCE_DIR}
OUTPUT_VARIABLE MY_WC_HASH
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 91881441c95..813ac013cdf 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -923,10 +923,6 @@ function(get_blender_version)
math(EXPR _out_version_major "${_out_version} / 100")
math(EXPR _out_version_minor "${_out_version} % 100")
- # Zero pad the minor version so `_out_version_minor` is always two characters.
- # This is needed if the minor version is a single digit.
- string(REGEX REPLACE "^([0-9])$" "0\\1" _out_version_minor "${_out_version_minor}")
-
# output vars
set(BLENDER_VERSION "${_out_version_major}.${_out_version_minor}" PARENT_SCOPE)
set(BLENDER_VERSION_MAJOR "${_out_version_major}" PARENT_SCOPE)
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile
index f9055f02bcf..201d2208486 100644
--- a/doc/doxygen/Doxyfile
+++ b/doc/doxygen/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = Blender
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = "V2.93"
+PROJECT_NUMBER = "V3.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/doc/python_api/examples/bpy.types.RenderEngine.py b/doc/python_api/examples/bpy.types.RenderEngine.py
index 45910194244..0b8795340ad 100644
--- a/doc/python_api/examples/bpy.types.RenderEngine.py
+++ b/doc/python_api/examples/bpy.types.RenderEngine.py
@@ -4,7 +4,9 @@ Simple Render Engine
"""
import bpy
-import bgl
+import array
+import gpu
+from gpu_extras.presets import draw_texture_2d
class CustomRenderEngine(bpy.types.RenderEngine):
@@ -100,8 +102,7 @@ class CustomRenderEngine(bpy.types.RenderEngine):
dimensions = region.width, region.height
# Bind shader that converts from scene linear to display space,
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA)
+ gpu.state.blend_set('ALPHA_PREMULT')
self.bind_display_space_shader(scene)
if not self.draw_data or self.draw_data.dimensions != dimensions:
@@ -110,7 +111,7 @@ class CustomRenderEngine(bpy.types.RenderEngine):
self.draw_data.draw()
self.unbind_display_space_shader()
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.blend_set('NONE')
class CustomDrawData:
@@ -119,68 +120,21 @@ class CustomDrawData:
self.dimensions = dimensions
width, height = dimensions
- pixels = [0.1, 0.2, 0.1, 1.0] * width * height
- pixels = bgl.Buffer(bgl.GL_FLOAT, width * height * 4, pixels)
+ pixels = width * height * array.array('f', [0.1, 0.2, 0.1, 1.0])
+ pixels = gpu.types.Buffer('FLOAT', width * height * 4, pixels)
# Generate texture
- self.texture = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGenTextures(1, self.texture)
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
- bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_RGBA16F, width, height, 0, bgl.GL_RGBA, bgl.GL_FLOAT, pixels)
- bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR)
- bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
+ self.texture = gpu.types.GPUTexture((width, height), format='RGBA16F', data=pixels)
- # Bind shader that converts from scene linear to display space,
- # use the scene's color management settings.
- shader_program = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, shader_program)
-
- # Generate vertex array
- self.vertex_array = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGenVertexArrays(1, self.vertex_array)
- bgl.glBindVertexArray(self.vertex_array[0])
-
- texturecoord_location = bgl.glGetAttribLocation(shader_program[0], "texCoord")
- position_location = bgl.glGetAttribLocation(shader_program[0], "pos")
-
- bgl.glEnableVertexAttribArray(texturecoord_location)
- bgl.glEnableVertexAttribArray(position_location)
-
- # Generate geometry buffers for drawing textured quad
- position = [0.0, 0.0, width, 0.0, width, height, 0.0, height]
- position = bgl.Buffer(bgl.GL_FLOAT, len(position), position)
- texcoord = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
- texcoord = bgl.Buffer(bgl.GL_FLOAT, len(texcoord), texcoord)
-
- self.vertex_buffer = bgl.Buffer(bgl.GL_INT, 2)
-
- bgl.glGenBuffers(2, self.vertex_buffer)
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[0])
- bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, position, bgl.GL_STATIC_DRAW)
- bgl.glVertexAttribPointer(position_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
-
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[1])
- bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, texcoord, bgl.GL_STATIC_DRAW)
- bgl.glVertexAttribPointer(texturecoord_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
-
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, 0)
- bgl.glBindVertexArray(0)
+ # Note: This is just a didactic example.
+ # In this case it would be more convenient to fill the texture with:
+ # self.texture.clear('FLOAT', value=[0.1, 0.2, 0.1, 1.0])
def __del__(self):
- bgl.glDeleteBuffers(2, self.vertex_buffer)
- bgl.glDeleteVertexArrays(1, self.vertex_array)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
- bgl.glDeleteTextures(1, self.texture)
+ del self.texture
def draw(self):
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
- bgl.glBindVertexArray(self.vertex_array[0])
- bgl.glDrawArrays(bgl.GL_TRIANGLE_FAN, 0, 4)
- bgl.glBindVertexArray(0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
+ draw_texture_2d(self.texture, (0, 0), self.texture.width, self.texture.height)
# RenderEngines also need to tell UI Panels that they are compatible with.
diff --git a/doc/python_api/examples/gpu.4.py b/doc/python_api/examples/gpu.4.py
index e05290a9442..b1a1ba827db 100644
--- a/doc/python_api/examples/gpu.4.py
+++ b/doc/python_api/examples/gpu.4.py
@@ -4,7 +4,6 @@ Mesh with Random Vertex Colors
"""
import bpy
import gpu
-import bgl
import numpy as np
from random import random
from gpu_extras.batch import batch_for_shader
@@ -31,9 +30,10 @@ batch = batch_for_shader(
def draw():
- bgl.glEnable(bgl.GL_DEPTH_TEST)
+ gpu.state.depth_test_set('LESS_EQUAL')
+ gpu.state.depth_mask_set(True)
batch.draw(shader)
- bgl.glDisable(bgl.GL_DEPTH_TEST)
+ gpu.state.depth_mask_set(False)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py
index 69af65e163e..334a606055a 100644
--- a/doc/python_api/examples/gpu.6.py
+++ b/doc/python_api/examples/gpu.6.py
@@ -6,11 +6,11 @@ To use this example you have to provide an image that should be displayed.
"""
import bpy
import gpu
-import bgl
from gpu_extras.batch import batch_for_shader
IMAGE_NAME = "Untitled"
image = bpy.data.images[IMAGE_NAME]
+texture = gpu.texture.from_image(image)
shader = gpu.shader.from_builtin('2D_IMAGE')
batch = batch_for_shader(
@@ -21,16 +21,9 @@ batch = batch_for_shader(
},
)
-if image.gl_load():
- raise Exception()
-
-
def draw():
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
-
shader.bind()
- shader.uniform_int("image", 0)
+ shader.uniform_sampler("image", texture)
batch.draw(shader)
diff --git a/doc/python_api/examples/gpu.7.py b/doc/python_api/examples/gpu.7.py
index 80cda45e690..01082e7b6fe 100644
--- a/doc/python_api/examples/gpu.7.py
+++ b/doc/python_api/examples/gpu.7.py
@@ -9,7 +9,6 @@ Generate a texture using Offscreen Rendering
"""
import bpy
import gpu
-import bgl
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_circle_2d
@@ -20,8 +19,8 @@ from gpu_extras.presets import draw_circle_2d
offscreen = gpu.types.GPUOffScreen(512, 512)
with offscreen.bind():
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]
gpu.matrix.load_matrix(Matrix.Identity(4))
@@ -75,13 +74,10 @@ batch = batch_for_shader(
def draw():
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, offscreen.color_texture)
-
shader.bind()
shader.uniform_float("modelMatrix", Matrix.Translation((1, 2, 3)) @ Matrix.Scale(3, 4))
shader.uniform_float("viewProjectionMatrix", bpy.context.region_data.perspective_matrix)
- shader.uniform_float("image", 0)
+ shader.uniform_sampler("image", offscreen.texture_color)
batch.draw(shader)
diff --git a/doc/python_api/examples/gpu.8.py b/doc/python_api/examples/gpu.8.py
index e67c601def9..664f14a23ca 100644
--- a/doc/python_api/examples/gpu.8.py
+++ b/doc/python_api/examples/gpu.8.py
@@ -7,11 +7,10 @@ If it already exists, it will override the existing one.
Currently almost all of the execution time is spent in the last line.
In the future this will hopefully be solved by implementing the Python buffer protocol
-for :class:`bgl.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
+for :class:`gpu.types.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
"""
import bpy
import gpu
-import bgl
import random
from mathutils import Matrix
from gpu_extras.presets import draw_circle_2d
@@ -25,8 +24,8 @@ RING_AMOUNT = 10
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
with offscreen.bind():
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]
gpu.matrix.load_matrix(Matrix.Identity(4))
@@ -37,9 +36,7 @@ with offscreen.bind():
(random.uniform(-1, 1), random.uniform(-1, 1)),
(1, 1, 1, 1), random.uniform(0.1, 1), 20)
- buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4)
- bgl.glReadBuffer(bgl.GL_BACK)
- bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
+ buffer = fb.read_color(0, 0, WIDTH, HEIGHT, 4, 0, 'UBYTE')
offscreen.free()
@@ -48,4 +45,6 @@ if not IMAGE_NAME in bpy.data.images:
bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)
image = bpy.data.images[IMAGE_NAME]
image.scale(WIDTH, HEIGHT)
+
+buffer.dimensions = WIDTH * HEIGHT * 4
image.pixels = [v / 255 for v in buffer]
diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py
index a4db576ecc0..e358cb517bd 100644
--- a/doc/python_api/examples/gpu.9.py
+++ b/doc/python_api/examples/gpu.9.py
@@ -7,7 +7,6 @@ You could also make this independent of a specific camera,
but Blender does not expose good functions to create view and projection matrices yet.
"""
import bpy
-import bgl
import gpu
from gpu_extras.presets import draw_texture_2d
@@ -34,8 +33,8 @@ def draw():
view_matrix,
projection_matrix)
- bgl.glDisable(bgl.GL_DEPTH_TEST)
- draw_texture_2d(offscreen.color_texture, (10, 10), WIDTH, HEIGHT)
+ gpu.state.depth_mask_set(False)
+ draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
diff --git a/doc/python_api/examples/mathutils.Matrix.LocRotScale.py b/doc/python_api/examples/mathutils.Matrix.LocRotScale.py
new file mode 100644
index 00000000000..016a5002e82
--- /dev/null
+++ b/doc/python_api/examples/mathutils.Matrix.LocRotScale.py
@@ -0,0 +1,5 @@
+# Compute local object transformation matrix:
+if obj.rotation_mode == 'QUATERNION':
+ matrix = mathutils.Matrix.LocRotScale(obj.location, obj.rotation_quaternion, obj.scale)
+else:
+ matrix = mathutils.Matrix.LocRotScale(obj.location, obj.rotation_euler, obj.scale)
diff --git a/doc/python_api/examples/mathutils.Matrix.py b/doc/python_api/examples/mathutils.Matrix.py
index 26c7ccba27c..84e09b97c15 100644
--- a/doc/python_api/examples/mathutils.Matrix.py
+++ b/doc/python_api/examples/mathutils.Matrix.py
@@ -14,10 +14,14 @@ mat_rot = mathutils.Matrix.Rotation(math.radians(45.0), 4, 'X')
mat_out = mat_loc @ mat_rot @ mat_sca
print(mat_out)
-# extract components back out of the matrix
+# extract components back out of the matrix as two vectors and a quaternion
loc, rot, sca = mat_out.decompose()
print(loc, rot, sca)
+# recombine extracted components
+mat_out2 = mathutils.Matrix.LocRotScale(loc, rot, sca)
+print(mat_out2)
+
# it can also be useful to access components of a matrix directly
mat = mathutils.Matrix()
mat[0][0], mat[1][0], mat[2][0] = 0.0, 1.0, 2.0
diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst
index e5ff56063b5..df6e7297e26 100644
--- a/doc/python_api/rst/info_gotcha.rst
+++ b/doc/python_api/rst/info_gotcha.rst
@@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C.
Blender does have the functionality for poll functions to describe why they fail,
but its currently not used much, if you're interested to help improve the API
- feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
+ feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
+ where its not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 107c233134e..032f8a86bd5 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -1036,7 +1036,6 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra):
context_type_map = {
# context_member: (RNA type, is_collection)
"active_annotation_layer": ("GPencilLayer", False),
- "active_base": ("ObjectBase", False),
"active_bone": ("EditBone", False),
"active_gpencil_frame": ("GreasePencilLayer", True),
"active_gpencil_layer": ("GPencilLayer", True),
@@ -1556,8 +1555,8 @@ def pyrna2sphinx(basepath):
fw(".. hlist::\n")
fw(" :columns: 2\n\n")
- # context does its own thing
- # "active_base": ("ObjectBase", False),
+ # Context does its own thing.
+ # "active_object": ("Object", False),
for ref_attr, (ref_type, ref_is_seq) in sorted(context_type_map.items()):
if ref_type == struct_id:
fw(" * :mod:`bpy.context.%s`\n" % ref_attr)
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
index 50a51ebe913..416ea25ee0c 100644
--- a/intern/clog/clog.c
+++ b/intern/clog/clog.c
@@ -322,7 +322,9 @@ static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
if (flt->match[0] == '*' && flt->match[len - 1] == '*') {
char *match = MEM_callocN(sizeof(char) * len - 1, __func__);
memcpy(match, flt->match + 1, len - 2);
- if (strstr(identifier, match) != NULL) {
+ const bool success = (strstr(identifier, match) != NULL);
+ MEM_freeN(match);
+ if (success) {
return (bool)i;
}
}
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 67b852013f3..7a1e5d62dd2 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -71,6 +71,16 @@ if(WITH_CYCLES_STANDALONE)
target_link_libraries(cycles ${LIBRARIES})
cycles_target_link_libraries(cycles)
+ if(APPLE)
+ if(WITH_OPENCOLORIO)
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit")
+ endif()
+ if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ # OpenImageDenoise uses BNNS from the Accelerate framework.
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate")
+ endif()
+ endif()
+
if(UNIX AND NOT APPLE)
set_target_properties(cycles PROPERTIES INSTALL_RPATH $ORIGIN/lib)
endif()
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index dfa696714fb..d8398772a84 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -19,16 +19,16 @@ from __future__ import annotations
def _is_using_buggy_driver():
- import bgl
+ import gpu
# We need to be conservative here because in multi-GPU systems display card
# might be quite old, but others one might be just good.
#
# So We shouldn't disable possible good dedicated cards just because display
# card seems weak. And instead we only blacklist configurations which are
# proven to cause problems.
- if bgl.glGetString(bgl.GL_VENDOR) == "ATI Technologies Inc.":
+ if gpu.platform.vendor_get() == "ATI Technologies Inc.":
import re
- version = bgl.glGetString(bgl.GL_VERSION)
+ version = gpu.platform.version_get()
if version.endswith("Compatibility Profile Context"):
# Old HD 4xxx and 5xxx series drivers did not have driver version
# in the version string, but those cards do not quite work and
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index dcf6e3cc949..cb84013c551 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -564,10 +564,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
if (!cancel && !motion) {
sync_background_light(b_v3d, use_portal);
- /* handle removed data and modified pointers */
+ /* Handle removed data and modified pointers, as this may free memory, delete Nodes in the
+ * right order to ensure that dependent data is freed after their users. Objects should be
+ * freed before particle systems and geometries. */
light_map.post_sync();
- geometry_map.post_sync();
object_map.post_sync();
+ geometry_map.post_sync();
particle_system_map.post_sync();
}
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 4af8da402b1..9d0f9f29f94 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -69,7 +69,8 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
experimental(false),
dicing_rate(1.0f),
max_subdivisions(12),
- progress(progress)
+ progress(progress),
+ has_updates_(true)
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
dicing_rate = preview ? RNA_float_get(&cscene, "preview_dicing_rate") :
@@ -84,7 +85,9 @@ BlenderSync::~BlenderSync()
void BlenderSync::reset(BL::BlendData &b_data, BL::Scene &b_scene)
{
/* Update data and scene pointers in case they change in session reset,
- * for example after undo. */
+ * for example after undo.
+ * Note that we do not modify the `has_updates_` flag here because the sync
+ * reset is also used during viewport navigation. */
this->b_data = b_data;
this->b_scene = b_scene;
}
@@ -117,6 +120,8 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
if (dicing_prop_changed) {
+ has_updates_ = true;
+
for (const pair<const GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) {
Geometry *geom = iter.second;
if (geom->is_mesh()) {
@@ -133,6 +138,12 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
/* Iterate over all IDs in this depsgraph. */
for (BL::DepsgraphUpdate &b_update : b_depsgraph.updates) {
+ /* TODO(sergey): Can do more selective filter here. For example, ignore changes made to
+ * screen datablock. Note that sync_data() needs to be called after object deletion, and
+ * currently this is ensured by the scene ID tagged for update, which sets the `has_updates_`
+ * flag. */
+ has_updates_ = true;
+
BL::ID b_id(b_update.id());
/* Material */
@@ -227,6 +238,10 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
int height,
void **python_thread_state)
{
+ if (!has_updates_) {
+ return;
+ }
+
scoped_timer timer;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
@@ -254,6 +269,8 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
free_data_after_sync(b_depsgraph);
VLOG(1) << "Total time spent synchronizing data: " << timer.get_time();
+
+ has_updates_ = false;
}
/* Integrator */
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index a222c5e490e..15a10f2b46b 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -264,6 +264,12 @@ class BlenderSync {
} view_layer;
Progress &progress;
+
+ protected:
+ /* Indicates that `sync_recalc()` detected changes in the scene.
+ * If this flag is false then the data is considered to be up-to-date and will not be
+ * synchronized at all. */
+ bool has_updates_ = true;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index bdf18d09b31..ecf79bcdfa6 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -427,7 +427,7 @@ class Device {
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
/* OptiX specific destructor. */
- virtual void release_optix_bvh(BVH *){};
+ virtual void release_optix_bvh(BVH * /*bvh*/){};
#ifdef WITH_NETWORK
/* networking */
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index bb6027254f9..01de0724cb2 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -960,14 +960,21 @@ class OptiXDevice : public CUDADevice {
// Create OptiX denoiser handle on demand when it is first used
OptixDenoiserOptions denoiser_options = {};
assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
+# if OPTIX_ABI_VERSION >= 47
+ denoiser_options.guideAlbedo = task.denoising.input_passes >= 2;
+ denoiser_options.guideNormal = task.denoising.input_passes >= 3;
+ check_result_optix_ret(optixDenoiserCreate(
+ context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser));
+# else
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
-# if OPTIX_ABI_VERSION < 28
+# if OPTIX_ABI_VERSION < 28
denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3;
-# endif
+# endif
check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser));
check_result_optix_ret(
optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0));
+# endif
// OptiX denoiser handle was created with the requested number of input passes
denoiser_input_passes = task.denoising.input_passes;
@@ -1037,10 +1044,34 @@ class OptiXDevice : public CUDADevice {
# endif
output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3;
+# if OPTIX_ABI_VERSION >= 47
+ OptixDenoiserLayer image_layers = {};
+ image_layers.input = input_layers[0];
+ image_layers.output = output_layers[0];
+
+ OptixDenoiserGuideLayer guide_layers = {};
+ guide_layers.albedo = input_layers[1];
+ guide_layers.normal = input_layers[2];
+# endif
+
// Finally run denonising
OptixDenoiserParams params = {}; // All parameters are disabled/zero
+# if OPTIX_ABI_VERSION >= 47
check_result_optix_ret(optixDenoiserInvoke(denoiser,
- 0,
+ NULL,
+ &params,
+ denoiser_state.device_pointer,
+ scratch_offset,
+ &guide_layers,
+ &image_layers,
+ 1,
+ overlap_offset.x,
+ overlap_offset.y,
+ denoiser_state.device_pointer + scratch_offset,
+ scratch_size));
+# else
+ check_result_optix_ret(optixDenoiserInvoke(denoiser,
+ NULL,
&params,
denoiser_state.device_pointer,
scratch_offset,
@@ -1051,6 +1082,7 @@ class OptiXDevice : public CUDADevice {
output_layers,
denoiser_state.device_pointer + scratch_offset,
scratch_size));
+# endif
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
void *output_args[] = {&input_ptr,
diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp
index c926f6ab8ef..57f25283f85 100644
--- a/intern/cycles/graph/node.cpp
+++ b/intern/cycles/graph/node.cpp
@@ -367,9 +367,17 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
case SocketType::TRANSFORM_ARRAY:
copy_array<Transform>(this, socket, &other, other_socket);
break;
- case SocketType::NODE_ARRAY:
+ case SocketType::NODE_ARRAY: {
copy_array<void *>(this, socket, &other, other_socket);
+
+ array<Node *> &node_array = get_socket_value<array<Node *>>(this, socket);
+
+ for (Node *node : node_array) {
+ node->reference();
+ }
+
break;
+ }
default:
assert(0);
break;
@@ -379,6 +387,14 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
const void *src = ((char *)&other) + other_socket.struct_offset;
void *dst = ((char *)this) + socket.struct_offset;
memcpy(dst, src, socket.size());
+
+ if (socket.type == SocketType::NODE) {
+ Node *node = get_socket_value<Node *>(this, socket);
+
+ if (node) {
+ node->reference();
+ }
+ }
}
}
@@ -773,6 +789,26 @@ void Node::set_owner(const NodeOwner *owner_)
owner = owner_;
}
+void Node::dereference_all_used_nodes()
+{
+ foreach (const SocketType &socket, type->inputs) {
+ if (socket.type == SocketType::NODE) {
+ Node *node = get_socket_value<Node *>(this, socket);
+
+ if (node) {
+ node->dereference();
+ }
+ }
+ else if (socket.type == SocketType::NODE_ARRAY) {
+ const array<Node *> &nodes = get_socket_value<array<Node *>>(this, socket);
+
+ for (Node *node : nodes) {
+ node->dereference();
+ }
+ }
+ }
+}
+
bool Node::socket_is_modified(const SocketType &input) const
{
return (socket_modified & input.modified_flag_bit) != 0;
@@ -803,6 +839,25 @@ template<typename T> void Node::set_if_different(const SocketType &input, T valu
socket_modified |= input.modified_flag_bit;
}
+void Node::set_if_different(const SocketType &input, Node *value)
+{
+ if (get_socket_value<Node *>(this, input) == value) {
+ return;
+ }
+
+ Node *old_node = get_socket_value<Node *>(this, input);
+ if (old_node) {
+ old_node->dereference();
+ }
+
+ if (value) {
+ value->reference();
+ }
+
+ get_socket_value<Node *>(this, input) = value;
+ socket_modified |= input.modified_flag_bit;
+}
+
template<typename T> void Node::set_if_different(const SocketType &input, array<T> &value)
{
if (!socket_is_modified(input)) {
@@ -815,6 +870,27 @@ template<typename T> void Node::set_if_different(const SocketType &input, array<
socket_modified |= input.modified_flag_bit;
}
+void Node::set_if_different(const SocketType &input, array<Node *> &value)
+{
+ if (!socket_is_modified(input)) {
+ if (get_socket_value<array<Node *>>(this, input) == value) {
+ return;
+ }
+ }
+
+ array<Node *> &old_nodes = get_socket_value<array<Node *>>(this, input);
+ for (Node *old_node : old_nodes) {
+ old_node->dereference();
+ }
+
+ for (Node *new_node : value) {
+ new_node->reference();
+ }
+
+ get_socket_value<array<Node *>>(this, input).steal_data(value);
+ socket_modified |= input.modified_flag_bit;
+}
+
void Node::print_modified_sockets() const
{
printf("Node : %s\n", name.c_str());
diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h
index 2fc9a1e0281..aa365baeccd 100644
--- a/intern/cycles/graph/node.h
+++ b/intern/cycles/graph/node.h
@@ -177,8 +177,32 @@ struct Node {
const NodeOwner *get_owner() const;
void set_owner(const NodeOwner *owner_);
+ int reference_count() const
+ {
+ return ref_count;
+ }
+
+ void reference()
+ {
+ ref_count += 1;
+ }
+
+ void dereference()
+ {
+ ref_count -= 1;
+ }
+
+ /* Set the reference count to zero. This should only be called when we know for sure that the
+ * Node is not used by anyone else. For now, this is only the case when "deleting" shaders, as
+ * they are never actually deleted. */
+ void clear_reference_count()
+ {
+ ref_count = 0;
+ }
+
protected:
const NodeOwner *owner;
+ int ref_count{0};
template<typename T> static T &get_socket_value(const Node *node, const SocketType &socket)
{
@@ -189,7 +213,19 @@ struct Node {
template<typename T> void set_if_different(const SocketType &input, T value);
+ /* Explicit overload for Node sockets so we can handle reference counting. The old Node is
+ * dereferenced, and the new one is referenced. */
+ void set_if_different(const SocketType &input, Node *value);
+
template<typename T> void set_if_different(const SocketType &input, array<T> &value);
+
+ /* Explicit overload for Node sockets so we can handle reference counting. The old Nodes are
+ * dereferenced, and the new ones are referenced. */
+ void set_if_different(const SocketType &input, array<Node *> &value);
+
+ /* Call this function in derived classes' destructors to ensure that used Nodes are dereferenced
+ * properly. */
+ void dereference_all_used_nodes();
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index f6b4b963a7a..ea0f16c9233 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -93,6 +93,7 @@ set(SRC_BVH_HEADERS
bvh/bvh_local.h
bvh/bvh_traversal.h
bvh/bvh_types.h
+ bvh/bvh_util.h
bvh/bvh_volume.h
bvh/bvh_volume_all.h
bvh/bvh_embree.h
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 3049f243ae9..3a3f38539c5 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -29,9 +29,10 @@
# include "kernel/bvh/bvh_embree.h"
#endif
-CCL_NAMESPACE_BEGIN
-
#include "kernel/bvh/bvh_types.h"
+#include "kernel/bvh/bvh_util.h"
+
+CCL_NAMESPACE_BEGIN
#ifndef __KERNEL_OPTIX__
@@ -533,97 +534,4 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
}
#endif /* __VOLUME_RECORD_ALL__ */
-/* Ray offset to avoid self intersection.
- *
- * This function should be used to compute a modified ray start position for
- * rays leaving from a surface. */
-
-ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
-{
-#ifdef __INTERSECTION_REFINE__
- const float epsilon_f = 1e-5f;
- /* ideally this should match epsilon_f, but instancing and motion blur
- * precision makes it problematic */
- const float epsilon_test = 1.0f;
- const int epsilon_i = 32;
-
- float3 res;
-
- /* x component */
- if (fabsf(P.x) < epsilon_test) {
- res.x = P.x + Ng.x * epsilon_f;
- }
- else {
- uint ix = __float_as_uint(P.x);
- ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
- res.x = __uint_as_float(ix);
- }
-
- /* y component */
- if (fabsf(P.y) < epsilon_test) {
- res.y = P.y + Ng.y * epsilon_f;
- }
- else {
- uint iy = __float_as_uint(P.y);
- iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
- res.y = __uint_as_float(iy);
- }
-
- /* z component */
- if (fabsf(P.z) < epsilon_test) {
- res.z = P.z + Ng.z * epsilon_f;
- }
- else {
- uint iz = __float_as_uint(P.z);
- iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
- res.z = __uint_as_float(iz);
- }
-
- return res;
-#else
- const float epsilon_f = 1e-4f;
- return P + epsilon_f * Ng;
-#endif
-}
-
-#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
-/* ToDo: Move to another file? */
-ccl_device int intersections_compare(const void *a, const void *b)
-{
- const Intersection *isect_a = (const Intersection *)a;
- const Intersection *isect_b = (const Intersection *)b;
-
- if (isect_a->t < isect_b->t)
- return -1;
- else if (isect_a->t > isect_b->t)
- return 1;
- else
- return 0;
-}
-#endif
-
-#if defined(__SHADOW_RECORD_ALL__)
-ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
-{
-# ifdef __KERNEL_GPU__
- /* Use bubble sort which has more friendly memory pattern on GPU. */
- bool swapped;
- do {
- swapped = false;
- for (int j = 0; j < num_hits - 1; ++j) {
- if (hits[j].t > hits[j + 1].t) {
- struct Intersection tmp = hits[j];
- hits[j] = hits[j + 1];
- hits[j + 1] = tmp;
- swapped = true;
- }
- }
- --num_hits;
- } while (swapped);
-# else
- qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
-# endif
-}
-#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h
index dccd257d2de..2e94b1d7c37 100644
--- a/intern/cycles/kernel/bvh/bvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h
@@ -180,25 +180,10 @@ ccl_device_inline
/* todo: optimize so primitive visibility flag indicates if
* the primitive has a transparent shadow shader? */
- int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
- int shader = 0;
-
-#ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
-#endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-#ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-#endif
- int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
+ const int flags = intersection_get_shader_flags(kg, isect_array);
/* if no transparent shadows, all light is blocked */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
+ if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) {
return true;
}
/* if maximum number of hits reached, block all light */
diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h
new file mode 100644
index 00000000000..a694e4dc259
--- /dev/null
+++ b/intern/cycles/kernel/bvh/bvh_util.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+CCL_NAMESPACE_BEGIN
+
+/* Ray offset to avoid self intersection.
+ *
+ * This function should be used to compute a modified ray start position for
+ * rays leaving from a surface. */
+
+ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
+{
+#ifdef __INTERSECTION_REFINE__
+ const float epsilon_f = 1e-5f;
+ /* ideally this should match epsilon_f, but instancing and motion blur
+ * precision makes it problematic */
+ const float epsilon_test = 1.0f;
+ const int epsilon_i = 32;
+
+ float3 res;
+
+ /* x component */
+ if (fabsf(P.x) < epsilon_test) {
+ res.x = P.x + Ng.x * epsilon_f;
+ }
+ else {
+ uint ix = __float_as_uint(P.x);
+ ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
+ res.x = __uint_as_float(ix);
+ }
+
+ /* y component */
+ if (fabsf(P.y) < epsilon_test) {
+ res.y = P.y + Ng.y * epsilon_f;
+ }
+ else {
+ uint iy = __float_as_uint(P.y);
+ iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
+ res.y = __uint_as_float(iy);
+ }
+
+ /* z component */
+ if (fabsf(P.z) < epsilon_test) {
+ res.z = P.z + Ng.z * epsilon_f;
+ }
+ else {
+ uint iz = __float_as_uint(P.z);
+ iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
+ res.z = __uint_as_float(iz);
+ }
+
+ return res;
+#else
+ const float epsilon_f = 1e-4f;
+ return P + epsilon_f * Ng;
+#endif
+}
+
+#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
+/* ToDo: Move to another file? */
+ccl_device int intersections_compare(const void *a, const void *b)
+{
+ const Intersection *isect_a = (const Intersection *)a;
+ const Intersection *isect_b = (const Intersection *)b;
+
+ if (isect_a->t < isect_b->t)
+ return -1;
+ else if (isect_a->t > isect_b->t)
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+#if defined(__SHADOW_RECORD_ALL__)
+ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
+{
+ kernel_assert(num_hits > 0);
+
+# ifdef __KERNEL_GPU__
+ /* Use bubble sort which has more friendly memory pattern on GPU. */
+ bool swapped;
+ do {
+ swapped = false;
+ for (int j = 0; j < num_hits - 1; ++j) {
+ if (hits[j].t > hits[j + 1].t) {
+ struct Intersection tmp = hits[j];
+ hits[j] = hits[j + 1];
+ hits[j + 1] = tmp;
+ swapped = true;
+ }
+ }
+ --num_hits;
+ } while (swapped);
+# else
+ qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
+# endif
+}
+#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
+
+/* Utility to quickly get a shader flags from an intersection. */
+
+ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg,
+ const Intersection *isect)
+{
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ int shader = 0;
+
+#ifdef __HAIR__
+ if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
+#endif
+ {
+ shader = kernel_tex_fetch(__tri_shader, prim);
+ }
+#ifdef __HAIR__
+ else {
+ float4 str = kernel_tex_fetch(__curves, prim);
+ shader = __float_as_int(str.z);
+ }
+#endif
+
+ return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
+}
+
+ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg,
+ const Intersection *isect)
+{
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ int shader = 0;
+
+#ifdef __HAIR__
+ if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
+#endif
+ {
+ shader = kernel_tex_fetch(__tri_shader, prim);
+ }
+#ifdef __HAIR__
+ else {
+ float4 str = kernel_tex_fetch(__curves, prim);
+ shader = __float_as_int(str.z);
+ }
+#endif
+
+ return shader & SHADER_MASK;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 5681510fc25..dd2390808ea 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -65,7 +65,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg,
uint visibility = path_state_ray_visibility(kg, state);
if (path_state_ao_bounce(kg, state)) {
- visibility = PATH_RAY_SHADOW;
ray->t = kernel_data.background.ao_distance;
}
@@ -416,7 +415,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
- break;
+ if (intersection_get_shader_flags(kg, &isect) &
+ (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
+ state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+ }
+ else {
+ break;
+ }
}
/* Setup shader data. */
@@ -554,7 +559,13 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
- break;
+ if (intersection_get_shader_flags(kg, &isect) &
+ (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
+ state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+ }
+ else {
+ break;
+ }
}
/* Setup shader data. */
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index c661d77edb1..74fa2826cd4 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -891,6 +891,8 @@ enum ShaderDataFlag {
SD_HAS_CONSTANT_EMISSION = (1 << 27),
/* Needs to access attributes for volume rendering */
SD_NEED_VOLUME_ATTRIBUTES = (1 << 28),
+ /* Shader has emission */
+ SD_HAS_EMISSION = (1 << 29),
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |
diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/shaders/node_noise_texture.osl
index 61c0216910b..01196ab633a 100644
--- a/intern/cycles/kernel/shaders/node_noise_texture.osl
+++ b/intern/cycles/kernel/shaders/node_noise_texture.osl
@@ -25,7 +25,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h
index 920dd7d9d02..61fd9553802 100644
--- a/intern/cycles/kernel/svm/svm_noisetex.h
+++ b/intern/cycles/kernel/svm/svm_noisetex.h
@@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index c67919b375a..feead27c5ca 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -24,6 +24,7 @@ set(INC_SYS
set(SRC
alembic.cpp
+ alembic_read.cpp
attribute.cpp
background.cpp
bake.cpp
@@ -67,6 +68,7 @@ set(SRC
set(SRC_HEADERS
alembic.h
+ alembic_read.h
attribute.h
bake.h
background.h
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
index c3a7b20f512..cf345ee075d 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -16,6 +16,7 @@
#include "render/alembic.h"
+#include "render/alembic_read.h"
#include "render/camera.h"
#include "render/curves.h"
#include "render/mesh.h"
@@ -34,7 +35,48 @@ using namespace Alembic::AbcGeom;
CCL_NAMESPACE_BEGIN
-/* TODO(@kevindietrich): motion blur support */
+/* TODO(kevindietrich): motion blur support. */
+
+template<typename SchemaType>
+static vector<FaceSetShaderIndexPair> parse_face_sets_for_shader_assignment(
+ SchemaType &schema, const array<Node *> &used_shaders)
+{
+ vector<FaceSetShaderIndexPair> result;
+
+ std::vector<std::string> face_set_names;
+ schema.getFaceSetNames(face_set_names);
+
+ if (face_set_names.empty()) {
+ return result;
+ }
+
+ for (const std::string &face_set_name : face_set_names) {
+ int shader_index = 0;
+
+ for (Node *node : used_shaders) {
+ if (node->name == face_set_name) {
+ break;
+ }
+
+ ++shader_index;
+ }
+
+ if (shader_index >= used_shaders.size()) {
+ /* use the first shader instead if none was found */
+ shader_index = 0;
+ }
+
+ const Alembic::AbcGeom::IFaceSet face_set = schema.getFaceSet(face_set_name);
+
+ if (!face_set.valid()) {
+ continue;
+ }
+
+ result.push_back({face_set, shader_index});
+ }
+
+ return result;
+}
void CachedData::clear()
{
@@ -54,7 +96,7 @@ void CachedData::clear()
subd_start_corner.clear();
transforms.clear();
triangles.clear();
- triangles_loops.clear();
+ uv_loops.clear();
vertices.clear();
for (CachedAttribute &attr : attributes) {
@@ -101,7 +143,7 @@ bool CachedData::is_constant() const
CHECK_IF_CONSTANT(subd_start_corner)
CHECK_IF_CONSTANT(transforms)
CHECK_IF_CONSTANT(triangles)
- CHECK_IF_CONSTANT(triangles_loops)
+ CHECK_IF_CONSTANT(uv_loops)
CHECK_IF_CONSTANT(vertices)
for (const CachedAttribute &attr : attributes) {
@@ -140,7 +182,7 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only)
subd_start_corner.invalidate_last_loaded_time();
transforms.invalidate_last_loaded_time();
triangles.invalidate_last_loaded_time();
- triangles_loops.invalidate_last_loaded_time();
+ uv_loops.invalidate_last_loaded_time();
vertices.invalidate_last_loaded_time();
}
@@ -161,7 +203,7 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
subd_start_corner.set_time_sampling(time_sampling);
transforms.set_time_sampling(time_sampling);
triangles.set_time_sampling(time_sampling);
- triangles_loops.set_time_sampling(time_sampling);
+ uv_loops.set_time_sampling(time_sampling);
vertices.set_time_sampling(time_sampling);
for (CachedAttribute &attr : attributes) {
@@ -169,36 +211,6 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
}
}
-/* get the sample times to load data for the given the start and end frame of the procedural */
-static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
- const TimeSampling &time_sampling,
- size_t num_samples)
-{
- set<chrono_t> result;
-
- if (num_samples < 2) {
- result.insert(0.0);
- return result;
- }
-
- double start_frame = (double)(proc->get_start_frame() / proc->get_frame_rate());
- double end_frame = (double)((proc->get_end_frame() + 1) / proc->get_frame_rate());
-
- size_t start_index = time_sampling.getFloorIndex(start_frame, num_samples).first;
- size_t end_index = time_sampling.getCeilIndex(end_frame, num_samples).first;
-
- for (size_t i = start_index; i < end_index; ++i) {
- result.insert(time_sampling.getSampleTime(i));
- }
-
- return result;
-}
-
-static float3 make_float3_from_yup(const V3f &v)
-{
- return make_float3(v.x, -v.z, v.y);
-}
-
static M44d convert_yup_zup(const M44d &mtx, float scale_mult)
{
V3d scale, shear, rotation, translation;
@@ -366,249 +378,6 @@ static Transform make_transform(const M44d &a, float scale)
return trans;
}
-static void add_uvs(AlembicProcedural *proc,
- const IV2fGeomParam &uvs,
- CachedData &cached_data,
- Progress &progress)
-{
- if (uvs.getScope() != kFacevaryingScope) {
- return;
- }
-
- const TimeSamplingPtr time_sampling_ptr = uvs.getTimeSampling();
-
- TimeSampling time_sampling;
- if (time_sampling_ptr) {
- time_sampling = *time_sampling_ptr;
- }
-
- std::string name = Alembic::Abc::GetSourceName(uvs.getMetaData());
-
- /* According to the convention, primary UVs should have had their name
- * set using Alembic::Abc::SetSourceName, but you can't expect everyone
- * to follow it! :) */
- if (name.empty()) {
- name = uvs.getName();
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(name), time_sampling);
- attr.std = ATTR_STD_UV;
-
- ccl::set<chrono_t> times = get_relevant_sample_times(proc, time_sampling, uvs.getNumSamples());
-
- /* Keys used to determine if the UVs do actually change over time. */
- ArraySample::Key previous_indices_key;
- ArraySample::Key previous_values_key;
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
-
- if (!uvsample.valid()) {
- continue;
- }
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- continue;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- const ArraySample::Key indices_key = uvsample.getIndices()->getKey();
- const ArraySample::Key values_key = uvsample.getVals()->getKey();
-
- if (indices_key == previous_indices_key && values_key == previous_values_key) {
- attr.data.reuse_data_for_last_time(time);
- }
- else {
- const unsigned int *indices = uvsample.getIndices()->get();
- const V2f *values = uvsample.getVals()->get();
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = indices[loop.x];
- unsigned int v1 = indices[loop.y];
- unsigned int v2 = indices[loop.z];
-
- data_float2[0] = make_float2(values[v0][0], values[v0][1]);
- data_float2[1] = make_float2(values[v1][0], values[v1][1]);
- data_float2[2] = make_float2(values[v2][0], values[v2][1]);
- data_float2 += 3;
- }
-
- attr.data.add_data(data, time);
- }
-
- previous_indices_key = indices_key;
- previous_values_key = values_key;
- }
-}
-
-static void add_normals(const Int32ArraySamplePtr face_indices,
- const IN3fGeomParam &normals,
- double time,
- CachedData &cached_data)
-{
- switch (normals.getScope()) {
- case kFacevaryingScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const int *face_indices_array = face_indices->get();
- const N3fArraySamplePtr values = sample.getVals();
-
- for (size_t i = 0; i < face_indices->size(); ++i) {
- int point_index = face_indices_array[i];
- data_float3[point_index] = make_float3_from_yup(values->get()[i]);
- }
-
- attr.data.add_data(data, time);
- break;
- }
- case kVaryingScope:
- case kVertexScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const Imath::V3f *values = sample.getVals()->get();
-
- for (size_t i = 0; i < vertices->size(); ++i) {
- data_float3[i] = make_float3_from_yup(values[i]);
- }
-
- attr.data.add_data(data, time);
-
- break;
- }
- default: {
- break;
- }
- }
-}
-
-static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
-{
- if (!positions) {
- return;
- }
-
- array<float3> vertices;
- vertices.reserve(positions->size());
-
- for (size_t i = 0; i < positions->size(); i++) {
- V3f f = positions->get()[i];
- vertices.push_back_reserved(make_float3_from_yup(f));
- }
-
- cached_data.vertices.add_data(vertices, time);
-}
-
-static void add_triangles(const Int32ArraySamplePtr face_counts,
- const Int32ArraySamplePtr face_indices,
- double time,
- CachedData &cached_data,
- const array<int> &polygon_to_shader)
-{
- if (!face_counts || !face_indices) {
- return;
- }
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- size_t num_triangles = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_triangles += face_counts_array[i] - 2;
- }
-
- array<int> shader;
- array<int3> triangles;
- array<int3> triangles_loops;
- shader.reserve(num_triangles);
- triangles.reserve(num_triangles);
- triangles_loops.reserve(num_triangles);
- int index_offset = 0;
-
- for (size_t i = 0; i < num_faces; i++) {
- int current_shader = 0;
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- for (int j = 0; j < face_counts_array[i] - 2; j++) {
- int v0 = face_indices_array[index_offset];
- int v1 = face_indices_array[index_offset + j + 1];
- int v2 = face_indices_array[index_offset + j + 2];
-
- shader.push_back_reserved(current_shader);
-
- /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
- triangles.push_back_reserved(make_int3(v2, v1, v0));
- triangles_loops.push_back_reserved(
- make_int3(index_offset + j + 2, index_offset + j + 1, index_offset));
- }
-
- index_offset += face_counts_array[i];
- }
-
- cached_data.triangles.add_data(triangles, time);
- cached_data.triangles_loops.add_data(triangles_loops, time);
- cached_data.shader.add_data(shader, time);
-}
-
NODE_DEFINE(AlembicObject)
{
NodeType *type = NodeType::add("alembic_object", create);
@@ -648,398 +417,137 @@ bool AlembicObject::has_data_loaded() const
return data_loaded;
}
-void AlembicObject::update_shader_attributes(const ICompoundProperty &arb_geom_params,
- Progress &progress)
-{
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- foreach (const AttributeRequest &attr, requested_attributes.requests) {
- if (progress.get_cancel()) {
- return;
- }
-
- bool attr_exists = false;
- foreach (CachedData::CachedAttribute &cached_attr, cached_data.attributes) {
- if (cached_attr.name == attr.name) {
- attr_exists = true;
- break;
- }
- }
-
- if (attr_exists) {
- continue;
- }
-
- read_attribute(arb_geom_params, attr.name, progress);
- }
-
- cached_data.invalidate_last_loaded_time(true);
- need_shader_update = false;
-}
-
-template<typename SchemaType>
-void AlembicObject::read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- ISampleSelector sample_sel)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ IPolyMeshSchema &schema,
+ Progress &progress)
{
- std::vector<std::string> face_sets;
- schema.getFaceSetNames(face_sets);
-
- if (face_sets.empty()) {
- return;
- }
-
- const Int32ArraySamplePtr face_counts = schema.getFaceCountsProperty().getValue();
-
- polygon_to_shader.resize(face_counts->size());
-
- foreach (const std::string &face_set_name, face_sets) {
- int shader_index = 0;
-
- foreach (Node *node, get_used_shaders()) {
- if (node->name == face_set_name) {
- break;
- }
-
- ++shader_index;
- }
-
- if (shader_index >= get_used_shaders().size()) {
- /* use the first shader instead if none was found */
- shader_index = 0;
- }
-
- const IFaceSet face_set = schema.getFaceSet(face_set_name);
-
- if (!face_set.valid()) {
- continue;
- }
-
- const IFaceSetSchema face_schem = face_set.getSchema();
- const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
- const Int32ArraySamplePtr group_faces = face_sample.getFaces();
- const size_t num_group_faces = group_faces->size();
-
- for (size_t l = 0; l < num_group_faces; l++) {
- size_t pos = (*group_faces)[l];
-
- if (pos >= polygon_to_shader.size()) {
- continue;
- }
-
- polygon_to_shader[pos] = shader_index;
- }
- }
-}
-
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- IPolyMeshSchema &schema,
- Progress &progress)
-{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- const IN3fGeomParam &normals = schema.getNormalsParam();
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* Key used to determine if the triangles change over time, if the key is the same as the
- * last one, we can avoid creating a new entry in the cache and simply point to the last
- * frame. */
- ArraySample::Key previous_key;
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IPolyMeshSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- /* Only copy triangles for other frames if the topology is changing over time as well. */
- if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) {
- const ArraySample::Key key = sample.getFaceIndices()->getKey();
-
- if (key == previous_key) {
- cached_data.triangles.reuse_data_for_last_time(time);
- cached_data.triangles_loops.reuse_data_for_last_time(time);
- cached_data.shader.reuse_data_for_last_time(time);
- }
- else {
- /* start by reading the face sets (per face shader), as we directly split polygons to
- * triangles
- */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- add_triangles(
- sample.getFaceCounts(), sample.getFaceIndices(), time, cached_data, polygon_to_shader);
- }
+ cached_data.clear();
- previous_key = key;
- }
+ PolyMeshSchemaData data;
+ data.topology_variance = schema.getTopologyVariance();
+ data.time_sampling = schema.getTimeSampling();
+ data.positions = schema.getPositionsProperty();
+ data.face_counts = schema.getFaceCountsProperty();
+ data.face_indices = schema.getFaceIndicesProperty();
+ data.normals = schema.getNormalsParam();
+ data.num_samples = schema.getNumSamples();
+ data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
- if (normals.valid()) {
- add_normals(sample.getFaceIndices(), normals, time, cached_data);
- }
- }
+ read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
- update_shader_attributes(schema.getArbGeomParams(), progress);
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
if (progress.get_cancel()) {
return;
}
- const IV2fGeomParam &uvs = schema.getUVsParam();
-
- if (uvs.valid()) {
- add_uvs(proc, uvs, cached_data, progress);
- }
-
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ ISubDSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ISubDSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- const Int32ArraySamplePtr face_counts = sample.getFaceCounts();
- const Int32ArraySamplePtr face_indices = sample.getFaceIndices();
-
- /* start by reading the face sets (per face shader) */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- /* read faces */
- array<int> subd_start_corner;
- array<int> shader;
- array<int> subd_num_corners;
- array<bool> subd_smooth;
- array<int> subd_ptex_offset;
- array<int> subd_face_corners;
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- int num_ngons = 0;
- int num_corners = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
- num_corners += face_counts_array[i];
- }
-
- subd_start_corner.reserve(num_faces);
- subd_num_corners.reserve(num_faces);
- subd_smooth.reserve(num_faces);
- subd_ptex_offset.reserve(num_faces);
- shader.reserve(num_faces);
- subd_face_corners.reserve(num_corners);
-
- int start_corner = 0;
- int current_shader = 0;
- int ptex_offset = 0;
-
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_corners = face_counts_array[i];
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- subd_start_corner.push_back_reserved(start_corner);
- subd_num_corners.push_back_reserved(num_corners);
-
- for (int j = 0; j < num_corners; ++j) {
- subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
- }
-
- shader.push_back_reserved(current_shader);
- subd_smooth.push_back_reserved(1);
- subd_ptex_offset.push_back_reserved(ptex_offset);
-
- ptex_offset += (num_corners == 4 ? 1 : num_corners);
-
- start_corner += num_corners;
- }
-
- cached_data.shader.add_data(shader, time);
- cached_data.subd_start_corner.add_data(subd_start_corner, time);
- cached_data.subd_num_corners.add_data(subd_num_corners, time);
- cached_data.subd_smooth.add_data(subd_smooth, time);
- cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
- cached_data.subd_face_corners.add_data(subd_face_corners, time);
- cached_data.num_ngons.add_data(num_ngons, time);
-
- /* read creases */
- Int32ArraySamplePtr creases_length = sample.getCreaseLengths();
- Int32ArraySamplePtr creases_indices = sample.getCreaseIndices();
- FloatArraySamplePtr creases_sharpnesses = sample.getCreaseSharpnesses();
-
- if (creases_length && creases_indices && creases_sharpnesses) {
- array<int> creases_edge;
- array<float> creases_weight;
-
- creases_edge.reserve(creases_sharpnesses->size() * 2);
- creases_weight.reserve(creases_sharpnesses->size());
-
- int length_offset = 0;
- int weight_offset = 0;
- for (size_t c = 0; c < creases_length->size(); ++c) {
- const int crease_length = creases_length->get()[c];
-
- for (size_t j = 0; j < crease_length - 1; ++j) {
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
- creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
- }
-
- length_offset += crease_length;
- }
-
- cached_data.subd_creases_edge.add_data(creases_edge, time);
- cached_data.subd_creases_weight.add_data(creases_weight, time);
- }
- }
+ cached_data.clear();
- /* TODO(@kevindietrich) : attributes, need test files */
+ SubDSchemaData data;
+ data.time_sampling = schema.getTimeSampling();
+ data.num_samples = schema.getNumSamples();
+ data.topology_variance = schema.getTopologyVariance();
+ data.face_counts = schema.getFaceCountsProperty();
+ data.face_indices = schema.getFaceIndicesProperty();
+ data.positions = schema.getPositionsProperty();
+ data.face_varying_interpolate_boundary = schema.getFaceVaryingInterpolateBoundaryProperty();
+ data.face_varying_propagate_corners = schema.getFaceVaryingPropagateCornersProperty();
+ data.interpolate_boundary = schema.getInterpolateBoundaryProperty();
+ data.crease_indices = schema.getCreaseIndicesProperty();
+ data.crease_lengths = schema.getCreaseLengthsProperty();
+ data.crease_sharpnesses = schema.getCreaseSharpnessesProperty();
+ data.corner_indices = schema.getCornerIndicesProperty();
+ data.corner_sharpnesses = schema.getCornerSharpnessesProperty();
+ data.holes = schema.getHolesProperty();
+ data.subdivision_scheme = schema.getSubdivisionSchemeProperty();
+ data.velocities = schema.getVelocitiesProperty();
+ data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
+
+ read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
+
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- const ICurvesSchema &schema,
- Progress &progress,
- float default_radius)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const ICurvesSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ICurvesSchema::Sample sample = schema.getValue(iss);
-
- const Int32ArraySamplePtr curves_num_vertices = sample.getCurvesNumVertices();
- const P3fArraySamplePtr position = sample.getPositions();
-
- const IFloatGeomParam widths_param = schema.getWidthsParam();
- FloatArraySamplePtr radiuses;
-
- if (widths_param.valid()) {
- IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(iss);
- radiuses = wsample.getVals();
- }
-
- const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
- float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : default_radius;
-
- array<float3> curve_keys;
- array<float> curve_radius;
- array<int> curve_first_key;
- array<int> curve_shader;
-
- const bool is_homogenous = schema.getTopologyVariance() == kHomogenousTopology;
-
- curve_keys.reserve(position->size());
- curve_radius.reserve(position->size());
- curve_first_key.reserve(curves_num_vertices->size());
- curve_shader.reserve(curves_num_vertices->size());
-
- int offset = 0;
- for (size_t i = 0; i < curves_num_vertices->size(); i++) {
- const int num_vertices = curves_num_vertices->get()[i];
-
- for (int j = 0; j < num_vertices; j++) {
- const V3f &f = position->get()[offset + j];
- curve_keys.push_back_reserved(make_float3_from_yup(f));
-
- if (do_radius) {
- radius = (*radiuses)[offset + j];
- }
-
- curve_radius.push_back_reserved(radius * radius_scale);
- }
-
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- curve_first_key.push_back_reserved(offset);
- curve_shader.push_back_reserved(0);
- }
-
- offset += num_vertices;
- }
+ cached_data.clear();
- cached_data.curve_keys.add_data(curve_keys, time);
- cached_data.curve_radius.add_data(curve_radius, time);
+ CurvesSchemaData data;
+ data.positions = schema.getPositionsProperty();
+ data.position_weights = schema.getPositionWeightsProperty();
+ data.normals = schema.getNormalsParam();
+ data.knots = schema.getKnotsProperty();
+ data.orders = schema.getOrdersProperty();
+ data.widths = schema.getWidthsParam();
+ data.velocities = schema.getVelocitiesProperty();
+ data.time_sampling = schema.getTimeSampling();
+ data.topology_variance = schema.getTopologyVariance();
+ data.num_samples = schema.getNumSamples();
+ data.num_vertices = schema.getNumVerticesProperty();
+ data.default_radius = proc->get_default_radius();
+ data.radius_scale = get_radius_scale();
+
+ read_geometry_data(proc, cached_data, data, progress);
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- cached_data.curve_first_key.add_data(curve_first_key, time);
- cached_data.curve_shader.add_data(curve_shader, time);
- }
+ if (progress.get_cancel()) {
+ return;
}
- // TODO(@kevindietrich): attributes, need example files
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::setup_transform_cache(float scale)
+void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale)
{
cached_data.transforms.clear();
cached_data.transforms.invalidate_last_loaded_time();
@@ -1102,188 +610,6 @@ AttributeRequestSet AlembicObject::get_requested_attributes()
return requested_attributes;
}
-void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress)
-{
- const PropertyHeader *prop = arb_geom_params.getPropertyHeader(attr_name.c_str());
-
- if (prop == nullptr) {
- return;
- }
-
- if (IV2fProperty::matches(prop->getMetaData()) && Alembic::AbcGeom::isUV(*prop)) {
- const IV2fGeomParam &param = IV2fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IV2fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- if (param.getScope() == kFacevaryingScope) {
- V2fArraySamplePtr values = sample.getVals();
- UInt32ArraySamplePtr indices = sample.getIndices();
-
- attribute.std = ATTR_STD_NONE;
- attribute.element = ATTR_ELEMENT_CORNER;
- attribute.type_desc = TypeFloat2;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = (*indices)[loop.x];
- unsigned int v1 = (*indices)[loop.y];
- unsigned int v2 = (*indices)[loop.z];
-
- data_float2[0] = make_float2((*values)[v0][0], (*values)[v0][1]);
- data_float2[1] = make_float2((*values)[v1][0], (*values)[v1][1]);
- data_float2[2] = make_float2((*values)[v2][0], (*values)[v2][1]);
- data_float2 += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC3fProperty::matches(prop->getMetaData())) {
- const IC3fGeomParam &param = IC3fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC3fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C3fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C3f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC4fProperty::matches(prop->getMetaData())) {
- const IC4fGeomParam &param = IC4fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC4fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C4fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C4f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
-}
-
/* Update existing attributes and remove any attribute not in the cached_data, those attributes
* were added by Cycles (e.g. face normals) */
static void update_attributes(AttributeSet &attributes, CachedData &cached_data, double frame_time)
@@ -1291,7 +617,11 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
set<Attribute *> cached_attributes;
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
- const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null();
+ const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time);
+
+ if (result.has_no_data_for_time()) {
+ continue;
+ }
Attribute *attr = nullptr;
if (attribute.std != ATTR_STD_NONE) {
@@ -1304,18 +634,19 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
cached_attributes.insert(attr);
- if (!attr_data) {
- /* no new data */
+ if (!result.has_new_data()) {
continue;
}
+ const ccl::array<char> &attr_data = result.get_data();
+
/* weak way of detecting if the topology has changed
* todo: reuse code from device_update patch */
- if (attr->buffer.size() != attr_data->size()) {
- attr->buffer.resize(attr_data->size());
+ if (attr->buffer.size() != attr_data.size()) {
+ attr->buffer.resize(attr_data.size());
}
- memcpy(attr->data(), attr_data->data(), attr_data->size());
+ memcpy(attr->data(), attr_data.data(), attr_data.size());
attr->modified = true;
}
@@ -1476,6 +807,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
read_subd(object, frame_time);
}
+ object->need_shader_update = false;
object->clear_modified();
}
@@ -1960,12 +1292,17 @@ void AlembicProcedural::build_caches(Progress &progress)
if (!object->has_data_loaded()) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
else if (object->schema_type == AlembicObject::CURVES) {
@@ -1973,24 +1310,29 @@ void AlembicProcedural::build_caches(Progress &progress)
object->radius_scale_is_modified()) {
ICurves curves(object->iobject, Alembic::Abc::kWrapExisting);
ICurvesSchema schema = curves.getSchema();
- object->load_all_data(this, schema, progress, default_radius);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
}
else if (object->schema_type == AlembicObject::SUBD) {
if (!object->has_data_loaded()) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
ISubDSchema schema = subd_mesh.getSchema();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
ISubDSchema schema = subd_mesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
- object->setup_transform_cache(scale);
+ object->setup_transform_cache(object->get_cached_data(), scale);
}
}
}
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
index 3bbd10fad61..61c0e40fe4a 100644
--- a/intern/cycles/render/alembic.h
+++ b/intern/cycles/render/alembic.h
@@ -152,6 +152,10 @@ template<typename T> class DataStore {
double last_loaded_time = std::numeric_limits<double>::max();
public:
+ /* Keys used to compare values. */
+ Alembic::AbcCoreAbstract::ArraySample::Key key1;
+ Alembic::AbcCoreAbstract::ArraySample::Key key2;
+
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
{
time_sampling = time_sampling_;
@@ -225,6 +229,11 @@ template<typename T> class DataStore {
index_data_map.push_back({time, data_index.source_time, data_index.index});
}
+ void add_no_data(double time)
+ {
+ index_data_map.push_back({time, time, -1ul});
+ }
+
bool is_constant() const
{
return data.size() <= 1;
@@ -284,7 +293,7 @@ struct CachedData {
DataStore<array<int3>> triangles{};
/* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
* (like UVs) */
- DataStore<array<int3>> triangles_loops{};
+ DataStore<array<int>> uv_loops{};
DataStore<array<int>> shader{};
/* subd data */
@@ -362,16 +371,18 @@ class AlembicObject : public Node {
void set_object(Object *object);
Object *get_object();
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::IPolyMeshSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::ISubDSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- const Alembic::AbcGeom::ICurvesSchema &schema,
- Progress &progress,
- float default_radius);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::IPolyMeshSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::ISubDSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const Alembic::AbcGeom::ICurvesSchema &schema,
+ Progress &progress);
bool has_data_loaded() const;
@@ -397,33 +408,21 @@ class AlembicObject : public Node {
CachedData &get_cached_data()
{
- return cached_data;
+ return cached_data_;
}
bool is_constant() const
{
- return cached_data.is_constant();
+ return cached_data_.is_constant();
}
Object *object = nullptr;
bool data_loaded = false;
- CachedData cached_data;
-
- void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- Progress &progress);
-
- void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress);
-
- template<typename SchemaType>
- void read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- Alembic::AbcGeom::ISampleSelector sample_sel);
+ CachedData cached_data_;
- void setup_transform_cache(float scale);
+ void setup_transform_cache(CachedData &cached_data, float scale);
AttributeRequestSet get_requested_attributes();
};
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
new file mode 100644
index 00000000000..4d09c40b07b
--- /dev/null
+++ b/intern/cycles/render/alembic_read.cpp
@@ -0,0 +1,1007 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/alembic_read.h"
+
+#include "render/alembic.h"
+#include "render/mesh.h"
+
+#include "util/util_progress.h"
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic::AbcGeom;
+
+CCL_NAMESPACE_BEGIN
+
+static float3 make_float3_from_yup(const V3f &v)
+{
+ return make_float3(v.x, -v.z, v.y);
+}
+
+/* get the sample times to load data for the given the start and end frame of the procedural */
+static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
+ const TimeSampling &time_sampling,
+ size_t num_samples)
+{
+ set<chrono_t> result;
+
+ if (num_samples < 2) {
+ result.insert(0.0);
+ return result;
+ }
+
+ // load the data for the entire animation
+ const double start_frame = static_cast<double>(proc->get_start_frame());
+ const double end_frame = static_cast<double>(proc->get_end_frame());
+
+ const double frame_rate = static_cast<double>(proc->get_frame_rate());
+ const double start_time = start_frame / frame_rate;
+ const double end_time = (end_frame + 1) / frame_rate;
+
+ const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first;
+ const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first;
+
+ for (size_t i = start_index; i < end_index; ++i) {
+ result.insert(time_sampling.getSampleTime(i));
+ }
+
+ return result;
+}
+
+/* Main function to read data, this will iterate over all the relevant sample times for the
+ * duration of the requested animation, and call the DataReadingFunc for each of those sample time.
+ */
+template<typename Params, typename DataReadingFunc>
+static void read_data_loop(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const Params &params,
+ DataReadingFunc &&func,
+ Progress &progress)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *params.time_sampling, params.num_samples);
+
+ cached_data.set_time_sampling(*params.time_sampling);
+
+ for (chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ func(cached_data, params, time);
+ }
+}
+
+/* Polygon Mesh Geometries. */
+
+/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used
+ * to avoid computing them in the GeometryManager in order to speed up data updates. */
+static void compute_vertex_normals(CachedData &cache, double current_time)
+{
+ if (cache.vertices.size() == 0) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr_normal = cache.add_attribute(
+ ustring("N"), cache.vertices.get_time_sampling());
+ attr_normal.std = ATTR_STD_VERTEX_NORMAL;
+ attr_normal.element = ATTR_ELEMENT_VERTEX;
+ attr_normal.type_desc = TypeNormal;
+
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(current_time).get_data_or_null();
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(current_time).get_data_or_null();
+
+ if (!vertices || !triangles) {
+ attr_normal.data.add_no_data(current_time);
+ return;
+ }
+
+ array<char> attr_data(vertices->size() * sizeof(float3));
+ float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data());
+ memset(attr_ptr, 0, vertices->size() * sizeof(float3));
+
+ for (size_t t = 0; t < triangles->size(); ++t) {
+ const int3 tri_int3 = triangles->data()[t];
+ Mesh::Triangle tri{};
+ tri.v[0] = tri_int3[0];
+ tri.v[1] = tri_int3[1];
+ tri.v[2] = tri_int3[2];
+
+ const float3 tri_N = tri.compute_normal(vertices->data());
+
+ for (int v = 0; v < 3; ++v) {
+ attr_ptr[tri_int3[v]] += tri_N;
+ }
+ }
+
+ for (size_t v = 0; v < vertices->size(); ++v) {
+ attr_ptr[v] = normalize(attr_ptr[v]);
+ }
+
+ attr_normal.data.add_data(attr_data, current_time);
+}
+
+static void add_normals(const Int32ArraySamplePtr face_indices,
+ const IN3fGeomParam &normals,
+ double time,
+ CachedData &cached_data)
+{
+ switch (normals.getScope()) {
+ case kFacevaryingScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const int *face_indices_array = face_indices->get();
+ const N3fArraySamplePtr values = sample.getVals();
+
+ for (size_t i = 0; i < face_indices->size(); ++i) {
+ int point_index = face_indices_array[i];
+ data_float3[point_index] = make_float3_from_yup(values->get()[i]);
+ }
+
+ attr.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope:
+ case kVertexScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const Imath::V3f *values = sample.getVals()->get();
+
+ for (size_t i = 0; i < vertices->size(); ++i) {
+ data_float3[i] = make_float3_from_yup(values[i]);
+ }
+
+ attr.data.add_data(data, time);
+
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
+{
+ if (!positions) {
+ return;
+ }
+
+ array<float3> vertices;
+ vertices.reserve(positions->size());
+
+ for (size_t i = 0; i < positions->size(); i++) {
+ V3f f = positions->get()[i];
+ vertices.push_back_reserved(make_float3_from_yup(f));
+ }
+
+ cached_data.vertices.add_data(vertices, time);
+}
+
+static void add_triangles(const Int32ArraySamplePtr face_counts,
+ const Int32ArraySamplePtr face_indices,
+ double time,
+ CachedData &cached_data,
+ const array<int> &polygon_to_shader)
+{
+ if (!face_counts || !face_indices) {
+ return;
+ }
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ size_t num_triangles = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_triangles += face_counts_array[i] - 2;
+ }
+
+ array<int> shader;
+ array<int3> triangles;
+ array<int> uv_loops;
+ shader.reserve(num_triangles);
+ triangles.reserve(num_triangles);
+ uv_loops.reserve(num_triangles * 3);
+ int index_offset = 0;
+
+ for (size_t i = 0; i < num_faces; i++) {
+ int current_shader = 0;
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ for (int j = 0; j < face_counts_array[i] - 2; j++) {
+ int v0 = face_indices_array[index_offset];
+ int v1 = face_indices_array[index_offset + j + 1];
+ int v2 = face_indices_array[index_offset + j + 2];
+
+ shader.push_back_reserved(current_shader);
+
+ /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
+ triangles.push_back_reserved(make_int3(v2, v1, v0));
+ uv_loops.push_back_reserved(index_offset + j + 2);
+ uv_loops.push_back_reserved(index_offset + j + 1);
+ uv_loops.push_back_reserved(index_offset);
+ }
+
+ index_offset += face_counts_array[i];
+ }
+
+ cached_data.triangles.add_data(triangles, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+ cached_data.shader.add_data(shader, time);
+}
+
+static array<int> compute_polygon_to_shader_map(
+ const Int32ArraySamplePtr &face_counts,
+ const vector<FaceSetShaderIndexPair> &face_set_shader_index,
+ ISampleSelector sample_sel)
+{
+ if (face_set_shader_index.empty()) {
+ return {};
+ }
+
+ if (!face_counts) {
+ return {};
+ }
+
+ if (face_counts->size() == 0) {
+ return {};
+ }
+
+ array<int> polygon_to_shader(face_counts->size());
+
+ for (const FaceSetShaderIndexPair &pair : face_set_shader_index) {
+ const IFaceSet &face_set = pair.face_set;
+ const IFaceSetSchema face_schem = face_set.getSchema();
+ const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+ const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+ const size_t num_group_faces = group_faces->size();
+
+ for (size_t l = 0; l < num_group_faces; l++) {
+ size_t pos = (*group_faces)[l];
+
+ if (pos >= polygon_to_shader.size()) {
+ continue;
+ }
+
+ polygon_to_shader[pos] = pair.shader_index;
+ }
+ }
+
+ return polygon_to_shader;
+}
+
+static void read_poly_mesh_geometry(CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ /* Only copy triangles for other frames if the topology is changing over time as well. */
+ if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) {
+ bool do_triangles = true;
+
+ /* Compare key with last one to check whether the topology changed. */
+ if (cached_data.triangles.size() > 0) {
+ const ArraySample::Key key = face_indices->getKey();
+
+ if (key == cached_data.triangles.key1) {
+ do_triangles = false;
+ }
+
+ cached_data.triangles.key1 = key;
+ }
+
+ if (do_triangles) {
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+ add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader);
+ }
+ else {
+ cached_data.triangles.reuse_data_for_last_time(time);
+ cached_data.uv_loops.reuse_data_for_last_time(time);
+ cached_data.shader.reuse_data_for_last_time(time);
+ }
+
+ /* Initialize the first key. */
+ if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) {
+ cached_data.triangles.key1 = face_indices->getKey();
+ }
+ }
+
+ if (data.normals.valid()) {
+ add_normals(face_indices, data.normals, time, cached_data);
+ }
+ else {
+ compute_vertex_normals(cached_data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress);
+}
+
+/* Subdivision Geometries */
+
+static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ array<int> subd_start_corner;
+ array<int> shader;
+ array<int> subd_num_corners;
+ array<bool> subd_smooth;
+ array<int> subd_ptex_offset;
+ array<int> subd_face_corners;
+ array<int> uv_loops;
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ int num_ngons = 0;
+ int num_corners = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
+ num_corners += face_counts_array[i];
+ }
+
+ subd_start_corner.reserve(num_faces);
+ subd_num_corners.reserve(num_faces);
+ subd_smooth.reserve(num_faces);
+ subd_ptex_offset.reserve(num_faces);
+ shader.reserve(num_faces);
+ subd_face_corners.reserve(num_corners);
+ uv_loops.reserve(num_corners);
+
+ int start_corner = 0;
+ int current_shader = 0;
+ int ptex_offset = 0;
+
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_corners = face_counts_array[i];
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ subd_start_corner.push_back_reserved(start_corner);
+ subd_num_corners.push_back_reserved(num_corners);
+
+ for (int j = 0; j < num_corners; ++j) {
+ subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
+ uv_loops.push_back_reserved(start_corner + j);
+ }
+
+ shader.push_back_reserved(current_shader);
+ subd_smooth.push_back_reserved(1);
+ subd_ptex_offset.push_back_reserved(ptex_offset);
+
+ ptex_offset += (num_corners == 4 ? 1 : num_corners);
+
+ start_corner += num_corners;
+ }
+
+ cached_data.shader.add_data(shader, time);
+ cached_data.subd_start_corner.add_data(subd_start_corner, time);
+ cached_data.subd_num_corners.add_data(subd_num_corners, time);
+ cached_data.subd_smooth.add_data(subd_smooth, time);
+ cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
+ cached_data.subd_face_corners.add_data(subd_face_corners, time);
+ cached_data.num_ngons.add_data(num_ngons, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+}
+
+static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ if (!(data.crease_indices.valid() && data.crease_indices.valid() &&
+ data.crease_sharpnesses.valid())) {
+ return;
+ }
+
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss);
+ const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
+ const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
+
+ if (creases_length && creases_indices && creases_sharpnesses) {
+ array<int> creases_edge;
+ array<float> creases_weight;
+
+ creases_edge.reserve(creases_sharpnesses->size() * 2);
+ creases_weight.reserve(creases_sharpnesses->size());
+
+ int length_offset = 0;
+ int weight_offset = 0;
+ for (size_t c = 0; c < creases_length->size(); ++c) {
+ const int crease_length = creases_length->get()[c];
+
+ for (size_t j = 0; j < crease_length - 1; ++j) {
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
+ creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
+ }
+
+ length_offset += crease_length;
+ }
+
+ cached_data.subd_creases_edge.add_data(creases_edge, time);
+ cached_data.subd_creases_weight.add_data(creases_weight, time);
+ }
+}
+
+static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) {
+ add_subd_polygons(cached_data, data, time);
+ add_subd_creases(cached_data, data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_subd_geometry, progress);
+}
+
+/* Curve Geometries. */
+
+static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss);
+ const P3fArraySamplePtr position = data.positions.getValue(iss);
+
+ FloatArraySamplePtr radiuses;
+
+ if (data.widths.valid()) {
+ IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss);
+ radiuses = wsample.getVals();
+ }
+
+ const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
+
+ array<float3> curve_keys;
+ array<float> curve_radius;
+ array<int> curve_first_key;
+ array<int> curve_shader;
+
+ const bool is_homogenous = data.topology_variance == kHomogenousTopology;
+
+ curve_keys.reserve(position->size());
+ curve_radius.reserve(position->size());
+ curve_first_key.reserve(curves_num_vertices->size());
+ curve_shader.reserve(curves_num_vertices->size());
+
+ int offset = 0;
+ for (size_t i = 0; i < curves_num_vertices->size(); i++) {
+ const int num_vertices = curves_num_vertices->get()[i];
+
+ for (int j = 0; j < num_vertices; j++) {
+ const V3f &f = position->get()[offset + j];
+ // todo(@kevindietrich): we are reading too much data?
+ curve_keys.push_back_slow(make_float3_from_yup(f));
+
+ if (do_radius) {
+ radius = (*radiuses)[offset + j];
+ }
+
+ curve_radius.push_back_slow(radius * data.radius_scale);
+ }
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ curve_first_key.push_back_reserved(offset);
+ curve_shader.push_back_reserved(0);
+ }
+
+ offset += num_vertices;
+ }
+
+ cached_data.curve_keys.add_data(curve_keys, time);
+ cached_data.curve_radius.add_data(curve_radius, time);
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ cached_data.curve_first_key.add_data(curve_first_key, time);
+ cached_data.curve_shader.add_data(curve_shader, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_curves_data, progress);
+}
+
+/* Attributes conversions. */
+
+/* Type traits for converting between Alembic and Cycles types.
+ */
+
+template<typename T> struct value_type_converter {
+ using cycles_type = float;
+ static constexpr TypeDesc type_desc = TypeFloat;
+ static constexpr const char *type_name = "float (default)";
+
+ static cycles_type convert_value(T value)
+ {
+ return static_cast<float>(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::V2f> {
+ using cycles_type = float2;
+ static constexpr TypeDesc type_desc = TypeFloat2;
+ static constexpr const char *type_name = "float2";
+
+ static cycles_type convert_value(Imath::V2f value)
+ {
+ return make_float2(value.x, value.y);
+ }
+};
+
+template<> struct value_type_converter<Imath::V3f> {
+ using cycles_type = float3;
+ static constexpr TypeDesc type_desc = TypeVector;
+ static constexpr const char *type_name = "float3";
+
+ static cycles_type convert_value(Imath::V3f value)
+ {
+ return make_float3_from_yup(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::C3f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgb";
+
+ static cycles_type convert_value(Imath::C3f value)
+ {
+ return color_float_to_byte(make_float3(value.x, value.y, value.z));
+ }
+};
+
+template<> struct value_type_converter<Imath::C4f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgba";
+
+ static cycles_type convert_value(Imath::C4f value)
+ {
+ return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a));
+ }
+};
+
+/* Main function used to read attributes of any type. */
+template<typename TRAIT>
+static void process_attribute(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const typename ITypedGeomParam<TRAIT>::Sample &sample,
+ double time)
+{
+ using abc_type = typename TRAIT::value_type;
+ using cycles_type = typename value_type_converter<abc_type>::cycles_type;
+
+ const TypedArraySample<TRAIT> &values = *sample.getVals();
+
+ switch (scope) {
+ case kConstantScope:
+ case kVertexScope: {
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ if (vertices->size() != values.size()) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(vertices->size() * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (size_t i = 0; i < values.size(); ++i) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope: {
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(time).get_data_or_null();
+
+ if (!triangles) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(triangles->size() * 3 * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (const int3 &tri : *triangles) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/* UVs are processed separately as their indexing is based on loops, instead of vertices or
+ * corners. */
+static void process_uvs(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const IV2fGeomParam::Sample &sample,
+ double time)
+{
+ if (scope != kFacevaryingScope) {
+ return;
+ }
+
+ const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null();
+
+ if (!uv_loops) {
+ return;
+ }
+
+ const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null();
+ const array<int> *corners =
+ cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null();
+
+ array<char> data;
+ if (triangles) {
+ data.resize(triangles->size() * 3 * sizeof(float2));
+ }
+ else if (corners) {
+ data.resize(corners->size() * sizeof(float2));
+ }
+ else {
+ return;
+ }
+
+ float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
+
+ const uint32_t *indices = sample.getIndices()->get();
+ const V2f *values = sample.getVals()->get();
+
+ for (const int uv_loop_index : *uv_loops) {
+ const uint32_t index = indices[uv_loop_index];
+ *data_float2++ = make_float2(values[index][0], values[index][1]);
+ }
+
+ attribute.data.add_data(data, time);
+}
+
+/* Type of the function used to parse one time worth of data, either process_uvs or
+ * process_attribute_generic. */
+template<typename TRAIT>
+using process_callback_type = void (*)(CachedData &,
+ CachedData::CachedAttribute &,
+ GeometryScope,
+ const typename ITypedGeomParam<TRAIT>::Sample &,
+ double);
+
+/* Main loop to process the attributes, this will look at the given param's TimeSampling and
+ * extract data based on which frame time is requested by the procedural and execute the callback
+ * for each of those requested time. */
+template<typename TRAIT>
+static void read_attribute_loop(AlembicProcedural *proc,
+ CachedData &cache,
+ const ITypedGeomParam<TRAIT> &param,
+ process_callback_type<TRAIT> callback,
+ Progress &progress,
+ AttributeStandard std = ATTR_STD_NONE)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *param.getTimeSampling(), param.getNumSamples());
+
+ if (times.empty()) {
+ return;
+ }
+
+ std::string name = param.getName();
+
+ if (std == ATTR_STD_UV) {
+ std::string uv_source_name = Alembic::Abc::GetSourceName(param.getMetaData());
+
+ /* According to the convention, primary UVs should have had their name
+ * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+ * to follow it! :) */
+ if (!uv_source_name.empty()) {
+ name = uv_source_name;
+ }
+ }
+
+ CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name),
+ *param.getTimeSampling());
+
+ using abc_type = typename TRAIT::value_type;
+
+ attribute.data.set_time_sampling(*param.getTimeSampling());
+ attribute.std = std;
+ attribute.type_desc = value_type_converter<abc_type>::type_desc;
+
+ if (attribute.type_desc == TypeRGBA) {
+ attribute.element = ATTR_ELEMENT_CORNER_BYTE;
+ }
+ else {
+ if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) {
+ attribute.element = ATTR_ELEMENT_CORNER;
+ }
+ else {
+ attribute.element = ATTR_ELEMENT_VERTEX;
+ }
+ }
+
+ for (const chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ ISampleSelector iss = ISampleSelector(time);
+ typename ITypedGeomParam<TRAIT>::Sample sample;
+ param.getIndexed(sample, iss);
+
+ if (!sample.valid()) {
+ continue;
+ }
+
+ if (!sample.getVals()) {
+ attribute.data.add_no_data(time);
+ continue;
+ }
+
+ /* Check whether we already loaded constant data. */
+ if (attribute.data.size() != 0) {
+ if (param.isConstant()) {
+ return;
+ }
+
+ const ArraySample::Key indices_key = sample.getIndices()->getKey();
+ const ArraySample::Key values_key = sample.getVals()->getKey();
+
+ const bool is_same_as_last_time = (indices_key == attribute.data.key1 &&
+ values_key == attribute.data.key2);
+
+ attribute.data.key1 = indices_key;
+ attribute.data.key2 = values_key;
+
+ if (is_same_as_last_time) {
+ attribute.data.reuse_data_for_last_time(time);
+ continue;
+ }
+ }
+
+ callback(cache, attribute, param.getScope(), sample, time);
+ }
+}
+
+/* Attributes requests. */
+
+/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we
+ * need the parent when downcasting to the proper type. */
+struct PropHeaderAndParent {
+ const PropertyHeader *prop;
+ ICompoundProperty parent;
+};
+
+/* Parse the ICompoundProperty to look for properties whose names appear in the
+ * AttributeRequestSet. This also looks into any child ICompoundProperty of the given
+ * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will
+ * use a zero value for the missing attribute. */
+static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes,
+ const ICompoundProperty &arb_geom_params,
+ vector<PropHeaderAndParent> &requested_properties)
+{
+ if (!arb_geom_params.valid()) {
+ return;
+ }
+
+ for (const AttributeRequest &req : requested_attributes.requests) {
+ const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str());
+
+ if (!property_header) {
+ continue;
+ }
+
+ requested_properties.push_back({property_header, arb_geom_params});
+ }
+
+ /* Look into children compound properties. */
+ for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) {
+ const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i);
+
+ if (property_header.isCompound()) {
+ ICompoundProperty compound_property = ICompoundProperty(arb_geom_params,
+ property_header.getName());
+ parse_requested_attributes_recursive(
+ requested_attributes, compound_property, requested_properties);
+ }
+ }
+}
+
+/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that
+ * we can simply return the list of properties instead of allocating it on the stack and passing it
+ * as a parameter. */
+static vector<PropHeaderAndParent> parse_requested_attributes(
+ const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params)
+{
+ vector<PropHeaderAndParent> requested_properties;
+ parse_requested_attributes_recursive(
+ requested_attributes, arb_geom_params, requested_properties);
+ return requested_properties;
+}
+
+/* Read the attributes requested by the shaders from the archive. This will recursively find named
+ * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child.
+ * The attributes are added to the CachedData's attribute list. For each attribute we will try to
+ * deduplicate data across consecutive frames. */
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const ICompoundProperty &arb_geom_params,
+ const IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress)
+{
+ if (default_uvs_param.valid()) {
+ /* Only the default UVs should be treated as the standard UV attribute. */
+ read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV);
+ }
+
+ vector<PropHeaderAndParent> requested_properties = parse_requested_attributes(
+ requested_attributes, arb_geom_params);
+
+ for (const PropHeaderAndParent &prop_and_parent : requested_properties) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ const PropertyHeader *prop = prop_and_parent.prop;
+ const ICompoundProperty &parent = prop_and_parent.parent;
+
+ if (IBoolGeomParam::matches(*prop)) {
+ const IBoolGeomParam &param = IBoolGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress);
+ }
+ else if (IInt32GeomParam::matches(*prop)) {
+ const IInt32GeomParam &param = IInt32GeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress);
+ }
+ else if (IFloatGeomParam::matches(*prop)) {
+ const IFloatGeomParam &param = IFloatGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress);
+ }
+ else if (IV2fGeomParam::matches(*prop)) {
+ const IV2fGeomParam &param = IV2fGeomParam(parent, prop->getName());
+ if (Alembic::AbcGeom::isUV(*prop)) {
+ read_attribute_loop(proc, cache, param, process_uvs, progress);
+ }
+ else {
+ read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress);
+ }
+ }
+ else if (IV3fGeomParam::matches(*prop)) {
+ const IV3fGeomParam &param = IV3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress);
+ }
+ else if (IN3fGeomParam::matches(*prop)) {
+ const IN3fGeomParam &param = IN3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress);
+ }
+ else if (IC3fGeomParam::matches(*prop)) {
+ const IC3fGeomParam &param = IC3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress);
+ }
+ else if (IC4fGeomParam::matches(*prop)) {
+ const IC4fGeomParam &param = IC4fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress);
+ }
+ }
+
+ cache.invalidate_last_loaded_time(true);
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/render/alembic_read.h
new file mode 100644
index 00000000000..9cc8622a1ba
--- /dev/null
+++ b/intern/cycles/render/alembic_read.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef WITH_ALEMBIC
+
+# include <Alembic/AbcCoreFactory/All.h>
+# include <Alembic/AbcGeom/All.h>
+
+# include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class AlembicProcedural;
+class AttributeRequestSet;
+class Progress;
+struct CachedData;
+
+/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's
+ * used_shaders list. */
+struct FaceSetShaderIndexPair {
+ Alembic::AbcGeom::IFaceSet face_set;
+ int shader_index;
+};
+
+/* Data of an IPolyMeshSchema that we need to read. */
+struct PolyMeshSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+
+ Alembic::AbcGeom::IN3fGeomParam normals;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress);
+
+/* Data of an ISubDSchema that we need to read. */
+struct SubDSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty crease_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty crease_lengths;
+ Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IInt32ArrayProperty corner_indices;
+ Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses;
+ Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary;
+ Alembic::AbcGeom::IInt32Property face_varying_propagate_corners;
+ Alembic::AbcGeom::IInt32Property interpolate_boundary;
+ Alembic::AbcGeom::IInt32ArrayProperty holes;
+ Alembic::AbcGeom::IStringProperty subdivision_scheme;
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress);
+
+/* Data of a ICurvesSchema that we need to read. */
+struct CurvesSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty num_vertices;
+
+ float default_radius;
+ float radius_scale;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+ // if this property is invalid then the weight for every point is 1
+ Alembic::AbcGeom::IFloatArrayProperty position_weights;
+ Alembic::AbcGeom::IN3fGeomParam normals;
+ Alembic::AbcGeom::IFloatGeomParam widths;
+ Alembic::AbcGeom::IUcharArrayProperty orders;
+ Alembic::AbcGeom::IFloatArrayProperty knots;
+
+ // TODO(@kevindietrich): type, basis, wrap
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress);
+
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress);
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp
index f0a779da012..b925e755434 100644
--- a/intern/cycles/render/background.cpp
+++ b/intern/cycles/render/background.cpp
@@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type())
Background::~Background()
{
+ dereference_all_used_nodes();
}
void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene)
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 6ab927a7909..16fc36231b4 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -85,6 +85,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type)
Geometry::~Geometry()
{
+ dereference_all_used_nodes();
delete bvh;
}
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index 4d11d04e4c8..7db122f69cb 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -50,7 +50,7 @@ enum PackFlags : uint32_t {
/* Pack the geometry information (e.g. triangle or curve keys indices). */
PACK_GEOMETRY = (1u << 0),
- /* Pack the vertice, for Meshes and Volumes' bouding meshes. */
+ /* Pack the vertices, for Meshes and Volumes' bounding meshes. */
PACK_VERTICES = (1u << 1),
/* Pack the visibility flags for each triangle or curve. */
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index a069b850512..5290d68e75a 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -159,6 +159,7 @@ NODE_DEFINE(Light)
Light::Light() : Node(get_node_type())
{
+ dereference_all_used_nodes();
}
void Light::tag_update(Scene *scene)
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 53c67049571..9bd6e3a5e2d 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/)
shading_system_init();
}
-void OSLShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void OSLShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->has_integrator_dependency = false;
/* generate surface shader */
- if (shader->used && graph && output->input("Surface")->link) {
+ if (shader->reference_count() && graph && output->input("Surface")->link) {
shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
if (has_bump)
@@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
}
/* generate volume shader */
- if (shader->used && graph && output->input("Volume")->link) {
+ if (shader->reference_count() && graph && output->input("Volume")->link) {
shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
shader->has_volume = true;
}
@@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->osl_volume_ref = OSL::ShaderGroupRef();
/* generate displacement shader */
- if (shader->used && graph && output->input("Displacement")->link) {
+ if (shader->reference_count() && graph && output->input("Displacement")->link) {
shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
shader->has_displacement = true;
}
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index ea2d0ca492c..f6aa98d867a 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -72,15 +72,18 @@ class OSLShaderManager : public ShaderManager {
static void free_memory();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- bool use_osl()
+ bool use_osl() override
{
return true;
}
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
/* osl compile and query */
static bool osl_compile(const string &inputfile, const string &outputfile);
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index 70474368a4e..c4e7d2c79d6 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -143,21 +143,27 @@ void Scene::free_memory(bool final)
delete bvh;
bvh = NULL;
- foreach (Shader *s, shaders)
- delete s;
- /* delete procedurals before other types as they may hold pointers to those types */
+ /* The order of deletion is important to make sure data is freed based on possible dependencies
+ * as the Nodes' reference counts are decremented in the destructors:
+ *
+ * - Procedurals can create and hold pointers to any other types.
+ * - Objects can hold pointers to Geometries and ParticleSystems
+ * - Lights and Geometries can hold pointers to Shaders.
+ *
+ * Similarly, we first delete all nodes and their associated device data, and then the managers
+ * and their associated device data.
+ */
foreach (Procedural *p, procedurals)
delete p;
- foreach (Geometry *g, geometry)
- delete g;
foreach (Object *o, objects)
delete o;
- foreach (Light *l, lights)
- delete l;
+ foreach (Geometry *g, geometry)
+ delete g;
foreach (ParticleSystem *p, particle_systems)
delete p;
+ foreach (Light *l, lights)
+ delete l;
- shaders.clear();
geometry.clear();
objects.clear();
lights.clear();
@@ -169,7 +175,25 @@ void Scene::free_memory(bool final)
film->device_free(device, &dscene, this);
background->device_free(device, &dscene);
integrator->device_free(device, &dscene, true);
+ }
+
+ if (final) {
+ delete camera;
+ delete dicing_camera;
+ delete film;
+ delete background;
+ delete integrator;
+ }
+
+ /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference
+ * count on some dangling pointer. */
+ foreach (Shader *s, shaders)
+ delete s;
+
+ shaders.clear();
+ /* Now that all nodes have been deleted, we can safely delete managers and device data. */
+ if (device) {
object_manager->device_free(device, &dscene, true);
geometry_manager->device_free(device, &dscene, true);
shader_manager->device_free(device, &dscene, this);
@@ -189,11 +213,6 @@ void Scene::free_memory(bool final)
if (final) {
delete lookup_tables;
- delete camera;
- delete dicing_camera;
- delete film;
- delete background;
- delete integrator;
delete object_manager;
delete geometry_manager;
delete shader_manager;
@@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
{
/* update scene */
if (need_update()) {
- /* Updated used shader tag so we know which features are need for the kernel. */
- shader_manager->update_shaders_used(this);
-
/* Update max_closures. */
KernelIntegrator *kintegrator = &dscene.data.integrator;
if (params.background) {
@@ -581,7 +597,7 @@ int Scene::get_max_closure_count()
int max_closures = 0;
for (int i = 0; i < shaders.size(); i++) {
Shader *shader = shaders[i];
- if (shader->used) {
+ if (shader->reference_count()) {
int num_closures = shader->graph->get_num_closures();
max_closures = max(max_closures, num_closures);
}
@@ -742,9 +758,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node)
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_node_impl(Shader * /*node*/)
+template<> void Scene::delete_node_impl(Shader *shader)
{
/* don't delete unused shaders, not supported */
+ shader->clear_reference_count();
}
template<> void Scene::delete_node_impl(Procedural *node)
@@ -811,9 +828,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/)
+template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/)
{
/* don't delete unused shaders, not supported */
+ for (Shader *shader : nodes) {
+ shader->clear_reference_count();
+ }
}
template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner)
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 5ecbd92d96d..59b60904746 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -16,7 +16,6 @@
#include "device/device.h"
-#include "render/alembic.h"
#include "render/background.h"
#include "render/camera.h"
#include "render/colorspace.h"
@@ -27,6 +26,7 @@
#include "render/nodes.h"
#include "render/object.h"
#include "render/osl.h"
+#include "render/procedural.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/svm.h"
@@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type())
displacement_method = DISPLACE_BUMP;
id = -1;
- used = false;
need_update_uvs = true;
need_update_attribute = true;
@@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene)
{
/* if an unused shader suddenly gets used somewhere, it needs to be
* recompiled because it was skipped for compilation before */
- if (!used) {
+ if (!reference_count()) {
tag_modified();
+ /* We do not reference here as the shader will be referenced when added to a socket. */
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
}
}
@@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
return id;
}
-void ShaderManager::update_shaders_used(Scene *scene)
+void ShaderManager::device_update(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update()) {
return;
}
- /* figure out which shaders are in use, so SVM/OSL can skip compiling them
- * for speed and avoid loading image textures into memory */
uint id = 0;
foreach (Shader *shader, scene->shaders) {
- shader->used = false;
shader->id = id++;
}
- scene->default_surface->used = true;
- scene->default_light->used = true;
- scene->default_background->used = true;
- scene->default_empty->used = true;
+ /* Those shaders should always be compiled as they are used as fallback if a shader cannot be
+ * found, e.g. bad shader index for the triangle shaders on a Mesh. */
+ assert(scene->default_surface->reference_count() != 0);
+ assert(scene->default_light->reference_count() != 0);
+ assert(scene->default_background->reference_count() != 0);
+ assert(scene->default_empty->reference_count() != 0);
- if (scene->background->get_shader())
- scene->background->get_shader()->used = true;
-
-#ifdef WITH_ALEMBIC
- foreach (Procedural *procedural, scene->procedurals) {
- AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural);
-
- foreach (Node *abc_node, abc_proc->get_objects()) {
- AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node);
-
- foreach (Node *node, abc_object->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
- }
- }
-#endif
-
- foreach (Geometry *geom, scene->geometry)
- foreach (Node *node, geom->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
-
- foreach (Light *light, scene->lights)
- if (light->get_shader())
- const_cast<Shader *>(light->get_shader())->used = true;
+ device_update_specific(device, dscene, scene, progress);
}
void ShaderManager::device_update_common(Device *device,
@@ -528,6 +504,8 @@ void ShaderManager::device_update_common(Device *device,
if (shader->get_use_mis())
flag |= SD_USE_MIS;
+ if (shader->has_surface_emission)
+ flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
if (shader->has_volume) {
@@ -637,6 +615,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_surface";
shader->set_graph(graph);
+ shader->reference();
scene->default_surface = shader;
shader->tag_update(scene);
}
@@ -655,6 +634,8 @@ void ShaderManager::add_default(Scene *scene)
shader->set_graph(graph);
scene->default_volume = shader;
shader->tag_update(scene);
+ /* No default reference for the volume to avoid compiling volume kernels if there are no actual
+ * volumes in the scene */
}
/* default light */
@@ -671,6 +652,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_light";
shader->set_graph(graph);
+ shader->reference();
scene->default_light = shader;
shader->tag_update(scene);
}
@@ -682,6 +664,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_background";
shader->set_graph(graph);
+ shader->reference();
scene->default_background = shader;
shader->tag_update(scene);
}
@@ -693,6 +676,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_empty";
shader->set_graph(graph);
+ shader->reference();
scene->default_empty = shader;
shader->tag_update(scene);
}
@@ -733,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene,
requested_features->nodes_features = 0;
for (int i = 0; i < scene->shaders.size(); i++) {
Shader *shader = scene->shaders[i];
- if (!shader->used) {
+ if (!shader->reference_count()) {
continue;
}
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index f47d64f346c..50c8bed4669 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -132,7 +132,6 @@ class Shader : public Node {
/* determined before compiling */
uint id;
- bool used;
#ifdef WITH_OSL
/* osl shading state references */
@@ -187,10 +186,11 @@ class ShaderManager {
}
/* device update */
- virtual void device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress) = 0;
+ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
+ virtual void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) = 0;
virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0;
void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
@@ -208,7 +208,6 @@ class ShaderManager {
static void add_default(Scene *scene);
/* Selective nodes compilation. */
- void update_shaders_used(Scene *scene);
void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features);
static void free_memory();
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index fce604234f1..5c793c5c016 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene,
<< summary.full_report();
}
-void SVMShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void SVMShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
}
- if (shader->used) {
+ if (shader->reference_count()) {
CompilerState state(graph);
if (clin->link) {
bool generate = false;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index a4ca68e1d8d..d23ff3e2a47 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -44,10 +44,13 @@ class SVMShaderManager : public ShaderManager {
SVMShaderManager();
~SVMShaderManager();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
protected:
void device_update_shader(Scene *scene,
diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h
index 107b36ce6cd..5ae56290f05 100644
--- a/intern/cycles/util/util_math_fast.h
+++ b/intern/cycles/util/util_math_fast.h
@@ -362,7 +362,7 @@ ccl_device float fast_atan2f(float y, float x)
ccl_device float fast_log2f(float x)
{
/* NOTE: clamp to avoid special cases and make result "safe" from large
- * negative values/nans. */
+ * negative values/NAN's. */
x = clamp(x, FLT_MIN, FLT_MAX);
unsigned bits = __float_as_uint(x);
int exponent = (int)(bits >> 23) - 127;
diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/util_simd.h
index 718ec9266b1..8e8caa98a1b 100644
--- a/intern/cycles/util/util_simd.h
+++ b/intern/cycles/util/util_simd.h
@@ -124,7 +124,7 @@ static struct StepTy {
template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const type &a)
{
if (i0 == i1 && i0 == i2 && i0 == i3) {
- return vdupq_laneq_s32(a, i0);
+ return type(vdupq_laneq_s32(int32x4_t(a), i0));
}
static const uint8_t tbl[16] = {(i0 * 4) + 0,
(i0 * 4) + 1,
@@ -143,7 +143,7 @@ template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const typ
(i3 * 4) + 2,
(i3 * 4) + 3};
- return vqtbl1q_s8(int8x16_t(a), *(int8x16_t *)tbl);
+ return type(vqtbl1q_s8(int8x16_t(a), *(uint8x16_t *)tbl));
}
template<class type, int i0, int i1, int i2, int i3>
@@ -167,7 +167,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2,
(i3 * 4) + 3};
- return vqtbl1q_s8(int8x16_t(b), *(int8x16_t *)tbl);
+ return type(vqtbl1q_s8(int8x16_t(b), *(uint8x16_t *)tbl));
}
else {
@@ -188,7 +188,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2 + 16,
(i3 * 4) + 3 + 16};
- return vqtbl2q_s8((int8x16x2_t){a, b}, *(int8x16_t *)tbl);
+ return type(vqtbl2q_s8((int8x16x2_t){int8x16_t(a), int8x16_t(b)}, *(uint8x16_t *)tbl));
}
}
#endif /* __KERNEL_NEON */
diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h
index 1488da46b09..4dbd5b8046e 100644
--- a/intern/cycles/util/util_sseb.h
+++ b/intern/cycles/util/util_sseb.h
@@ -283,7 +283,7 @@ __forceinline uint32_t popcnt(const sseb &a)
{
# if defined(__KERNEL_NEON__)
const int32x4_t mask = {1, 1, 1, 1};
- int32x4_t t = vandq_s32(a.m128, mask);
+ int32x4_t t = vandq_s32(vreinterpretq_s32_m128(a.m128), mask);
return vaddvq_s32(t);
# else
return _mm_popcnt_u32(_mm_movemask_ps(a));
@@ -299,7 +299,7 @@ __forceinline uint32_t popcnt(const sseb &a)
__forceinline bool reduce_and(const sseb &a)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(a.m128) == -4;
+ return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) == -4;
# else
return _mm_movemask_ps(a) == 0xf;
# endif
@@ -307,7 +307,7 @@ __forceinline bool reduce_and(const sseb &a)
__forceinline bool reduce_or(const sseb &a)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(a.m128) != 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) != 0x0;
# else
return _mm_movemask_ps(a) != 0x0;
# endif
@@ -315,7 +315,7 @@ __forceinline bool reduce_or(const sseb &a)
__forceinline bool all(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) == -4;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == -4;
# else
return _mm_movemask_ps(b) == 0xf;
# endif
@@ -323,7 +323,7 @@ __forceinline bool all(const sseb &b)
__forceinline bool any(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) != 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) != 0x0;
# else
return _mm_movemask_ps(b) != 0x0;
# endif
@@ -331,7 +331,7 @@ __forceinline bool any(const sseb &b)
__forceinline bool none(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) == 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == 0x0;
# else
return _mm_movemask_ps(b) == 0x0;
# endif
diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/util_ssef.h
index d039b50a7d2..0c81ed87553 100644
--- a/intern/cycles/util/util_ssef.h
+++ b/intern/cycles/util/util_ssef.h
@@ -596,7 +596,7 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssef shuffle(const ssef &b)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssef, i0, i1, i2, i3>(b.m128);
+ return shuffle_neon<float32x4_t, i0, i1, i2, i3>(b.m128);
# else
return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0)));
# endif
@@ -625,7 +625,7 @@ __forceinline const ssef shuffle(const ssef &a, const ssef &b)
template<size_t i0> __forceinline const ssef shuffle(const ssef &a, const ssef &b)
{
# ifdef __KERNEL_NEON__
- return shuffle<float32x4_t, i0, i0, i0, i0>(a, b);
+ return shuffle_neon<float32x4_t, i0, i0, i0, i0>(a, b);
# else
return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i0, i0, i0, i0));
# endif
diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h
index 3ec69ab3700..cd51dbff2f1 100644
--- a/intern/cycles/util/util_ssei.h
+++ b/intern/cycles/util/util_ssei.h
@@ -446,7 +446,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssei, i0, i1, i2, i3>(a);
+ int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a));
+ return vreinterpretq_m128i_s32(result);
# else
return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
# endif
@@ -456,7 +457,9 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a, const ssei &b)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssei, i0, i1, i2, i3>(a, b);
+ int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a),
+ vreinterpretq_s32_m128i(b));
+ return vreinterpretq_m128i_s32(result);
# else
return _mm_castps_si128(
_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
@@ -514,7 +517,7 @@ __forceinline const ssei vreduce_add(const ssei &v)
__forceinline int reduce_min(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vminvq_s32(v);
+ return vminvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_min(v));
# endif
@@ -522,7 +525,7 @@ __forceinline int reduce_min(const ssei &v)
__forceinline int reduce_max(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vmaxvq_s32(v);
+ return vmaxvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_max(v));
# endif
@@ -530,7 +533,7 @@ __forceinline int reduce_max(const ssei &v)
__forceinline int reduce_add(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vaddvq_s32(v);
+ return vaddvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_add(v));
# endif
diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h
index b445ab1488f..71bf9c65911 100644
--- a/intern/cycles/util/util_texture.h
+++ b/intern/cycles/util/util_texture.h
@@ -21,18 +21,12 @@
CCL_NAMESPACE_BEGIN
-/* Texture limits on devices. */
-#define TEX_NUM_MAX (INT_MAX >> 4)
-
/* Color to use when textures are not found. */
#define TEX_IMAGE_MISSING_R 1
#define TEX_IMAGE_MISSING_G 0
#define TEX_IMAGE_MISSING_B 1
#define TEX_IMAGE_MISSING_A 1
-/* Texture type. */
-#define kernel_tex_type(tex) (tex & IMAGE_DATA_TYPE_MASK)
-
/* Interpolation types for textures
* cuda also use texture space to store other objects */
typedef enum InterpolationType {
@@ -45,9 +39,6 @@ typedef enum InterpolationType {
INTERPOLATION_NUM_TYPES,
} InterpolationType;
-/* Texture types
- * Since we store the type in the lower bits of a flat index,
- * the shift and bit mask constant below need to be kept in sync. */
typedef enum ImageDataType {
IMAGE_DATA_TYPE_FLOAT4 = 0,
IMAGE_DATA_TYPE_BYTE4 = 1,
@@ -75,9 +66,6 @@ typedef enum ImageAlphaType {
IMAGE_ALPHA_NUM_TYPES,
} ImageAlphaType;
-#define IMAGE_DATA_TYPE_SHIFT 4
-#define IMAGE_DATA_TYPE_MASK 0xF
-
/* Extension types for textures.
*
* Defines how the image is extrapolated past its original bounds. */
diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h
index 727fd4b9601..54a004d53e2 100644
--- a/intern/ffmpeg/ffmpeg_compat.h
+++ b/intern/ffmpeg/ffmpeg_compat.h
@@ -22,10 +22,17 @@
#include <libavformat/avformat.h>
-/* check our ffmpeg is new enough, avoids user complaints */
-#if (LIBAVFORMAT_VERSION_MAJOR < 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 52) && (LIBAVFORMAT_VERSION_MINOR <= 64))
-# error "FFmpeg 0.7 or newer is needed, Upgrade your FFmpeg or disable it"
+/* Check if our ffmpeg is new enough, avoids user complaints.
+ * Minimum supported version is currently 3.2.0 which mean the following library versions:
+ * libavutil > 55.30
+ * libavcodec > 57.60
+ * libavformat > 57.50
+ *
+ * We only check for one of these as they are usually updated in tandem.
+ */
+#if (LIBAVFORMAT_VERSION_MAJOR < 57) || \
+ ((LIBAVFORMAT_VERSION_MAJOR == 57) && (LIBAVFORMAT_VERSION_MINOR <= 50))
+# error "FFmpeg 3.2.0 or newer is needed, Upgrade your FFmpeg or disable it"
#endif
/* end sanity check */
@@ -36,274 +43,6 @@
# define FFMPEG_INLINE static inline
#endif
-#include <libavcodec/avcodec.h>
-#include <libavutil/mathematics.h>
-#include <libavutil/opt.h>
-#include <libavutil/rational.h>
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 101))
-# define FFMPEG_HAVE_PARSE_UTILS 1
-# include <libavutil/parseutils.h>
-#endif
-
-#include <libswscale/swscale.h>
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 105))
-# define FFMPEG_HAVE_AVIO 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 53) || \
- ((LIBAVCODEC_VERSION_MAJOR == 53) && (LIBAVCODEC_VERSION_MINOR > 1)) || \
- ((LIBAVCODEC_VERSION_MAJOR == 53) && (LIBAVCODEC_VERSION_MINOR == 1) && \
- (LIBAVCODEC_VERSION_MICRO >= 1)) || \
- ((LIBAVCODEC_VERSION_MAJOR == 52) && (LIBAVCODEC_VERSION_MINOR >= 121))
-# define FFMPEG_HAVE_DEFAULT_VAL_UNION 1
-#endif
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 101))
-# define FFMPEG_HAVE_AV_DUMP_FORMAT 1
-#endif
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 45))
-# define FFMPEG_HAVE_AV_GUESS_FORMAT 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 52) || \
- ((LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 23))
-# define FFMPEG_HAVE_DECODE_AUDIO3 1
-# define FFMPEG_HAVE_DECODE_VIDEO2 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 52) || \
- ((LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 64))
-# define FFMPEG_HAVE_AVMEDIA_TYPES 1
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 52) || \
- (LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 29)) && \
- ((LIBSWSCALE_VERSION_MAJOR > 0) || \
- (LIBSWSCALE_VERSION_MAJOR >= 0) && (LIBSWSCALE_VERSION_MINOR >= 10))
-# define FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR > 14))
-# define FFMPEG_HAVE_CANON_H264_RESOLUTION_FIX
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 53) || \
- (LIBAVCODEC_VERSION_MAJOR >= 53) && (LIBAVCODEC_VERSION_MINOR >= 60))
-# define FFMPEG_HAVE_ENCODE_AUDIO2
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 53) || \
- (LIBAVCODEC_VERSION_MAJOR >= 53) && (LIBAVCODEC_VERSION_MINOR >= 42))
-# define FFMPEG_HAVE_DECODE_AUDIO4
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_AVFRAME_SAMPLE_RATE
-#endif
-
-#if ((LIBAVUTIL_VERSION_MAJOR > 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR >= 21))
-# define FFMPEG_FFV1_ALPHA_SUPPORTED
-# define FFMPEG_SAMPLE_FMT_S16P_SUPPORTED
-#else
-
-FFMPEG_INLINE
-int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt)
-{
- /* no planar formats in FFmpeg < 0.9 */
- (void)sample_fmt;
- return 0;
-}
-
-#endif
-
-/* XXX TODO Probably fix to correct modern flags in code? Not sure how old FFMPEG we want to
- * support though, so for now this will do. */
-
-#ifndef FF_MIN_BUFFER_SIZE
-# ifdef AV_INPUT_BUFFER_MIN_SIZE
-# define FF_MIN_BUFFER_SIZE AV_INPUT_BUFFER_MIN_SIZE
-# endif
-#endif
-
-#ifndef FF_INPUT_BUFFER_PADDING_SIZE
-# ifdef AV_INPUT_BUFFER_PADDING_SIZE
-# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE
-# endif
-#endif
-
-#ifndef CODEC_FLAG_GLOBAL_HEADER
-# ifdef AV_CODEC_FLAG_GLOBAL_HEADER
-# define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
-# endif
-#endif
-
-#ifndef CODEC_FLAG_GLOBAL_HEADER
-# ifdef AV_CODEC_FLAG_GLOBAL_HEADER
-# define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
-# endif
-#endif
-
-#ifndef CODEC_FLAG_INTERLACED_DCT
-# ifdef AV_CODEC_FLAG_INTERLACED_DCT
-# define CODEC_FLAG_INTERLACED_DCT AV_CODEC_FLAG_INTERLACED_DCT
-# endif
-#endif
-
-#ifndef CODEC_FLAG_INTERLACED_ME
-# ifdef AV_CODEC_FLAG_INTERLACED_ME
-# define CODEC_FLAG_INTERLACED_ME AV_CODEC_FLAG_INTERLACED_ME
-# endif
-#endif
-
-/* FFmpeg upstream 1.0 is the first who added AV_ prefix. */
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
-# define AV_CODEC_ID_NONE CODEC_ID_NONE
-# define AV_CODEC_ID_MPEG4 CODEC_ID_MPEG4
-# define AV_CODEC_ID_MJPEG CODEC_ID_MJPEG
-# define AV_CODEC_ID_DNXHD CODEC_ID_DNXHD
-# define AV_CODEC_ID_MPEG2VIDEO CODEC_ID_MPEG2VIDEO
-# define AV_CODEC_ID_MPEG1VIDEO CODEC_ID_MPEG1VIDEO
-# define AV_CODEC_ID_DVVIDEO CODEC_ID_DVVIDEO
-# define AV_CODEC_ID_THEORA CODEC_ID_THEORA
-# define AV_CODEC_ID_PNG CODEC_ID_PNG
-# define AV_CODEC_ID_QTRLE CODEC_ID_QTRLE
-# define AV_CODEC_ID_FFV1 CODEC_ID_FFV1
-# define AV_CODEC_ID_HUFFYUV CODEC_ID_HUFFYUV
-# define AV_CODEC_ID_H264 CODEC_ID_H264
-# define AV_CODEC_ID_FLV1 CODEC_ID_FLV1
-
-# define AV_CODEC_ID_AAC CODEC_ID_AAC
-# define AV_CODEC_ID_AC3 CODEC_ID_AC3
-# define AV_CODEC_ID_MP3 CODEC_ID_MP3
-# define AV_CODEC_ID_MP2 CODEC_ID_MP2
-# define AV_CODEC_ID_FLAC CODEC_ID_FLAC
-# define AV_CODEC_ID_PCM_U8 CODEC_ID_PCM_U8
-# define AV_CODEC_ID_PCM_S16LE CODEC_ID_PCM_S16LE
-# define AV_CODEC_ID_PCM_S24LE CODEC_ID_PCM_S24LE
-# define AV_CODEC_ID_PCM_S32LE CODEC_ID_PCM_S32LE
-# define AV_CODEC_ID_PCM_F32LE CODEC_ID_PCM_F32LE
-# define AV_CODEC_ID_PCM_F64LE CODEC_ID_PCM_F64LE
-# define AV_CODEC_ID_VORBIS CODEC_ID_VORBIS
-#endif
-
-FFMPEG_INLINE
-int av_get_cropped_height_from_codec(AVCodecContext *pCodecCtx)
-{
- int y = pCodecCtx->height;
-
-#ifndef FFMPEG_HAVE_CANON_H264_RESOLUTION_FIX
- /* really bad hack to remove this dreadfull black bar at the bottom
- with Canon footage and old ffmpeg versions.
- (to fix this properly in older ffmpeg versions one has to write a new
- demuxer...)
-
- see the actual fix here for reference:
-
- http://git.libav.org/?p=libav.git;a=commit;h=30f515091c323da59c0f1b533703dedca2f4b95d
-
- We do our best to apply this only to matching footage.
-*/
- if (pCodecCtx->width == 1920 && pCodecCtx->height == 1088 &&
- pCodecCtx->pix_fmt == PIX_FMT_YUVJ420P && pCodecCtx->codec_id == AV_CODEC_ID_H264) {
- y = 1080;
- }
-#endif
-
- return y;
-}
-
-#if ((LIBAVUTIL_VERSION_MAJOR < 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR < 22))
-FFMPEG_INLINE
-int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- av_set_string3(obj, name, val, 1, &rv);
- return rv != NULL;
-}
-
-FFMPEG_INLINE
-int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- rv = av_set_int(obj, name, val);
- return rv != NULL;
-}
-
-FFMPEG_INLINE
-int av_opt_set_double(void *obj, const char *name, double val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- rv = av_set_double(obj, name, val);
- return rv != NULL;
-}
-
-# define AV_OPT_TYPE_INT FF_OPT_TYPE_INT
-# define AV_OPT_TYPE_INT64 FF_OPT_TYPE_INT64
-# define AV_OPT_TYPE_STRING FF_OPT_TYPE_STRING
-# define AV_OPT_TYPE_CONST FF_OPT_TYPE_CONST
-# define AV_OPT_TYPE_DOUBLE FF_OPT_TYPE_DOUBLE
-# define AV_OPT_TYPE_FLOAT FF_OPT_TYPE_FLOAT
-#endif
-
-#if ((LIBAVUTIL_VERSION_MAJOR < 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR < 54))
-FFMPEG_INLINE
-enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt)
-{
- if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB)
- return AV_SAMPLE_FMT_NONE;
- return sample_fmt;
-}
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR < 53) || \
- (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR < 35))
-FFMPEG_INLINE
-int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)
-{
- /* TODO: no options are taking into account */
- (void)options;
- return avcodec_open(avctx, codec);
-}
-#endif
-
-#if ((LIBAVFORMAT_VERSION_MAJOR < 53) || \
- (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR < 21))
-FFMPEG_INLINE
-AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c)
-{
- /* TODO: no codec is taking into account */
- (void)c;
- return av_new_stream(s, 0);
-}
-
-FFMPEG_INLINE
-int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
-{
- /* TODO: no options are taking into account */
- (void)options;
- return av_find_stream_info(ic);
-}
-#endif
-
-#if ((LIBAVFORMAT_VERSION_MAJOR > 53) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 53) && (LIBAVFORMAT_VERSION_MINOR > 32)) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 53) && (LIBAVFORMAT_VERSION_MINOR == 24) && \
- (LIBAVFORMAT_VERSION_MICRO >= 100)))
FFMPEG_INLINE
void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
{
@@ -323,103 +62,12 @@ void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
{
my_update_cur_dts(s, ref_st, timestamp);
}
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR < 54) || \
- (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR < 28))
-FFMPEG_INLINE
-void avcodec_free_frame(AVFrame **frame)
-{
- /* don't need to do anything with old AVFrame
- * since it does not have malloced members */
- (void)frame;
-}
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_AVFRAME_SAMPLE_RATE
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
-#endif
-
-#ifndef FFMPEG_HAVE_AVIO
-# define AVIO_FLAG_WRITE URL_WRONLY
-# define avio_open url_fopen
-# define avio_tell url_ftell
-# define avio_close url_fclose
-# define avio_size url_fsize
-#endif
-
-/* There are some version in between, which have avio_... functions but no
- * AVIO_FLAG_... */
-#ifndef AVIO_FLAG_WRITE
-# define AVIO_FLAG_WRITE URL_WRONLY
-#endif
-
-#ifndef AV_PKT_FLAG_KEY
-# define AV_PKT_FLAG_KEY PKT_FLAG_KEY
-#endif
-
-#ifndef FFMPEG_HAVE_AV_DUMP_FORMAT
-# define av_dump_format dump_format
-#endif
-
-#ifndef FFMPEG_HAVE_AV_GUESS_FORMAT
-# define av_guess_format guess_format
-#endif
-
-#ifndef FFMPEG_HAVE_PARSE_UTILS
-# define av_parse_video_rate av_parse_video_frame_rate
-#endif
-
-#ifdef FFMPEG_HAVE_DEFAULT_VAL_UNION
-# define FFMPEG_DEF_OPT_VAL_INT(OPT) OPT->default_val.i64
-# define FFMPEG_DEF_OPT_VAL_DOUBLE(OPT) OPT->default_val.dbl
-#else
-# define FFMPEG_DEF_OPT_VAL_INT(OPT) OPT->default_val
-# define FFMPEG_DEF_OPT_VAL_DOUBLE(OPT) OPT->default_val
-#endif
-
-#ifndef FFMPEG_HAVE_AVMEDIA_TYPES
-# define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
-# define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
-#endif
-
-#ifndef FFMPEG_HAVE_DECODE_AUDIO3
-FFMPEG_INLINE
-int avcodec_decode_audio3(AVCodecContext *avctx,
- int16_t *samples,
- int *frame_size_ptr,
- AVPacket *avpkt)
-{
- return avcodec_decode_audio2(avctx, samples, frame_size_ptr, avpkt->data, avpkt->size);
-}
-#endif
-
-#ifndef FFMPEG_HAVE_DECODE_VIDEO2
-FFMPEG_INLINE
-int avcodec_decode_video2(AVCodecContext *avctx,
- AVFrame *picture,
- int *got_picture_ptr,
- AVPacket *avpkt)
-{
- return avcodec_decode_video(avctx, picture, got_picture_ptr, avpkt->data, avpkt->size);
-}
-#endif
FFMPEG_INLINE
int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture)
{
int64_t pts;
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 34, 100)
pts = picture->pts;
-#else
- pts = picture->pkt_pts;
-#endif
if (pts == AV_NOPTS_VALUE) {
pts = picture->pkt_dts;
@@ -432,124 +80,16 @@ int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture)
return pts;
}
-/* obsolete constant formerly defined in FFMpeg libavcodec/avcodec.h */
-#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
-# define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
-#endif
-
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 1, 0)
-FFMPEG_INLINE
-int avcodec_encode_video2(AVCodecContext *avctx,
- AVPacket *pkt,
- const AVFrame *frame,
- int *got_output)
-{
- int outsize, ret;
-
- ret = av_new_packet(pkt, avctx->width * avctx->height * 7 + 10000);
- if (ret < 0)
- return ret;
-
- outsize = avcodec_encode_video(avctx, pkt->data, pkt->size, frame);
- if (outsize <= 0) {
- *got_output = 0;
- av_free_packet(pkt);
- }
- else {
- *got_output = 1;
- av_shrink_packet(pkt, outsize);
- if (avctx->coded_frame) {
- pkt->pts = avctx->coded_frame->pts;
- if (avctx->coded_frame->key_frame)
- pkt->flags |= AV_PKT_FLAG_KEY;
- }
- }
-
- return outsize >= 0 ? 0 : outsize;
-}
-
-#endif
-
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 17, 0)
-FFMPEG_INLINE
-void avformat_close_input(AVFormatContext **ctx)
-{
- av_close_input_file(*ctx);
- *ctx = NULL;
-}
-#endif
-
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
-FFMPEG_INLINE
-AVFrame *av_frame_alloc(void)
-{
- return avcodec_alloc_frame();
-}
-
-FFMPEG_INLINE
-void av_frame_free(AVFrame **frame)
-{
- av_freep(frame);
-}
-#endif
-
-FFMPEG_INLINE
-const char *av_get_metadata_key_value(AVDictionary *metadata, const char *key)
-{
- if (metadata == NULL) {
- return NULL;
- }
- AVDictionaryEntry *tag = NULL;
- while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
- if (!strcmp(tag->key, key)) {
- return tag->value;
- }
- }
- return NULL;
-}
-
-FFMPEG_INLINE
-bool av_check_encoded_with_ffmpeg(AVFormatContext *ctx)
-{
- const char *encoder = av_get_metadata_key_value(ctx->metadata, "ENCODER");
- if (encoder != NULL && !strncmp(encoder, "Lavf", 4)) {
- return true;
- }
- return false;
-}
-
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0)
-# define AV_OPT_SEARCH_FAKE_OBJ 0
-#endif
-
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
-# define FFMPEG_HAVE_DEPRECATED_FLAGS2
-#endif
-
-/* Since FFmpeg-1.1 this constant have AV_ prefix. */
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 3, 100)
-# define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
-# define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
-# define AV_PIX_FMT_BGRA PIX_FMT_BGRA
-# define AV_PIX_FMT_ARGB PIX_FMT_ARGB
-# define AV_PIX_FMT_RGBA PIX_FMT_RGBA
-#endif
-
-/* New API from FFmpeg-2.0 which soon became recommended one. */
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100)
-# define av_frame_alloc avcodec_alloc_frame
-# define av_frame_free avcodec_free_frame
-# define av_frame_unref avcodec_get_frame_defaults
-#endif
-
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 24, 102)
-
-/* NOTE: The code in this block are from FFmpeg 2.6.4, which is licensed by LGPL. */
+/* -------------------------------------------------------------------- */
+/** \name Deinterlace code block
+ *
+ * NOTE: The code in this block are from FFmpeg 2.6.4, which is licensed by LGPL.
+ * \{ */
-# define MAX_NEG_CROP 1024
+#define MAX_NEG_CROP 1024
-# define times4(x) x, x, x, x
-# define times256(x) times4(times4(times4(times4(times4(x)))))
+#define times4(x) x, x, x, x
+#define times256(x) times4(times4(times4(times4(times4(x)))))
static const uint8_t ff_compat_crop_tab[256 + 2 * MAX_NEG_CROP] = {
times256(0x00), 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
@@ -575,8 +115,8 @@ static const uint8_t ff_compat_crop_tab[256 + 2 * MAX_NEG_CROP] = {
0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
0xFB, 0xFC, 0xFD, 0xFE, 0xFF, times256(0xFF)};
-# undef times4
-# undef times256
+#undef times4
+#undef times256
/* filter parameters: [-1 4 2 4 -1] // 8 */
FFMPEG_INLINE
@@ -668,8 +208,9 @@ int deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int
uint8_t *src_m1, *src_0, *src_p1, *src_p2;
int y;
uint8_t *buf = (uint8_t *)av_malloc(width);
- if (!buf)
+ if (!buf) {
return AVERROR(ENOMEM);
+ }
src_m1 = src1;
memcpy(buf, src_m1, width);
@@ -689,24 +230,21 @@ int deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int
return 0;
}
-# ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-# endif
-
FFMPEG_INLINE
-int avpicture_deinterlace(
- AVPicture *dst, const AVPicture *src, enum AVPixelFormat pix_fmt, int width, int height)
+int av_image_deinterlace(
+ AVFrame *dst, const AVFrame *src, enum AVPixelFormat pix_fmt, int width, int height)
{
int i, ret;
if (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P &&
pix_fmt != AV_PIX_FMT_YUV422P && pix_fmt != AV_PIX_FMT_YUVJ422P &&
pix_fmt != AV_PIX_FMT_YUV444P && pix_fmt != AV_PIX_FMT_YUV411P &&
- pix_fmt != AV_PIX_FMT_GRAY8)
+ pix_fmt != AV_PIX_FMT_GRAY8) {
return -1;
- if ((width & 3) != 0 || (height & 3) != 0)
+ }
+ if ((width & 3) != 0 || (height & 3) != 0) {
return -1;
+ }
for (i = 0; i < 3; i++) {
if (i == 1) {
@@ -732,8 +270,9 @@ int avpicture_deinterlace(
}
if (src == dst) {
ret = deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i], width, height);
- if (ret < 0)
+ if (ret < 0) {
return ret;
+ }
}
else {
deinterlace_bottom_field(
@@ -743,10 +282,6 @@ int avpicture_deinterlace(
return 0;
}
-# ifdef __GNUC__
-# pragma GCC diagnostic pop
-# endif
-
-#endif
+/** \} Deinterlace code block */
#endif
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 1739659ab88..f90e8a973bf 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
intern/GHOST_SystemPaths.h
intern/GHOST_TimerManager.h
intern/GHOST_TimerTask.h
+ intern/GHOST_Util.h
intern/GHOST_Window.h
intern/GHOST_WindowManager.h
)
@@ -438,6 +439,7 @@ endif()
if(WITH_XR_OPENXR)
list(APPEND SRC
intern/GHOST_Xr.cpp
+ intern/GHOST_XrAction.cpp
intern/GHOST_XrContext.cpp
intern/GHOST_XrEvent.cpp
intern/GHOST_XrGraphicsBinding.cpp
@@ -446,6 +448,7 @@ if(WITH_XR_OPENXR)
GHOST_IXrContext.h
intern/GHOST_IXrGraphicsBinding.h
+ intern/GHOST_XrAction.h
intern/GHOST_XrContext.h
intern/GHOST_XrException.h
intern/GHOST_XrSession.h
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index d79111b742f..2bd9af6df5c 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -276,7 +276,7 @@ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle);
* wait (block) until the next event before returning.
* \return Indication of the presence of events.
*/
-extern int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent);
+extern bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent);
/**
* Retrieves events from the queue and send them to the event consumers.
@@ -1059,7 +1059,110 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
* \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure.
*/
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_context);
-#endif
+
+/* actions */
+/**
+ * Create an OpenXR action set for input/output.
+ */
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_context, const GHOST_XrActionSetInfo *info);
+
+/**
+ * Destroy a previously created OpenXR action set.
+ */
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Create OpenXR input/output actions.
+ */
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos);
+
+/**
+ * Destroy previously created OpenXR actions.
+ */
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names);
+
+/**
+ * Create spaces for pose-based OpenXR actions.
+ */
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Destroy previously created spaces for OpenXR actions.
+ */
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Create input/output path bindings for OpenXR actions.
+ */
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Destroy previously created bindings for OpenXR actions.
+ */
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Attach all created action sets to the current OpenXR session.
+ */
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_context);
+
+/**
+ * Update button/tracking states for OpenXR actions.
+ *
+ * \param action_set_name: The name of the action set to sync. If NULL, all action sets
+ * attached to the session will be synced.
+ */
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Apply an OpenXR haptic output action.
+ */
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude);
+
+/**
+ * Stop a previously applied OpenXR haptic output action.
+ */
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+/**
+ * Get action set custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name);
+
+/**
+ * Get action custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/intern/ghost/GHOST_IXrContext.h b/intern/ghost/GHOST_IXrContext.h
index dd266a3b6ae..86fe78814a7 100644
--- a/intern/ghost/GHOST_IXrContext.h
+++ b/intern/ghost/GHOST_IXrContext.h
@@ -22,6 +22,8 @@
#include "GHOST_Types.h"
+class GHOST_XrSession;
+
class GHOST_IXrContext {
public:
virtual ~GHOST_IXrContext() = default;
@@ -31,6 +33,10 @@ class GHOST_IXrContext {
virtual bool isSessionRunning() const = 0;
virtual void drawSessionViews(void *draw_customdata) = 0;
+ /* Needed for the GHOST C api. */
+ virtual GHOST_XrSession *getSession() = 0;
+ virtual const GHOST_XrSession *getSession() const = 0;
+
virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0;
virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index bd7ba6657f0..3a8d0fbfecf 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -416,7 +416,10 @@ typedef enum {
GHOST_kGrabNormal,
/** Wrap the mouse location to prevent limiting screen bounds. */
GHOST_kGrabWrap,
- /** Hide the mouse while grabbing and restore the original location on release (numbuts). */
+ /**
+ * Hide the mouse while grabbing and restore the original location on release
+ * (used for number buttons and some other draggable UI elements).
+ */
GHOST_kGrabHide,
} GHOST_TGrabCursorMode;
@@ -631,7 +634,9 @@ typedef enum GHOST_TXrGraphicsBinding {
typedef void (*GHOST_XrErrorHandlerFn)(const struct GHOST_XrError *);
+typedef void (*GHOST_XrSessionCreateFn)(void);
typedef void (*GHOST_XrSessionExitFn)(void *customdata);
+typedef void (*GHOST_XrCustomdataFreeFn)(void *customdata);
typedef void *(*GHOST_XrGraphicsContextBindFn)(void);
typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_ContextHandle graphics_context);
@@ -662,6 +667,7 @@ typedef struct {
typedef struct {
GHOST_XrPose base_pose;
+ GHOST_XrSessionCreateFn create_fn;
GHOST_XrSessionExitFn exit_fn;
void *exit_customdata;
} GHOST_XrSessionBeginInfo;
@@ -688,4 +694,54 @@ typedef struct GHOST_XrError {
void *customdata;
} GHOST_XrError;
-#endif
+typedef struct GHOST_XrActionSetInfo {
+ const char *name;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrActionSet */
+} GHOST_XrActionSetInfo;
+
+/** XR action type. Enum values match those in OpenXR's
+ * XrActionType enum for consistency. */
+typedef enum GHOST_XrActionType {
+ GHOST_kXrActionTypeBooleanInput = 1,
+ GHOST_kXrActionTypeFloatInput = 2,
+ GHOST_kXrActionTypeVector2fInput = 3,
+ GHOST_kXrActionTypePoseInput = 4,
+ GHOST_kXrActionTypeVibrationOutput = 100,
+} GHOST_XrActionType;
+
+typedef struct GHOST_XrActionInfo {
+ const char *name;
+ GHOST_XrActionType type;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrAction */
+} GHOST_XrActionInfo;
+
+typedef struct GHOST_XrActionSpaceInfo {
+ const char *action_name;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** Poses for each subaction path. */
+ const GHOST_XrPose *poses;
+} GHOST_XrActionSpaceInfo;
+
+typedef struct GHOST_XrActionBindingInfo {
+ const char *action_name;
+ GHOST_TUns32 count_interaction_paths;
+ /** Interaction path: User (subaction) path + component path. */
+ const char **interaction_paths;
+} GHOST_XrActionBindingInfo;
+
+typedef struct GHOST_XrActionProfileInfo {
+ const char *profile_path;
+ GHOST_TUns32 count_bindings;
+ const GHOST_XrActionBindingInfo *bindings;
+} GHOST_XrActionProfileInfo;
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index af65e1cd4d2..955f35274ea 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -33,6 +33,7 @@
#include "intern/GHOST_Debug.h"
#ifdef WITH_XR_OPENXR
# include "GHOST_IXrContext.h"
+# include "intern/GHOST_XrSession.h"
#endif
#include "intern/GHOST_CallbackEventConsumer.h"
#include "intern/GHOST_XrException.h"
@@ -248,11 +249,11 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle)
return (int)system->getFullScreen();
}
-int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
+bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
- return (int)system->processEvents(waitForEvent ? true : false);
+ return system->processEvents(waitForEvent);
}
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
@@ -953,4 +954,145 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
return 0; /* Only reached if exception is thrown. */
}
-#endif
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_contexthandle,
+ const GHOST_XrActionSetInfo *info)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSet(*info), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSet(action_set_name), xr_context);
+}
+
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActions(action_set_name, count, infos), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context);
+}
+
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionBindings(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->attachActionSets(), xr_context);
+ return 0;
+}
+
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->syncActions(action_set_name), xr_context);
+ return 0;
+}
+
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->applyHapticAction(
+ action_set_name, action_name, *duration, *frequency, *amplitude),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name), xr_context);
+}
+
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionSetCustomdata(action_set_name), xr_context);
+ return 0;
+}
+
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionCustomdata(action_set_name, action_name),
+ xr_context);
+ return 0;
+}
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
index 82ed9de230d..a62db31c952 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -112,8 +112,7 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
}
}
-/* based on a code from Saul Rennison
- * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */
+/* Based on: https://stackoverflow.com/a/2766963/432509 */
typedef enum DecodeState_e {
STATE_SEARCH = 0, ///< searching for an ampersand to convert
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 050c90097c5..6ddb7f031f5 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -1711,7 +1711,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
dx = [event scrollingDeltaX];
dy = [event scrollingDeltaY];
- /* However, wacom tablet (intuos5) needs old deltas,
+ /* However, Wacom tablet (intuos5) needs old deltas,
* it then has momentum and phase at zero. */
if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
dx = [event deltaX];
diff --git a/intern/ghost/intern/GHOST_Util.h b/intern/ghost/intern/GHOST_Util.h
new file mode 100644
index 00000000000..8be5e373b28
--- /dev/null
+++ b/intern/ghost/intern/GHOST_Util.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+#include <functional>
+
+/**
+ * RAII wrapper for typical C `void *` custom data.
+ * Used for exception safe custom-data handling during constructor calls.
+ */
+struct GHOST_C_CustomDataWrapper {
+ using FreeFn = std::function<void(void *)>;
+
+ void *custom_data_;
+ FreeFn free_fn_;
+
+ GHOST_C_CustomDataWrapper(void *custom_data, FreeFn free_fn)
+ : custom_data_(custom_data), free_fn_(free_fn)
+ {
+ }
+ ~GHOST_C_CustomDataWrapper()
+ {
+ if (free_fn_ != nullptr && custom_data_ != nullptr) {
+ free_fn_(custom_data_);
+ }
+ }
+};
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index ef155d96f99..bf142239606 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -121,22 +121,28 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
monitor.dwFlags = 0;
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
- /* Adjust our requested size to allow for caption and borders and constrain to monitor. */
- AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
+ /* Constrain size to fit within this monitor. */
width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left);
- left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top);
- top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
-
- m_hWnd = ::CreateWindowExW(extended_style, // window extended style
- s_windowClassName, // pointer to registered class name
- title_16, // pointer to window name
- style, // window style
- left, // horizontal position of window
- top, // vertical position of window
- width, // window width
- height, // window height
- m_parentWindowHwnd, // handle to parent or owner window
+
+ win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
+ win_rect.right = win_rect.left + width;
+ win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
+ win_rect.bottom = win_rect.top + height;
+
+ /* Adjust our requested values to allow for caption, borders, shadows, etc.
+ Windows API Note: You cannot specify WS_OVERLAPPED when calling. */
+ AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style);
+
+ m_hWnd = ::CreateWindowExW(extended_style, // window extended style
+ s_windowClassName, // pointer to registered class name
+ title_16, // pointer to window name
+ style, // window style
+ win_rect.left, // horizontal position of window
+ win_rect.top, // vertical position of window
+ win_rect.right - win_rect.left, // window width
+ win_rect.bottom - win_rect.top, // window height
+ m_parentWindowHwnd, // handle to parent or owner window
0, // handle to menu or child-window identifier
::GetModuleHandle(0), // handle to application instance
0); // pointer to window-creation data
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index 413a883784b..b004f7e7b19 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -242,7 +242,7 @@ typedef enum {
} GHOST_MouseCaptureEventWin32;
/**
- * GHOST window on M$ Windows OSs.
+ * GHOST window on MS Windows OSs.
*/
class GHOST_WindowWin32 : public GHOST_Window {
public:
diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp
new file mode 100644
index 00000000000..172ac40c84f
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.cpp
@@ -0,0 +1,477 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cassert>
+#include <cstring>
+
+#include "GHOST_Types.h"
+
+#include "GHOST_XrException.h"
+#include "GHOST_Xr_intern.h"
+
+#include "GHOST_XrAction.h"
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSpace
+ *
+ * \{ */
+
+GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx)
+{
+ const char *subaction_path = info.subaction_paths[subaction_idx];
+ CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path),
+ (std::string("Failed to get user path \"") + subaction_path + "\".").data());
+
+ XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO};
+ action_space_info.action = action;
+ action_space_info.subactionPath = m_subaction_path;
+ copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace);
+
+ CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space),
+ (std::string("Failed to create space \"") + subaction_path + "\" for action \"" +
+ info.action_name + "\".")
+ .data());
+}
+
+GHOST_XrActionSpace::~GHOST_XrActionSpace()
+{
+ if (m_space != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroySpace(m_space));
+ }
+}
+
+XrSpace GHOST_XrActionSpace::getSpace() const
+{
+ return m_space;
+}
+
+const XrPath &GHOST_XrActionSpace::getSubactionPath() const
+{
+ return m_subaction_path;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionProfile
+ *
+ * \{ */
+
+GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ CHECK_XR(
+ xrStringToPath(instance, profile_path, &m_profile),
+ (std::string("Failed to get interaction profile path \"") + profile_path + "\".").data());
+
+ /* Create bindings. */
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ bindings_info.interactionProfile = m_profile;
+ bindings_info.countSuggestedBindings = 1;
+
+ for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths;
+ ++interaction_idx) {
+ const char *interaction_path = info.interaction_paths[interaction_idx];
+ if (m_bindings.find(interaction_path) != m_bindings.end()) {
+ continue;
+ }
+
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding),
+ (std::string("Failed to get interaction path \"") + interaction_path + "\".").data());
+ bindings_info.suggestedBindings = &sbinding;
+
+ /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it
+ * greatly improves error checking to suggest them here first. */
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ (std::string("Failed to create binding for profile \"") + profile_path +
+ "\" and action \"" + info.action_name +
+ "\". Are the profile and action paths correct?")
+ .data());
+
+ m_bindings.insert({interaction_path, sbinding.binding});
+ }
+}
+
+void GHOST_XrActionProfile::getBindings(
+ XrAction action, std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>>::iterator it = r_bindings.find(
+ m_profile);
+ if (it == r_bindings.end()) {
+ it = r_bindings
+ .emplace(std::piecewise_construct, std::make_tuple(m_profile), std::make_tuple())
+ .first;
+ }
+
+ std::vector<XrActionSuggestedBinding> &sbindings = it->second;
+
+ for (auto &[path, binding] : m_bindings) {
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ sbinding.binding = binding;
+
+ sbindings.push_back(std::move(sbinding));
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrAction
+ *
+ * \{ */
+
+GHOST_XrAction::GHOST_XrAction(XrInstance instance,
+ XrActionSet action_set,
+ const GHOST_XrActionInfo &info)
+ : m_type(info.type),
+ m_states(info.states),
+ m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ m_subaction_paths.resize(info.count_subaction_paths);
+
+ for (uint32_t i = 0; i < info.count_subaction_paths; ++i) {
+ CHECK_XR(xrStringToPath(instance, info.subaction_paths[i], &m_subaction_paths[i]),
+ (std::string("Failed to get user path \"") + info.subaction_paths[i] + "\".").data());
+ }
+
+ XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO};
+ strcpy(action_info.actionName, info.name);
+ strcpy(action_info.localizedActionName, info.name); /* Just use same name for localized. This can
+ be changed in the future if necessary. */
+
+ switch (info.type) {
+ case GHOST_kXrActionTypeBooleanInput:
+ action_info.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
+ break;
+ case GHOST_kXrActionTypeFloatInput:
+ action_info.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
+ break;
+ case GHOST_kXrActionTypeVector2fInput:
+ action_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
+ break;
+ case GHOST_kXrActionTypePoseInput:
+ action_info.actionType = XR_ACTION_TYPE_POSE_INPUT;
+ break;
+ case GHOST_kXrActionTypeVibrationOutput:
+ action_info.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
+ break;
+ }
+ action_info.countSubactionPaths = info.count_subaction_paths;
+ action_info.subactionPaths = m_subaction_paths.data();
+
+ CHECK_XR(xrCreateAction(action_set, &action_info, &m_action),
+ (std::string("Failed to create action \"") + info.name +
+ "\". Action name and/or paths are invalid. Name must not contain upper "
+ "case letters or special characters other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrAction::~GHOST_XrAction()
+{
+ if (m_action != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyAction(m_action));
+ }
+}
+
+bool GHOST_XrAction::createSpace(XrInstance instance,
+ XrSession session,
+ const GHOST_XrActionSpaceInfo &info)
+{
+ uint32_t subaction_idx = 0;
+ for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) {
+ return false;
+ }
+ }
+
+ for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ m_spaces.emplace(std::piecewise_construct,
+ std::make_tuple(info.subaction_paths[subaction_idx]),
+ std::make_tuple(instance, session, m_action, info, subaction_idx));
+ }
+
+ return true;
+}
+
+void GHOST_XrAction::destroySpace(const char *subaction_path)
+{
+ if (m_spaces.find(subaction_path) != m_spaces.end()) {
+ m_spaces.erase(subaction_path);
+ }
+}
+
+bool GHOST_XrAction::createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ if (m_profiles.find(profile_path) != m_profiles.end()) {
+ return false;
+ }
+
+ m_profiles.emplace(std::piecewise_construct,
+ std::make_tuple(profile_path),
+ std::make_tuple(instance, m_action, profile_path, info));
+
+ return true;
+}
+
+void GHOST_XrAction::destroyBinding(const char *interaction_profile_path)
+{
+ if (m_profiles.find(interaction_profile_path) != m_profiles.end()) {
+ m_profiles.erase(interaction_profile_path);
+ }
+}
+
+void GHOST_XrAction::updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO};
+ state_info.action = m_action;
+
+ const size_t count_subaction_paths = m_subaction_paths.size();
+ for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) {
+ state_info.subactionPath = m_subaction_paths[subaction_idx];
+
+ switch (m_type) {
+ case GHOST_kXrActionTypeBooleanInput: {
+ XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN};
+ CHECK_XR(xrGetActionStateBoolean(session, &state_info, &state),
+ (std::string("Failed to get state for boolean action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ ((bool *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeFloatInput: {
+ XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT};
+ CHECK_XR(
+ xrGetActionStateFloat(session, &state_info, &state),
+ (std::string("Failed to get state for float action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ ((float *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVector2fInput: {
+ XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F};
+ CHECK_XR(xrGetActionStateVector2f(session, &state_info, &state),
+ (std::string("Failed to get state for vector2f action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ memcpy(((float(*)[2])m_states)[subaction_idx], &state.currentState, sizeof(float[2]));
+ }
+ break;
+ }
+ case GHOST_kXrActionTypePoseInput: {
+ XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE};
+ CHECK_XR(
+ xrGetActionStatePose(session, &state_info, &state),
+ (std::string("Failed to get state for pose action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ XrSpace pose_space = XR_NULL_HANDLE;
+ for (auto &[path, space] : m_spaces) {
+ if (space.getSubactionPath() == state_info.subactionPath) {
+ pose_space = space.getSpace();
+ break;
+ }
+ }
+
+ if (pose_space != XR_NULL_HANDLE) {
+ XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION};
+ CHECK_XR(
+ xrLocateSpace(
+ pose_space, reference_space, predicted_display_time, &space_location),
+ (std::string("Failed to query pose space for action \"") + action_name + "\".")
+ .data());
+ copy_openxr_pose_to_ghost_pose(space_location.pose,
+ ((GHOST_XrPose *)m_states)[subaction_idx]);
+ }
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVibrationOutput: {
+ break;
+ }
+ }
+ }
+}
+
+void GHOST_XrAction::applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ XrHapticVibration vibration{XR_TYPE_HAPTIC_VIBRATION};
+ vibration.duration = (duration == 0) ? XR_MIN_HAPTIC_DURATION :
+ static_cast<XrDuration>(duration);
+ vibration.frequency = frequency;
+ vibration.amplitude = amplitude;
+
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration),
+ (std::string("Failed to apply haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name)
+{
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrStopHapticFeedback(session, &haptic_info),
+ (std::string("Failed to stop haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void *GHOST_XrAction::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrAction::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[path, profile] : m_profiles) {
+ profile.getBindings(m_action, r_bindings);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSet
+ *
+ * \{ */
+
+GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info)
+ : m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ XrActionSetCreateInfo action_set_info{XR_TYPE_ACTION_SET_CREATE_INFO};
+ strcpy(action_set_info.actionSetName, info.name);
+ strcpy(action_set_info.localizedActionSetName,
+ info.name); /* Just use same name for localized. This can be changed in the future if
+ necessary. */
+ action_set_info.priority = 0; /* Use same (default) priority for all action sets. */
+
+ CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set),
+ (std::string("Failed to create action set \"") + info.name +
+ "\". Name must not contain upper case letters or special characters "
+ "other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrActionSet::~GHOST_XrActionSet()
+{
+ /* This needs to be done before xrDestroyActionSet() to avoid an assertion in the GHOST_XrAction
+ * destructor (which calls xrDestroyAction()). */
+ m_actions.clear();
+
+ if (m_action_set != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyActionSet(m_action_set));
+ }
+}
+
+bool GHOST_XrActionSet::createAction(XrInstance instance, const GHOST_XrActionInfo &info)
+{
+ if (m_actions.find(info.name) != m_actions.end()) {
+ return false;
+ }
+
+ m_actions.emplace(std::piecewise_construct,
+ std::make_tuple(info.name),
+ std::make_tuple(instance, m_action_set, info));
+
+ return true;
+}
+
+void GHOST_XrActionSet::destroyAction(const char *action_name)
+{
+ if (m_actions.find(action_name) != m_actions.end()) {
+ m_actions.erase(action_name);
+ }
+}
+
+GHOST_XrAction *GHOST_XrActionSet::findAction(const char *action_name)
+{
+ std::map<std::string, GHOST_XrAction>::iterator it = m_actions.find(action_name);
+ if (it == m_actions.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+void GHOST_XrActionSet::updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ for (auto &[name, action] : m_actions) {
+ action.updateState(session, name.data(), reference_space, predicted_display_time);
+ }
+}
+
+XrActionSet GHOST_XrActionSet::getActionSet() const
+{
+ return m_action_set;
+}
+
+void *GHOST_XrActionSet::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrActionSet::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[name, action] : m_actions) {
+ action.getBindings(r_bindings);
+ }
+}
+
+/** \} */
diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h
new file mode 100644
index 00000000000..bdc6cafb4a9
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.h
@@ -0,0 +1,145 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrSpace, XrPath,
+ * etc.). */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "GHOST_Util.h"
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSpace {
+ public:
+ GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx);
+ ~GHOST_XrActionSpace();
+
+ XrSpace getSpace() const;
+ const XrPath &getSubactionPath() const;
+
+ private:
+ XrSpace m_space = XR_NULL_HANDLE;
+ XrPath m_subaction_path = XR_NULL_PATH;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionProfile {
+ public:
+ GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ ~GHOST_XrActionProfile() = default;
+
+ void getBindings(XrAction action,
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrPath m_profile = XR_NULL_PATH;
+ /* Bindings identified by interaction (user (subaction) + component) path. */
+ std::map<std::string, XrPath> m_bindings;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrAction {
+ public:
+ GHOST_XrAction() = delete; /* Default constructor for map storage. */
+ GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info);
+ ~GHOST_XrAction();
+
+ bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info);
+ void destroySpace(const char *subaction_path);
+
+ bool createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ void destroyBinding(const char *profile_path);
+
+ void updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+ void applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticFeedback(XrSession session, const char *action_name);
+
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrAction m_action = XR_NULL_HANDLE;
+ GHOST_XrActionType m_type;
+ std::vector<XrPath> m_subaction_paths;
+ /** States for each subaction path. */
+ void *m_states;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrAction */
+
+ /* Spaces identified by user (subaction) path. */
+ std::map<std::string, GHOST_XrActionSpace> m_spaces;
+ /* Profiles identified by interaction profile path. */
+ std::map<std::string, GHOST_XrActionProfile> m_profiles;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSet {
+ public:
+ GHOST_XrActionSet() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info);
+ ~GHOST_XrActionSet();
+
+ bool createAction(XrInstance instance, const GHOST_XrActionInfo &info);
+ void destroyAction(const char *action_name);
+ GHOST_XrAction *findAction(const char *action_name);
+
+ void updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+
+ XrActionSet getActionSet() const;
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrActionSet m_action_set = XR_NULL_HANDLE;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrActionSet */
+
+ std::map<std::string, GHOST_XrAction> m_actions;
+};
+
+/* -------------------------------------------------------------------- */
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 2bf67c121f8..daad0b8190a 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -55,7 +55,6 @@ void *GHOST_XrContext::s_error_handler_customdata = nullptr;
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
@@ -153,7 +152,6 @@ void GHOST_XrContext::storeInstanceProperties()
/* -------------------------------------------------------------------- */
/** \name Debug Printing
- *
* \{ */
void GHOST_XrContext::printInstanceInfo()
@@ -242,14 +240,13 @@ void GHOST_XrContext::initDebugMessenger()
/* -------------------------------------------------------------------- */
/** \name Error handling
- *
* \{ */
void GHOST_XrContext::dispatchErrorMessage(const GHOST_XrException *exception) const
{
GHOST_XrError error;
- error.user_message = exception->m_msg;
+ error.user_message = exception->m_msg.data();
error.customdata = s_error_handler_customdata;
if (isDebugMode()) {
@@ -273,7 +270,6 @@ void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *c
/* -------------------------------------------------------------------- */
/** \name OpenXR API-Layers and Extensions
- *
* \{ */
/**
@@ -378,7 +374,7 @@ void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_name
for (const std::string &layer : try_layers) {
if (openxr_layer_is_available(m_oxr->layers, layer)) {
- r_ext_names.push_back(layer.c_str());
+ r_ext_names.push_back(layer.data());
}
}
}
@@ -488,6 +484,7 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse(
void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
{
+ m_custom_funcs.session_create_fn = begin_info->create_fn;
m_custom_funcs.session_exit_fn = begin_info->exit_fn;
m_custom_funcs.session_exit_customdata = begin_info->exit_customdata;
@@ -538,6 +535,16 @@ void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChan
* Public as in, exposed in the Ghost API.
* \{ */
+GHOST_XrSession *GHOST_XrContext::getSession()
+{
+ return m_session.get();
+}
+
+const GHOST_XrSession *GHOST_XrContext::getSession() const
+{
+ return m_session.get();
+}
+
void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
GHOST_XrGraphicsContextUnbindFn unbind_fn)
{
@@ -564,7 +571,6 @@ bool GHOST_XrContext::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name Ghost Internal Accessors and Mutators
- *
* \{ */
GHOST_TXrOpenXRRuntimeID GHOST_XrContext::getOpenXRRuntimeID() const
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index 59c7786ed7a..f29d7349f7e 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -35,6 +35,7 @@ struct GHOST_XrCustomFuncs {
/** Function to release (possibly free) a graphics context. */
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr;
+ GHOST_XrSessionCreateFn session_create_fn = nullptr;
GHOST_XrSessionExitFn session_exit_fn = nullptr;
void *session_exit_customdata = nullptr;
@@ -72,6 +73,10 @@ class GHOST_XrContext : public GHOST_IXrContext {
bool isSessionRunning() const override;
void drawSessionViews(void *draw_customdata) override;
+ /** Needed for the GHOST C api. */
+ GHOST_XrSession *getSession() override;
+ const GHOST_XrSession *getSession() const override;
+
static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
void dispatchErrorMessage(const class GHOST_XrException *exception) const override;
diff --git a/intern/ghost/intern/GHOST_XrException.h b/intern/ghost/intern/GHOST_XrException.h
index 30c33eaf98f..e93164e04c8 100644
--- a/intern/ghost/intern/GHOST_XrException.h
+++ b/intern/ghost/intern/GHOST_XrException.h
@@ -21,6 +21,7 @@
#pragma once
#include <exception>
+#include <string>
class GHOST_XrException : public std::exception {
friend class GHOST_XrContext;
@@ -33,10 +34,10 @@ class GHOST_XrException : public std::exception {
const char *what() const noexcept override
{
- return m_msg;
+ return m_msg.data();
}
private:
- const char *m_msg;
+ std::string m_msg;
int m_result;
};
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index a1fd7fc2781..a7438fae13c 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -28,6 +28,7 @@
#include "GHOST_C-api.h"
#include "GHOST_IXrGraphicsBinding.h"
+#include "GHOST_XrAction.h"
#include "GHOST_XrContext.h"
#include "GHOST_XrException.h"
#include "GHOST_XrSwapchain.h"
@@ -46,6 +47,8 @@ struct OpenXRSessionData {
XrSpace view_space;
std::vector<XrView> views;
std::vector<GHOST_XrSwapchain> swapchains;
+
+ std::map<std::string, GHOST_XrActionSet> action_sets;
};
struct GHOST_XrDrawInfo {
@@ -59,7 +62,6 @@ struct GHOST_XrDrawInfo {
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrSession::GHOST_XrSession(GHOST_XrContext &xr_context)
@@ -72,6 +74,7 @@ GHOST_XrSession::~GHOST_XrSession()
unbindGraphicsContext();
m_oxr->swapchains.clear();
+ m_oxr->action_sets.clear();
if (m_oxr->reference_space != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space));
@@ -110,7 +113,6 @@ void GHOST_XrSession::initSystem()
/* -------------------------------------------------------------------- */
/** \name State Management
- *
* \{ */
static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose)
@@ -179,7 +181,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
std::ostringstream strstream;
strstream << "Available graphics context version does not meet the following requirements: "
<< requirement_str;
- throw GHOST_XrException(strstream.str().c_str());
+ throw GHOST_XrException(strstream.str().data());
}
m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
@@ -196,6 +198,9 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
prepareDrawing();
create_reference_spaces(*m_oxr, begin_info->base_pose);
+
+ /* Create and bind actions here. */
+ m_context->getCustomFuncs().session_create_fn();
}
void GHOST_XrSession::requestEnd()
@@ -225,10 +230,9 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
assert(m_oxr->session == XR_NULL_HANDLE || m_oxr->session == lifecycle.session);
switch (lifecycle.state) {
- case XR_SESSION_STATE_READY: {
+ case XR_SESSION_STATE_READY:
beginSession();
break;
- }
case XR_SESSION_STATE_STOPPING:
endSession();
break;
@@ -245,7 +249,6 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
/* -------------------------------------------------------------------- */
/** \name Drawing
- *
* \{ */
void GHOST_XrSession::prepareDrawing()
@@ -352,18 +355,6 @@ void GHOST_XrSession::draw(void *draw_customdata)
endFrameDrawing(layers);
}
-static void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
-{
- /* Set and convert to Blender coordinate space. */
- r_ghost_pose.position[0] = oxr_pose.position.x;
- r_ghost_pose.position[1] = oxr_pose.position.y;
- r_ghost_pose.position[2] = oxr_pose.position.z;
- r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
- r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
- r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
- r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
-}
-
static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
{
/* Set and convert to Blender coordinate space. */
@@ -457,7 +448,6 @@ bool GHOST_XrSession::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name State Queries
- *
* \{ */
bool GHOST_XrSession::isRunning() const
@@ -505,3 +495,340 @@ void GHOST_XrSession::unbindGraphicsContext()
}
/** \} */ /* Graphics Context Injection */
+
+/* -------------------------------------------------------------------- */
+/** \name Actions
+ *
+ * \{ */
+
+static GHOST_XrActionSet *find_action_set(OpenXRSessionData *oxr, const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet>::iterator it = oxr->action_sets.find(action_set_name);
+ if (it == oxr->action_sets.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+bool GHOST_XrSession::createActionSet(const GHOST_XrActionSetInfo &info)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(info.name) != action_sets.end()) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ action_sets.emplace(
+ std::piecewise_construct, std::make_tuple(info.name), std::make_tuple(instance, info));
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSet(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(action_set_name) != action_sets.end()) {
+ action_sets.erase(action_set_name);
+ }
+}
+
+bool GHOST_XrSession::createActions(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!action_set->createAction(instance, infos[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ action_set->destroyAction(action_names[i]);
+ }
+}
+
+bool GHOST_XrSession::createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+ XrSession session = m_oxr->session;
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ if (!action->createSpace(instance, session, info)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ action->destroySpace(info.subaction_paths[subaction_idx]);
+ }
+ }
+}
+
+bool GHOST_XrSession::createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->createBinding(instance, profile_path, binding);
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->destroyBinding(profile_path);
+ }
+ }
+}
+
+bool GHOST_XrSession::attachActionSets()
+{
+ /* Suggest action bindings for all action sets. */
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> profile_bindings;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_set.getBindings(profile_bindings);
+ }
+
+ if (profile_bindings.size() < 1) {
+ return false;
+ }
+
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ XrInstance instance = m_context->getInstance();
+
+ for (auto &[profile, bindings] : profile_bindings) {
+ bindings_info.interactionProfile = profile;
+ bindings_info.countSuggestedBindings = (uint32_t)bindings.size();
+ bindings_info.suggestedBindings = bindings.data();
+
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ "Failed to suggest interaction profile bindings.");
+ }
+
+ /* Attach action sets. */
+ XrSessionActionSetsAttachInfo attach_info{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};
+ attach_info.countActionSets = (uint32_t)m_oxr->action_sets.size();
+
+ /* Create an aligned copy of the action sets to pass to xrAttachSessionActionSets(). */
+ std::vector<XrActionSet> action_sets(attach_info.countActionSets);
+ uint32_t i = 0;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_sets[i++] = action_set.getActionSet();
+ }
+ attach_info.actionSets = action_sets.data();
+
+ CHECK_XR(xrAttachSessionActionSets(m_oxr->session, &attach_info),
+ "Failed to attach XR action sets.");
+
+ return true;
+}
+
+bool GHOST_XrSession::syncActions(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+
+ XrActionsSyncInfo sync_info{XR_TYPE_ACTIONS_SYNC_INFO};
+ sync_info.countActiveActionSets = (action_set_name != nullptr) ? 1 :
+ (uint32_t)action_sets.size();
+ if (sync_info.countActiveActionSets < 1) {
+ return false;
+ }
+
+ std::vector<XrActiveActionSet> active_action_sets(sync_info.countActiveActionSets);
+ GHOST_XrActionSet *action_set = nullptr;
+
+ if (action_set_name != nullptr) {
+ action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrActiveActionSet &active_action_set = active_action_sets[0];
+ active_action_set.actionSet = action_set->getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ else {
+ uint32_t i = 0;
+ for (auto &[name, action_set] : action_sets) {
+ XrActiveActionSet &active_action_set = active_action_sets[i++];
+ active_action_set.actionSet = action_set.getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ }
+ sync_info.activeActionSets = active_action_sets.data();
+
+ CHECK_XR(xrSyncActions(m_oxr->session, &sync_info), "Failed to synchronize XR actions.");
+
+ /* Update action states (i.e. Blender custom data). */
+ XrSession session = m_oxr->session;
+ XrSpace reference_space = m_oxr->reference_space;
+ const XrTime &predicted_display_time = m_draw_info->frame_state.predictedDisplayTime;
+
+ if (action_set != nullptr) {
+ action_set->updateStates(session, reference_space, predicted_display_time);
+ }
+ else {
+ for (auto &[name, action_set] : action_sets) {
+ action_set.updateStates(session, reference_space, predicted_display_time);
+ }
+ }
+
+ return true;
+}
+
+bool GHOST_XrSession::applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return false;
+ }
+
+ action->applyHapticFeedback(m_oxr->session, action_name, duration, frequency, amplitude);
+
+ return true;
+}
+
+void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return;
+ }
+
+ action->stopHapticFeedback(m_oxr->session, action_name);
+}
+
+void *GHOST_XrSession::getActionSetCustomdata(const char *action_set_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ return action_set->getCustomdata();
+}
+
+void *GHOST_XrSession::getActionCustomdata(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return nullptr;
+ }
+
+ return action->getCustomdata();
+}
+
+/** \} */ /* Actions */
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index 79a586411e9..d09c78e1ea7 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -52,6 +52,43 @@ class GHOST_XrSession {
void draw(void *draw_customdata);
+ /** Action functions to be called pre-session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+ bool createActionSet(const GHOST_XrActionSetInfo &info);
+ void destroyActionSet(const char *action_set_name);
+ bool createActions(const char *action_set_name, uint32_t count, const GHOST_XrActionInfo *infos);
+ void destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names);
+ bool createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ void destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ bool createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ void destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ bool attachActionSets();
+
+ /** Action functions to be called post-session start. */
+ bool syncActions(
+ const char *action_set_name = nullptr); /* If action_set_name is nullptr, all attached
+ * action sets will be synced. */
+ bool applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticAction(const char *action_set_name, const char *action_name);
+
+ /* Custom data (owned by Blender, not GHOST) accessors. */
+ void *getActionSetCustomdata(const char *action_set_name);
+ void *getActionCustomdata(const char *action_set_name, const char *action_name);
+
private:
/** Pointer back to context managing this session. Would be nice to avoid, but needed to access
* custom callbacks set before session start. */
diff --git a/intern/ghost/intern/GHOST_Xr_intern.h b/intern/ghost/intern/GHOST_Xr_intern.h
index 137541c4528..0616e426da3 100644
--- a/intern/ghost/intern/GHOST_Xr_intern.h
+++ b/intern/ghost/intern/GHOST_Xr_intern.h
@@ -45,3 +45,27 @@
(void)_res; \
} \
(void)0
+
+inline void copy_ghost_pose_to_openxr_pose(const GHOST_XrPose &ghost_pose, XrPosef &r_oxr_pose)
+{
+ /* Set and convert to OpenXR coordinate space. */
+ r_oxr_pose.position.x = ghost_pose.position[0];
+ r_oxr_pose.position.y = ghost_pose.position[1];
+ r_oxr_pose.position.z = ghost_pose.position[2];
+ r_oxr_pose.orientation.w = ghost_pose.orientation_quat[0];
+ r_oxr_pose.orientation.x = ghost_pose.orientation_quat[1];
+ r_oxr_pose.orientation.y = ghost_pose.orientation_quat[2];
+ r_oxr_pose.orientation.z = ghost_pose.orientation_quat[3];
+}
+
+inline void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
+{
+ /* Set and convert to Blender coordinate space. */
+ r_ghost_pose.position[0] = oxr_pose.position.x;
+ r_ghost_pose.position[1] = oxr_pose.position.y;
+ r_ghost_pose.position[2] = oxr_pose.position.z;
+ r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
+ r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
+ r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
+ r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
+}
diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c
index 4283f990cfb..8cd1b5acb89 100644
--- a/intern/ghost/test/gears/GHOST_C-Test.c
+++ b/intern/ghost/test/gears/GHOST_C-Test.c
@@ -477,7 +477,7 @@ int main(int argc, char **argv)
/* Enter main loop */
while (!sExitRequested) {
- if (!GHOST_ProcessEvents(shSystem, 0)) {
+ if (!GHOST_ProcessEvents(shSystem, false)) {
#ifdef WIN32
/* If there were no events, be nice to other applications */
Sleep(10);
diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c
index 8c8858fc6d8..3b424f1ca89 100644
--- a/intern/ghost/test/multitest/MultiTest.c
+++ b/intern/ghost/test/multitest/MultiTest.c
@@ -926,7 +926,7 @@ void multitestapp_exit(MultiTestApp *app)
void multitestapp_run(MultiTestApp *app)
{
while (!app->exit) {
- GHOST_ProcessEvents(app->sys, 1);
+ GHOST_ProcessEvents(app->sys, true);
GHOST_DispatchEvents(app->sys);
}
}
diff --git a/intern/itasc/kdl/frames.hpp b/intern/itasc/kdl/frames.hpp
index 6cb336801a9..662eb3e857a 100644
--- a/intern/itasc/kdl/frames.hpp
+++ b/intern/itasc/kdl/frames.hpp
@@ -364,7 +364,7 @@ public:
//! Along an arbitrary axes. It is not necessary to normalize rotaxis.
//! returns identity rotation matrix in the case that the norm of rotaxis
- //! is to small to be used.
+ //! is too small to be used.
// @see Rot2 if you want to handle this error in another way.
static Rotation Rot(const Vector& rotaxis,double angle);
diff --git a/intern/itasc/kdl/utilities/error.h b/intern/itasc/kdl/utilities/error.h
index f2377702b9b..c6ab3ebdc69 100644
--- a/intern/itasc/kdl/utilities/error.h
+++ b/intern/itasc/kdl/utilities/error.h
@@ -161,7 +161,7 @@ class Error_MotionPlanning : public Error {};
class Error_MotionPlanning_Circle_ToSmall : public Error_MotionPlanning {
public:
- virtual const char* Description() const { return "Circle : radius is to small";}
+ virtual const char* Description() const { return "Circle : radius is too small";}
virtual int GetType() const {return 3001;}
};
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject f7b706dd6434db2d752f47c4b8c3148b2990fd7
+Subproject 5ab29b1331d2103dae634b987f121c4599459d7
diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png
index 70a68ce6847..babb3e30c6d 100644
--- a/release/datafiles/splash.png
+++ b/release/datafiles/splash.png
Binary files differ
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject af6356139616749c952ff5dfc6bf6dfa668e332
+Subproject 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 8970953d4a8a4ea3bf77c66370c817ed0cf1308
+Subproject 7d78c8a63f2f4b146f9327ddc0d567a5921b94e
diff --git a/release/scripts/freestyle/styles/ignore_small_occlusions.py b/release/scripts/freestyle/styles/ignore_small_occlusions.py
index efa228f4663..a5fb4671917 100644
--- a/release/scripts/freestyle/styles/ignore_small_occlusions.py
+++ b/release/scripts/freestyle/styles/ignore_small_occlusions.py
@@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####
-# Filename : ignore_small_oclusions.py
+# Filename : ignore_small_occlusions.py
# Author : Stephane Grabli
# Date : 04/08/2005
# Purpose : The strokes are drawn through small occlusions
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 387691f9f05..1e308dc9602 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -543,22 +543,6 @@ def module_bl_info(mod, info_basis=None):
if not addon_info["name"]:
addon_info["name"] = mod.__name__
- # Replace 'wiki_url' with 'doc_url'.
- doc_url = addon_info.pop("wiki_url", None)
- if doc_url is not None:
- # Unlikely, but possible that both are set.
- if not addon_info["doc_url"]:
- addon_info["doc_url"] = doc_url
- if _bpy.app.debug:
- print(
- "Warning: add-on \"%s\": 'wiki_url' in 'bl_info' "
- "is deprecated please use 'doc_url' instead!\n"
- " %s" % (
- addon_info['name'],
- getattr(mod, "__file__", None),
- )
- )
-
doc_url = addon_info["doc_url"]
if doc_url:
doc_url_prefix = "{BLENDER_MANUAL_URL}"
diff --git a/release/scripts/modules/bl_ui_utils/bug_report_url.py b/release/scripts/modules/bl_ui_utils/bug_report_url.py
index 5676e0d6815..3fc57467dac 100644
--- a/release/scripts/modules/bl_ui_utils/bug_report_url.py
+++ b/release/scripts/modules/bl_ui_utils/bug_report_url.py
@@ -21,7 +21,7 @@
def url_prefill_from_blender(addon_info=None):
import bpy
- import bgl
+ import gpu
import struct
import platform
import urllib.parse
@@ -38,9 +38,9 @@ def url_prefill_from_blender(addon_info=None):
)
fh.write(
"Graphics card: %s %s %s\n" % (
- bgl.glGetString(bgl.GL_RENDERER),
- bgl.glGetString(bgl.GL_VENDOR),
- bgl.glGetString(bgl.GL_VERSION),
+ gpu.platform.renderer_get(),
+ gpu.platform.vendor_get(),
+ gpu.platform.version_get(),
)
)
fh.write(
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index fad52eae84a..e9e9671cc35 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -370,7 +370,7 @@ def module_names(path, recursive=False):
def basename(path):
"""
- Equivalent to os.path.basename, but skips a "//" prefix.
+ Equivalent to ``os.path.basename``, but skips a "//" prefix.
Use for Windows compatibility.
"""
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
index db982e119d4..1656c21a137 100644
--- a/release/scripts/modules/bpy_extras/asset_utils.py
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -34,7 +34,7 @@ __all__ = (
class SpaceAssetInfo:
@classmethod
def is_asset_browser(cls, space_data: bpy.types.Space):
- return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
+ return space_data and space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
@classmethod
def is_asset_browser_poll(cls, context: Context):
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index 4ea8c88e8d9..aa540eeb23b 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -657,16 +657,14 @@ class Gizmo(StructRNA):
use_blend = color[3] < 1.0
if use_blend:
- # TODO: wrap GPU_blend from GPU state.
- from bgl import glEnable, glDisable, GL_BLEND
- glEnable(GL_BLEND)
+ gpu.state.blend_set('ALPHA')
with gpu.matrix.push_pop():
gpu.matrix.multiply_matrix(matrix)
batch.draw()
if use_blend:
- glDisable(GL_BLEND)
+ gpu.state.blend_set('NONE')
@staticmethod
def new_custom_shape(type, verts):
diff --git a/release/scripts/modules/gpu_extras/presets.py b/release/scripts/modules/gpu_extras/presets.py
index 81d515904a1..f490e1e74ba 100644
--- a/release/scripts/modules/gpu_extras/presets.py
+++ b/release/scripts/modules/gpu_extras/presets.py
@@ -57,12 +57,12 @@ def draw_circle_2d(position, color, radius, segments=32):
batch.draw()
-def draw_texture_2d(texture_id, position, width, height):
+def draw_texture_2d(texture, position, width, height):
"""
Draw a 2d texture.
- :arg texture_id: OpenGL id of the texture (e.g. :class:`bpy.types.Image.bindcode`).
- :type texture_id: int
+ :arg texture: GPUTexture to draw (e.g. gpu.texture.from_image(image) for :class:`bpy.types.Image`).
+ :type texture: :class:`gpu.types.GPUTexture`
:arg position: Position of the lower left corner.
:type position: 2D Vector
:arg width: Width of the image when drawn (not necessarily
@@ -72,7 +72,6 @@ def draw_texture_2d(texture_id, position, width, height):
:type height: float
"""
import gpu
- import bgl
from . batch import batch_for_shader
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
@@ -83,14 +82,20 @@ def draw_texture_2d(texture_id, position, width, height):
{"pos": coords, "texCoord": coords},
)
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id)
-
with gpu.matrix.push_pop():
gpu.matrix.translate(position)
gpu.matrix.scale((width, height))
shader = gpu.shader.from_builtin('2D_IMAGE')
shader.bind()
- shader.uniform_int("image", 0)
+
+ if isinstance(texture, int):
+ # Call the legacy bgl to not break the existing API
+ import bgl
+ bgl.glActiveTexture(bgl.GL_TEXTURE0)
+ bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture)
+ shader.uniform_int("image", 0)
+ else:
+ shader.uniform_sampler("image", texture)
+
batch.draw(shader)
diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py
index 190f0282339..b7a15bbbc19 100644
--- a/release/scripts/modules/keyingsets_utils.py
+++ b/release/scripts/modules/keyingsets_utils.py
@@ -219,6 +219,40 @@ def RKS_GEN_scaling(_ksi, _context, ks, data):
else:
ks.paths.add(id_block, path)
+
+# Custom Properties
+def RKS_GEN_custom_props(_ksi, _context, ks, data):
+ # get id-block and path info
+ id_block, base_path, grouping = get_transform_generators_base_info(data)
+
+ # Only some RNA types can be animated.
+ prop_type_compat = {bpy.types.BoolProperty,
+ bpy.types.IntProperty,
+ bpy.types.FloatProperty}
+
+ # When working with a pose, 'id_block' is the armature object (which should
+ # get the animation data), whereas 'data' is the bone being keyed.
+ for cprop_name in data.keys():
+ # ignore special "_RNA_UI" used for UI editing
+ if cprop_name == "_RNA_UI":
+ continue
+
+ prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name)
+ try:
+ rna_property = data.path_resolve(prop_path, False)
+ except ValueError as ex:
+ # This happens when a custom property is set to None. In that case it cannot
+ # be converted to an FCurve-compatible value, so we can't keyframe it anyway.
+ continue
+ if rna_property.rna_type not in prop_type_compat:
+ continue
+
+ path = "%s%s" % (base_path, prop_path)
+ if grouping:
+ ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
+ else:
+ ks.paths.add(id_block, path)
+
# ------
diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py
index 6076bf00063..365d6c0087f 100644
--- a/release/scripts/modules/rna_keymap_ui.py
+++ b/release/scripts/modules/rna_keymap_ui.py
@@ -421,7 +421,7 @@ def draw_keymaps(context, layout):
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
if not filter_text:
- # When the keyconfig defines it's own preferences.
+ # When the keyconfig defines its own preferences.
kc_prefs = kc_active.preferences
if kc_prefs is not None:
box = col.box()
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index b6fda8911cc..7e2d09efcd4 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -305,7 +305,7 @@ url_manual_mapping = (
("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"),
("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"),
("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"),
- ("bpy.types.objectlineart.use_crease_override*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart-use-crease-override"),
+ ("bpy.types.objectlineart.use_crease_override*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-use-crease-override"),
("bpy.types.rendersettings.preview_pixel_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-preview-pixel-size"),
("bpy.types.rendersettings.use_crop_to_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-crop-to-border"),
("bpy.types.rendersettings.use_file_extension*", "render/output/properties/output.html#bpy-types-rendersettings-use-file-extension"),
@@ -449,7 +449,7 @@ url_manual_mapping = (
("bpy.types.nodesocketinterface*.max_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-max-value"),
("bpy.types.nodesocketinterface*.min_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-min-value"),
("bpy.types.nodesocketinterface.hide_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-hide-value"),
- ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart-crease-threshold"),
+ ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-crease-threshold"),
("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"),
("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"),
("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"),
@@ -487,6 +487,7 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"),
("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"),
("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"),
+ ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"),
("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"),
("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"),
("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"),
@@ -947,7 +948,7 @@ url_manual_mapping = (
("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"),
("bpy.types.imagesequence.use_flip*", "video_editing/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"),
("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"),
- ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/lineart.html#bpy-types-lineartgpencilmodifier"),
+ ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"),
("bpy.types.mesh.auto_smooth_angle*", "modeling/meshes/structure.html#bpy-types-mesh-auto-smooth-angle"),
("bpy.types.objectsolverconstraint*", "animation/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"),
("bpy.types.opacitygpencilmodifier*", "grease_pencil/modifiers/color/opacity.html#bpy-types-opacitygpencilmodifier"),
@@ -1110,6 +1111,7 @@ url_manual_mapping = (
("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"),
("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"),
("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"),
+ ("bpy.types.geometrynodeboundbox*", "modeling/geometry_nodes/geometry/bounding_box.html#bpy-types-geometrynodeboundbox"),
("bpy.types.geometrynodemeshcone*", "modeling/geometry_nodes/mesh_primitives/cone.html#bpy-types-geometrynodemeshcone"),
("bpy.types.geometrynodemeshcube*", "modeling/geometry_nodes/mesh_primitives/cube.html#bpy-types-geometrynodemeshcube"),
("bpy.types.geometrynodemeshgrid*", "modeling/geometry_nodes/mesh_primitives/grid.html#bpy-types-geometrynodemeshgrid"),
@@ -1152,6 +1154,7 @@ url_manual_mapping = (
("bpy.ops.clip.set_scene_frames*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-scene-frames"),
("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"),
("bpy.ops.curve.spline_type_set*", "modeling/curves/editing/curve.html#bpy-ops-curve-spline-type-set"),
+ ("bpy.ops.file.unpack_libraries*", "files/blend/packed_data.html#bpy-ops-file-unpack-libraries"),
("bpy.ops.gpencil.move_to_layer*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-move-to-layer"),
("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"),
("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"),
@@ -1219,7 +1222,7 @@ url_manual_mapping = (
("bpy.types.mesh.use_paint_mask*", "sculpt_paint/brush/introduction.html#bpy-types-mesh-use-paint-mask"),
("bpy.types.movietrackingcamera*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera"),
("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"),
- ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart-usage"),
+ ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-usage"),
("bpy.types.particledupliweight*", "physics/particles/emitter/vertex_groups.html#bpy-types-particledupliweight"),
("bpy.types.poseboneconstraints*", "animation/armatures/posing/bone_constraints/index.html#bpy-types-poseboneconstraints"),
("bpy.types.rigidbodyconstraint*", "physics/rigid_body/constraints/index.html#bpy-types-rigidbodyconstraint"),
@@ -1260,6 +1263,7 @@ url_manual_mapping = (
("bpy.ops.console.autocomplete*", "editors/python_console.html#bpy-ops-console-autocomplete"),
("bpy.ops.curve.dissolve_verts*", "modeling/curves/editing/curve.html#bpy-ops-curve-dissolve-verts"),
("bpy.ops.curve.duplicate_move*", "modeling/curves/editing/curve.html#bpy-ops-curve-duplicate-move"),
+ ("bpy.ops.file.autopack_toggle*", "files/blend/packed_data.html#bpy-ops-file-autopack-toggle"),
("bpy.ops.fluid.bake_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-bake-particles"),
("bpy.ops.fluid.free_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-free-particles"),
("bpy.ops.gpencil.extrude_move*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-extrude-move"),
@@ -1297,6 +1301,7 @@ url_manual_mapping = (
("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"),
("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"),
("bpy.types.armatureconstraint*", "animation/constraints/relationship/armature.html#bpy-types-armatureconstraint"),
+ ("bpy.types.compositornodeblur*", "compositing/types/filter/blur_node.html#bpy-types-compositornodeblur"),
("bpy.types.compositornodecomb*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodecomb"),
("bpy.types.compositornodecrop*", "compositing/types/distort/crop.html#bpy-types-compositornodecrop"),
("bpy.types.compositornodeflip*", "compositing/types/distort/flip.html#bpy-types-compositornodeflip"),
@@ -1358,6 +1363,7 @@ url_manual_mapping = (
("bpy.ops.curve.primitive*add*", "modeling/curves/primitives.html#bpy-ops-curve-primitive-add"),
("bpy.ops.curve.smooth_radius*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-radius"),
("bpy.ops.curve.smooth_weight*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-weight"),
+ ("bpy.ops.file.pack_libraries*", "files/blend/packed_data.html#bpy-ops-file-pack-libraries"),
("bpy.ops.font.change_spacing*", "modeling/texts/editing.html#bpy-ops-font-change-spacing"),
("bpy.ops.gpencil.stroke_flip*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-flip"),
("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"),
@@ -1453,7 +1459,7 @@ url_manual_mapping = (
("bpy.types.viewlayer.use_sky*", "render/layers/introduction.html#bpy-types-viewlayer-use-sky"),
("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"),
("bpy.types.worldmistsettings*", "render/cycles/world_settings.html#bpy-types-worldmistsettings"),
- ("bpy.ops.anim.channels_move*", "editors/nla/editing.html#bpy-ops-anim-channels-move"),
+ ("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"),
("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"),
("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"),
("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"),
@@ -1691,6 +1697,7 @@ url_manual_mapping = (
("bpy.ops.clip.set_origin*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-origin"),
("bpy.ops.curve.subdivide*", "modeling/curves/editing/segments.html#bpy-ops-curve-subdivide"),
("bpy.ops.ed.undo_history*", "interface/undo_redo.html#bpy-ops-ed-undo-history"),
+ ("bpy.ops.file.unpack_all*", "files/blend/packed_data.html#bpy-ops-file-unpack-all"),
("bpy.ops.fluid.bake_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-bake-data"),
("bpy.ops.fluid.bake_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-bake-mesh"),
("bpy.ops.fluid.free_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-free-data"),
@@ -1749,7 +1756,7 @@ url_manual_mapping = (
("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"),
("bpy.types.nodesmodifier*", "modeling/modifiers/generate/geometry_nodes.html#bpy-types-nodesmodifier"),
("bpy.types.object.parent*", "scene_layout/object/editing/parent.html#bpy-types-object-parent"),
- ("bpy.types.objectlineart*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart"),
+ ("bpy.types.objectlineart*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart"),
("bpy.types.oceanmodifier*", "modeling/modifiers/physics/ocean.html#bpy-types-oceanmodifier"),
("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"),
("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"),
@@ -1845,6 +1852,7 @@ url_manual_mapping = (
("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"),
("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"),
("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"),
+ ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"),
("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"),
("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"),
("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"),
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index e3158118146..54cde1e1c04 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -235,7 +235,7 @@ def draw(layout, context, context_member, property_type, use_edit=True):
assert(isinstance(rna_item, property_type))
- items = rna_item.items()
+ items = list(rna_item.items())
items.sort()
# TODO: Allow/support adding new custom props to overrides.
diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py
index cb80529f0f3..192fc1a201f 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -23,11 +23,12 @@
def write_sysinfo(filepath):
import sys
+ import platform
import subprocess
import bpy
- import bgl
+ import gpu
# pretty repr
def prepr(v):
@@ -63,7 +64,7 @@ def write_sysinfo(filepath):
))
output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time)))
- output.write("platform: %s\n" % prepr(bpy.app.build_platform))
+ output.write("platform: %s\n" % prepr(platform.platform()))
output.write("binary path: %s\n" % prepr(bpy.app.binary_path))
output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags))
output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags))
@@ -189,46 +190,29 @@ def write_sysinfo(filepath):
if bpy.app.background:
output.write("\nOpenGL: missing, background mode\n")
else:
- output.write(title("OpenGL"))
- version = bgl.glGetString(bgl.GL_RENDERER)
- output.write("renderer:\t%r\n" % version)
- output.write("vendor:\t\t%r\n" % (bgl.glGetString(bgl.GL_VENDOR)))
- output.write("version:\t%r\n" % (bgl.glGetString(bgl.GL_VERSION)))
+ output.write(title("GPU"))
+ output.write("renderer:\t%r\n" % gpu.platform.renderer_get())
+ output.write("vendor:\t\t%r\n" % gpu.platform.vendor_get())
+ output.write("version:\t%r\n" % gpu.platform.version_get())
output.write("extensions:\n")
- limit = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGetIntegerv(bgl.GL_NUM_EXTENSIONS, limit)
-
- glext = []
- for i in range(limit[0]):
- glext.append(bgl.glGetStringi(bgl.GL_EXTENSIONS, i))
-
- glext = sorted(glext)
+ glext = sorted(gpu.capabilities.extensions_get())
for l in glext:
output.write("\t%s\n" % l)
- output.write(title("Implementation Dependent OpenGL Limits"))
- bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_VERTICES, limit)
- output.write("Maximum DrawElements Vertices:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_INDICES, limit)
- output.write("Maximum DrawElements Indices:\t%d\n" % limit[0])
+ output.write(title("Implementation Dependent GPU Limits"))
+ output.write("Maximum Batch Vertices:\t%d\n" % gpu.capabilities.max_batch_vertices_get())
+ output.write("Maximum Batch Indices:\t%d\n" % gpu.capabilities.max_batch_indices_get())
output.write("\nGLSL:\n")
- bgl.glGetIntegerv(bgl.GL_MAX_VARYING_FLOATS, limit)
- output.write("Maximum Varying Floats:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_ATTRIBS, limit)
- output.write("Maximum Vertex Attributes:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_UNIFORM_COMPONENTS, limit)
- output.write("Maximum Vertex Uniform Components:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, limit)
- output.write("Maximum Fragment Uniform Components:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Vertex Image Units:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Fragment Image Units:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Pipeline Image Units:\t%d\n" % limit[0])
+ output.write("Maximum Varying Floats:\t%d\n" % gpu.capabilities.max_varying_floats_get())
+ output.write("Maximum Vertex Attributes:\t%d\n" % gpu.capabilities.max_vertex_attribs_get())
+ output.write("Maximum Vertex Uniform Components:\t%d\n" % gpu.capabilities.max_uniforms_vert_get())
+ output.write("Maximum Fragment Uniform Components:\t%d\n" % gpu.capabilities.max_uniforms_frag_get())
+ output.write("Maximum Vertex Image Units:\t%d\n" % gpu.capabilities.max_textures_vert_get())
+ output.write("Maximum Fragment Image Units:\t%d\n" % gpu.capabilities.max_textures_frag_get())
+ output.write("Maximum Pipeline Image Units:\t%d\n" % gpu.capabilities.max_textures_get())
if bpy.app.build_options.cycles:
import cycles
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 9404bfe327a..9bccc69d41f 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -278,15 +278,11 @@ def _template_items_uv_select_mode(params):
else:
return [
*_template_items_editmode_mesh_select_mode(params),
+ # Hack to prevent fall-through, when sync select isn't enabled (and the island button isn't visible).
("mesh.select_mode", {"type": 'FOUR', "value": 'PRESS'}, None),
- ("wm.context_set_enum", {"type": 'ONE', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'VERTEX')]}),
- ("wm.context_set_enum", {"type": 'TWO', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'EDGE')]}),
- ("wm.context_set_enum", {"type": 'THREE', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'FACE')]}),
- ("wm.context_set_enum", {"type": 'FOUR', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'ISLAND')]}),
+ *(("wm.context_set_enum", {"type": NUMBERS_1[i], "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", ty)]})
+ for i, ty in enumerate(('VERTEX', 'EDGE', 'FACE', 'ISLAND')))
]
@@ -2009,8 +2005,7 @@ def km_file_browser_main(params):
)
items.extend([
- ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
- {"properties": [("need_active", True)]}),
+ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
# Both .execute and .select are needed here. The former only works if
# there's a file operator (i.e. not in regular editor mode) but is
# needed to load files. The latter makes selection work if there's no
@@ -3382,6 +3377,11 @@ def km_grease_pencil_stroke_paint_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Draw delete menu
op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'X', "value": 'PRESS'}),
# Animation menu
@@ -3549,6 +3549,11 @@ def km_grease_pencil_stroke_sculpt_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Copy
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
# Display
@@ -3763,6 +3768,11 @@ def km_grease_pencil_stroke_weight_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_weight_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Display
*_grease_pencil_display(),
# Keyframe menu
@@ -3820,6 +3830,11 @@ def km_grease_pencil_stroke_vertex_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Display
*_grease_pencil_display(),
# Tools
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 2880d56a005..dbf583149e3 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -1263,8 +1263,7 @@ def km_file_browser_main(params):
)
items.extend([
- ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
- {"properties": [("need_active", True)]}),
+ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'},
diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py
index 0470f542c63..45d1ea98a8a 100644
--- a/release/scripts/startup/bl_operators/clip.py
+++ b/release/scripts/startup/bl_operators/clip.py
@@ -206,8 +206,8 @@ class CLIP_OT_filter_tracks(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return (space.type == 'CLIP_EDITOR') and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
num_tracks = self._filter_values(context, self.track_threshold)
@@ -221,8 +221,8 @@ class CLIP_OT_set_active_clip(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
clip = context.space_data.clip
@@ -268,8 +268,8 @@ class CLIP_OT_track_to_empty(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@@ -293,7 +293,7 @@ class CLIP_OT_bundles_to_mesh(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
- return (sc.type == 'CLIP_EDITOR') and sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
from bpy_extras.io_utils import unpack_list
@@ -341,12 +341,8 @@ class CLIP_OT_delete_proxy(Operator):
@classmethod
def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
-
sc = context.space_data
-
- return sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def invoke(self, context, event):
wm = context.window_manager
@@ -424,12 +420,8 @@ class CLIP_OT_set_viewport_background(Operator):
@classmethod
def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
-
sc = context.space_data
-
- return sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@@ -563,13 +555,11 @@ class CLIP_OT_setup_tracking_scene(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
-
- if sc.type != 'CLIP_EDITOR':
- return False
-
- clip = sc.clip
-
- return clip and clip.tracking.reconstruction.is_valid
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.reconstruction.is_valid:
+ return True
+ return False
@staticmethod
def _setupScene(context):
@@ -1018,13 +1008,11 @@ class CLIP_OT_track_settings_as_default(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
-
- if sc.type != 'CLIP_EDITOR':
- return False
-
- clip = sc.clip
-
- return clip and clip.tracking.tracks.active
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.tracks.active:
+ return True
+ return False
def execute(self, context):
sc = context.space_data
@@ -1068,11 +1056,12 @@ class CLIP_OT_track_settings_to_track(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- if space.type != 'CLIP_EDITOR':
- return False
- clip = space.clip
- return clip and clip.tracking.tracks.active
+ sc = context.space_data
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.tracks.active:
+ return True
+ return False
def execute(self, context):
space = context.space_data
diff --git a/release/scripts/startup/bl_operators/constraint.py b/release/scripts/startup/bl_operators/constraint.py
index 49fc6a04112..f18f3bb3a49 100644
--- a/release/scripts/startup/bl_operators/constraint.py
+++ b/release/scripts/startup/bl_operators/constraint.py
@@ -33,6 +33,11 @@ class CONSTRAINT_OT_add_target(Operator):
bl_label = "Add Target"
bl_options = {'UNDO', 'INTERNAL'}
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
context.constraint.targets.new()
return {'FINISHED'}
@@ -46,6 +51,11 @@ class CONSTRAINT_OT_remove_target(Operator):
index: IntProperty()
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
tgts = context.constraint.targets
tgts.remove(tgts[self.index])
@@ -58,6 +68,11 @@ class CONSTRAINT_OT_normalize_target_weights(Operator):
bl_label = "Normalize Weights"
bl_options = {'UNDO', 'INTERNAL'}
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
tgts = context.constraint.targets
total = sum(t.weight for t in tgts)
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
index e0d0fc1e145..6150789ea10 100644
--- a/release/scripts/startup/bl_operators/node.py
+++ b/release/scripts/startup/bl_operators/node.py
@@ -120,7 +120,7 @@ class NodeAddOperator:
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree to add nodes to
- return ((space.type == 'NODE_EDITOR') and
+ return (space and (space.type == 'NODE_EDITOR') and
space.edit_tree and not space.edit_tree.library)
# Default execute simply adds a node
@@ -265,7 +265,7 @@ class NODE_OT_collapse_hide_unused_toggle(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
- return ((space.type == 'NODE_EDITOR') and
+ return (space and (space.type == 'NODE_EDITOR') and
(space.edit_tree and not space.edit_tree.library))
def execute(self, context):
@@ -296,7 +296,7 @@ class NODE_OT_tree_path_parent(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
- return (space.type == 'NODE_EDITOR' and len(space.path) > 1)
+ return (space and (space.type == 'NODE_EDITOR') and len(space.path) > 1)
def execute(self, context):
space = context.space_data
@@ -315,6 +315,8 @@ class NODE_OT_active_preview_toggle(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
+ if space is None:
+ return False
if space.type != 'NODE_EDITOR':
return False
if space.edit_tree is None:
diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 7547184dc04..3969386bad7 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -99,6 +99,15 @@ class PREFERENCES_OT_copy_prev(Operator):
version = bpy.app.version
version_new = ((version[0] * 100) + version[1])
version_old = ((version[0] * 100) + version[1]) - 1
+
+ # Special case, remove when the version is > 3.0.
+ if version_new == 300:
+ version_new = 294
+ version_old = 293
+ else:
+ print("TODO: remove exception!")
+ # End special case.
+
# Ensure we only try to copy files from a point release.
# The check below ensures the second numbers match.
while (version_new % 100) // 10 == (version_old % 100) // 10:
@@ -581,7 +590,7 @@ class PREFERENCES_OT_addon_install(Operator):
name="Target Path",
items=(
('DEFAULT', "Default", ""),
- ('PREFS', "User Prefs", ""),
+ ('PREFS', "Preferences", ""),
),
)
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index ff5bcdb034f..0fa82e36d20 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -208,7 +208,8 @@ class VIEW3D_OT_transform_gizmo_set(Operator):
@classmethod
def poll(cls, context):
- return context.area.type == 'VIEW_3D'
+ area = context.area
+ return area and (area.type == 'VIEW_3D')
def execute(self, context):
space_data = context.space_data
diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index f3e116ca321..6452ad8465b 100644
--- a/release/scripts/startup/bl_ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -292,10 +292,15 @@ class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel):
sub = col.column()
sub.active = bool(pchan and pchan.custom_shape)
sub.separator()
- sub.prop(pchan, "custom_shape_scale", text="Scale")
+
+ sub.prop(pchan, "custom_shape_scale_xyz", text="Scale")
+ sub.prop(pchan, "custom_shape_translation", text="Translation")
+ sub.prop(pchan, "custom_shape_rotation_euler", text="Rotation")
+
sub.prop_search(pchan, "custom_shape_transform",
ob.pose, "bones", text="Override Transform")
sub.prop(pchan, "use_custom_shape_bone_size")
+
sub.separator()
sub.prop(bone, "show_wire", text="Wireframe")
diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py
index 69720a6c54b..e71ea2f31a4 100644
--- a/release/scripts/startup/bl_ui/properties_data_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py
@@ -113,7 +113,8 @@ class GPENCIL_MT_layer_context_menu(Menu):
layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down")
layout.separator()
- layout.menu("VIEW3D_MT_gpencil_copy_layer")
+ layout.operator("gpencil.layer_duplicate_object", text="Copy Layer to Selected").only_active=True
+ layout.operator("gpencil.layer_duplicate_object", text="Copy All Layers to Selected").only_active=False
class DATA_PT_gpencil_layers(DataButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 766b82f8ba4..55a49878b71 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -821,6 +821,12 @@ class GreasePencilLayerMasksPanel:
col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="")
col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="")
+ col2.separator()
+
+ sub = col2.column(align=True)
+ sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP'
+ sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN'
+
class GreasePencilLayerRelationsPanel:
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index aca7ba3c5ad..217ec248764 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -286,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
mat = context.material
lineart = mat.lineart
layout.prop(lineart, "use_transparency")
- if lineart.use_transparency:
-
- layout.label(text="Transparency Masks:")
-
- row = layout.row(align=True)
- for i in range(8):
- row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
+ row = layout.row(align=True, heading="Masks")
+ row.active = lineart.use_transparency
+ for i in range(8):
+ row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py
index 6a5c000116f..9d099ff2231 100644
--- a/release/scripts/startup/bl_ui/properties_material_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py
@@ -46,13 +46,19 @@ class GPENCIL_MT_material_context_menu(Menu):
layout.separator()
- layout.operator("object.material_slot_remove_unused")
- layout.operator("gpencil.stroke_merge_material", text="Merge Similar")
-
- layout.separator()
layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Vertex Color")
layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Vertex Color")
+ layout.separator()
+
+ layout.operator("gpencil.materials_copy_to_object", text="Copy Material to Selected").only_active = True
+ layout.operator("gpencil.materials_copy_to_object", text="Copy All Materials to Selected").only_active = False
+
+ layout.separator()
+
+ layout.operator("gpencil.stroke_merge_material", text="Merge Similar")
+ layout.operator("object.material_slot_remove_unused")
+
class GPENCIL_UL_matslots(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 033e6196323..4ea1ec26738 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -329,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
row = layout.row(heading="Override Crease")
row.prop(lineart, "use_crease_override", text="")
- row.prop(lineart, "crease_threshold", slider=True, text="")
+ subrow = row.row()
+ subrow.active = lineart.use_crease_override
+ subrow.prop(lineart, "crease_threshold", slider=True, text="")
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 22ef0fe77dd..f3462dfb35d 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -1225,7 +1225,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row = layout.row(align=True)
row.prop(gp_settings, "fill_factor")
row = layout.row(align=True)
- row.prop(gp_settings, "fill_leak", text="Leak Size")
+ row.prop(gp_settings, "dilate")
row = layout.row(align=True)
row.prop(brush, "size", text="Thickness")
layout.use_property_split = use_property_split_prev
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index a9f040db9b5..ae15fda2c69 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -1803,9 +1803,10 @@ class PARTICLE_PT_force_fields_type1(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- col = layout.column()
- col.prop(part.force_field_1, "type", text="Type 1")
- basic_force_field_settings_ui(self, part.force_field_1)
+ if part.force_field_1:
+ col = layout.column()
+ col.prop(part.force_field_1, "type", text="Type 1")
+ basic_force_field_settings_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
@@ -1819,9 +1820,10 @@ class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- col = layout.column()
- col.prop(part.force_field_2, "type", text="Type 2")
- basic_force_field_settings_ui(self, part.force_field_2)
+ if part.force_field_2:
+ col = layout.column()
+ col.prop(part.force_field_2, "type", text="Type 2")
+ basic_force_field_settings_ui(self, part.force_field_2)
class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
@@ -1836,7 +1838,8 @@ class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- basic_force_field_falloff_ui(self, part.force_field_1)
+ if part.force_field_1:
+ basic_force_field_falloff_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
@@ -1851,7 +1854,8 @@ class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- basic_force_field_falloff_ui(self, part.force_field_2)
+ if part.force_field_2:
+ basic_force_field_falloff_ui(self, part.force_field_2)
class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 7f55e4888cf..f13a808e324 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -79,7 +79,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
col = flow.column()
- if obj.field.type == 'NONE':
+ if not obj.field or obj.field.type == 'NONE':
col.operator("object.forcefield_toggle", text="Force Field", icon='FORCE_FORCE')
else:
col.operator("object.forcefield_toggle", text="Force Field", icon='X')
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index cd65980fc0d..3a97b104271 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -92,16 +92,15 @@ class INFO_MT_area(Menu):
layout.separator()
- layout.operator("screen.area_dupli", icon='WINDOW')
-
- layout.separator()
-
layout.operator("screen.screen_full_area")
layout.operator(
"screen.screen_full_area",
- text="Toggle Fullscreen Area",
- icon='FULLSCREEN_ENTER',
- ).use_hide_panels = True
+ text="Toggle Fullscreen Area").use_hide_panels = True
+ layout.operator("screen.area_dupli")
+
+ layout.separator()
+
+ layout.operator("screen.area_close")
class INFO_MT_context_menu(Menu):
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index f6a03b4769c..b24b6e84939 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -1453,7 +1453,7 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
if strip.scene_input == 'CAMERA':
layout = layout.column(heading="Show")
- layout.prop(strip, "use_grease_pencil", text="Grease Pencil")
+ layout.prop(strip, "use_annotations", text="Annotations")
if scene:
# Warning, this is not a good convention to follow.
# Expose here because setting the alpha from the 'Render' menu is very inconvenient.
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index adab0b0c88a..5e68896a2a7 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -504,8 +504,6 @@ class TOPBAR_MT_file_external_data(Menu):
icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
layout.operator("file.autopack_toggle", icon=icon)
- layout.separator()
-
pack_all = layout.row()
pack_all.operator("file.pack_all")
pack_all.active = not bpy.data.use_autopack
@@ -516,8 +514,16 @@ class TOPBAR_MT_file_external_data(Menu):
layout.separator()
+ layout.operator("file.pack_libraries")
+ layout.operator("file.unpack_libraries")
+
+ layout.separator()
+
layout.operator("file.make_paths_relative")
layout.operator("file.make_paths_absolute")
+
+ layout.separator()
+
layout.operator("file.report_missing_files")
layout.operator("file.find_missing_files")
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 04b7a11bde1..de78b88c0f6 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel):
layout.prop(system, "audio_device", expand=False)
sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
- sub.active = system.audio_device not in {'NONE', 'Null'}
+ sub.active = system.audio_device not in {'NONE', 'None'}
sub.prop(system, "audio_channels", text="Channels")
sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
sub.prop(system, "audio_sample_rate", text="Sample Rate")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index bdc924e3197..18c6564b7d4 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -4956,28 +4956,6 @@ class VIEW3D_MT_assign_material(Menu):
icon='LAYER_ACTIVE' if mat == mat_active else 'BLANK1').material = mat.name
-class VIEW3D_MT_gpencil_copy_layer(Menu):
- bl_label = "Copy Layer to Object"
-
- def draw(self, context):
- layout = self.layout
- view_layer = context.view_layer
- obact = context.active_object
- gpl = context.active_gpencil_layer
-
- done = False
- if gpl is not None:
- for ob in view_layer.objects:
- if ob.type == 'GPENCIL' and ob != obact:
- layout.operator("gpencil.layer_duplicate_object", text=ob.name).object = ob.name
- done = True
-
- if done is False:
- layout.label(text="No destination object", icon='ERROR')
- else:
- layout.label(text="No layer to copy", icon='ERROR')
-
-
class VIEW3D_MT_edit_gpencil(Menu):
bl_label = "Grease Pencil"
@@ -6191,6 +6169,12 @@ class VIEW3D_PT_overlay_geometry(Panel):
sub.prop(overlay, "wireframe_opacity", text="Opacity")
row = col.row(align=True)
+
+ # These properties should be always available in the UI for all modes
+ # other than Object.
+ # Even when the Fade Inactive Geometry overlay is not affecting the
+ # current active object depending on its mode, it will always affect
+ # the rest of the scene.
if context.mode != 'OBJECT':
row.prop(overlay, "show_fade_inactive", text="")
sub = row.row()
@@ -7642,7 +7626,6 @@ classes = (
VIEW3D_MT_weight_gpencil,
VIEW3D_MT_gpencil_animation,
VIEW3D_MT_gpencil_simplify,
- VIEW3D_MT_gpencil_copy_layer,
VIEW3D_MT_gpencil_autoweights,
VIEW3D_MT_gpencil_edit_context_menu,
VIEW3D_MT_edit_curve,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index ab012a6f2ef..08d581bfa24 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1468,6 +1468,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
row.prop(gp_settings, "show_fill_extend", text="", icon='GRID')
col.separator()
+ col.prop(gp_settings, "fill_leak", text="Leak Size")
+
+ col.separator()
col.prop(gp_settings, "fill_simplify_level", text="Simplify")
if gp_settings.fill_draw_mode != 'STROKE':
col = layout.column(align=False, heading="Ignore Transparent")
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index 012febc7cc7..83151a3480c 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location"
ANIM_KS_ROTATION_ID = "Rotation"
ANIM_KS_SCALING_ID = "Scaling"
ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale"
+ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp"
ANIM_KS_AVAILABLE_ID = "Available"
ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter"
ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected"
@@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
+# LocRotScaleCProp
+class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo):
+ """Key location/rotation/scale as well as custom properties"""
+ bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID
+ bl_label = "Location, Rotation, Scale & Custom Properties"
+
+ poll = keyingsets_utils.RKS_POLL_selected_items
+ iterator = keyingsets_utils.RKS_ITER_selected_item
+
+ def generate(self, context, ks, data):
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data)
+
+
# RotScale
class BUILTIN_KSI_RotScale(KeyingSetInfo):
"""Insert a keyframe on each of the rotation and scale channels"""
@@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
bl_label = "Available"
# poll - selected objects or selected object with animation data
- def poll(ksi, context):
+ def poll(self, context):
ob = context.active_object
if ob:
# TODO: this fails if one animation-less object is active, but many others are selected
@@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
###############################
-
-# All properties that are likely to get animated in a character rig
-class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
- """Insert a keyframe for all properties that are likely to get animated in a character rig """ \
- """(useful when blocking out a shot)"""
- bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
- bl_label = "Whole Character"
-
+class WholeCharacterMixin:
# these prefixes should be avoided, as they are not really bones
# that animators should be touching (or need to touch)
badBonePrefixes = (
@@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
)
# poll - pose-mode on active object only
- def poll(ksi, context):
+ def poll(self, context):
return ((context.active_object) and (context.active_object.pose) and
(context.active_object.mode == 'POSE'))
# iterator - all bones regardless of selection
- def iterator(ksi, context, ks):
+ def iterator(self, context, ks):
for bone in context.active_object.pose.bones:
- if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
- ksi.generate(context, ks, bone)
+ if not bone.name.startswith(self.badBonePrefixes):
+ self.generate(context, ks, bone)
# generator - all unlocked bone transforms + custom properties
- def generate(ksi, context, ks, bone):
+ def generate(self, context, ks, bone):
# loc, rot, scale - only include unlocked ones
if not bone.bone.use_connect:
- ksi.doLoc(ks, bone)
+ self.doLoc(ks, bone)
if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
- ksi.doRot4d(ks, bone)
+ self.doRot4d(ks, bone)
else:
- ksi.doRot3d(ks, bone)
- ksi.doScale(ks, bone)
+ self.doRot3d(ks, bone)
+ self.doScale(ks, bone)
# bbone properties?
- ksi.doBBone(context, ks, bone)
+ self.doBBone(context, ks, bone)
# custom props?
- ksi.doCustomProps(ks, bone)
-
+ self.doCustomProps(ks, bone)
# ----------------
# helper to add some bone's property to the Keying Set
- def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
+ def addProp(self, ks, bone, prop, index=-1, use_groups=True):
# add the property name to the base path
id_path = bone.path_from_id()
id_block = bone.id_data
@@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# ----------------
# location properties
- def doLoc(ksi, ks, bone):
+ def doLoc(self, ks, bone):
if bone.lock_location == (False, False, False):
- ksi.addProp(ks, bone, "location")
+ self.addProp(ks, bone, "location")
else:
for i in range(3):
if not bone.lock_location[i]:
- ksi.addProp(ks, bone, "location", i)
+ self.addProp(ks, bone, "location", i)
# rotation properties
- def doRot4d(ksi, ks, bone):
+ def doRot4d(self, ks, bone):
# rotation mode affects the property used
if bone.rotation_mode == 'QUATERNION':
prop = "rotation_quaternion"
@@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
if bone.lock_rotations_4d:
# can check individually
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False):
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
else:
if bone.lock_rotation_w is False:
- ksi.addProp(ks, bone, prop, 0) # w = 0
+ self.addProp(ks, bone, prop, 0) # w = 0
for i in range(3):
if not bone.lock_rotation[i]:
- ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
+ self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
elif True not in bone.lock_rotation:
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
# other than all open unless we keyframe the whole lot
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
- def doRot3d(ksi, ks, bone):
+ def doRot3d(self, ks, bone):
if bone.lock_rotation == (False, False, False):
- ksi.addProp(ks, bone, "rotation_euler")
+ self.addProp(ks, bone, "rotation_euler")
else:
for i in range(3):
if not bone.lock_rotation[i]:
- ksi.addProp(ks, bone, "rotation_euler", i)
+ self.addProp(ks, bone, "rotation_euler", i)
# scale properties
- def doScale(ksi, ks, bone):
+ def doScale(self, ks, bone):
if bone.lock_scale == (0, 0, 0):
- ksi.addProp(ks, bone, "scale")
+ self.addProp(ks, bone, "scale")
else:
for i in range(3):
if not bone.lock_scale[i]:
- ksi.addProp(ks, bone, "scale", i)
+ self.addProp(ks, bone, "scale", i)
# ----------------
# bendy bone properties
- def doBBone(ksi, context, ks, pchan):
+ def doBBone(self, context, ks, pchan):
bone = pchan.bone
# This check is crude, but is the best we can do for now
@@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# (and the bone is a control bone). This may lead to some
# false positives...
if bone.bbone_segments > 1:
- keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan)
+ keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan)
# ----------------
# custom properties
- def doCustomProps(ksi, ks, bone):
+ def doCustomProps(self, ks, bone):
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
@@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
continue
if rna_property.rna_type in prop_type_compat:
- ksi.addProp(ks, bone, prop_path)
+ self.addProp(ks, bone, prop_path)
elif prop_rna.is_animatable:
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
-# All properties that are likely to get animated in a character rig, only selected bones.
+
+class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo):
+ """Insert a keyframe for all properties that are likely to get animated in a character rig """ \
+ """(useful when blocking out a shot)"""
+ bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
+ bl_label = "Whole Character"
-class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo):
+class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(only selected bones)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID
bl_label = "Whole Character (Selected Bones Only)"
# iterator - all bones regardless of selection
- def iterator(ksi, context, ks):
+ def iterator(self, context, ks):
# Use either the selected bones, or all of them if none are selected.
bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones
for bone in bones:
- if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
+ if bone.name.startswith(self.badBonePrefixes):
continue
- ksi.generate(context, ks, bone)
-
- # Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter.
- poll = BUILTIN_KSI_WholeCharacter.poll
- generate = BUILTIN_KSI_WholeCharacter.generate
- addProp = BUILTIN_KSI_WholeCharacter.addProp
- doLoc = BUILTIN_KSI_WholeCharacter.doLoc
- doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d
- doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d
- doScale = BUILTIN_KSI_WholeCharacter.doScale
- doBBone = BUILTIN_KSI_WholeCharacter.doBBone
- doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps
+ self.generate(context, ks, bone)
+
###############################
@@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -664,6 +668,7 @@ classes = (
BUILTIN_KSI_Scaling,
BUILTIN_KSI_LocRot,
BUILTIN_KSI_LocRotScale,
+ BUILTIN_KSI_LocRotScaleCProp,
BUILTIN_KSI_LocScale,
BUILTIN_KSI_RotScale,
BUILTIN_KSI_DeltaLocation,
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index ab4d9353e1b..bae2c14e3d9 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -488,22 +488,29 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeClamp"),
NodeItem("GeometryNodeAttributeCompare"),
NodeItem("GeometryNodeAttributeConvert"),
+ NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
+ NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
+ NodeItem("GeometryNodeAttributeTransfer"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
+ GeometryNodeCategory("GEO_CURVE", "Curve", items=[
+ NodeItem("GeometryNodeCurveToMesh"),
+ NodeItem("GeometryNodeCurveResample"),
+ ]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeBoundBox"),
NodeItem("GeometryNodeTransform"),
@@ -516,8 +523,13 @@ geometry_node_categories = [
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
+ NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
]),
+ GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
+ NodeItem("GeometryNodeMaterialAssign"),
+ NodeItem("GeometryNodeMaterialReplace"),
+ ]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
@@ -550,6 +562,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
+ NodeItem("GeometryNodeSwitch"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeSeparateXYZ"),
diff --git a/release/scripts/templates_py/gizmo_custom_geometry.py b/release/scripts/templates_py/gizmo_custom_geometry.py
index 4105a99c633..701b1ac48ba 100644
--- a/release/scripts/templates_py/gizmo_custom_geometry.py
+++ b/release/scripts/templates_py/gizmo_custom_geometry.py
@@ -1,4 +1,4 @@
-# Example of a custom widget that defines it's own geometry.
+# Example of a custom widget that defines its own geometry.
#
# Usage: Select a light in the 3D view and drag the arrow at it's rear
# to change it's energy value.
diff --git a/release/scripts/templates_py/operator_modal_draw.py b/release/scripts/templates_py/operator_modal_draw.py
index 1e185ecfe2b..16c6f6dbe22 100644
--- a/release/scripts/templates_py/operator_modal_draw.py
+++ b/release/scripts/templates_py/operator_modal_draw.py
@@ -1,5 +1,4 @@
import bpy
-import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader
@@ -17,16 +16,16 @@ def draw_callback_px(self, context):
# 50% alpha, 2 pixel width line
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(2)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(2.0)
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": self.mouse_path})
shader.bind()
shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5))
batch.draw(shader)
# restore opengl defaults
- bgl.glLineWidth(1)
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('NONE')
class ModalDrawOperator(bpy.types.Operator):
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index e27cb2be8ee..3d81fcba37d 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime);
void BKE_pose_channels_free(struct bPose *pose);
void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user);
-void BKE_pose_channels_hash_make(struct bPose *pose);
+void BKE_pose_channels_hash_ensure(struct bPose *pose);
void BKE_pose_channels_hash_free(struct bPose *pose);
void BKE_pose_channels_remove(struct Object *ob,
@@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan);
struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_active(struct Object *ob);
struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob);
-struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name);
+struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name);
void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose);
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 120b4e08b9c..c3bc4d3ca4a 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -20,299 +20,326 @@
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
+#include "FN_generic_virtual_array.hh"
#include "BKE_attribute.h"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
-
-namespace blender::bke {
-
-using fn::CPPType;
-
-const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
-CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
-CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
-AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+#include "BLI_function_ref.hh"
/**
- * This class offers an indirection for reading an attribute.
- * This is useful for the following reasons:
- * - Blender does not store all attributes the same way.
- * The simplest case are custom data layers with primitive types.
- * A bit more complex are mesh attributes like the position of vertices,
- * which are embedded into the MVert struct.
- * Even more complex to access are vertex weights.
- * - Sometimes attributes are stored on one domain, but we want to access
- * the attribute on a different domain. Therefore, we have to interpolate
- * between the domains.
+ * Contains information about an attribute in a geometry component.
+ * More information can be added in the future. E.g. whether the attribute is builtin and how it is
+ * stored (uv map, vertex group, ...).
*/
-class ReadAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* Protects the span below, so that no two threads initialize it at the same time. */
- mutable std::mutex span_mutex_;
- /* When it is not null, it points to the attribute array or a temporary array that contains all
- * the attribute values. */
- mutable void *array_buffer_ = nullptr;
- /* Is true when the buffer above is owned by the attribute accessor. */
- mutable bool array_is_temporary_ = false;
+struct AttributeMetaData {
+ AttributeDomain domain;
+ CustomDataType data_type;
- public:
- ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
+ return (a.domain == b.domain) && (a.data_type == b.data_type);
}
+};
- virtual ~ReadAttribute();
-
- AttributeDomain domain() const
+/**
+ * Base class for the attribute initializer types described below.
+ */
+struct AttributeInit {
+ enum class Type {
+ Default,
+ VArray,
+ MoveArray,
+ };
+ Type type;
+ AttributeInit(const Type type) : type(type)
{
- return domain_;
}
+};
- const CPPType &cpp_type() const
+/**
+ * Create an attribute using the default value for the data type.
+ * The default values may depend on the attribute provider implementation.
+ */
+struct AttributeInitDefault : public AttributeInit {
+ AttributeInitDefault() : AttributeInit(Type::Default)
{
- return cpp_type_;
}
+};
- CustomDataType custom_data_type() const
- {
- return custom_data_type_;
- }
+/**
+ * Create an attribute by copying data from an existing virtual array. The virtual array
+ * must have the same type as the newly created attribute.
+ *
+ * Note that this can be used to fill the new attribute with the default
+ */
+struct AttributeInitVArray : public AttributeInit {
+ const blender::fn::GVArray *varray;
- int64_t size() const
+ AttributeInitVArray(const blender::fn::GVArray *varray)
+ : AttributeInit(Type::VArray), varray(varray)
{
- return size_;
}
+};
+
+/**
+ * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
+ * Sometimes data is created before a geometry component is available. In that case, it's
+ * preferable to move data directly to the created attribute to avoid a new allocation and a copy.
+ *
+ * Note that this will only have a benefit for attributes that are stored directly as contiguous
+ * arrays, so not for some built-in attributes.
+ *
+ * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
+ * can't be used directly, and that is generally how Blender expects custom data to be allocated.
+ */
+struct AttributeInitMove : public AttributeInit {
+ void *data = nullptr;
- void get(const int64_t index, void *r_value) const
+ AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
}
+};
- /* Get a span that contains all attribute values. */
- fn::GSpan get_span() const;
+/* Returns false when the iteration should be stopped. */
+using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
+ const AttributeMetaData &meta_data)>;
- template<typename T> Span<T> get_span() const
+namespace blender::bke {
+
+using fn::CPPType;
+using fn::GVArray;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+
+const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
+CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
+CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
+AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it.
+ */
+struct ReadAttributeLookup {
+ /* The virtual array that is used to read from this attribute. */
+ GVArrayPtr varray;
+ /* Domain the attribute lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
{
- return this->get_span().typed<T>();
+ return this->varray.get() != nullptr;
}
+};
- protected:
- /* r_value is expected to be uninitialized. */
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
-
- virtual void initialize_span() const;
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
+ */
+struct WriteAttributeLookup {
+ /* The virtual array that is used to read from and write to the attribute. */
+ GVMutableArrayPtr varray;
+ /* Domain the attributes lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
+ {
+ return this->varray.get() != nullptr;
+ }
};
/**
- * This exists for similar reasons as the ReadAttribute class, except that
- * it does not deal with interpolation between domains.
+ * An output attribute allows writing to an attribute (and optionally reading as well). It adds
+ * some convenience features on top of `GVMutableArray` that are very commonly used.
+ *
+ * Supported convenience features:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - Supports simple access to a span containing the attribute values (that avoids the use of
+ * VMutableArray_Span in many cases).
+ * - An output attribute can live side by side with an existing attribute with a different domain
+ * or data type. The old attribute will only be overwritten when the #save function is called.
*/
-class WriteAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* When not null, this points either to the attribute array or to a temporary array. */
- void *array_buffer_ = nullptr;
- /* True, when the buffer points to a temporary array. */
- bool array_is_temporary_ = false;
- /* This helps to protect against forgetting to apply changes done to the array. */
- bool array_should_be_applied_ = false;
+class OutputAttribute {
+ public:
+ using SaveFn = std::function<void(OutputAttribute &)>;
+
+ private:
+ GVMutableArrayPtr varray_;
+ AttributeDomain domain_;
+ SaveFn save_;
+ std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
+ bool ignore_old_values_ = false;
+ bool save_has_been_called_ = false;
public:
- WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ OutputAttribute() = default;
+
+ OutputAttribute(GVMutableArrayPtr varray,
+ AttributeDomain domain,
+ SaveFn save,
+ const bool ignore_old_values)
+ : varray_(std::move(varray)),
+ domain_(domain),
+ save_(std::move(save)),
+ ignore_old_values_(ignore_old_values)
{
}
- virtual ~WriteAttribute();
+ OutputAttribute(OutputAttribute &&other) = default;
- AttributeDomain domain() const
+ ~OutputAttribute();
+
+ operator bool() const
{
- return domain_;
+ return varray_.get() != nullptr;
}
- const CPPType &cpp_type() const
+ GVMutableArray &operator*()
{
- return cpp_type_;
+ return *varray_;
}
- CustomDataType custom_data_type() const
+ GVMutableArray *operator->()
{
- return custom_data_type_;
+ return varray_.get();
}
- int64_t size() const
+ GVMutableArray &varray()
{
- return size_;
+ return *varray_;
}
- void get(const int64_t index, void *r_value) const
+ AttributeDomain domain() const
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
+ return domain_;
}
- void set(const int64_t index, const void *value)
+ const CPPType &cpp_type() const
{
- BLI_assert(index < size_);
- this->set_internal(index, value);
+ return varray_->type();
}
- /* Get a span that new attribute values can be written into. When all values have been changed,
- * #apply_span has to be called. */
- fn::GMutableSpan get_span();
- /* The span returned by this method might not contain the current attribute values. */
- fn::GMutableSpan get_span_for_write_only();
- /* Write the changes to the span into the actual attribute, if they aren't already. */
- void apply_span();
-
- template<typename T> MutableSpan<T> get_span()
+ CustomDataType custom_data_type() const
{
- return this->get_span().typed<T>();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- template<typename T> MutableSpan<T> get_span_for_write_only()
+ fn::GMutableSpan as_span()
{
- return this->get_span_for_write_only().typed<T>();
+ if (!optional_span_varray_.has_value()) {
+ const bool materialize_old_values = !ignore_old_values_;
+ optional_span_varray_.emplace(*varray_, materialize_old_values);
+ }
+ fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
+ return span_varray;
}
- protected:
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
- virtual void set_internal(const int64_t index, const void *value) = 0;
+ template<typename T> MutableSpan<T> as_span()
+ {
+ return this->as_span().typed<T>();
+ }
- virtual void initialize_span(const bool write_only);
- virtual void apply_span_if_necessary();
+ void save();
};
-using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
-using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
-
-/* This provides type safe access to an attribute.
- * The underlying ReadAttribute is owned optionally. */
-template<typename T> class TypedReadAttribute {
+/**
+ * Same as OutputAttribute, but should be used when the data type is known at compile time.
+ */
+template<typename T> class OutputAttribute_Typed {
private:
- std::unique_ptr<const ReadAttribute> owned_attribute_;
- const ReadAttribute *attribute_;
+ OutputAttribute attribute_;
+ std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
+ VMutableArray<T> *varray_ = nullptr;
public:
- TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
+ OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ if (attribute_) {
+ optional_varray_.emplace(attribute_.varray());
+ varray_ = &**optional_varray_;
+ }
}
- TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
+ operator bool() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return varray_ != nullptr;
}
- int64_t size() const
+ VMutableArray<T> &operator*()
{
- return attribute_->size();
+ return *varray_;
}
- T operator[](const int64_t index) const
+ VMutableArray<T> *operator->()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return varray_;
}
- /* Get a span to that contains all attribute values for faster and more convenient access. */
- Span<T> get_span() const
+ VMutableArray<T> &varray()
{
- return attribute_->get_span().template typed<T>();
+ return *varray_;
}
-};
-
-/* This provides type safe access to an attribute.
- * The underlying WriteAttribute is owned optionally. */
-template<typename T> class TypedWriteAttribute {
- private:
- std::unique_ptr<WriteAttribute> owned_attribute_;
- WriteAttribute *attribute_;
- public:
- TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
+ AttributeDomain domain() const
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ return attribute_.domain();
}
- TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
+ const CPPType &cpp_type() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return CPPType::get<T>();
}
- int64_t size() const
+ CustomDataType custom_data_type() const
{
- return attribute_->size();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- T operator[](const int64_t index) const
+ MutableSpan<T> as_span()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return attribute_.as_span<T>();
}
- void set(const int64_t index, const T &value)
+ void save()
{
- attribute_->set(index, &value);
+ attribute_.save();
}
+};
- /* Get a span that new values can be written into. Once all values have been updated #apply_span
- * has to be called. */
- MutableSpan<T> get_span()
- {
- return attribute_->get_span().typed<T>();
- }
- /* The span returned by this method might not contain the current attribute values. */
- MutableSpan<T> get_span_for_write_only()
- {
- return attribute_->get_span_for_write_only().typed<T>();
- }
+/**
+ * A basic container around DNA CustomData so that its users
+ * don't have to implement special copy and move constructors.
+ */
+class CustomDataAttributes {
+ /**
+ * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
+ * itself, so keep track of the size here so this class can implement its own destructor.
+ * If the implementation of the attribute storage changes, this could be removed.
+ */
+ int size_;
- /* Write back all changes to the actual attribute, if necessary. */
- void apply_span()
- {
- attribute_->apply_span();
- }
-};
+ public:
+ CustomData data;
+
+ CustomDataAttributes();
+ ~CustomDataAttributes();
+ CustomDataAttributes(const CustomDataAttributes &other);
+ CustomDataAttributes(CustomDataAttributes &&other);
+
+ void reallocate(const int size);
-using BooleanReadAttribute = TypedReadAttribute<bool>;
-using FloatReadAttribute = TypedReadAttribute<float>;
-using Float2ReadAttribute = TypedReadAttribute<float2>;
-using Float3ReadAttribute = TypedReadAttribute<float3>;
-using Int32ReadAttribute = TypedReadAttribute<int>;
-using Color4fReadAttribute = TypedReadAttribute<Color4f>;
-using BooleanWriteAttribute = TypedWriteAttribute<bool>;
-using FloatWriteAttribute = TypedWriteAttribute<float>;
-using Float2WriteAttribute = TypedWriteAttribute<float2>;
-using Float3WriteAttribute = TypedWriteAttribute<float3>;
-using Int32WriteAttribute = TypedWriteAttribute<int>;
-using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
+ std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
+ std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
+ bool create(const blender::StringRef name, const CustomDataType data_type);
+ bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
+ bool remove(const blender::StringRef name);
+
+ bool foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const;
+};
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 16fc0db60fb..0afdc436415 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
+
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
@@ -21,13 +23,17 @@
#include "DNA_customdata_types.h"
+#include "FN_cpp_type.hh"
+
namespace blender::attribute_math {
+using fn::CPPType;
+
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
template<typename Func>
-void convert_to_static_type(const CustomDataType data_type, const Func &func)
+inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
{
switch (data_type) {
case CD_PROP_FLOAT:
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
+template<typename Func>
+inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
+{
+ if (cpp_type.is<float>()) {
+ func(float());
+ }
+ else if (cpp_type.is<float2>()) {
+ func(float2());
+ }
+ else if (cpp_type.is<float3>()) {
+ func(float3());
+ }
+ else if (cpp_type.is<int>()) {
+ func(int());
+ }
+ else if (cpp_type.is<bool>()) {
+ func(bool());
+ }
+ else if (cpp_type.is<Color4f>()) {
+ func(Color4f());
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+}
+
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
@@ -101,6 +133,48 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mix two values of the same type.
+ *
+ * This is just basic linear interpolation.
+ * \{ */
+
+template<typename T> T mix2(const float factor, const T &a, const T &b);
+
+template<> inline bool mix2(const float factor, const bool &a, const bool &b)
+{
+ return ((1.0f - factor) * a + factor * b) >= 0.5f;
+}
+
+template<> inline int mix2(const float factor, const int &a, const int &b)
+{
+ return static_cast<int>((1.0f - factor) * a + factor * b);
+}
+
+template<> inline float mix2(const float factor, const float &a, const float &b)
+{
+ return (1.0f - factor) * a + factor * b;
+}
+
+template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
+{
+ return float2::interpolate(a, b, factor);
+}
+
+template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+{
+ return float3::interpolate(a, b, factor);
+}
+
+template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b)
+{
+ Color4f result;
+ interp_v4_v4v4(result, a, b, factor);
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mix a dynamic amount of values with weights for many elements.
*
* This section provides an abstraction for "mixers". The abstraction encapsulates details about
@@ -153,8 +227,10 @@ template<typename T> class SimpleMixer {
}
};
-/** This mixer accumulates values in a type that is different from the one that is mixed. Some
- * types cannot encode the floating point weights in their values (e.g. int and bool). */
+/**
+ * This mixer accumulates values in a type that is different from the one that is mixed.
+ * Some types cannot encode the floating point weights in their values (e.g. int and bool).
+ */
template<typename T, typename AccumulationT, T (*ConvertToT)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index f74f7fe84de..0bab980cfcd 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,15 +31,15 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 293
+#define BLENDER_VERSION 300
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
-#define BLENDER_VERSION_CYCLE beta
+#define BLENDER_VERSION_CYCLE alpha
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 20
+#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h
index fadba5644de..f04b5e45720 100644
--- a/source/blender/blenkernel/BKE_callbacks.h
+++ b/source/blender/blenkernel/BKE_callbacks.h
@@ -59,6 +59,7 @@ typedef enum {
BKE_CB_EVT_VERSION_UPDATE,
BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST,
BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST,
+ BKE_CB_EVT_XR_SESSION_START_PRE,
BKE_CB_EVT_TOT,
} eCbEvent;
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 3d30188e517..50aa6027840 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
-const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
+
+/**
+ * Values to create the message that describes the reason poll failed.
+ *
+ * \note This must be called in the same context as the poll function that created it.
+ */
+struct bContextPollMsgDyn_Params {
+ /** The result is allocated . */
+ char *(*get_fn)(bContext *C, void *user_data);
+ /** Optionally free the user-data. */
+ void (*free_fn)(bContext *C, void *user_data);
+ void *user_data;
+};
+
+const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params);
+void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index c00310408af..5f6a9ec7b91 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -36,25 +36,13 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
+ GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
-typedef enum InstancedDataType {
- INSTANCE_DATA_TYPE_OBJECT = 0,
- INSTANCE_DATA_TYPE_COLLECTION = 1,
-} InstancedDataType;
-
-typedef struct InstancedData {
- InstancedDataType type;
- union {
- struct Object *object;
- struct Collection *collection;
- } data;
-} InstancedData;
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index d94b2e7902b..3b3856f11ab 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -25,11 +25,11 @@
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
-#include "BLI_function_ref.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_user_counter.hh"
+#include "BLI_vector_set.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
@@ -39,6 +39,7 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
+class CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -56,74 +57,6 @@ class ComponentAttributeProviders;
class GeometryComponent;
/**
- * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
- * destination yet. Therefore, once the attribute has been filled with data, the #save method has
- * to be called, to store the attribute where it belongs (possibly by replacing an existing
- * attribute with the same name).
- *
- * This is useful for example in the Attribute Color Ramp node, when the same attribute name is
- * used as input and output. Typically the input is a float attribute, and the output is a color.
- * Those two attributes cannot exist at the same time, due to a name collision. To handle this
- * situation well, first the output colors have to be computed before the input floats are deleted.
- * Therefore, the outputs have to be written to a temporary buffer that replaces the existing
- * attribute once all computations are done.
- */
-class OutputAttributePtr {
- private:
- blender::bke::WriteAttributePtr attribute_;
-
- public:
- OutputAttributePtr() = default;
- OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
- OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string name,
- CustomDataType data_type);
-
- ~OutputAttributePtr();
-
- /* Returns false, when this wrapper is empty. */
- operator bool() const
- {
- return static_cast<bool>(attribute_);
- }
-
- /* Get a reference to the underlying #WriteAttribute. */
- blender::bke::WriteAttribute &get()
- {
- BLI_assert(attribute_);
- return *attribute_;
- }
-
- blender::bke::WriteAttribute &operator*()
- {
- return *attribute_;
- }
-
- blender::bke::WriteAttribute *operator->()
- {
- return attribute_.get();
- }
-
- void save();
- void apply_span_and_save();
-};
-
-/**
- * Contains information about an attribute in a geometry component.
- * More information can be added in the future. E.g. whether the attribute is builtin and how it is
- * stored (uv map, vertex group, ...).
- */
-struct AttributeMetaData {
- AttributeDomain domain;
- CustomDataType data_type;
-};
-
-/* Returns false when the iteration should be stopped. */
-using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
- const AttributeMetaData &meta_data)>;
-
-/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
@@ -156,26 +89,34 @@ class GeometryComponent {
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
+ /* Return the data type and domain of an attribute with the given name if it exists. */
+ std::optional<AttributeMetaData> attribute_get_meta_data(
+ const blender::StringRef attribute_name) const;
+
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const;
+
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
- virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
+ virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
@@ -183,82 +124,104 @@ class GeometryComponent {
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type);
+ const CustomDataType data_type,
+ const AttributeInit &initializer);
+
+ /* Try to create the builtin attribute with the given name. No data type or domain has to be
+ * provided, because those are fixed for builtin attributes. */
+ bool attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns null when it does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain and data type.
+ * Returns null when the attribute does not exist or cannot be converted to the requested domain
+ * and data type. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const;
- /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
- * Returns null when the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain. The data type is
+ * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
+ * requested domain. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns a constant attribute based on the default value if the attribute does not exist.
- * Never returns null. */
- blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const;
+ /* Get a virtual array to read data of an attribute with the given data type. The domain is
+ * left unchanged. Returns null when the attribute does not exist or cannot be converted to the
+ * requested data type. */
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const;
- /* Get a typed read-only attribute for the given domain and type. */
- template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_for_read(
+ /* Get a virtual array to read the data of an attribute. If that is not possible, the returned
+ * virtual array will contain a default value. This never returns null. */
+ std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
- const T &default_value) const
+ const CustomDataType data_type,
+ const void *default_value = nullptr) const;
+
+ /* Should be used instead of the method above when the requested data type is known at compile
+ * time for better type safety. */
+ template<typename T>
+ blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
+ std::unique_ptr varray = this->attribute_get_for_read(
+ attribute_name, domain, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
- /* Get a read-only dummy attribute that always returns the same value. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
- const CustomDataType data_type,
- const void *value) const;
+ /**
+ * Returns an "output attribute", which is essentially a mutable virtual array with some commonly
+ * used convince features. The returned output attribute might be empty if requested attribute
+ * cannot exist on the geometry.
+ *
+ * The included convenience features are:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - If the attribute name exists already, but has a different type/domain, a temporary attribute
+ * is created that will overwrite the existing attribute in the end.
+ */
+ blender::bke::OutputAttribute attribute_try_get_for_output(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value = nullptr);
- /* Create a read-only dummy attribute that always returns the same value.
- * The given value is converted to the correct type if necessary. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
+ /* Same as attribute_try_get_for_output, but should be used when the original values in the
+ * attributes are not read, i.e. the attribute is used only for output. Since values are not read
+ * from this attribute, no default value is necessary. */
+ blender::bke::OutputAttribute attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const;
+ const CustomDataType data_type);
- /* Get a read-only dummy attribute that always returns the same value. */
+ /* Statically typed method corresponding to the equally named generic one. */
template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
- const T &value) const
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
+ const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
- const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_constant_for_read(domain, type, &value);
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
}
- /**
- * If an attribute with the given params exist, it is returned.
- * If no attribute with the given name exists, create it and
- * fill it with the default value if it is provided.
- * If an attribute with the given name but different domain or type exists, a temporary attribute
- * is created that has to be saved after the output has been computed. This avoids deleting
- * another attribute, before a computation is finished.
- *
- * This might return no attribute when the attribute cannot exist on the component.
- */
- OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value = nullptr);
+ /* Statically typed method corresponding to the equally named generic one. */
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name, const AttributeDomain domain)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
+ }
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
@@ -328,23 +291,34 @@ struct GeometrySet {
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_curve(
+ CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
+ bool has_curve() const;
+
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
+ const CurveEval *get_curve_for_read() const;
+
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
+ CurveEval *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_volume(Volume *volume,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_curve(CurveEval *curve,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -377,8 +351,10 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
- blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
bool is_empty() const final;
@@ -424,12 +400,113 @@ class PointCloudComponent : public GeometryComponent {
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
+/** A geometry component that stores curve data, in other words, a group of splines. */
+class CurveComponent : public GeometryComponent {
+ private:
+ CurveEval *curve_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ public:
+ CurveComponent();
+ ~CurveComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_curve() const;
+ void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ CurveEval *release();
+
+ const CurveEval *get_for_read() const;
+ CurveEval *get_for_write();
+
+ int attribute_domain_size(const AttributeDomain domain) const final;
+
+ bool is_empty() const final;
+
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
+};
+
+class InstanceReference {
+ public:
+ enum class Type {
+ /**
+ * An empty instance. This allows an `InstanceReference` to be default constructed without
+ * being in an invalid state. There might also be other use cases that we haven't explored much
+ * yet (such as changing the instance later on, and "disabling" some instances).
+ */
+ None,
+ Object,
+ Collection,
+ };
+
+ private:
+ Type type_ = Type::None;
+ /** Depending on the type this is either null, an Object or Collection pointer. */
+ void *data_ = nullptr;
+
+ public:
+ InstanceReference() = default;
+
+ InstanceReference(Object &object) : type_(Type::Object), data_(&object)
+ {
+ }
+
+ InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection)
+ {
+ }
+
+ Type type() const
+ {
+ return type_;
+ }
+
+ Object &object() const
+ {
+ BLI_assert(type_ == Type::Object);
+ return *(Object *)data_;
+ }
+
+ Collection &collection() const
+ {
+ BLI_assert(type_ == Type::Collection);
+ return *(Collection *)data_;
+ }
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash(data_);
+ }
+
+ friend bool operator==(const InstanceReference &a, const InstanceReference &b)
+ {
+ return a.data_ == b.data_;
+ }
+};
+
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
- blender::Vector<blender::float4x4> transforms_;
- blender::Vector<int> ids_;
- blender::Vector<InstancedData> instanced_data_;
+ /**
+ * Indexed set containing information about the data that is instanced.
+ * Actual instances store an index ("handle") into this set.
+ */
+ blender::VectorSet<InstanceReference> references_;
+
+ /** Index into `references_`. Determines what data is instanced. */
+ blender::Vector<int> instance_reference_handles_;
+ /** Transformation of the instances. */
+ blender::Vector<blender::float4x4> instance_transforms_;
+ /**
+ * IDs of the instances. They are used for consistency over multiple frames for things like
+ * motion blur.
+ */
+ blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
@@ -444,14 +521,22 @@ class InstancesComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- void add_instance(Object *object, blender::float4x4 transform, const int id = -1);
- void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1);
- void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1);
-
- blender::Span<InstancedData> instanced_data() const;
- blender::Span<blender::float4x4> transforms() const;
- blender::Span<int> ids() const;
- blender::MutableSpan<blender::float4x4> transforms();
+
+ void reserve(int min_capacity);
+ void resize(int capacity);
+
+ int add_reference(InstanceReference reference);
+ void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
+
+ blender::Span<InstanceReference> references() const;
+
+ blender::Span<int> instance_reference_handles() const;
+ blender::MutableSpan<int> instance_reference_handles();
+ blender::MutableSpan<blender::float4x4> instance_transforms();
+ blender::Span<blender::float4x4> instance_transforms() const;
+ blender::MutableSpan<int> instance_ids();
+ blender::Span<int> instance_ids() const;
+
int instances_amount() const;
blender::Span<int> almost_unique_ids() const;
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 4b4886e8bf3..bb145580928 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -108,7 +108,10 @@ void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
-struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header);
struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src,
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index a9bd0a524c4..8fc3ce133a0 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -111,7 +111,10 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDstroke *gps,
const short tag);
-bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
+bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode);
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
const int index_from,
const int index_to);
@@ -135,7 +138,7 @@ bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDstroke *gps,
const int before_index,
struct bGPDstroke **remaining_gps);
-bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
+bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist, const short mode);
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index b9a478f8227..f1ed5a453ba 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -85,10 +85,12 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce,
- const bool do_post_process);
+ const bool do_post_process,
+ struct ReportList *reports);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
- struct ViewLayer *view_layer);
+ struct ViewLayer *view_layer,
+ struct ReportList *reports);
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 14ea50f808a..dc471fcb62f 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -51,6 +51,9 @@ void BKE_object_material_remap(struct Object *ob, const unsigned int *remap);
void BKE_object_material_remap_calc(struct Object *ob_dst,
struct Object *ob_src,
short *remap_src_to_dst);
+void BKE_object_material_from_eval_data(struct Main *bmain,
+ struct Object *ob_orig,
+ struct ID *data_eval);
struct Material *BKE_material_add(struct Main *bmain, const char *name);
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
void BKE_gpencil_material_attr_init(struct Material *ma);
@@ -105,6 +108,12 @@ struct Material *BKE_id_material_pop(struct Main *bmain,
/* index is an int because of RNA. */
int index);
void BKE_id_material_clear(struct Main *bmain, struct ID *id);
+
+/* eval api */
+struct Material *BKE_object_material_get_eval(struct Object *ob, short act);
+int BKE_object_material_count_eval(struct Object *ob);
+void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material);
+
/* rendering */
void ramp_blend(int type, float r_col[3], const float fac, const float col[3]);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 22ce197088a..62837c4f2a7 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -578,9 +578,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly,
struct CustomData *ldata,
int totpoly);
-/* merge verts */
-/* Enum for merge_mode of CDDM_merge_verts.
- * Refer to mesh.c for details. */
+/* Merge verts. */
+/* Enum for merge_mode of #BKE_mesh_merge_verts.
+ * Refer to mesh_merge.c for details. */
enum {
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
new file mode 100644
index 00000000000..f504650e349
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+
+#include "BKE_attribute.h"
+
+struct Mesh;
+
+namespace blender::bke::mesh_surface_sample {
+
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::GVArray;
+
+void sample_point_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_corner_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_face_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index aea07c45412..48b4540e3d9 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
- struct Volume *(*modifyVolume)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Volume *volume);
-
/********************* Optional functions *********************/
/**
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 16d48024d07..af238fda659 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
struct NlaTrack *nlt,
const bool use_same_actions,
const int flag);
-void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
+void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag);
+
+/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
+ * point at those copies. */
+void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
+ struct AnimData *adt_dest,
+ const struct AnimData *adt_source,
+ int flag);
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index d6c4ad037e2..6bebf74824d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -325,6 +325,7 @@ typedef struct bNodeType {
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
+ bool geometry_node_execute_supports_lazyness;
/* RNA integration */
ExtensionRNA rna_ext;
@@ -410,6 +411,9 @@ typedef struct bNodeTreeType {
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
+ /* Check if the socket type is valid for this tree type. */
+ bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype);
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeTreeType;
@@ -1413,6 +1417,15 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
+#define GEO_NODE_SWITCH 1043
+#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
+#define GEO_NODE_CURVE_TO_MESH 1045
+#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
+#define GEO_NODE_CURVE_RESAMPLE 1047
+#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
+#define GEO_NODE_MATERIAL_ASSIGN 1049
+#define GEO_NODE_INPUT_MATERIAL 1050
+#define GEO_NODE_MATERIAL_REPLACE 1051
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index 5f9c039ef9e..8bf89cd8f58 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -95,8 +95,16 @@ struct AvailableAttributeInfo {
};
struct NodeUIStorage {
+ std::mutex mutex;
blender::Vector<NodeWarning> warnings;
blender::Set<AvailableAttributeInfo> attribute_hints;
+
+ NodeUIStorage() = default;
+ /* Needed because the mutex can't be moved or copied. */
+ NodeUIStorage(NodeUIStorage &&other)
+ : warnings(std::move(other.warnings)), attribute_hints(std::move(other.attribute_hints))
+ {
+ }
};
struct NodeTreeUIStorage {
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 9fe286df36d..f3a5c794de8 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -374,6 +374,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene,
void BKE_object_runtime_reset(struct Object *object);
void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag);
+void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 228b52123f3..73413b61456 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -486,7 +486,11 @@ typedef struct SculptSession {
/* Total number of polys of the base mesh. */
int totfaces;
/* Face sets store its visibility in the sign of the integer, using the absolute value as the
- * Face Set ID. Positive IDs are visible, negative IDs are hidden. */
+ * Face Set ID. Positive IDs are visible, negative IDs are hidden.
+ * The 0 ID is not used by the tools or the visibility system, it is just used when creating new
+ * geometry (the trim tool, for example) to detect which geometry was just added, so it can be
+ * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
+ * to 0. */
int *face_sets;
/* BMesh for dynamic topology sculpting */
diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
deleted file mode 100644
index bbee09c7bf4..00000000000
--- a/source/blender/blenkernel/BKE_persistent_data_handle.hh
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- *
- * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is
- * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data.
- */
-
-#include "BLI_map.hh"
-
-#include "DNA_ID.h"
-
-struct Collection;
-struct Object;
-
-namespace blender::bke {
-
-class PersistentDataHandleMap;
-
-class PersistentDataHandle {
- private:
- /* Negative values indicate that the handle is "empty". */
- int32_t handle_;
-
- friend PersistentDataHandleMap;
-
- protected:
- PersistentDataHandle(int handle) : handle_(handle)
- {
- }
-
- public:
- PersistentDataHandle() : handle_(-1)
- {
- }
-
- friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return a.handle_ == b.handle_;
- }
-
- friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return !(a == b);
- }
-
- friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a)
- {
- stream << a.handle_;
- return stream;
- }
-
- uint64_t hash() const
- {
- return static_cast<uint64_t>(handle_);
- }
-};
-
-class PersistentIDHandle : public PersistentDataHandle {
- friend PersistentDataHandleMap;
- using PersistentDataHandle::PersistentDataHandle;
-};
-
-class PersistentObjectHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentCollectionHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentDataHandleMap {
- private:
- Map<int32_t, ID *> id_by_handle_;
- Map<ID *, int32_t> handle_by_id_;
-
- public:
- void add(int32_t handle, ID &id)
- {
- BLI_assert(handle >= 0);
- handle_by_id_.add(&id, handle);
- id_by_handle_.add(handle, &id);
- }
-
- PersistentIDHandle lookup(ID *id) const
- {
- const int handle = handle_by_id_.lookup_default(id, -1);
- return PersistentIDHandle(handle);
- }
-
- PersistentObjectHandle lookup(Object *object) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)object, -1);
- return PersistentObjectHandle(handle);
- }
-
- PersistentCollectionHandle lookup(Collection *collection) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)collection, -1);
- return PersistentCollectionHandle(handle);
- }
-
- ID *lookup(const PersistentIDHandle &handle) const
- {
- ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr);
- return id;
- }
-
- Object *lookup(const PersistentObjectHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_OB) {
- return nullptr;
- }
- return (Object *)id;
- }
-
- Collection *lookup(const PersistentCollectionHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_GR) {
- return nullptr;
- }
- return (Collection *)id;
- }
-};
-
-} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 4f8b21141b6..58dc90f62dc 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -47,7 +47,7 @@ typedef struct BodyPoint {
} BodyPoint;
/* allocates and initializes general main data */
-extern struct SoftBody *sbNew(struct Scene *scene);
+extern struct SoftBody *sbNew(void);
/* frees internal data and soft-body itself */
extern void sbFree(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
new file mode 100644
index 00000000000..9e5552082af
--- /dev/null
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -0,0 +1,517 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <mutex>
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+
+struct Curve;
+
+class Spline;
+using SplinePtr = std::unique_ptr<Spline>;
+
+/**
+ * A spline is an abstraction of a single branch-less curve section, its evaluation methods,
+ * and data. The spline data itself is just control points and a set of attributes by the set
+ * of "evaluated" data is often used instead.
+ *
+ * Any derived class of Spline has to manage two things:
+ * 1. Interpolating arbitrary attribute data from the control points to evaluated points.
+ * 2. Evaluating the positions based on the stored control point data.
+ *
+ * Beyond that, everything is the base class's responsibility, with minor exceptions. Further
+ * evaluation happens in a layer on top of the evaluated points generated by the derived types.
+ *
+ * There are a few methods to evaluate a spline:
+ * 1. #evaluated_positions and #interpolate_to_evaluated_points give data for the initial
+ * evaluated points, depending on the resolution.
+ * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
+ * along the length of a curve.
+ * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
+ * along the spline which can be used to interpolate data from method 1.
+ *
+ * Commonly used evaluated data is stored in caches on the spline itself so that operations on
+ * splines don't need to worry about taking ownership of evaluated data when they don't need to.
+ */
+class Spline {
+ public:
+ enum class Type {
+ Bezier,
+ NURBS,
+ Poly,
+ };
+
+ enum NormalCalculationMode {
+ ZUp,
+ Minimum,
+ Tangent,
+ };
+ /* Only #Zup is supported at the moment. */
+ NormalCalculationMode normal_mode;
+
+ blender::bke::CustomDataAttributes attributes;
+
+ protected:
+ Type type_;
+ bool is_cyclic_ = false;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
+ mutable std::mutex tangent_cache_mutex_;
+ mutable bool tangent_cache_dirty_ = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_normals_cache_;
+ mutable std::mutex normal_cache_mutex_;
+ mutable bool normal_cache_dirty_ = true;
+
+ /** Accumulated lengths along the evaluated points. */
+ mutable blender::Vector<float> evaluated_lengths_cache_;
+ mutable std::mutex length_cache_mutex_;
+ mutable bool length_cache_dirty_ = true;
+
+ public:
+ virtual ~Spline() = default;
+ Spline(const Type type) : type_(type)
+ {
+ }
+ Spline(Spline &other)
+ : normal_mode(other.normal_mode),
+ attributes(other.attributes),
+ type_(other.type_),
+ is_cyclic_(other.is_cyclic_)
+ {
+ }
+
+ virtual SplinePtr copy() const = 0;
+
+ Spline::Type type() const;
+
+ /** Return the number of control points. */
+ virtual int size() const = 0;
+ int segments_size() const;
+ bool is_cyclic() const;
+ void set_cyclic(const bool value);
+
+ virtual void resize(const int size) = 0;
+ virtual blender::MutableSpan<blender::float3> positions() = 0;
+ virtual blender::Span<blender::float3> positions() const = 0;
+ virtual blender::MutableSpan<float> radii() = 0;
+ virtual blender::Span<float> radii() const = 0;
+ virtual blender::MutableSpan<float> tilts() = 0;
+ virtual blender::Span<float> tilts() const = 0;
+
+ virtual void translate(const blender::float3 &translation);
+ virtual void transform(const blender::float4x4 &matrix);
+
+ /**
+ * Mark all caches for re-computation. This must be called after any operation that would
+ * change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
+ */
+ virtual void mark_cache_invalid() = 0;
+ virtual int evaluated_points_size() const = 0;
+ int evaluated_edges_size() const;
+
+ float length() const;
+
+ virtual blender::Span<blender::float3> evaluated_positions() const = 0;
+
+ blender::Span<float> evaluated_lengths() const;
+ blender::Span<blender::float3> evaluated_tangents() const;
+ blender::Span<blender::float3> evaluated_normals() const;
+
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ struct LookupResult {
+ /**
+ * The index of the evaluated point before the result location. In other words, the index of
+ * the edge that the result lies on. If the sampled factor/length is the very end of the
+ * spline, this will be the second to last index, if it's the very beginning, this will be 0.
+ */
+ int evaluated_index;
+ /**
+ * The index of the evaluated point after the result location, accounting for wrapping when
+ * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
+ * be the last index (#evaluated_points_size - 1).
+ */
+ int next_evaluated_index;
+ /**
+ * The portion of the way from the evaluated point at #evaluated_index to the next point.
+ * If the sampled factor/length is the very end of the spline, this will be the 1.0f
+ */
+ float factor;
+ };
+ LookupResult lookup_evaluated_factor(const float factor) const;
+ LookupResult lookup_evaluated_length(const float length) const;
+
+ blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
+ LookupResult lookup_data_from_index_factor(const float index_factor) const;
+
+ /**
+ * Interpolate a virtual array of data with the size of the number of control points to the
+ * evaluated points. For poly splines, the lifetime of the returned virtual array must not
+ * exceed the lifetime of the input data.
+ */
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const = 0;
+
+ protected:
+ virtual void correct_end_tangents() const = 0;
+};
+
+/**
+ * A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
+ * by constraining the alignment of curve handles. Evaluation stores the positions and a map of
+ * factors and indices in a list of floats, which is then used to interpolate any other data.
+ */
+class BezierSpline final : public Spline {
+ public:
+ enum class HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ Free,
+ /** The location is automatically calculated to be smooth. */
+ Auto,
+ /** The location is calculated to point to the next/previous control point. */
+ Vector,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ Align,
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ int resolution_;
+
+ blender::Vector<HandleType> handle_types_left_;
+ blender::Vector<HandleType> handle_types_right_;
+
+ /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
+ mutable blender::Vector<blender::float3> handle_positions_left_;
+ mutable blender::Vector<blender::float3> handle_positions_right_;
+
+ mutable std::mutex auto_handle_mutex_;
+ mutable bool auto_handles_dirty_ = true;
+
+ /** Start index in evaluated points array for every control point. */
+ mutable blender::Vector<int> offset_cache_;
+ mutable std::mutex offset_cache_mutex_;
+ mutable bool offset_cache_dirty_ = true;
+
+ /** Cache of evaluated positions. */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ /** Cache of "index factors" based calculated from the evaluated positions. */
+ mutable blender::Vector<float> evaluated_mapping_cache_;
+ mutable std::mutex mapping_cache_mutex_;
+ mutable bool mapping_cache_dirty_ = true;
+
+ public:
+ virtual SplinePtr copy() const final;
+ BezierSpline() : Spline(Type::Bezier)
+ {
+ }
+ BezierSpline(const BezierSpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ resolution_(other.resolution_),
+ handle_types_left_(other.handle_types_left_),
+ handle_types_right_(other.handle_types_right_),
+ handle_positions_left_(other.handle_positions_left_),
+ handle_positions_right_(other.handle_positions_right_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+
+ void add_point(const blender::float3 position,
+ const HandleType handle_type_start,
+ const blender::float3 handle_position_start,
+ const HandleType handle_type_end,
+ const blender::float3 handle_position_end,
+ const float radius,
+ const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<HandleType> handle_types_left() const;
+ blender::MutableSpan<HandleType> handle_types_left();
+ blender::Span<blender::float3> handle_positions_left() const;
+ blender::MutableSpan<blender::float3> handle_positions_left();
+ blender::Span<HandleType> handle_types_right() const;
+ blender::MutableSpan<HandleType> handle_types_right();
+ blender::Span<blender::float3> handle_positions_right() const;
+ blender::MutableSpan<blender::float3> handle_positions_right();
+
+ void translate(const blender::float3 &translation) override;
+ void transform(const blender::float4x4 &matrix) override;
+
+ bool point_is_sharp(const int index) const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<int> control_point_offsets() const;
+ blender::Span<float> evaluated_mappings() const;
+ blender::Span<blender::float3> evaluated_positions() const final;
+ struct InterpolationData {
+ int control_point_index;
+ int next_control_point_index;
+ /**
+ * Linear interpolation weight between the two indices, from 0 to 1.
+ * Higher means closer to next control point.
+ */
+ float factor;
+ };
+ InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
+
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const override;
+
+ private:
+ void ensure_auto_handles() const;
+ void correct_end_tangents() const final;
+ bool segment_is_vector(const int start_index) const;
+ void evaluate_bezier_segment(const int index,
+ const int next_index,
+ blender::MutableSpan<blender::float3> positions) const;
+};
+
+/**
+ * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
+ * influenced by a vector of knots, weights for each point, and the order of the spline. Every
+ * mapping of data to evaluated points is handled the same way, but the positions are cached in
+ * the spline.
+ */
+class NURBSpline final : public Spline {
+ public:
+ enum class KnotsMode {
+ Normal,
+ EndPoint,
+ Bezier,
+ };
+
+ /** Method used to recalculate the knots vector when points are added or removed. */
+ KnotsMode knots_mode;
+
+ struct BasisCache {
+ /** The influence at each control point `i + #start_index`. */
+ blender::Vector<float> weights;
+ /**
+ * An offset for the start of #weights: the first control point index with a non-zero weight.
+ */
+ int start_index;
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ blender::Vector<float> weights_;
+ int resolution_;
+ /**
+ * Defines the number of nearby control points that influence a given evaluated point. Higher
+ * orders give smoother results. The number of control points must be greater than or equal to
+ * this value.
+ */
+ uint8_t order_;
+
+ /**
+ * Determines where and how the control points affect the evaluated points. The length should
+ * always be the value returned by #knots_size(), and each value should be greater than or equal
+ * to the previous. Only invalidated when a point is added or removed.
+ */
+ mutable blender::Vector<float> knots_;
+ mutable std::mutex knots_mutex_;
+ mutable bool knots_dirty_ = true;
+
+ /** Cache of control point influences on each evaluated point. */
+ mutable blender::Vector<BasisCache> basis_cache_;
+ mutable std::mutex basis_cache_mutex_;
+ mutable bool basis_cache_dirty_ = true;
+
+ /**
+ * Cache of position data calculated from the basis cache. Though it is interpolated
+ * in the same way as any other attribute, it is stored to save unnecessary recalculation.
+ */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ public:
+ SplinePtr copy() const final;
+ NURBSpline() : Spline(Type::NURBS)
+ {
+ }
+ NURBSpline(const NURBSpline &other)
+ : Spline((Spline &)other),
+ knots_mode(other.knots_mode),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ weights_(other.weights_),
+ resolution_(other.resolution_),
+ order_(other.order_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+ uint8_t order() const;
+ void set_order(const uint8_t value);
+
+ void add_point(const blender::float3 position,
+ const float radius,
+ const float tilt,
+ const float weight);
+
+ bool check_valid_size_and_order() const;
+ int knots_size() const;
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<float> knots() const;
+
+ blender::MutableSpan<float> weights();
+ blender::Span<float> weights() const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+ void calculate_knots() const;
+ void calculate_basis_cache() const;
+};
+
+/**
+ * A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
+ * the two is for reduced complexity and increased performance, since interpolating data to control
+ * points does not change it.
+ */
+class PolySpline final : public Spline {
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+
+ public:
+ SplinePtr copy() const final;
+ PolySpline() : Spline(Type::Poly)
+ {
+ }
+ PolySpline(const PolySpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_)
+ {
+ }
+
+ int size() const final;
+
+ void add_point(const blender::float3 position, const float radius, const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+};
+
+/**
+ * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since
+ * more of the data is stored in the splines, but also just to be different than the name in DNA.
+ */
+class CurveEval {
+ private:
+ blender::Vector<SplinePtr> splines_;
+
+ public:
+ blender::bke::CustomDataAttributes attributes;
+
+ CurveEval() = default;
+ CurveEval(const CurveEval &other) : attributes(other.attributes)
+ {
+ for (const SplinePtr &spline : other.splines()) {
+ this->add_spline(spline->copy());
+ }
+ }
+
+ blender::Span<SplinePtr> splines() const;
+ blender::MutableSpan<SplinePtr> splines();
+
+ void add_spline(SplinePtr spline);
+ void remove_splines(blender::IndexMask mask);
+
+ void translate(const blender::float3 &translation);
+ void transform(const blender::float4x4 &matrix);
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ blender::Array<int> control_point_offsets() const;
+ blender::Array<int> evaluated_point_offsets() const;
+
+ void assert_valid_point_attributes() const;
+};
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 59e2c74ead1..021d7e15814 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -112,12 +112,13 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
+ intern/curve_eval.cc
intern/curveprofile.c
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
- intern/displist.c
+ intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
intern/editlattice.c
@@ -133,6 +134,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -190,6 +192,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
+ intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -240,6 +243,10 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
+ intern/spline_base.cc
+ intern/spline_bezier.cc
+ intern/spline_nurbs.cc
+ intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -321,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
+ BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -379,6 +387,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
+ BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h
@@ -397,7 +406,6 @@ set(SRC
BKE_paint.h
BKE_particle.h
BKE_pbvh.h
- BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_preferences.h
@@ -583,10 +591,6 @@ if(WITH_CODEC_FFMPEG)
${FFMPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/writeffmpeg.c
- )
endif()
if(WITH_PYTHON)
@@ -765,6 +769,7 @@ if(WITH_GTESTS)
intern/fcurve_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
+ intern/lib_id_test.cc
intern/tracking_test.cc
)
set(TEST_INC
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 5bf87f53f75..d4dd7e248d5 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -1861,9 +1861,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
- /* Ensure normals calculation below is correct. */
- BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH));
- BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh);
+ /* Ensure normals calculation below is correct (normal settings have transferred properly).
+ * However, nodes modifiers might create meshes from scratch or transfer meshes from other
+ * objects with different settings, and in general it doesn't make sense to guarantee that
+ * the settings are the same as the original mesh. If necessary, this could become a modifier
+ * type flag. */
BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
/* Compute normals. */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 0b38e2d9f75..a7e36b09516 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name)
* \note Use with care, not on Armature poses but for temporal ones.
* \note (currently used for action constraints and in rebuild_pose).
*/
-bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
+bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name)
{
bPoseChannel *chan;
@@ -656,7 +656,9 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
BLI_strncpy(chan->name, name, sizeof(chan->name));
- chan->custom_scale = 1.0f;
+ copy_v3_fl(chan->custom_scale_xyz, 1.0f);
+ zero_v3(chan->custom_translation);
+ zero_v3(chan->custom_rotation_euler);
/* init vars to prevent math errors */
unit_qt(chan->quat);
@@ -815,7 +817,7 @@ void BKE_pose_copy_data_ex(bPose **dst,
*/
if (outPose->chanbase.first != outPose->chanbase.last) {
outPose->chanhash = NULL;
- BKE_pose_channels_hash_make(outPose);
+ BKE_pose_channels_hash_ensure(outPose);
}
outPose->iksolver = src->iksolver;
@@ -945,7 +947,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
* Removes the hash for quick lookup of channels, must
* be done when adding/removing channels.
*/
-void BKE_pose_channels_hash_make(bPose *pose)
+void BKE_pose_channels_hash_ensure(bPose *pose)
{
if (!pose->chanhash) {
bPoseChannel *pchan;
@@ -1191,7 +1193,7 @@ void BKE_pose_free(bPose *pose)
* and ID-Props, used when duplicating bones in editmode.
* (unlike copy_pose_channel_data which only does posing-related stuff).
*
- * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify)
+ * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure)
*/
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
{
@@ -1235,8 +1237,10 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
+ copy_v3_v3(pchan->custom_scale_xyz, pchan_from->custom_scale_xyz);
+ copy_v3_v3(pchan->custom_translation, pchan_from->custom_translation);
+ copy_v3_v3(pchan->custom_rotation_euler, pchan_from->custom_rotation_euler);
- pchan->custom_scale = pchan_from->custom_scale;
pchan->drawflag = pchan_from->drawflag;
}
@@ -1774,7 +1778,7 @@ void what_does_obaction(Object *ob,
* allocation and also will make lookup slower.
*/
if (pose->chanbase.first != pose->chanbase.last) {
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
}
if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(pose);
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 447ed8fbe14..44b760aefc8 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -354,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
}
/* duplicate NLA data */
- BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag);
+ BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
/* duplicate drivers (F-Curves) */
BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
@@ -947,7 +947,7 @@ static bool nlastrips_path_rename_fix(ID *owner_id,
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->act->curves, verify_paths);
}
/* Ignore own F-Curves, since those are local. */
- /* Check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
is_changed |= nlastrips_path_rename_fix(
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
}
@@ -1177,7 +1177,7 @@ static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
any_removed |= fcurves_path_remove_fix(prefix, &strip->act->curves);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
}
return any_removed;
@@ -1245,7 +1245,7 @@ static void nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, AllFCurvesCb
fcurves_apply_cb(id, &strip->act->curves, wrapper->func, wrapper->user_data);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nlastrips_apply_all_curves_cb(id, &strip->strips, wrapper);
}
}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 6f4af6f655d..e347306e0ae 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1040,6 +1040,7 @@ static NlaEvalChannelSnapshot *nlaevalchan_snapshot_new(NlaEvalChannel *nec)
nec_snapshot->channel = nec;
nec_snapshot->length = length;
nlavalidmask_init(&nec_snapshot->blend_domain, length);
+ nlavalidmask_init(&nec_snapshot->remap_domain, length);
return nec_snapshot;
}
@@ -1050,6 +1051,7 @@ static void nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
BLI_assert(!nec_snapshot->is_base);
nlavalidmask_free(&nec_snapshot->blend_domain);
+ nlavalidmask_free(&nec_snapshot->remap_domain);
MEM_freeN(nec_snapshot);
}
@@ -1650,6 +1652,363 @@ static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_v
}
/* ---------------------- */
+
+/* Assert necs and necs->channel is nonNull. */
+static void nlaevalchan_assert_nonNull(NlaEvalChannelSnapshot *necs)
+{
+ UNUSED_VARS_NDEBUG(necs);
+ BLI_assert(necs != NULL && necs->channel != NULL);
+}
+
+/* Assert that the channels given can be blended or combined together. */
+static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ UNUSED_VARS_NDEBUG(lower_necs, upper_necs, blended_necs);
+ BLI_assert(!ELEM(NULL, lower_necs, blended_necs));
+ BLI_assert(upper_necs == NULL || lower_necs->length == upper_necs->length);
+ BLI_assert(lower_necs->length == blended_necs->length);
+}
+
+/* Assert that the channels given can be blended or combined together as a quaternion. */
+static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, blended_necs);
+ BLI_assert(lower_necs->length == 4);
+}
+
+static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelSnapshot *src)
+{
+ memcpy(dst->values, src->values, src->length * sizeof(float));
+}
+
+/**
+ * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied.
+ */
+static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return true;
+}
+
+/**
+ * Based on blend-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_blend_value(
+ upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Based on mix-mode, provided by one the necs,
+ * combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /* Assumes every base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_combine_value(
+ mix_mode, base_values[j], lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Quaternion combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ */
+static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /** No need to check per index. We limit to all or nothing combining for quaternions. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return;
+ }
+
+ nla_combine_quaternion(
+ lower_necs->values, upper_necs->values, upper_influence, r_blended_necs->values);
+}
+
+/**
+ * Based on blend-mode and mix-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ *
+ * \param lower_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param upper_necs: Never NULL.
+ *
+ */
+static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_nonNull(r_blended_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_blended_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/**
+ * Based on blend-mode, solve for the upper values such that when lower blended with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_strip_value(upper_blendmode,
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Based on mix-mode, solve for the upper values such that when lower combined with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ /* Assumes every channel's base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_strip_value(mix_mode,
+ base_values[j],
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Solve for the upper values such that when lower quaternion combined with upper then we get
+ * blended values as a result.
+ *
+ * All blended values must be in the remap domain. If successfully remapped, then all upper values
+ * are placed in the remap domain so caller knows the result is usable.
+ */
+static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
+
+ /* Must check each domain index individually in case animator had a non-combine NLA strip with a
+ * subset of quaternion channels and remapping through any of them failed and thus potentially
+ * has undefined values. */
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
+ return;
+ }
+ }
+
+ const bool success = nla_combine_quaternion_get_inverted_strip_values(
+ lower_necs->values, blended_necs->values, upper_influence, r_upper_necs->values);
+
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, success, 4);
+}
+
+/**
+ * Based on blend-mode and mix mode, solve for the upper values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param lower_necs: Never NULL.
+ * \param blended_necs: Never NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_upper_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+
+ if (IS_EQF(upper_influence, 0.0f)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, r_upper_necs->length);
+ return;
+ }
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_upper_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2048,12 +2407,12 @@ static void nla_eval_domain_strips(PointerRNA *ptr,
GSet *touched_actions)
{
LISTBASE_FOREACH (NlaStrip *, strip, strips) {
- /* check strip's action */
+ /* Check strip's action. */
if (strip->act) {
nla_eval_domain_action(ptr, channels, strip->act, touched_actions);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nla_eval_domain_strips(ptr, channels, &strip->strips, touched_actions);
}
}
@@ -2500,9 +2859,9 @@ void nlasnapshot_ensure_channels(NlaEvalData *eval_data, NlaEvalSnapshot *snapsh
* Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
* to the given \a upper_blendmode and \a upper_influence.
*
- * For \a upper_snapshot, blending limited to values in the \a blend_domain. For Replace blendmode,
- * this allows the upper snapshot to have a location XYZ channel where only a subset of values are
- * blended.
+ * For \a upper_snapshot, blending limited to values in the \a blend_domain.
+ * For Replace blend-mode, this allows the upper snapshot to have a location XYZ channel
+ * where only a subset of values are blended.
*/
void nlasnapshot_blend(NlaEvalData *eval_data,
NlaEvalSnapshot *lower_snapshot,
@@ -2513,11 +2872,7 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
{
nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels);
- const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f);
-
LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
- const int length = nec->base_snapshot.length;
-
NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
if (upper_necs == NULL && lower_necs == NULL) {
@@ -2530,49 +2885,44 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
}
NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec);
+ nlaevalchan_blendOrcombine(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
- /** Always copy \a lower_snapshot to result, irrelevant of whether \a upper_snapshot has a
- * corresponding channel. This only matters when \a lower_snapshot not the same as
- * \a r_blended_snapshot. */
- memcpy(result_necs->values, lower_necs->values, length * sizeof(float));
- if (upper_necs == NULL || zero_upper_influence) {
+/**
+ * Using \a blended_snapshot and \a lower_snapshot, we can solve for the \a r_upper_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are inverted.
+ *
+ * For \a r_upper_snapshot, disables \a NlaEvalChannelSnapshot->remap_domain for failed inversions.
+ * Only values within the \a remap_domain are processed.
+ */
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_upper_snapshot, eval_data->num_channels);
+
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /** We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
continue;
}
- if (upper_blendmode == NLASTRIP_MODE_COMBINE) {
- const int mix_mode = nec->mix_mode;
- if (mix_mode == NEC_MIX_QUATERNION) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
- continue;
- }
-
- nla_combine_quaternion(
- lower_necs->values, upper_necs->values, upper_influence, result_necs->values);
- }
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
-
- result_necs->values[j] = nla_combine_value(mix_mode,
- nec->base_snapshot.values[j],
- lower_necs->values[j],
- upper_necs->values[j],
- upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
+ if (lower_necs == NULL) {
+ lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec);
}
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
- result_necs->values[j] = nla_blend_value(
- upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_upper_snapshot, nec);
+ nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, result_necs);
}
}
@@ -2670,74 +3020,64 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
return false;
}
- /* Find the evaluation channel for the NLA stack below current strip. */
+ /** Create \a blended_snapshot and fill with input \a values. */
+ NlaEvalData *eval_data = &context->lower_eval_data;
+ NlaEvalSnapshot blended_snapshot;
+ nlaeval_snapshot_init(&blended_snapshot, eval_data, NULL);
+
NlaEvalChannelKey key = {
.ptr = *prop_ptr,
.prop = prop,
};
- /**
- * Remove lower NLA stack effects.
- *
- * Using the tweak strip's blended result and the lower snapshot value, we can solve for the
- * tweak strip value it must evaluate to.
- */
- NlaEvalData *const lower_eval_data = &context->lower_eval_data;
- NlaEvalChannel *const lower_nec = nlaevalchan_verify_key(lower_eval_data, NULL, &key);
- if ((lower_nec->base_snapshot.length != count)) {
+ NlaEvalChannel *nec = nlaevalchan_verify_key(eval_data, NULL, &key);
+ BLI_assert(nec);
+ if (nec->base_snapshot.length != count) {
BLI_assert(!"invalid value count");
+ nlaeval_snapshot_free_data(&blended_snapshot);
return false;
}
- /* Invert the blending operation to compute the desired strip values. */
- NlaEvalChannelSnapshot *const lower_nec_snapshot = nlaeval_snapshot_find_channel(
- &lower_eval_data->eval_snapshot, lower_nec);
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
+ memcpy(blended_necs->values, values, sizeof(float) * count);
+ BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
- float *lower_values = lower_nec_snapshot->values;
+ /** Remove lower NLA stack effects. */
+ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
+ &context->lower_eval_data.eval_snapshot,
+ &blended_snapshot,
+ blend_mode,
+ influence,
+ &blended_snapshot);
- if (blend_mode == NLASTRIP_MODE_COMBINE) {
- /* Quaternion combine handles all sub-channels as a unit. */
- if (lower_nec->mix_mode == NEC_MIX_QUATERNION) {
- if (r_force_all == NULL) {
- return false;
- }
+ /** Write results into \a values. */
+ bool successful_remap = true;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE) {
+ if (r_force_all != NULL) {
*r_force_all = true;
-
- if (!nla_combine_quaternion_get_inverted_strip_values(
- lower_values, values, influence, values)) {
- return false;
- }
+ index = -1;
}
else {
- float *base_values = lower_nec->base_snapshot.values;
-
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_combine_get_inverted_strip_value(lower_nec->mix_mode,
- base_values[i],
- lower_values[i],
- values[i],
- influence,
- &values[i])) {
- return false;
- }
- }
- }
+ successful_remap = false;
}
}
- else {
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_blend_get_inverted_strip_value(
- blend_mode, lower_values[i], values[i], influence, &values[i])) {
- return false;
- }
- }
+
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
+ successful_remap = false;
}
+
+ values[i] = blended_necs->values[i];
}
- return true;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+
+ return successful_remap;
}
/**
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 1075a46e72b..bcfd34ab42f 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version)
{
static char version_str[5];
BLI_assert(version < 1000);
- BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
+ BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
return version_str;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index da8a3b49f3c..4ea71922df5 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
static int rebuild_pose_bone(
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
{
- bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */
+ bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */
pchan->bone = bone;
pchan->parent = parchan;
@@ -2562,7 +2562,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
/* and a check for garbage */
BKE_pose_channels_clear_with_null_bone(pose, do_id_user);
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
/* Find the custom B-Bone handles. */
@@ -2881,7 +2881,8 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
NULL;
if (bb_custom) {
float mat[4][4], smat[4][4];
- scale_m4_fl(smat, PCHAN_CUSTOM_DRAW_SIZE(pchan));
+ scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
+ rescale_m4(smat, pchan->custom_scale_xyz);
mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat);
BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index ac582fc30e7..62833e10438 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -44,196 +44,11 @@ using blender::float3;
using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
-using blender::bke::ReadAttributePtr;
-using blender::bke::WriteAttributePtr;
using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
namespace blender::bke {
-/* -------------------------------------------------------------------- */
-/** \name Attribute Accessor implementations
- * \{ */
-
-ReadAttribute::~ReadAttribute()
-{
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-fn::GSpan ReadAttribute::get_span() const
-{
- if (size_ == 0) {
- return fn::GSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- std::lock_guard lock{span_mutex_};
- if (array_buffer_ == nullptr) {
- this->initialize_span();
- }
- }
- return fn::GSpan(cpp_type_, array_buffer_, size_);
-}
-
-void ReadAttribute::initialize_span() const
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- for (const int i : IndexRange(size_)) {
- this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-WriteAttribute::~WriteAttribute()
-{
- if (array_should_be_applied_) {
- CLOG_ERROR(&LOG, "Forgot to call apply_span.");
- }
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-/**
- * Get a mutable span that can be modified. When all modifications to the attribute are done,
- * #apply_span should be called. */
-fn::GMutableSpan WriteAttribute::get_span()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(false);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-fn::GMutableSpan WriteAttribute::get_span_for_write_only()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(true);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-void WriteAttribute::initialize_span(const bool write_only)
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- if (write_only) {
- /* This does nothing for trivial types, but is necessary for general correctness. */
- cpp_type_.construct_default_n(array_buffer_, size_);
- }
- else {
- for (const int i : IndexRange(size_)) {
- this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
- }
-}
-
-void WriteAttribute::apply_span()
-{
- this->apply_span_if_necessary();
- array_should_be_applied_ = false;
-}
-
-void WriteAttribute::apply_span_if_necessary()
-{
- /* Only works when the span has been initialized beforehand. */
- BLI_assert(array_buffer_ != nullptr);
-
- const int element_size = cpp_type_.size();
- for (const int i : IndexRange(size_)) {
- this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-/* This is used by the #OutputAttributePtr class. */
-class TemporaryWriteAttribute final : public WriteAttribute {
- public:
- GMutableSpan data;
- GeometryComponent &component;
- std::string final_name;
-
- TemporaryWriteAttribute(AttributeDomain domain,
- GMutableSpan data,
- GeometryComponent &component,
- std::string final_name)
- : WriteAttribute(domain, data.type(), data.size()),
- data(data),
- component(component),
- final_name(std::move(final_name))
- {
- }
-
- ~TemporaryWriteAttribute() override
- {
- if (data.data() != nullptr) {
- cpp_type_.destruct_n(data.data(), data.size());
- MEM_freeN(data.data());
- }
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- data.type().copy_to_uninitialized(data[index], r_value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data.type().copy_to_initialized(value, data[index]);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-class ConvertedReadAttribute final : public ReadAttribute {
- private:
- const CPPType &from_type_;
- const CPPType &to_type_;
- ReadAttributePtr base_attribute_;
- void (*convert_)(const void *src, void *dst);
-
- public:
- ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
- : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
- from_type_(base_attribute->cpp_type()),
- to_type_(to_type),
- base_attribute_(std::move(base_attribute))
- {
- const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type)
- ->convert_single_to_uninitialized;
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
- base_attribute_->get(index, buffer);
- convert_(buffer, r_value);
- }
-};
-
-/** \} */
-
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
{
switch (type) {
@@ -329,10 +144,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
static int attribute_domain_priority(const AttributeDomain domain)
{
switch (domain) {
-#if 0
case ATTR_DOMAIN_CURVE:
return 0;
-#endif
case ATTR_DOMAIN_FACE:
return 1;
case ATTR_DOMAIN_EDGE:
@@ -368,7 +181,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
return highest_priority_domain;
}
-ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
+void OutputAttribute::save()
+{
+ save_has_been_called_ = true;
+ if (optional_span_varray_.has_value()) {
+ optional_span_varray_->save();
+ }
+ if (save_) {
+ save_(*this);
+ }
+}
+
+OutputAttribute::~OutputAttribute()
+{
+ if (!save_has_been_called_) {
+ if (varray_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+}
+
+GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
const GeometryComponent &component) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -384,7 +217,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
-WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
+GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@@ -430,7 +263,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
return delete_success;
}
-bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const
+static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
+bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
+ const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
@@ -443,10 +312,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co
/* Exists already. */
return false;
}
+
const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_add_layer(
- custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
- const bool success = data != nullptr;
+ const bool success = add_custom_data_layer_from_attribute_init(
+ *custom_data, stored_type_, domain_size, initializer);
if (success) {
custom_data_access_.update_custom_data_pointers(component);
}
@@ -463,7 +332,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
return data != nullptr;
}
-ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
+ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -496,7 +365,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
return {};
}
-WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
+WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -548,10 +417,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
return false;
}
+static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
+ CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
@@ -569,10 +480,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
}
}
const int domain_size = component.attribute_domain_size(domain_);
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ add_named_custom_data_layer_from_attribute_init(
+ attribute_name, *custom_data, data_type, domain_size, initializer);
return true;
}
@@ -595,7 +504,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
return true;
}
-ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
+ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -606,14 +515,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(layer.data, domain_size);
+ return {as_read_attribute_(layer.data, domain_size), domain_};
}
}
}
return {};
}
-WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
+WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -630,7 +539,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
if (data_old != data_new) {
custom_data_access_.update_custom_data_pointers(component);
}
- return as_write_attribute_(layer.data, domain_size);
+ return {as_write_attribute_(layer.data, domain_size), domain_};
}
}
}
@@ -682,6 +591,105 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
+CustomDataAttributes::CustomDataAttributes()
+{
+ CustomData_reset(&data);
+ size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+ CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+ size_ = other.size_;
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+ size_ = other.size_;
+ data = other.data;
+ CustomData_reset(&other.data);
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+ BLI_assert(size_ != 0);
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+ BLI_assert(size_ != 0);
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GMutableSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+ const CustomDataType data_type,
+ void *buffer)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+ bool result = false;
+ for (const int i : IndexRange(data.totlayer)) {
+ const CustomDataLayer &layer = data.layers[i];
+ if (layer.name == name) {
+ CustomData_free_layer(&data, layer.type, size_, i);
+ result = true;
+ }
+ }
+ return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+ size_ = size;
+ CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
/* -------------------------------------------------------------------- */
@@ -708,7 +716,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
return 0;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ return providers->builtin_attribute_providers().contains_as(attribute_name);
+}
+
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name) const
{
using namespace blender::bke;
@@ -719,11 +737,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_read(*this);
+ return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
+ ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -731,16 +749,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
return {};
}
-ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
- ReadAttributePtr attribute, const AttributeDomain new_domain) const
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (attribute && attribute->domain() == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
return {};
}
-WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
+blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
+ const StringRef attribute_name)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
@@ -750,11 +771,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_write(*this);
+ return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
+ WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -784,7 +805,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type)
+ const CustomDataType data_type,
+ const AttributeInit &initializer)
{
using namespace blender::bke;
if (attribute_name.is_empty()) {
@@ -803,17 +825,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
if (builtin_provider->data_type() != data_type) {
return false;
}
- return builtin_provider->try_create(*this);
+ return builtin_provider->try_create(*this, initializer);
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
+ if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
return true;
}
}
return false;
}
+bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer)
+{
+ using namespace blender::bke;
+ if (attribute_name.is_empty()) {
+ return false;
+ }
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider == nullptr) {
+ return false;
+ }
+ return builtin_provider->try_create(*this, initializer);
+}
+
Set<std::string> GeometryComponent::attribute_names() const
{
Set<std::string> attributes;
@@ -867,264 +908,283 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (attribute) {
return true;
}
return false;
}
-static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
- const blender::fn::CPPType &to_type)
+std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
+ const StringRef attribute_name) const
{
- const blender::fn::CPPType &from_type = attribute->cpp_type();
- if (from_type == to_type) {
- return attribute;
- }
+ std::optional<AttributeMetaData> result{std::nullopt};
+ this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (attribute_name == name) {
+ result = meta_data;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
+ std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
+{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
- if (!conversions.is_convertible(from_type, to_type)) {
- return {};
- }
-
- return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
+ return conversions.try_convert(std::move(varray), to_type);
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
+ std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
+ if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
+ varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
+ if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
- if (attribute->cpp_type() != *cpp_type) {
- attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
- if (!attribute) {
+ if (varray->type() != *cpp_type) {
+ varray = try_adapt_data_type(std::move(varray), *cpp_type);
+ if (!varray) {
return {};
}
}
- return attribute;
+ return varray;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain) const
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
+ const StringRef attribute_name, const AttributeDomain domain) const
{
if (!this->attribute_domain_supported(domain)) {
return {};
}
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
- return {};
- }
+ if (attribute.domain != domain) {
+ return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
}
- return attribute;
+ return std::move(attribute.varray);
}
-ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
- if (attribute) {
- return attribute;
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
}
- return this->attribute_get_constant_for_read(domain, data_type, default_value);
-}
-
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
- const AttributeDomain domain, const CustomDataType data_type, const void *value) const
-{
- BLI_assert(this->attribute_domain_supported(domain));
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
- if (value == nullptr) {
- value = cpp_type->default_value();
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(type != nullptr);
+ if (attribute.varray->type() == *type) {
+ return attribute;
}
- const int domain_size = this->attribute_domain_size(domain);
- return std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *cpp_type, value);
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted(
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
+ const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const
+ const CustomDataType data_type,
+ const void *default_value) const
{
- BLI_assert(this->attribute_domain_supported(domain));
- if (value == nullptr || in_data_type == out_data_type) {
- return this->attribute_get_constant_for_read(domain, out_data_type, value);
+ std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+ if (varray) {
+ return varray;
+ }
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ if (default_value == nullptr) {
+ default_value = type->default_value();
}
-
- const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- in_data_type);
- const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- out_data_type);
- BLI_assert(in_cpp_type != nullptr);
- BLI_assert(out_cpp_type != nullptr);
-
- const blender::nodes::DataTypeConversions &conversions =
- blender::nodes::get_implicit_type_conversions();
- BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
-
- void *out_value = alloca(out_cpp_type->size());
- conversions.convert_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value);
-
const int domain_size = this->attribute_domain_size(domain);
- blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *out_cpp_type, out_value);
-
- out_cpp_type->destruct(out_value);
- return attribute;
+ return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
-OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
-
- WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
+class GVMutableAttribute_For_OutputAttribute
+ : public blender::fn::GVMutableArray_For_GMutableSpan {
+ public:
+ GeometryComponent *component;
+ std::string final_name;
- /* If the attribute doesn't exist, make a new one with the correct type. */
- if (!attribute) {
- this->attribute_try_create(attribute_name, domain, data_type);
- attribute = this->attribute_try_get_for_write(attribute_name);
- if (attribute && default_value != nullptr) {
- void *data = attribute->get_span_for_write_only().data();
- cpp_type->fill_initialized(default_value, data, attribute->size());
- attribute->apply_span();
- }
- return OutputAttributePtr(std::move(attribute));
+ GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
+ GeometryComponent &component,
+ std::string final_name)
+ : blender::fn::GVMutableArray_For_GMutableSpan(data),
+ component(&component),
+ final_name(std::move(final_name))
+ {
}
- /* If an existing attribute has a matching domain and type, just use that. */
- if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
- return OutputAttributePtr(std::move(attribute));
+ ~GVMutableAttribute_For_OutputAttribute() override
+ {
+ type_->destruct_n(data_, size_);
+ MEM_freeN(data_);
}
+};
- /* Otherwise create a temporary buffer to use before saving the new attribute. */
- return OutputAttributePtr(*this, domain, attribute_name, data_type);
-}
-
-/* Construct from an attribute that already exists in the geometry component. */
-OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute)
- : attribute_(std::move(attribute))
+static void save_output_attribute(blender::bke::OutputAttribute &output_attribute)
{
-}
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
-/* Construct a temporary attribute that has to replace an existing one later on. */
-OutputAttributePtr::OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string final_name,
- CustomDataType data_type)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
+ GVMutableAttribute_For_OutputAttribute &varray =
+ dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
- const int domain_size = component.attribute_domain_size(domain);
- void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__);
- GMutableSpan new_span{*cpp_type, buffer, domain_size};
+ GeometryComponent &component = *varray.component;
+ const StringRefNull name = varray.final_name;
+ const AttributeDomain domain = output_attribute.domain();
+ const CustomDataType data_type = output_attribute.custom_data_type();
+ const CPPType &cpp_type = output_attribute.cpp_type();
- /* Copy converted values from conflicting attribute, in case the value is read.
- * TODO: An optimization could be to not do this, when the caller says that the attribute will
- * only be written. */
- ReadAttributePtr src_attribute = component.attribute_get_for_read(
- final_name, domain, data_type, nullptr);
- for (const int i : blender::IndexRange(domain_size)) {
- src_attribute->get(i, new_span[i]);
+ component.attribute_try_delete(name);
+ if (!component.attribute_try_create(
+ varray.final_name, domain, data_type, AttributeInitDefault())) {
+ CLOG_WARN(&LOG,
+ "Could not create the '%s' attribute with type '%s'.",
+ name.c_str(),
+ cpp_type.name().c_str());
+ return;
+ }
+ WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ for (const int i : IndexRange(varray.size())) {
+ varray.get(i, buffer);
+ write_attribute.varray->set_by_relocate(i, buffer);
}
-
- attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
- domain, new_span, component, std::move(final_name));
}
-/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
- * might delete another attribute with the same name. */
-void OutputAttributePtr::save()
+static blender::bke::OutputAttribute create_output_attribute(
+ GeometryComponent &component,
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const bool ignore_old_values,
+ const void *default_value)
{
- if (!attribute_) {
- CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore.");
- return;
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ if (attribute_name.is_empty()) {
+ return {};
}
- blender::bke::TemporaryWriteAttribute *attribute =
- dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get());
+ const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- if (attribute == nullptr) {
- /* The attribute is saved already. */
- attribute_.reset();
- return;
+ if (component.attribute_is_builtin(attribute_name)) {
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const int64_t domain_size = component.attribute_domain_size(domain);
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create_builtin(attribute_name,
+ AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
+ }
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Builtin attribute does not exist and can't be created. */
+ return {};
+ }
+ }
+ if (attribute.domain != domain) {
+ /* Builtin attribute is on different domain. */
+ return {};
+ }
+ GVMutableArrayPtr varray = std::move(attribute.varray);
+ if (varray->type() == *cpp_type) {
+ /* Builtin attribute matches exactly. */
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ }
+ /* Builtin attribute is on the same domain but has a different data type. */
+ varray = conversions.try_convert(std::move(varray), *cpp_type);
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
}
- StringRefNull name = attribute->final_name;
- const blender::fn::CPPType &cpp_type = attribute->cpp_type();
+ const int domain_size = component.attribute_domain_size(domain);
- /* Delete an existing attribute with the same name if necessary. */
- attribute->component.attribute_try_delete(name);
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create(
+ attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
+ }
- if (!attribute->component.attribute_try_create(
- name, attribute_->domain(), attribute_->custom_data_type())) {
- /* Cannot create the target attribute for some reason. */
- CLOG_WARN(&LOG,
- "Creating the '%s' attribute with type '%s' failed.",
- name.c_str(),
- cpp_type.name().c_str());
- attribute_.reset();
- return;
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Can't create the attribute. */
+ return {};
+ }
+ }
+ if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
+ /* Existing generic attribute matches exactly. */
+ return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
- WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name);
-
- GMutableSpan temp_span = attribute->data;
- GMutableSpan new_span = new_attribute->get_span_for_write_only();
- BLI_assert(temp_span.size() == new_span.size());
-
- /* Currently we copy over the attribute. In the future we want to reuse the buffer. */
- cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size());
- new_attribute->apply_span();
+ /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
+ * attribute after processing is done. */
+ void *data = MEM_mallocN_aligned(
+ cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ if (ignore_old_values) {
+ /* This does nothing for trivially constructible types, but is necessary for correctness. */
+ cpp_type->construct_default_n(data, domain);
+ }
+ else {
+ /* Fill the temporary array with values from the existing attribute. */
+ GVArrayPtr old_varray = component.attribute_get_for_read(
+ attribute_name, domain, data_type, default_value);
+ old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
+ }
+ GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
+ GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
- attribute_.reset();
+ return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
-OutputAttributePtr::~OutputAttributePtr()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value)
{
- if (attribute_) {
- CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save.");
- }
+ return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
}
-/* Utility function to call #apply_span and #save in the right order. */
-void OutputAttributePtr::apply_span_and_save()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
- BLI_assert(attribute_);
- attribute_->apply_span();
- this->save();
+ return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
-
-/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 806d10e9e89..b3a795faa30 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -24,166 +24,8 @@
namespace blender::bke {
-class ConstantReadAttribute final : public ReadAttribute {
- private:
- void *value_;
-
- public:
- ConstantReadAttribute(AttributeDomain domain,
- const int64_t size,
- const CPPType &type,
- const void *value)
- : ReadAttribute(domain, type, size)
- {
- value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
- type.copy_to_uninitialized(value, value_);
- }
-
- ~ConstantReadAttribute() override
- {
- this->cpp_type_.destruct(value_);
- MEM_freeN(value_);
- }
-
- void get_internal(const int64_t UNUSED(index), void *r_value) const override
- {
- this->cpp_type_.copy_to_uninitialized(value_, r_value);
- }
-
- void initialize_span() const override
- {
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
- }
-};
-
-template<typename T> class ArrayReadAttribute final : public ReadAttribute {
- private:
- Span<T> data_;
-
- public:
- ArrayReadAttribute(AttributeDomain domain, Span<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
- private:
- Array<T> data_;
-
- public:
- OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-class DerivedArrayReadAttribute final : public ReadAttribute {
- private:
- Span<StructT> data_;
-
- public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-};
-
-template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<T> data_;
-
- public:
- ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
- : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data_[index] = *reinterpret_cast<const T *>(value);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data_.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &)>
-class DerivedArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<StructT> data_;
-
- public:
- DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- StructT &struct_value = data_[index];
- const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- SetFunc(struct_value, typed_value);
- }
-};
+using fn::GVArrayPtr;
+using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
{
}
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
StringRefNull name() const
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
*/
class DynamicAttributesProvider {
public:
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const = 0;
+ virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type)) const
+ const CustomDataType UNUSED(data_type),
+ const AttributeInit &UNUSED(initializer)) const
{
/* Some providers should not create new attributes. */
return false;
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const final;
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
template<typename T>
- ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
- const int domain_size) const
+ ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayReadAttribute<T>>(
- domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVArray_For_Span<T>>(
+ Span(static_cast<const T *>(layer.data), domain_size)),
+ domain_};
}
template<typename T>
- WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayWriteAttribute<T>>(
- domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan(static_cast<T *>(layer.data), domain_size)),
+ domain_};
}
bool type_is_supported(CustomDataType data_type) const
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* the #MVert struct, but is exposed as float3 attribute.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
- bool try_create(GeometryComponent &component) const final;
+ bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
};
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e8879cdda8f..e84b485c466 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -132,7 +132,7 @@ static void blender_version_init(void)
BLI_snprintf(blender_version_string,
ARRAY_SIZE(blender_version_string),
- "%d.%02d.%d%s",
+ "%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index a61e7a2d1d8..54fd3f55c31 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -399,7 +399,8 @@ static void setup_app_data(bContext *C,
BKE_lib_override_library_main_resync(
bmain,
curscene,
- bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
+ bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene),
+ reports);
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
BKE_lib_override_library_main_operations_create(bmain, true);
}
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index ef567044282..20c5af0efb6 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothfac = 0.1f;
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 1;
+ brush->gpencil_settings->dilate_pixels = 1;
brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES;
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index bcd2c75be0d..9293a2b449a 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -2840,7 +2840,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
* including rotation order, otherwise this fails. */
pchan = cob->pchan;
- tchan = BKE_pose_channel_verify(&pose, pchan->name);
+ tchan = BKE_pose_channel_ensure(&pose, pchan->name);
tchan->rotmode = pchan->rotmode;
/* evaluate action using workob (it will only set the PoseChannel in question) */
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index cbf7a4483c0..81830f5bb61 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
- const char *operator_poll_msg; /* reason for poll failing */
+
+ /* Operator poll. */
+ /**
+ * Store the reason the poll function fails (static string, not allocated).
+ * For more advanced formatting use `operator_poll_msg_dyn_params`.
+ */
+ const char *operator_poll_msg;
+ /**
+ * Store values to dynamically to create the string (called when a tool-tip is shown).
+ */
+ struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
+ memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
+
return newC;
}
void CTX_free(bContext *C)
{
+ /* This may contain a dynamically allocated message, free. */
+ CTX_wm_operator_poll_msg_clear(C);
+
MEM_freeN(C);
}
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
+void CTX_wm_operator_poll_msg_clear(bContext *C)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->free_fn != NULL) {
+ params->free_fn(C, params->user_data);
+ }
+ params->get_fn = NULL;
+ params->free_fn = NULL;
+ params->user_data = NULL;
+
+ C->wm.operator_poll_msg = NULL;
+}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
C->wm.operator_poll_msg = msg;
}
-const char *CTX_wm_operator_poll_msg_get(bContext *C)
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
+ C->wm.operator_poll_msg_dyn_params = *params;
+}
+
+const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->get_fn != NULL) {
+ char *msg = params->get_fn(C, params->user_data);
+ if (msg != NULL) {
+ *r_free = true;
+ }
+ return msg;
+ }
+
+ *r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
new file mode 100644
index 00000000000..9cafe1124b1
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -0,0 +1,272 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+
+#include "DNA_curve_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Map;
+using blender::Span;
+using blender::StringRefNull;
+
+blender::Span<SplinePtr> CurveEval::splines() const
+{
+ return splines_;
+}
+
+blender::MutableSpan<SplinePtr> CurveEval::splines()
+{
+ return splines_;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
+void CurveEval::add_spline(SplinePtr spline)
+{
+ splines_.append(std::move(spline));
+}
+
+void CurveEval::remove_splines(blender::IndexMask mask)
+{
+ for (int i = mask.size() - 1; i >= 0; i--) {
+ splines_.remove_and_reorder(mask.indices()[i]);
+ }
+}
+
+void CurveEval::translate(const float3 &translation)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->translate(translation);
+ spline->mark_cache_invalid();
+ }
+}
+
+void CurveEval::transform(const float4x4 &matrix)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->transform(matrix);
+ }
+}
+
+void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ for (const SplinePtr &spline : this->splines()) {
+ spline->bounds_min_max(min, max, use_evaluated);
+ }
+}
+
+/**
+ * Return the start indices for each of the curve spline's evaluated points, as if they were part
+ * of a flattened array. This can be used to facilitate parallelism by avoiding the need to
+ * accumulate an offset while doing more complex calculations.
+ *
+ * \note The result array is one longer than the spline count; the last element is the total size.
+ */
+blender::Array<int> CurveEval::control_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+/**
+ * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
+ */
+blender::Array<int> CurveEval::evaluated_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->evaluated_points_size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
+{
+ switch (dna_handle_type) {
+ case HD_FREE:
+ return BezierSpline::HandleType::Free;
+ case HD_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case HD_VECT:
+ return BezierSpline::HandleType::Vector;
+ case HD_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case HD_AUTO_ANIM:
+ return BezierSpline::HandleType::Auto;
+ case HD_ALIGN_DOUBLESIDE:
+ return BezierSpline::HandleType::Align;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
+{
+ switch (twist_mode) {
+ case CU_TWIST_Z_UP:
+ return Spline::NormalCalculationMode::ZUp;
+ case CU_TWIST_MINIMUM:
+ return Spline::NormalCalculationMode::Minimum;
+ case CU_TWIST_TANGENT:
+ return Spline::NormalCalculationMode::Tangent;
+ }
+ BLI_assert_unreachable();
+ return Spline::NormalCalculationMode::Minimum;
+}
+
+static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
+{
+ switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
+ case CU_NURB_ENDPOINT:
+ return NURBSpline::KnotsMode::EndPoint;
+ case CU_NURB_BEZIER:
+ return NURBSpline::KnotsMode::Bezier;
+ default:
+ return NURBSpline::KnotsMode::Normal;
+ }
+
+ BLI_assert_unreachable();
+ return NURBSpline::KnotsMode::Normal;
+}
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+
+ const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
+
+ /* TODO: Optimize by reserving the correct points size. */
+ LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
+ switch (nurb->type) {
+ case CU_BEZIER: {
+ std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
+ spline->add_point(bezt.vec[1],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
+ bezt.vec[0],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
+ bezt.vec[2],
+ bezt.radius,
+ bezt.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_NURBS: {
+ std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+ spline->set_order(nurb->orderu);
+ spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_POLY: {
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ }
+
+ /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+ curve->attributes.reallocate(curve->splines().size());
+
+ /* Note: Normal mode is stored separately in each spline to facilitate combining splines
+ * from multiple curve objects, where the value may be different. */
+ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
+ dna_curve.twist_mode);
+ for (SplinePtr &spline : curve->splines()) {
+ spline->normal_mode = normal_mode;
+ }
+
+ return curve;
+}
+
+/**
+ * Check the invariants that curve control point attributes should always uphold, necessary
+ * because attributes are stored on splines rather than in a flat array on the curve:
+ * - The same set of attributes exists on every spline.
+ * - Attributes with the same name have the same type on every spline.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+ if (splines_.size() == 0) {
+ return;
+ }
+ const int layer_len = splines_.first()->attributes.data.totlayer;
+ Map<StringRefNull, AttributeMetaData> map;
+ for (const SplinePtr &spline : splines_) {
+ BLI_assert(spline->attributes.data.totlayer == layer_len);
+ spline->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ map.add_or_modify(
+ name,
+ [&](AttributeMetaData *map_data) {
+ /* All unique attribute names should be added on the first spline. */
+ BLI_assert(spline == splines_.first());
+ *map_data = meta_data;
+ },
+ [&](AttributeMetaData *map_data) {
+ /* Attributes on different splines should all have the same type. */
+ BLI_assert(meta_data == *map_data);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index 752e0d4dfcf..00cdc7b3031 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
{
const float total_length = BKE_curveprofile_total_length(profile);
const float segment_length = total_length / n_segments;
- float length_travelled = 0.0f;
float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
float distance_to_previous_table_point = 0.0f;
int i_table = 0;
@@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
for (int i = 1; i < n_segments; i++) {
/* Travel over all of the points that fit inside this segment. */
while (distance_to_next_table_point < segment_left) {
- length_travelled += distance_to_next_table_point;
segment_left -= distance_to_next_table_point;
i_table++;
distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
@@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
/* We sampled in between this table point and the next, so the next travel step is smaller. */
distance_to_next_table_point -= segment_left;
distance_to_previous_table_point += segment_left;
- length_travelled += segment_left;
segment_left = segment_length;
}
}
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc
index ad8939fa5d1..20534ef933b 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -21,9 +21,9 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -82,7 +82,7 @@ void BKE_displist_free(ListBase *lb)
{
DispList *dl;
- while ((dl = BLI_pophead(lb))) {
+ while ((dl = (DispList *)BLI_pophead(lb))) {
BKE_displist_elem_free(dl);
}
}
@@ -95,7 +95,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type)
}
}
- DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp");
dl->type = type;
BLI_addtail(lb, dl);
@@ -110,7 +110,7 @@ DispList *BKE_displist_find(ListBase *lb, int type)
}
}
- return NULL;
+ return nullptr;
}
bool BKE_displist_has_faces(const ListBase *lb)
@@ -129,11 +129,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
BKE_displist_free(lbn);
LISTBASE_FOREACH (const DispList *, dl, lb) {
- DispList *dln = MEM_dupallocN(dl);
+ DispList *dln = (DispList *)MEM_dupallocN(dl);
BLI_addtail(lbn, dln);
- dln->verts = MEM_dupallocN(dl->verts);
- dln->nors = MEM_dupallocN(dl->nors);
- dln->index = MEM_dupallocN(dl->index);
+ dln->verts = (float *)MEM_dupallocN(dl->verts);
+ dln->nors = (float *)MEM_dupallocN(dl->nors);
+ dln->index = (int *)MEM_dupallocN(dl->index);
}
}
@@ -146,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb)
LISTBASE_FOREACH (DispList *, dl, lb) {
if (dl->type == DL_INDEX3) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->flag & DL_BACK_CURVE) {
dl->nors[2] = -1.0f;
@@ -158,8 +158,8 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
else if (dl->type == DL_SURF) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
vdata = dl->verts;
ndata = dl->nors;
@@ -338,9 +338,9 @@ static void curve_to_displist(const Curve *cu,
* and resolution > 1. */
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
- dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = samples_len;
@@ -393,8 +393,8 @@ static void curve_to_displist(const Curve *cu,
}
else if (nu->type == CU_NURBS) {
const int len = (resolution * SEGMENTSU(nu));
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -402,12 +402,12 @@ static void curve_to_displist(const Curve *cu,
dl->charidx = nu->charidx;
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
- BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
}
else if (nu->type == CU_POLY) {
const int len = nu->pntsu;
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -435,7 +435,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const float normal_proj[3],
const bool flip_normal)
{
- if (dispbase == NULL) {
+ if (dispbase == nullptr) {
return;
}
if (BLI_listbase_is_empty(dispbase)) {
@@ -471,14 +471,14 @@ void BKE_displist_fill(const ListBase *dispbase,
sf_ctx.poly_nr++;
/* Make verts and edges. */
- ScanFillVert *sf_vert = NULL;
- ScanFillVert *sf_vert_last = NULL;
- ScanFillVert *sf_vert_new = NULL;
+ ScanFillVert *sf_vert = nullptr;
+ ScanFillVert *sf_vert_last = nullptr;
+ ScanFillVert *sf_vert_new = nullptr;
for (int i = 0; i < dl->nr; i++) {
sf_vert_last = sf_vert;
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
totvert++;
- if (sf_vert_last == NULL) {
+ if (sf_vert_last == nullptr) {
sf_vert_new = sf_vert;
}
else {
@@ -486,7 +486,7 @@ void BKE_displist_fill(const ListBase *dispbase,
}
}
- if (sf_vert != NULL && sf_vert_new != NULL) {
+ if (sf_vert != nullptr && sf_vert_new != nullptr) {
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
}
}
@@ -503,7 +503,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
if (totvert != 0 && triangles_len != 0) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
dlnew->type = DL_INDEX3;
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
@@ -511,8 +511,8 @@ void BKE_displist_fill(const ListBase *dispbase,
dlnew->nr = totvert;
dlnew->parts = triangles_len;
- dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
+ dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
/* vert data */
int i;
@@ -551,16 +551,16 @@ void BKE_displist_fill(const ListBase *dispbase,
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
- ListBase front = {NULL, NULL};
- ListBase back = {NULL, NULL};
+ ListBase front = {nullptr, nullptr};
+ ListBase back = {nullptr, nullptr};
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
if (dl->type == DL_SURF) {
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&front, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -577,9 +577,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
}
}
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&back, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -634,16 +634,16 @@ static float displist_calc_taper(Depsgraph *depsgraph,
Object *taperobj,
float fac)
{
- DispList *dl;
-
- if (taperobj == NULL || taperobj->type != OB_CURVE) {
+ if (taperobj == nullptr || taperobj->type != OB_CURVE) {
return 1.0;
}
- dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
- if (dl == NULL) {
+ DispList *dl = taperobj->runtime.curve_cache ?
+ (DispList *)taperobj->runtime.curve_cache->disp.first :
+ nullptr;
+ if (dl == nullptr) {
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
- dl = taperobj->runtime.curve_cache->disp.first;
+ dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
}
if (dl) {
float minx, dx, *fp;
@@ -693,7 +693,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
BKE_displist_free(&(ob->runtime.curve_cache->disp));
}
else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for MBall");
}
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
@@ -738,9 +739,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene,
required_mode |= eModifierMode_Editmode;
}
- pretessellatePoint = NULL;
+ pretessellatePoint = nullptr;
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -777,22 +778,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
int numElems = 0, numVerts = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- ModifierApplyFlag apply_flag = 0;
- float(*deformedVerts)[3] = NULL;
- float *keyVerts = NULL;
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
+ float(*deformedVerts)[3] = nullptr;
+ float *keyVerts = nullptr;
int required_mode;
bool modified = false;
BKE_modifiers_clear_errors(ob);
if (editmode) {
- apply_flag |= MOD_APPLY_USECACHE;
+ apply_flag = MOD_APPLY_USECACHE;
}
if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
+ apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -823,7 +824,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
if (pretessellatePoint) {
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -836,7 +837,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
- mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
+ mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
modified = true;
if (md == pretessellatePoint) {
@@ -869,7 +870,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
}
- allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
+ allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
@@ -903,16 +904,16 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
- const Curve *cu = ob->data;
+ const Curve *cu = (const Curve *)ob->data;
int required_mode = 0, totvert = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- Mesh *modified = NULL, *mesh_applied;
- float(*vertCos)[3] = NULL;
+ Mesh *modified = nullptr, *mesh_applied;
+ float(*vertCos)[3] = nullptr;
int useCache = !for_render;
- ModifierApplyFlag apply_flag = 0;
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
+ apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -920,9 +921,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
const ModifierEvalContext mectx_deform = {
- depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
const ModifierEvalContext mectx_apply = {
- depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
@@ -935,22 +936,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
if (r_final && *r_final) {
- BKE_id_free(NULL, *r_final);
+ BKE_id_free(nullptr, *r_final);
}
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
}
/* If we need normals, no choice, have to convert to mesh now. */
- bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
+ bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
/* XXX 2.8 : now that batch cache is stored inside the ob->data
* we need to create a Mesh for each curve that uses modifiers. */
- if (modified == NULL /* && need_normal */) {
- if (vertCos != NULL) {
+ if (modified == nullptr /* && need_normal */) {
+ if (vertCos != nullptr) {
displist_vert_coords_apply(dispbase, vertCos);
}
@@ -976,7 +977,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
- mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
+ mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
}
}
else {
@@ -991,8 +992,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (modified) {
if (vertCos) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
- NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1013,7 +1014,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
/* Vertex coordinates were applied to necessary data, could free it */
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
if (need_normal) {
@@ -1025,7 +1026,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
/* Modifier returned a new derived mesh */
if (modified && modified != mesh_applied) { /* Modifier */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
modified = mesh_applied;
}
@@ -1034,8 +1035,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
if (modified) {
- Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1046,7 +1048,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
displist_vert_coords_apply(dispbase, vertCos);
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
}
@@ -1081,18 +1083,18 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
MEM_SAFE_FREE(modified->mat);
/* Set flag which makes it easier to see what's going on in a debugger. */
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
- modified->mat = MEM_dupallocN(cu->mat);
+ modified->mat = (Material **)MEM_dupallocN(cu->mat);
modified->totcol = cu->totcol;
(*r_final) = modified;
}
else {
- (*r_final) = NULL;
+ (*r_final) = nullptr;
}
}
- else if (modified != NULL) {
+ else if (modified != nullptr) {
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
}
@@ -1103,8 +1105,8 @@ static void displist_surf_indices(DispList *dl)
dl->totindex = 0;
- index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
- "index array nurbs");
+ index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
+ "index array nurbs");
for (a = 0; a < dl->parts; a++) {
@@ -1136,8 +1138,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
const bool for_render,
const bool for_orco)
{
- ListBase nubase = {NULL, NULL};
- Curve *cu = ob->data;
+ ListBase nubase = {nullptr, nullptr};
+ Curve *cu = (Curve *)ob->data;
DispList *dl;
float *data;
int len;
@@ -1174,8 +1176,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (nu->pntsv == 1) {
len = SEGMENTSU(nu) * resolu;
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
@@ -1195,13 +1197,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
dl->type = DL_SEGM;
}
- BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
}
else {
len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
@@ -1258,7 +1260,7 @@ static void rotateBevelPiece(const Curve *cu,
vec[1] = fp[2];
vec[2] = 0.0;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
copy_qt_qt(quat, bevp->quat);
}
@@ -1276,7 +1278,7 @@ static void rotateBevelPiece(const Curve *cu,
else {
float sina, cosa;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
sina = bevp->sina;
cosa = bevp->cosa;
@@ -1307,8 +1309,8 @@ static void fillBevelCap(const Nurb *nu,
{
DispList *dl;
- dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
- dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
dl->type = DL_POLY;
@@ -1469,7 +1471,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
const bool for_orco,
Mesh **r_final)
{
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
/* we do allow duplis... this is only displist on curve level */
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
@@ -1481,7 +1483,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
ListBase dlbev;
- ListBase nubase = {NULL, NULL};
+ ListBase nubase = {nullptr, nullptr};
bool force_mesh_conversion = false;
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
@@ -1494,7 +1496,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
- ob->runtime.curve_cache->anim_path_accum_length = NULL;
+ ob->runtime.curve_cache->anim_path_accum_length = nullptr;
}
if (ob->type == OB_FONT) {
@@ -1520,8 +1522,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else {
const float widfac = cu->width - 1.0f;
- BevList *bl = ob->runtime.curve_cache->bev.first;
- Nurb *nu = nubase.first;
+ BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
+ Nurb *nu = (Nurb *)nubase.first;
for (; bl && nu; bl = bl->next, nu = nu->next) {
float *data;
@@ -1532,8 +1534,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
/* exception handling; curve without bevel or extrude, with width correction */
if (BLI_listbase_is_empty(&dlbev)) {
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
- dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
BLI_addtail(dispbase, dl);
if (bl->poly != -1) {
@@ -1565,8 +1567,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
}
else {
- ListBase bottom_capbase = {NULL, NULL};
- ListBase top_capbase = {NULL, NULL};
+ ListBase bottom_capbase = {nullptr, nullptr};
+ ListBase top_capbase = {nullptr, nullptr};
float bottom_no[3] = {0.0f};
float top_no[3] = {0.0f};
float first_blend = 0.0f, last_blend = 0.0f;
@@ -1585,8 +1587,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
/* for each part of the bevel use a separate displblock */
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
- dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
+ dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
BLI_addtail(dispbase, dl);
dl->type = DL_SURF;
@@ -1616,7 +1618,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
float radius_factor = 1.0;
float *cur_data = data;
- if (cu->taperobj == NULL) {
+ if (cu->taperobj == nullptr) {
radius_factor = bevp->radius;
}
else {
@@ -1666,7 +1668,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
}
else {
- rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
+ rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
}
if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
@@ -1737,15 +1739,16 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
BKE_object_free_derived_caches(ob);
if (!ob->runtime.curve_cache) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for curve types");
}
dispbase = &(ob->runtime.curve_cache->disp);
- Mesh *mesh_eval = NULL;
+ Mesh *mesh_eval = nullptr;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
- if (mesh_eval != NULL) {
+ if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
}
@@ -1759,8 +1762,9 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
Mesh **r_final,
const bool for_orco)
{
- if (ob->runtime.curve_cache == NULL) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ if (ob->runtime.curve_cache == nullptr) {
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
@@ -1797,8 +1801,8 @@ static void boundbox_displist_object(Object *ob)
*/
/* object's BB is calculated from final displist */
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
}
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
new file mode 100644
index 00000000000..d08681da6ec
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -0,0 +1,1019 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_geometry_set.hh"
+
+#include "attribute_access_intern.hh"
+
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_GSpan;
+using blender::fn::GVMutableArray_For_GMutableSpan;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
+{
+}
+
+CurveComponent::~CurveComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *CurveComponent::copy() const
+{
+ CurveComponent *new_component = new CurveComponent();
+ if (curve_ != nullptr) {
+ new_component->curve_ = new CurveEval(*curve_);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void CurveComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (curve_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ delete curve_;
+ }
+ curve_ = nullptr;
+ }
+}
+
+bool CurveComponent::has_curve() const
+{
+ return curve_ != nullptr;
+}
+
+/* Clear the component and replace it with the new curve. */
+void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ curve_ = curve;
+ ownership_ = ownership;
+}
+
+CurveEval *CurveComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ CurveEval *curve = curve_;
+ curve_ = nullptr;
+ return curve;
+}
+
+const CurveEval *CurveComponent::get_for_read() const
+{
+ return curve_;
+}
+
+CurveEval *CurveComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return curve_;
+}
+
+bool CurveComponent::is_empty() const
+{
+ return curve_ == nullptr;
+}
+
+bool CurveComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void CurveComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access Helper Functions
+ * \{ */
+
+int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ if (curve_ == nullptr) {
+ return 0;
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ int total = 0;
+ for (const SplinePtr &spline : curve_->splines()) {
+ total += spline->size();
+ }
+ return total;
+ }
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return curve_->splines().size();
+ }
+ return 0;
+}
+
+static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ CurveComponent &curve_component = static_cast<CurveComponent &>(component);
+ return curve_component.get_for_write();
+}
+
+static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return curve_component.get_for_read();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Spline Attributes
+ *
+ * Attributes with a value for every spline, stored contiguously or in every spline separately.
+ * \{ */
+
+namespace blender::bke {
+
+class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ BuiltinSplineAttributeProvider(std::string attribute_name,
+ const CustomDataType attribute_type,
+ const WritableEnum writable,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_CURVE,
+ attribute_type,
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_read_attribute_(*curve);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ if (writable_ != Writable) {
+ return {};
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_write_attribute_(*curve);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
+ }
+};
+
+static int get_spline_resolution(const SplinePtr &spline)
+{
+ if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
+ return bezier_spline->resolution();
+ }
+ if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
+ return nurb_spline->resolution();
+ }
+ return 1;
+}
+
+static void set_spline_resolution(SplinePtr &spline, const int resolution)
+{
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
+ bezier_spline->set_resolution(std::max(resolution, 1));
+ }
+ if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
+ nurb_spline->set_resolution(std::max(resolution, 1));
+ }
+}
+
+static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
+ int,
+ get_spline_resolution,
+ set_spline_resolution>>(
+ curve.splines());
+}
+
+static bool get_cyclic_value(const SplinePtr &spline)
+{
+ return spline->is_cyclic();
+}
+
+static void set_cyclic_value(SplinePtr &spline, const bool value)
+{
+ if (spline->is_cyclic() != value) {
+ spline->set_cyclic(value);
+ spline->mark_cache_invalid();
+ }
+}
+
+static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
+ curve.splines());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Control Point Attributes
+ *
+ * Attributes with a value for every control point. Most of the complexity here is due to the fact
+ * that we must provide access to the attribute data as if it was a contiguous array when it is
+ * really stored separately on each spline. That will be inherently rather slow, but these virtual
+ * array implementations try to make it workable in common situations.
+ * \{ */
+
+namespace {
+struct PointIndices {
+ int spline_index;
+ int point_index;
+};
+} // namespace
+static PointIndices lookup_point_indices(Span<int> offsets, const int index)
+{
+ const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
+ offsets.begin() - 1;
+ const int index_in_spline = index - offsets[spline_index];
+ return {spline_index, index_in_spline};
+}
+
+template<typename T>
+static void point_attribute_materialize(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int i : r_span.index_range()) {
+ const int dst_index = mask[i];
+
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ r_span[dst_index] = data[spline_index][index_in_spline];
+ }
+ }
+}
+
+template<typename T>
+static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ T *dst = r_span.data();
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int i : r_span.index_range()) {
+ const int dst_index = mask[i];
+
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ }
+ }
+}
+
+/**
+ * Virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VArray_For_SplinePoints : public VArray<T> {
+ private:
+ const Array<Span<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
+ : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Mutable virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
+ private:
+ Array<MutableSpan<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
+ : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void set_impl(const int64_t index, T value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ data_[indices.spline_index][indices.point_index] = value;
+ }
+
+ void set_all_impl(Span<T> src) final
+ {
+ for (const int spline_index : data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offsets = offsets_[spline_index + 1];
+ data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(
+ {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+};
+
+template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+template<typename T>
+GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+/**
+ * Virtual array implementation specifically for control point positions. This is only needed for
+ * Bezier splines, where adjusting the position also requires adjusting handle positions depending
+ * on handle types. We pay a small price for this when other spline types are mixed with Bezier.
+ *
+ * \note There is no need to check the handle type to avoid changing auto handles, since
+ * retrieving write access to the position data will mark them for recomputation anyway.
+ */
+class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
+ : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return splines_[indices.spline_index]->positions()[indices.point_index];
+ }
+
+ void set_impl(const int64_t index, float3 value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ Spline &spline = *splines_[indices.spline_index];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ const float3 delta = value - bezier_spline->positions()[indices.point_index];
+ bezier_spline->handle_positions_left()[indices.point_index] += delta;
+ bezier_spline->handle_positions_right()[indices.point_index] += delta;
+ bezier_spline->positions()[indices.point_index] = value;
+ }
+ else {
+ spline.positions()[indices.point_index] = value;
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ MutableSpan<float3> positions = bezier_spline->positions();
+ MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left();
+ MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right();
+ for (const int i : IndexRange(next_offset - offset)) {
+ const float3 delta = src[offset + i] - positions[i];
+ handle_positions_left[i] += delta;
+ handle_positions_right[i] += delta;
+ positions[i] = src[offset + i];
+ }
+ }
+ else {
+ spline.positions().copy_from(src.slice(offset, next_offset - offset));
+ }
+ }
+ }
+
+ /** Utility so we can pass positions to the materialize functions above. */
+ Array<Span<float3>> get_position_spans() const
+ {
+ Array<Span<float3>> spans(splines_.size());
+ for (const int i : spans.index_range()) {
+ spans[i] = splines_[i]->positions();
+ }
+ return spans;
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Provider for any builtin control point attribute that doesn't need
+ * special handling like access to other arrays in the spline.
+ */
+template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider {
+ protected:
+ using GetSpan = Span<T> (*)(const Spline &spline);
+ using GetMutableSpan = MutableSpan<T> (*)(Spline &spline);
+ using UpdateOnWrite = void (*)(Spline &spline);
+ const GetSpan get_span_;
+ const GetMutableSpan get_mutable_span_;
+ const UpdateOnWrite update_on_write_;
+
+ public:
+ BuiltinPointAttributeProvider(std::string attribute_name,
+ const WritableEnum writable,
+ const GetSpan get_span,
+ const GetMutableSpan get_mutable_span,
+ const UpdateOnWrite update_on_write)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_POINT,
+ bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ get_span_(get_span),
+ get_mutable_span_(get_mutable_span),
+ update_on_write_(update_on_write)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<Span<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_span_(*splines[i]);
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
+ get_mutable_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<MutableSpan<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_mutable_span_(*splines[i]);
+ if (update_on_write_) {
+ update_on_write_(*splines[i]);
+ }
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
+/**
+ * Special attribute provider for the position attribute. Keeping this separate means we don't
+ * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the
+ * positions is more clear.
+ */
+class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> {
+ public:
+ PositionAttributeProvider()
+ : BuiltinPointAttributeProvider(
+ "position",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); })
+ {
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ bool curve_has_bezier_spline = false;
+ for (SplinePtr &spline : curve->splines()) {
+ if (spline->type() == Spline::Type::Bezier) {
+ curve_has_bezier_spline = true;
+ break;
+ }
+ }
+
+ /* Use the regular position virtual array when there aren't any Bezier splines
+ * to avoid the overhead of checking the spline type for every point. */
+ if (!curve_has_bezier_spline) {
+ return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
+ }
+
+ /* Changing the positions requires recalculation of cached evaluated data in many cases.
+ * This could set more specific flags in the future to avoid unnecessary recomputation. */
+ for (SplinePtr &spline : curve->splines()) {
+ spline->mark_cache_invalid();
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
+ offsets.last(), curve->splines(), std::move(offsets));
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dynamic Control Point Attributes
+ *
+ * The dynamic control point attribute implementation is very similar to the builtin attribute
+ * implementation-- it uses the same virtual array types. In order to work, this code depends on
+ * the fact that all a curve's splines will have the same attributes and they all have the same
+ * type.
+ * \{ */
+
+class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+
+ public:
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ Vector<GSpan> spans; /* GSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ /* This function is almost the same as #try_get_for_read, but without const. */
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<MutableSpan<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ bool layer_freed = false;
+ for (SplinePtr &spline : curve->splines()) {
+ spline->attributes.remove(attribute_name);
+ }
+ return layer_freed;
+ }
+
+ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
+ const CustomDataType data_type,
+ const int total_size)
+ {
+ switch (initializer.type) {
+ case AttributeInit::Type::Default:
+ /* This function shouldn't be called in this case, since there
+ * is no need to copy anything to the new custom data array. */
+ BLI_assert_unreachable();
+ return {};
+ case AttributeInit::Type::VArray:
+ return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
+ case AttributeInit::Type::MoveArray:
+ return std::make_unique<fn::GVArray_For_GSpan>(
+ GSpan(*bke::custom_data_type_to_cpp_type(data_type),
+ static_cast<const AttributeInitMove &>(initializer).data,
+ total_size));
+ }
+ BLI_assert_unreachable();
+ return {};
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+
+ /* First check the one case that allows us to avoid copying the input data. */
+ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+
+ /* Otherwise just create a custom data layer on each of the splines. */
+ for (const int i : splines.index_range()) {
+ if (!splines[i]->attributes.create(attribute_name, data_type)) {
+ /* If attribute creation fails on one of the splines, we cannot leave the custom data
+ * layers in the previous splines around, so delete them before returning. However,
+ * this is not an expected case. */
+ BLI_assert_unreachable();
+ return false;
+ }
+ }
+
+ /* With a default initializer type, we can keep the values at their initial values. */
+ if (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+
+ /* In a debug build, check that all corresponding custom data layers have the same type. */
+ curve->assert_valid_point_attributes();
+
+ /* Use the first spline as a representative for all the others. */
+ splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
+
+ return true;
+ }
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Provider Declaration
+ * \{ */
+
+/**
+ * In this function all the attribute providers for a curve component are created.
+ * Most data in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_curve()
+{
+ static BuiltinSplineAttributeProvider resolution("resolution",
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Writable,
+ make_resolution_read_attribute,
+ make_resolution_write_attribute);
+
+ static BuiltinSplineAttributeProvider cyclic("cyclic",
+ CD_PROP_BOOL,
+ BuiltinAttributeProvider::Writable,
+ make_cyclic_read_attribute,
+ make_cyclic_write_attribute);
+
+ static CustomDataAccessInfo spline_custom_data_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ nullptr};
+
+ static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
+ spline_custom_data_access);
+
+ static PositionAttributeProvider position;
+
+ static BuiltinPointAttributeProvider<float> radius(
+ "radius",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.radii(); },
+ [](Spline &spline) { return spline.radii(); },
+ nullptr);
+
+ static BuiltinPointAttributeProvider<float> tilt(
+ "tilt",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); });
+
+ static DynamicPointAttributeProvider point_custom_data;
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_curve();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index feb30e8575a..3b1b7456162 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -42,72 +42,104 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
- new_component->transforms_ = transforms_;
- new_component->instanced_data_ = instanced_data_;
- new_component->ids_ = ids_;
+ new_component->instance_reference_handles_ = instance_reference_handles_;
+ new_component->instance_transforms_ = instance_transforms_;
+ new_component->instance_ids_ = instance_ids_;
+ new_component->references_ = references_;
return new_component;
}
+void InstancesComponent::reserve(int min_capacity)
+{
+ instance_reference_handles_.reserve(min_capacity);
+ instance_transforms_.reserve(min_capacity);
+ instance_ids_.reserve(min_capacity);
+}
+
+/**
+ * Resize the transform, handles, and ID vectors to the specified capacity.
+ *
+ * \note This function should be used carefully, only when it's guaranteed
+ * that the data will be filled.
+ */
+void InstancesComponent::resize(int capacity)
+{
+ instance_reference_handles_.resize(capacity);
+ instance_transforms_.resize(capacity);
+ instance_ids_.resize(capacity);
+}
+
void InstancesComponent::clear()
{
- instanced_data_.clear();
- transforms_.clear();
- ids_.clear();
+ instance_reference_handles_.clear();
+ instance_transforms_.clear();
+ instance_ids_.clear();
+
+ references_.clear();
}
-void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
+void InstancesComponent::add_instance(const int instance_handle,
+ const float4x4 &transform,
+ const int id)
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_OBJECT;
- data.data.object = object;
- this->add_instance(data, transform, id);
+ BLI_assert(instance_handle >= 0);
+ BLI_assert(instance_handle < references_.size());
+ instance_reference_handles_.append(instance_handle);
+ instance_transforms_.append(transform);
+ instance_ids_.append(id);
}
-void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
+blender::Span<int> InstancesComponent::instance_reference_handles() const
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_COLLECTION;
- data.data.collection = collection;
- this->add_instance(data, transform, id);
+ return instance_reference_handles_;
}
-void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
+blender::MutableSpan<int> InstancesComponent::instance_reference_handles()
{
- instanced_data_.append(data);
- transforms_.append(transform);
- ids_.append(id);
+ return instance_reference_handles_;
}
-Span<InstancedData> InstancesComponent::instanced_data() const
+blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
+{
+ return instance_transforms_;
+}
+blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const
{
- return instanced_data_;
+ return instance_transforms_;
}
-Span<float4x4> InstancesComponent::transforms() const
+blender::MutableSpan<int> InstancesComponent::instance_ids()
{
- return transforms_;
+ return instance_ids_;
+}
+blender::Span<int> InstancesComponent::instance_ids() const
+{
+ return instance_ids_;
}
-Span<int> InstancesComponent::ids() const
+/**
+ * Returns a handle for the given reference.
+ * If the reference exists already, the handle of the existing reference is returned.
+ * Otherwise a new handle is added.
+ */
+int InstancesComponent::add_reference(InstanceReference reference)
{
- return ids_;
+ return references_.index_of_or_add_as(reference);
}
-MutableSpan<float4x4> InstancesComponent::transforms()
+blender::Span<InstanceReference> InstancesComponent::references() const
{
- return transforms_;
+ return references_;
}
int InstancesComponent::instances_amount() const
{
- const int size = instanced_data_.size();
- BLI_assert(transforms_.size() == size);
- return size;
+ return instance_transforms_.size();
}
bool InstancesComponent::is_empty() const
{
- return transforms_.size() == 0;
+ return this->instance_reference_handles_.size() == 0;
}
bool InstancesComponent::owns_direct_data() const
@@ -178,8 +210,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(ids_);
+ if (almost_unique_ids_.size() != instance_ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
}
return almost_unique_ids_;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 5697fb2ccde..2ecd0e6bd85 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -32,7 +32,7 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
-using blender::bke::ReadAttributePtr;
+using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -201,14 +201,14 @@ namespace blender::bke {
template<typename T>
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
- const T value = attribute[loop_index];
+ const T value = old_values[loop_index];
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
@@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
- adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
- r_values[loop_index] = attribute[vertex_index];
+ r_values[loop_index] = old_values[vertex_index];
}
}
-static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
@@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
- adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
- std::move(values));
+ adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
-static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
} // namespace blender::bke
-ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
- const AttributeDomain new_domain) const
+blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
+ blender::fn::GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (!attribute) {
+ if (!varray) {
return {};
}
- if (attribute->size() == 0) {
+ if (varray->size() == 0) {
return {};
}
- const AttributeDomain old_domain = attribute->domain();
- if (old_domain == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
- switch (old_domain) {
+ switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
default:
break;
}
@@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- AttributeDomain Domain>
-static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
- Domain, Span<StructT>((const StructT *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ Span<StructT>((const StructT *)data, domain_size));
}
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &),
- AttributeDomain Domain>
-static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
+ void (*SetFunc)(StructT &, ElemT)>
+static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
- Domain, MutableSpan<StructT>((StructT *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
+ MutableSpan<StructT>((StructT *)data, domain_size));
}
static float3 get_vertex_position(const MVert &vert)
@@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert)
return float3(vert.co);
}
-static void set_vertex_position(MVert &vert, const float3 &position)
+static void set_vertex_position(MVert &vert, float3 position)
{
copy_v3_v3(vert.co, position);
}
@@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly)
return static_cast<int>(mpoly.mat_nr);
}
-static void set_material_index(MPoly &mpoly, const int &index)
+static void set_material_index(MPoly &mpoly, int index)
{
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
}
@@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly)
return mpoly.flag & ME_SMOOTH;
}
-static void set_shade_smooth(MPoly &mpoly, const bool &value)
+static void set_shade_smooth(MPoly &mpoly, bool value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
@@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv)
return float2(uv.uv);
}
-static void set_loop_uv(MLoopUV &uv, const float2 &co)
+static void set_loop_uv(MLoopUV &uv, float2 co)
{
copy_v2_v2(uv.uv, co);
}
@@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col)
return linear_color;
}
-static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
+static void set_loop_color(MLoopCol &col, Color4f linear_color)
{
linearrgb_to_srgb_uchar4(&col.r, linear_color);
}
@@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge)
return edge.crease / 255.0f;
}
-static void set_crease(MEdge &edge, const float &value)
+static void set_crease(MEdge &edge, float value)
{
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
-class VertexWeightWriteAttribute final : public WriteAttribute {
+class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
- : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- get_internal(dverts_, dvert_index_, index, r_value);
+ return get_internal(dverts_, dvert_index_, index);
}
- void set_internal(const int64_t index, const void *value) override
+ void set_impl(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
- weight->weight = *reinterpret_cast<const float *>(value);
+ weight->weight = value;
}
- static void get_internal(const MDeformVert *dverts,
- const int dvert_index,
- const int64_t index,
- void *r_value)
+ static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
{
if (dverts == nullptr) {
- *(float *)r_value = 0.0f;
- return;
+ return 0.0f;
}
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
- *(float *)r_value = weight.weight;
- return;
+ return weight.weight;
}
}
- *(float *)r_value = 0.0f;
+ return 0.0f;
}
};
-class VertexWeightReadAttribute final : public ReadAttribute {
+class VArray_For_VertexWeights final : public VArray<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
- : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
};
@@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
*/
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
@@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh == nullptr || mesh->dvert == nullptr) {
static const float default_value = 0.0f;
- return std::make_unique<ConstantReadAttribute>(
- ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ return {std::make_unique<fn::GVArray_For_SingleValueRef>(
+ CPPType::get<float>(), mesh->totvert, &default_value),
+ ATTR_DOMAIN_POINT};
}
- return std::make_unique<VertexWeightReadAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
@@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {
+ std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
@@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
- return std::make_unique<ArrayReadAttribute<float3>>(
- ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
+ return std::make_unique<fn::GVArray_For_Span<float3>>(
+ Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
- return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
+ return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
- WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
@@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return false;
}
- bool try_create(GeometryComponent &UNUSED(component)) const final
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
@@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
- make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
- make_derived_write_attribute<MVert,
- float3,
- get_vertex_position,
- set_vertex_position,
- ATTR_DOMAIN_POINT>,
+ make_derived_read_attribute<MVert, float3, get_vertex_position>,
+ make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
@@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
- make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
- make_derived_write_attribute<MPoly,
- int,
- get_material_index,
- set_material_index,
- ATTR_DOMAIN_FACE>,
+ make_derived_read_attribute<MPoly, int, get_material_index>,
+ make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
nullptr);
static BuiltinCustomDataLayerProvider shade_smooth(
@@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
- make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
- make_derived_write_attribute<MPoly,
- bool,
- get_shade_smooth,
- set_shade_smooth,
- ATTR_DOMAIN_FACE>,
+ make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
+ make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
nullptr);
static BuiltinCustomDataLayerProvider crease(
@@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
edge_access,
- make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
- make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
+ make_derived_read_attribute<MEdge, float, get_crease>,
+ make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
nullptr);
static NamedLegacyCustomDataProvider uvs(
@@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT2,
CD_MLOOPUV,
corner_access,
- make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
- make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
+ make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
+ make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
static NamedLegacyCustomDataProvider vertex_colors(
ATTR_DOMAIN_CORNER,
CD_PROP_COLOR,
CD_MLOOPCOL,
corner_access,
- make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
- make_derived_write_attribute<MLoopCol,
- Color4f,
- get_loop_color,
- set_loop_color,
- ATTR_DOMAIN_CORNER>);
+ make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
+ make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index 135de14b4f7..6c4af7a6d23 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
-template<typename T, AttributeDomain Domain>
-static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+template<typename T>
+static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
-template<typename T, AttributeDomain Domain>
-static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+template<typename T>
+static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan<T>((T *)data, domain_size));
}
/**
@@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
},
update_custom_data_pointers};
- static BuiltinCustomDataLayerProvider position(
- "position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
- nullptr);
- static BuiltinCustomDataLayerProvider radius(
- "radius",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT,
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::Deletable,
- point_access,
- make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
- nullptr);
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ nullptr);
+ static BuiltinCustomDataLayerProvider radius("radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float>,
+ make_array_write_attribute<float>,
+ nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
}
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 3e457e48076..3d85118deee 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
+ case GEO_COMPONENT_TYPE_CURVE:
+ return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +185,11 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
BKE_volume_min_max(volume, *r_min, *r_max);
}
+ const CurveEval *curve = this->get_curve_for_read();
+ if (curve != nullptr) {
+ /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
+ curve->bounds_min_max(*r_min, *r_max, true);
+ }
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -252,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+/* Returns a read-only curve or null. */
+const CurveEval *GeometrySet::get_curve_for_read() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -273,6 +288,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
+/* Returns true when the geometry set has a curve component that has a curve. */
+bool GeometrySet::has_curve() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return component != nullptr && component->has_curve();
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -292,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
+/* Create a new geometry set that only contains the given curve. */
+GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+ return geometry_set;
+}
+
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -299,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
+/* Clear the existing curve and replace it with the given one. */
+void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+}
+
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -306,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
pointcloud_component.replace(pointcloud, ownership);
}
+/* Clear the existing volume and replace with the given one. */
+void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
+{
+ VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
+ volume_component.replace(volume, ownership);
+}
+
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
@@ -327,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
+/* Returns a mutable curve or null. No ownership is transferred. */
+CurveEval *GeometrySet::get_curve_for_write()
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ return component.get_for_write();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 07d0e520c93..9abd00c2b4f 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -15,10 +15,12 @@
*/
#include "BKE_geometry_set_instances.hh"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
@@ -50,6 +52,16 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS
}
}
+static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ BLI_assert(object.type == OB_CURVE);
+ if (object.data != nullptr) {
+ std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data);
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ curve_component.replace(curve.release(), GeometryOwnershipType::Owned);
+ }
+}
+
/**
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
@@ -73,6 +85,9 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
if (object.type == OB_MESH) {
add_final_mesh_as_geometry_component(object, geometry_set);
}
+ else if (object.type == OB_CURVE) {
+ add_curve_data_as_geometry_component(object, geometry_set);
+ }
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
* #geometry_set_eval case above. */
@@ -135,21 +150,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
- Span<float4x4> transforms = instances_component.transforms();
- Span<InstancedData> instances = instances_component.instanced_data();
- for (const int i : instances.index_range()) {
- const InstancedData &data = instances[i];
+ Span<float4x4> transforms = instances_component.instance_transforms();
+ Span<int> handles = instances_component.instance_reference_handles();
+ Span<InstanceReference> references = instances_component.references();
+ for (const int i : transforms.index_range()) {
+ const InstanceReference &reference = references[handles[i]];
const float4x4 instance_transform = transform * transforms[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- geometry_set_collect_recursive_object(object, instance_transform, r_sets);
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets);
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ geometry_set_collect_recursive_object(object, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ geometry_set_collect_recursive_collection_instance(
+ collection, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
}
}
}
@@ -253,19 +275,24 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se
return true;
}
- for (const InstancedData &data : instances_component->instanced_data()) {
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- if (!object_instance_attribute_foreach(object, callback, limit, count)) {
- return false;
+ 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;
}
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
- return false;
+ case InstanceReference::Type::Collection: {
+ const Collection &collection = reference.collection();
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
@@ -335,6 +362,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const int tot_transforms = set_group.transforms.size();
@@ -348,6 +377,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
+ for (const int slot_index : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[slot_index];
+ materials.add(material);
+ }
}
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
@@ -370,6 +403,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
break;
}
}
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -383,6 +420,14 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
+
+ Array<int> material_index_map(mesh.totcol);
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
@@ -412,6 +457,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh.totvert;
@@ -449,13 +501,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
- result.attribute_try_create(entry.key, domain_output, data_type_output);
- WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
- if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
- write_attribute->domain() != domain_output) {
+ result.attribute_try_create(
+ entry.key, domain_output, data_type_output, AttributeInitDefault());
+ WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
+ if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
+ write_attribute.domain != domain_output) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+
+ fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -467,11 +521,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
- fn::GSpan src_span = source_attribute->get_span();
+ fn::GVArray_GSpan src_span{*source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
@@ -486,8 +540,41 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
- write_attribute->apply_span();
+ dst_span.save();
+ }
+}
+
+static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result)
+{
+ CurveEval *new_curve = new CurveEval();
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ if (!set.has_curve()) {
+ continue;
+ }
+
+ const CurveEval &source_curve = *set.get_curve_for_read();
+ for (const SplinePtr &source_spline : source_curve.splines()) {
+ for (const float4x4 &transform : set_group.transforms) {
+ SplinePtr new_spline = source_spline->copy();
+ new_spline->transform(transform);
+ new_curve->add_spline(std::move(new_spline));
+ }
+ }
+ }
+
+ for (SplinePtr &spline : new_curve->splines()) {
+ /* Spline instances should have no custom attributes, since they always come
+ * from original objects which currently do not support custom attributes.
+ *
+ * This is only true as long as a #GeometrySet cannot be instanced directly. */
+ BLI_assert(spline->attributes.data.totlayer == 0);
+ UNUSED_VARS_NDEBUG(spline);
}
+
+ new_curve->attributes.reallocate(new_curve->splines().size());
+
+ result.replace(new_curve);
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
@@ -556,6 +643,12 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
UNUSED_VARS(set_groups, dst_component);
}
+static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
+{
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ join_curve_splines(set_groups, dst_component);
+}
+
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
{
if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) {
@@ -587,6 +680,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
join_instance_groups_mesh(set_groups, false, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
+ join_instance_groups_curve(set_groups, new_geometry_set);
return new_geometry_set;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 9c84d155330..421cb0ac4f1 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -653,9 +653,13 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
* \param gpd: Grease pencil data-block
* \param name: Name of the layer
* \param setactive: Set as active
+ * \param add_to_header: Used to force the layer added at header
* \return Pointer to new layer
*/
-bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
+bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header)
{
bGPDlayer *gpl = NULL;
bGPDlayer *gpl_active = NULL;
@@ -671,14 +675,18 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
gpl_active = BKE_gpencil_layer_active_get(gpd);
/* Add to data-block. */
- if (gpl_active == NULL) {
- BLI_addtail(&gpd->layers, gpl);
+ if (add_to_header) {
+ BLI_addhead(&gpd->layers, gpl);
}
else {
- /* if active layer, add after that layer */
- BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ if (gpl_active == NULL) {
+ BLI_addtail(&gpd->layers, gpl);
+ }
+ else {
+ /* if active layer, add after that layer */
+ BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ }
}
-
/* annotation vs GP Object behavior is slightly different */
if (gpd->flag & GP_DATA_ANNOTATIONS) {
/* set default color of new strokes for this layer */
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 88d3e917a7a..906d0fb0792 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -515,7 +515,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (collection != NULL) {
gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
+ gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true, false);
}
}
}
@@ -523,7 +523,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (gpl == NULL) {
gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index fb6500cfe1b..7f839650f33 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -530,14 +530,23 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/**
* Backbone stretch similar to Freestyle.
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- * \param tip_length: Ignore tip jittering, set zero to use default value.
+ * \param gps: Stroke to sample.
+ * \param dist: Distance of one segment.
+ * \param overshoot_fac: How exact is the follow curve algorithm.
+ * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
*/
-bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode)
{
+#define BOTH 0
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
- float threshold = (tip_length == 0 ? 0.001f : tip_length);
+ int i;
+ float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@@ -547,33 +556,36 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float t
second_last = &pt[gps->totpoints - 2];
next_pt = &pt[1];
- float len1 = 0.0f;
- float len2 = 0.0f;
+ if (mode == BOTH || mode == START) {
+ float len1 = 0.0f;
+ i = 1;
+ while (len1 < threshold && gps->totpoints > i) {
+ next_pt = &pt[i];
+ len1 = len_v3v3(&next_pt->x, &pt->x);
+ i++;
+ }
+ float extend1 = (len1 + dist) / len1;
+ float result1[3];
- int i = 1;
- while (len1 < threshold && gps->totpoints > i) {
- next_pt = &pt[i];
- len1 = len_v3v3(&next_pt->x, &pt->x);
- i++;
+ interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+ copy_v3_v3(&pt->x, result1);
}
- i = 2;
- while (len2 < threshold && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- len2 = len_v3v3(&last_pt->x, &second_last->x);
- i++;
- }
-
- float extend1 = (len1 + dist) / len1;
- float extend2 = (len2 + dist) / len2;
-
- float result1[3], result2[3];
+ if (mode == BOTH || mode == END) {
+ float len2 = 0.0f;
+ i = 2;
+ while (len2 < threshold && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 = len_v3v3(&last_pt->x, &second_last->x);
+ i++;
+ }
- interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
- interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+ float extend2 = (len2 + dist) / len2;
+ float result2[3];
+ interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
- copy_v3_v3(&pt->x, result1);
- copy_v3_v3(&last_pt->x, result2);
+ copy_v3_v3(&last_pt->x, result2);
+ }
return true;
}
@@ -702,48 +714,64 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
+ * \param mode: 1->Start, 2->End
*/
-bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
+bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *second_last;
int i;
- if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ if (gps->totpoints < 2) {
+ if (gps->totpoints == 1) {
+ second_last = &pt[1];
+ if (len_v3v3(&second_last->x, &pt->x) < dist) {
+ BKE_gpencil_stroke_trim_points(gps, 0, 0);
+ return true;
+ }
+ }
+
return false;
}
second_last = &pt[gps->totpoints - 2];
- float len1, this_len1, cut_len1;
- float len2, this_len2, cut_len2;
- int index_start, index_end;
-
- len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
-
- i = 1;
- while (len1 < dist && gps->totpoints > i - 1) {
- this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
- len1 += this_len1;
- cut_len1 = len1 - dist;
- i++;
+ float len1, cut_len1;
+ float len2, cut_len2;
+ len1 = len2 = cut_len1 = cut_len2 = 0.0f;
+
+ int index_start = 0;
+ int index_end = 0;
+ if (mode == START) {
+ i = 0;
+ index_end = gps->totpoints - 1;
+ while (len1 < dist && gps->totpoints > i + 1) {
+ len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
+ cut_len1 = len1 - dist;
+ i++;
+ }
+ index_start = i - 1;
}
- index_start = i - 2;
- i = 2;
- while (len2 < dist && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
- len2 += this_len2;
- cut_len2 = len2 - dist;
- i++;
+ if (mode == END) {
+ index_start = 0;
+ i = 2;
+ while (len2 < dist && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 += len_v3v3(&second_last[1].x, &second_last->x);
+ cut_len2 = len2 - dist;
+ i++;
+ }
+ index_end = gps->totpoints - i + 2;
}
- index_end = gps->totpoints - i + 2;
- if (len1 < dist || len2 < dist || index_end <= index_start) {
+ if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
- if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+ if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
index_start = index_end = 0; /* no length left to cut */
}
@@ -753,22 +781,8 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
return false;
}
- pt = gps->points;
-
- float cut1 = cut_len1 / this_len1;
- float cut2 = cut_len2 / this_len2;
-
- float result1[3], result2[3];
-
- interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
- interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
-
- copy_v3_v3(&pt[0].x, result1);
- copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
-
return true;
}
-
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
@@ -1050,7 +1064,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
normalize_v3(locx);
normalize_v3(locy);
- /* Calculcate last point first. */
+ /* Calculate last point first. */
const bGPDspoint *pt_last = &points[totpoints - 1];
float tmp[3];
sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
@@ -2439,7 +2453,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == NULL) {
- gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -2492,7 +2506,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == NULL) {
- gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 7429fe050dc..c2e5006cbc1 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -525,7 +525,13 @@ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data)
/* Increase used IDs refcount if needed and required. */
if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) {
- id_us_plus(id);
+ if ((data->flag & LIB_ID_CREATE_NO_MAIN) != 0) {
+ BLI_assert(cb_data->id_self->tag & LIB_TAG_NO_MAIN);
+ id_us_plus_no_lib(id);
+ }
+ else {
+ id_us_plus(id);
+ }
}
return IDWALK_RET_NOP;
@@ -578,7 +584,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag)
}
}
- /* Early output is source is NULL. */
+ /* Early output if source is NULL. */
if (id == NULL) {
return NULL;
}
@@ -1355,12 +1361,12 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
BLI_remlink(lb, id);
/* Check if we can actually insert id before or after id_sorting_hint, if given. */
- if (!ELEM(id_sorting_hint, NULL, id)) {
+ if (!ELEM(id_sorting_hint, NULL, id) && id_sorting_hint->lib == id->lib) {
BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0);
ID *id_sorting_hint_next = id_sorting_hint->next;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 &&
- (id_sorting_hint_next == NULL ||
+ (id_sorting_hint_next == NULL || id_sorting_hint_next->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) {
BLI_insertlinkafter(lb, id_sorting_hint, id);
return;
@@ -1368,7 +1374,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
ID *id_sorting_hint_prev = id_sorting_hint->prev;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 &&
- (id_sorting_hint_prev == NULL ||
+ (id_sorting_hint_prev == NULL || id_sorting_hint_prev->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) {
BLI_insertlinkbefore(lb, id_sorting_hint, id);
return;
@@ -1383,16 +1389,33 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
/* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at
* once using the same base name), newly inserted items will generally be towards the end
* (higher extension numbers). */
- for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL;
- idtest = idtest->prev, item_array_index--) {
+ bool is_in_library = false;
+ item_array_index = ID_SORT_STEP_SIZE - 1;
+ for (idtest = lb->last; idtest != NULL; idtest = idtest->prev) {
+ if (is_in_library) {
+ if (idtest->lib != id->lib) {
+ /* We got out of expected library 'range' in the list, so we are done here and can move on
+ * to the next step. */
+ break;
+ }
+ }
+ else if (idtest->lib == id->lib) {
+ /* We are entering the expected library 'range' of IDs in the list. */
+ is_in_library = true;
+ }
+
+ if (!is_in_library) {
+ continue;
+ }
+
item_array[item_array_index] = idtest;
if (item_array_index == 0) {
- if ((idtest->lib == NULL && id->lib != NULL) ||
- BLI_strcasecmp(idtest->name, id->name) <= 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) <= 0) {
break;
}
item_array_index = ID_SORT_STEP_SIZE;
}
+ item_array_index--;
}
/* Step two: we go forward in the selected chunk of items and check all of them, as we know
@@ -1404,7 +1427,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
* So we can increment that index in any case. */
for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) {
idtest = item_array[item_array_index];
- if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) > 0) {
BLI_insertlinkbefore(lb, idtest, id);
break;
}
@@ -1412,12 +1435,18 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
if (item_array_index == ID_SORT_STEP_SIZE) {
if (idtest == NULL) {
/* If idtest is NULL here, it means that in the first loop, the last comparison was
- * performed exactly on the first item of the list, and that it also failed. In other
- * words, all items in the list are greater than inserted one, so we can put it at the
- * start of the list. */
- /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be
- * explicit here. */
- BLI_addhead(lb, id);
+ * performed exactly on the first item of the list, and that it also failed. And that the
+ * second loop was not walked at all.
+ *
+ * In other words, if `id` is local, all the items in the list are greater than the inserted
+ * one, so we can put it at the start of the list. Or, if `id` is linked, it is the first one
+ * of its library, and we can put it at the very end of the list. */
+ if (ID_IS_LINKED(id)) {
+ BLI_addtail(lb, id);
+ }
+ else {
+ BLI_addhead(lb, id);
+ }
}
else {
BLI_insertlinkafter(lb, idtest, id);
@@ -1685,12 +1714,14 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
*/
bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
{
- bool result;
+ bool result = false;
char name[MAX_ID_NAME - 2];
- /* if library, don't rename */
+ /* If library, don't rename, but do ensure proper sorting. */
if (ID_IS_LINKED(id)) {
- return false;
+ id_sort_by_name(lb, id, NULL);
+
+ return result;
}
/* if no name given, use name of current ID
diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc
new file mode 100644
index 00000000000..9bfd19ae4d6
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+
+namespace blender::bke::tests {
+
+struct LibIDMainSortTestContext {
+ Main *bmain;
+};
+
+static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx)
+{
+ ctx->bmain = BKE_main_new();
+}
+
+static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx)
+{
+ BKE_main_free(ctx->bmain);
+}
+
+static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
+{
+ ID *prev_id = nullptr;
+ for (ID *id : list) {
+ EXPECT_EQ(id->prev, prev_id);
+ if (prev_id != nullptr) {
+ EXPECT_EQ(prev_id->next, id);
+ }
+ prev_id = id;
+ }
+ EXPECT_EQ(prev_id->next, nullptr);
+}
+
+TEST(lib_id_main_sort, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_c);
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_sort, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_a->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_a);
+ test_lib_id_main_sort_check_order({id_c, id_b, id_a});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 54d14e33209..8341c5b6e78 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -723,7 +723,7 @@ static void lib_override_library_create_post_process(Main *bmain,
switch (GS(id_ref->name)) {
case ID_GR: {
/* Adding the object to a specific collection outside of the root overridden one is a
- * fairly bad idea (it breaks the override hierarchy concept). But htere is no other
+ * fairly bad idea (it breaks the override hierarchy concept). But there is no other
* way to do this currently (we cannot add new collections to overridden root one,
* this is not currently supported).
* Since that will be fairly annoying and noisy, only do that in case the override
@@ -878,7 +878,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
ID *id_root,
Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce,
- const bool do_post_process)
+ const bool do_post_process,
+ ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
@@ -1057,6 +1058,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Delete old override IDs.
* Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
* This improves performances anyway, so everything is fine. */
+ int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
/* Note that this works because linked IDs are always after local ones (including overrides),
@@ -1081,6 +1083,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag &= ~LIB_TAG_MISSING;
CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
}
+#if 0
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
@@ -1088,6 +1091,17 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
}
+#else
+ else {
+ /* Delete them nevertheless, with fat warning, user needs to decide whether they want to
+ * save that version of the file (and accept the loss), or not. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_WARN(
+ &LOG, "Old override %s is being deleted even though it was user-edited", id->name);
+ user_edited_overrides_deletion_count++;
+ }
+#endif
}
}
FOREACH_MAIN_ID_END;
@@ -1098,6 +1112,15 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
+ if (user_edited_overrides_deletion_count > 0) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "During resync of data-block %s, %d obsolete overrides were deleted, that had "
+ "local changes defined by user",
+ id_root->name + 2,
+ user_edited_overrides_deletion_count);
+ }
+
if (do_post_process) {
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
@@ -1136,7 +1159,10 @@ bool BKE_lib_override_library_resync(Main *bmain,
* Then it will handle the resync of necessary IDs (through calls to
* #BKE_lib_override_library_resync).
*/
-void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ReportList *reports)
{
/* We use a specific collection to gather/store all 'orphaned' override collections and objects
* generated by re-sync-process. This avoids putting them in scene's master collection. */
@@ -1252,7 +1278,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
const bool success = BKE_lib_override_library_resync(
- bmain, scene, view_layer, id, override_resync_residual_storage, false, false);
+ bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@@ -1643,7 +1669,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
return true;
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
{
if (id->override_library == NULL) {
@@ -1677,7 +1703,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *
}
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
{
ID *id;
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index cbbe07f99d8..3281783d81a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -56,7 +56,7 @@ typedef struct LibraryForeachIDData {
*/
ID *self_id;
- /** Flags controlling the bahaviour of the 'foreach id' looping code. */
+ /** Flags controlling the behavior of the 'foreach id' looping code. */
int flag;
/** Generic flags to be passed to all callback calls for current processed data. */
int cb_flag;
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 37d47a984cc..73b64e6efb3 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -77,6 +77,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "GPU_material.h"
@@ -700,6 +701,98 @@ Material *BKE_object_material_get(Object *ob, short act)
return ma_p ? *ma_p : NULL;
}
+static ID *get_evaluated_object_data_with_materials(Object *ob)
+{
+ ID *data = ob->data;
+ /* Meshes in edit mode need special handling. */
+ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) {
+ Mesh *mesh = ob->data;
+ if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) {
+ data = &mesh->edit_mesh->mesh_eval_final->id;
+ }
+ }
+ return data;
+}
+
+/**
+ * On evaluated objects the number of materials on an object and its data might go out of sync.
+ * This is because during evaluation materials can be added/removed on the object data.
+ *
+ * For rendering or exporting we generally use the materials on the object data. However, some
+ * material indices might be overwritten by the object.
+ */
+Material *BKE_object_material_get_eval(Object *ob, short act)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ const int slot_index = act - 1;
+
+ if (slot_index < 0) {
+ return NULL;
+ }
+ ID *data = get_evaluated_object_data_with_materials(ob);
+ const short *tot_slots_data_ptr = BKE_id_material_len_p(data);
+ const int tot_slots_data = tot_slots_data_ptr ? *tot_slots_data_ptr : 0;
+ if (slot_index >= tot_slots_data) {
+ return NULL;
+ }
+ const int tot_slots_object = ob->totcol;
+
+ Material ***materials_data_ptr = BKE_id_material_array_p(data);
+ Material **materials_data = materials_data_ptr ? *materials_data_ptr : NULL;
+ Material **materials_object = ob->mat;
+
+ /* Check if slot is overwritten by object. */
+ if (slot_index < tot_slots_object) {
+ if (ob->matbits) {
+ if (ob->matbits[slot_index]) {
+ Material *material = materials_object[slot_index];
+ if (material != NULL) {
+ return material;
+ }
+ }
+ }
+ }
+ /* Otherwise use data from object-data. */
+ if (slot_index < tot_slots_data) {
+ Material *material = materials_data[slot_index];
+ return material;
+ }
+ return NULL;
+}
+
+int BKE_object_material_count_eval(Object *ob)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ ID *id = get_evaluated_object_data_with_materials(ob);
+ const short *len_p = BKE_id_material_len_p(id);
+ return len_p ? *len_p : 0;
+}
+
+void BKE_id_material_eval_assign(ID *id, int slot, Material *material)
+{
+ Material ***materials_ptr = BKE_id_material_array_p(id);
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (ELEM(NULL, materials_ptr, len_ptr)) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ const int slot_index = slot - 1;
+ const int old_length = *len_ptr;
+
+ if (slot_index >= old_length) {
+ /* Need to grow slots array. */
+ const int new_length = slot_index + 1;
+ *materials_ptr = MEM_reallocN(*materials_ptr, sizeof(void *) * new_length);
+ *len_ptr = new_length;
+ for (int i = old_length; i < new_length; i++) {
+ (*materials_ptr)[i] = NULL;
+ }
+ }
+
+ (*materials_ptr)[slot_index] = material;
+}
+
Material *BKE_gpencil_material(Object *ob, short act)
{
Material *ma = BKE_object_material_get(ob, act);
@@ -1028,6 +1121,43 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
+/**
+ * Copy materials from evaluated geometry to the original geometry of an object.
+ */
+void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
+{
+ ID *data_orig = ob_orig->data;
+
+ short *orig_totcol = BKE_id_material_len_p(data_orig);
+ Material ***orig_mat = BKE_id_material_array_p(data_orig);
+
+ short *eval_totcol = BKE_id_material_len_p(data_eval);
+ Material ***eval_mat = BKE_id_material_array_p(data_eval);
+
+ if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
+ return;
+ }
+
+ /* Remove old materials from original geometry. */
+ for (int i = 0; i < *orig_totcol; i++) {
+ id_us_min(&(*orig_mat)[i]->id);
+ }
+ MEM_SAFE_FREE(*orig_mat);
+
+ /* Create new material slots based on materials on evaluated geometry. */
+ *orig_totcol = *eval_totcol;
+ *orig_mat = MEM_callocN(sizeof(void *) * (*eval_totcol), __func__);
+ for (int i = 0; i < *eval_totcol; i++) {
+ Material *material_eval = (*eval_mat)[i];
+ if (material_eval != NULL) {
+ Material *material_orig = (Material *)DEG_get_original_id(&material_eval->id);
+ (*orig_mat)[i] = material_orig;
+ id_us_plus(&material_orig->id);
+ }
+ }
+ BKE_object_materials_test(bmain, ob_orig, data_orig);
+}
+
/* XXX - this calls many more update calls per object then are needed, could be optimized */
void BKE_object_material_array_assign(Main *bmain,
struct Object *ob,
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 1550401cc9c..bb46c7b16c0 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -162,7 +162,7 @@ static void make_box_from_metaelem(Box *r, const MetaElem *ml)
}
/**
- * Partitions part of mainb array [start, end) along axis s. Returns i,
+ * Partitions part of #process.mainb array [start, end) along axis s. Returns i,
* where centroids of elements in the [start, i) segment lie "on the right side" of div,
* and elements in the [i, end) segment lie "on the left"
*/
@@ -1170,8 +1170,9 @@ static void polygonize(PROCESS *process)
/**
* Iterates over ALL objects in the scene and all of its sets, including
- * making all duplis(not only metas). Copies metas to mainb array.
- * Computes bounding boxes for building BVH. */
+ * making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
+ * Computes bounding boxes for building BVH.
+ */
static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Object *ob)
{
Scene *sce_iter = scene;
@@ -1435,7 +1436,7 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
if (process.totelem > 0) {
build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
- /* Don't polygonize meta-balls with too high resolution (base mball to small)
+ /* Don't polygonize meta-balls with too high resolution (base mball too small)
* note: Eps was 0.0001f but this was giving problems for blood animation for
* the open movie "Sintel", using 0.00001f. */
if (ob->scale[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) ||
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index d013becb372..e893a3983bd 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object)
*
* Note that there are extra fields in there like bevel and path, but those are not needed during
* conversion, so they are not copied to save unnecessary allocations. */
- if (object->runtime.curve_cache != NULL) {
+ if (temp_object->runtime.curve_cache == NULL) {
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
+ }
+
+ if (object->runtime.curve_cache != NULL) {
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
}
+
/* Constructive modifiers will use mesh to store result. */
if (object->runtime.data_eval != NULL) {
BKE_id_copy_ex(
@@ -1057,17 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object)
return temp_object;
}
+/**
+ * Populate `object->runtime.curve_cache` which is then used to create the mesh.
+ */
static void curve_to_mesh_eval_ensure(Object *object)
{
- if (object->runtime.curve_cache == NULL) {
- object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
- }
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
- remapped_object.runtime.bb = NULL;
+ BKE_object_runtime_reset(&remapped_object);
+
remapped_object.data = &remapped_curve;
+ if (object->runtime.curve_cache == NULL) {
+ object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ }
+
+ /* Temporarily share the curve-cache with the temporary object, owned by `object`. */
+ remapped_object.runtime.curve_cache = object->runtime.curve_cache;
+
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@@ -1078,8 +1090,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object bevel_object = {{NULL}};
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
- bevel_object.runtime.bb = NULL;
BLI_listbase_clear(&bevel_object.modifiers);
+ BKE_object_runtime_reset(&bevel_object);
remapped_curve.bevobj = &bevel_object;
}
@@ -1087,8 +1099,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object taper_object = {{NULL}};
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
- taper_object.runtime.bb = NULL;
BLI_listbase_clear(&taper_object.modifiers);
+ BKE_object_runtime_reset(&taper_object);
remapped_curve.taperobj = &taper_object;
}
@@ -1110,12 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
- MEM_SAFE_FREE(remapped_object.runtime.bb);
- MEM_SAFE_FREE(taper_object.runtime.bb);
- MEM_SAFE_FREE(bevel_object.runtime.bb);
+ /* Owned by `object` & needed by the caller to create the mesh. */
+ remapped_object.runtime.curve_cache = NULL;
- BKE_object_free_curve_cache(&bevel_object);
- BKE_object_free_curve_cache(&taper_object);
+ BKE_object_runtime_free_data(&remapped_object);
+ BKE_object_runtime_free_data(&taper_object);
+ BKE_object_runtime_free_data(&taper_object);
}
static Mesh *mesh_new_from_curve_type_object(Object *object)
@@ -1555,7 +1567,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
- int did_shapekeys = 0;
+ bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@@ -1610,7 +1622,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
- did_shapekeys = 1;
+ did_shapekeys = true;
}
/* copy texture space */
@@ -1639,13 +1651,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
- /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
- * BKE_mesh_update_customdata_pointers() call below. */
- tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
- tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
-
- CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
- CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
+ CustomData_add_layer(&tmp.ldata,
+ CD_MLOOP,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mloop :
+ MEM_dupallocN(mesh_src->mloop),
+ tmp.totloop);
+ CustomData_add_layer(&tmp.pdata,
+ CD_MPOLY,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mpoly :
+ MEM_dupallocN(mesh_src->mpoly),
+ tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@@ -1664,9 +1681,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
- /* since 2.65 caller must do! */
- // BKE_mesh_tessface_calc(&tmp);
-
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
new file mode 100644
index 00000000000..91c9951ae89
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+namespace blender::bke::mesh_surface_sample {
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ const T v0 = data_in[v0_index];
+ const T v1 = data_in[v1_index];
+ const T v2 = data_in[v2_index];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totvert);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_point_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int loop_index_0 = looptri.tri[0];
+ const int loop_index_1 = looptri.tri[1];
+ const int loop_index_2 = looptri.tri[2];
+
+ const T v0 = data_in[loop_index_0];
+ const T v1 = data_in[loop_index_1];
+ const T v2 = data_in[loop_index_2];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totloop);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_corner_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : data_out.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int poly_index = looptri.poly;
+ data_out[i] = data_in[poly_index];
+ }
+}
+
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_in.size() == mesh.totpoly);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 9c2cd03dbc2..0f2f56f4f2b 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip)
int width, height;
MovieClipUser user = {0};
- user.framenr = 1;
+ user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1);
BKE_movieclip_get_size(clip, &user, &width, &height);
if (width && height) {
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 97aa6b07ab0..92e21183acb 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain,
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
-void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
+void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
{
NlaTrack *nlt, *nlt_d;
@@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl
}
}
+/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */
+static void update_active_strip(AnimData *adt_dest,
+ NlaTrack *track_dest,
+ const AnimData *adt_source,
+ NlaTrack *track_source)
+{
+ BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
+
+ NlaStrip *strip_dest = track_dest->strips.first;
+ LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) {
+ if (strip_source == adt_source->actstrip) {
+ adt_dest->actstrip = strip_dest;
+ }
+
+ strip_dest = strip_dest->next;
+ }
+}
+
+/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
+static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
+{
+ BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) ==
+ BLI_listbase_count(&adt_dest->nla_tracks));
+
+ NlaTrack *track_dest = adt_dest->nla_tracks.first;
+ LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
+ if (track_source == adt_source->act_track) {
+ adt_dest->act_track = track_dest;
+ /* Assumption: the active strip is on the active track. */
+ update_active_strip(adt_dest, track_dest, adt_source, track_source);
+ }
+
+ track_dest = track_dest->next;
+ }
+}
+
+void BKE_nla_tracks_copy_from_adt(Main *bmain,
+ AnimData *adt_dest,
+ const AnimData *adt_source,
+ const int flag)
+{
+ adt_dest->act_track = NULL;
+ adt_dest->actstrip = NULL;
+
+ BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
+ update_active_track(adt_dest, adt_source);
+}
+
/* Adding ------------------------------------------- */
/* Add a NLA Track to the given AnimData
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 68ae6963a3e..643dc58af18 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -303,6 +303,16 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -434,6 +444,12 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_COLLECTION:
BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
break;
+ case SOCK_TEXTURE:
+ BLO_write_struct(writer, bNodeSocketValueTexture, sock->default_value);
+ break;
+ case SOCK_MATERIAL:
+ BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -501,6 +517,12 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
+ else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) {
+ BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec);
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb);
+ }
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (nss->bytecode) {
@@ -676,6 +698,18 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage);
break;
}
+ case GEO_NODE_ATTRIBUTE_CURVE_MAP: {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BLO_read_data_address(reader, &data->curve_vec);
+ if (data->curve_vec) {
+ BKE_curvemapping_blend_read(reader, data->curve_vec);
+ }
+ BLO_read_data_address(reader, &data->curve_rgb);
+ if (data->curve_rgb) {
+ BKE_curvemapping_blend_read(reader, data->curve_rgb);
+ }
+ break;
+ }
case SH_NODE_SCRIPT: {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
BLO_read_data_address(reader, &nss->bytecode);
@@ -802,6 +836,16 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
BLO_read_id_address(reader, lib, &default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -887,6 +931,16 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1454,6 +1508,16 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus((ID *)default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1493,6 +1557,20 @@ static void socket_id_user_decrement(bNodeSocket *sock)
}
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1636,6 +1714,10 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketGeometry";
case SOCK_COLLECTION:
return "NodeSocketCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketMaterial";
}
return nullptr;
}
@@ -1707,6 +1789,10 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceGeometry";
case SOCK_COLLECTION:
return "NodeSocketInterfaceCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketInterfaceTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketInterfaceMaterial";
}
return nullptr;
}
@@ -4241,7 +4327,7 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
if (GS(id->name) == ID_NT) {
bNodeTree *ngroup = (bNodeTree *)id;
- if (ngroup->type == NTREE_GEOMETRY) {
+ if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
@@ -4949,6 +5035,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
+ register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
@@ -4956,14 +5043,21 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
+ register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
+ register_node_type_geo_attribute_vector_rotate();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
+ register_node_type_geo_curve_to_mesh();
+ register_node_type_geo_curve_resample();
register_node_type_geo_edge_split();
+ register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
+ register_node_type_geo_material_assign();
+ register_node_type_geo_material_replace();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
@@ -4983,6 +5077,7 @@ static void registerGeometryNodes()
register_node_type_geo_sample_texture();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
+ register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index cc910bab6ac..7a28fd295fb 100644
--- a/source/blender/blenkernel/intern/node_ui_storage.cc
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -152,6 +152,7 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
node_error_message_log(ntree, node, message, type);
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ std::lock_guard lock{node_ui_storage.mutex};
node_ui_storage.warnings.append({type, std::move(message)});
}
@@ -163,6 +164,7 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const CustomDataType data_type)
{
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ std::lock_guard lock{node_ui_storage.mutex};
node_ui_storage.attribute_hints.add_as(
AvailableAttributeInfo{attribute_name, domain, data_type});
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 34c77cd9155..034af924ab1 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ob->type == OB_HAIR) {
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
- if (ob->type == OB_POINTCLOUD) {
+ if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
return (mti->modifyGeometrySet != NULL);
}
- if (ob->type == OB_VOLUME) {
- return (mti->modifyVolume != NULL);
- }
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;
@@ -4350,7 +4347,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
}
/* Speed optimization for animation lookups. */
if (ob->pose != NULL) {
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(ob->pose);
}
@@ -5121,6 +5118,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
}
/**
+ * The function frees memory used by the runtime data, but not the runtime field itself.
+ *
+ * All runtime data is cleared to ensure it's not used again,
+ * in keeping with other `_free_data(..)` functions.
+ */
+void BKE_object_runtime_free_data(Object *object)
+{
+ /* Currently this is all that's needed. */
+ BKE_object_free_derived_caches(object);
+
+ BKE_object_runtime_reset(object);
+}
+
+/**
* Find an associated armature object.
*/
static Object *obrel_armature_find(Object *ob)
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 835b62c06bf..768fa9373c1 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -842,38 +842,38 @@ static void make_duplis_instances_component(const DupliContext *ctx)
return;
}
- Span<float4x4> instance_offset_matrices = component->transforms();
+ Span<float4x4> instance_offset_matrices = component->instance_transforms();
+ Span<int> instance_reference_handles = component->instance_reference_handles();
Span<int> almost_unique_ids = component->almost_unique_ids();
- Span<InstancedData> instanced_data = component->instanced_data();
+ Span<InstanceReference> references = component->references();
- for (int i = 0; i < component->instances_amount(); i++) {
- const InstancedData &data = instanced_data[i];
+ for (int64_t i : instance_offset_matrices.index_range()) {
+ const InstanceReference &reference = references[instance_reference_handles[i]];
const int id = almost_unique_ids[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- Object *object = data.data.object;
- if (object != nullptr) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
float matrix[4][4];
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
- make_dupli(ctx, object, matrix, id);
+ make_dupli(ctx, &object, matrix, id);
float space_matrix[4][4];
- mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object->imat);
+ mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
- make_recursive_duplis(ctx, object, space_matrix, id);
+ make_recursive_duplis(ctx, &object, space_matrix, id);
+ break;
}
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- Collection *collection = data.data.collection;
- if (collection != nullptr) {
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
float collection_matrix[4][4];
unit_m4(collection_matrix);
- sub_v3_v3(collection_matrix[3], collection->instance_offset);
+ sub_v3_v3(collection_matrix[3], collection.instance_offset);
mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
if (object == ctx->object) {
continue;
}
@@ -885,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx)
make_recursive_duplis(ctx, object, collection_matrix, id);
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index ae685357151..a873ecec6f1 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -101,6 +101,8 @@ static void particle_settings_init(ID *id)
MEMCPY_STRUCT_AFTER(particle_settings, DNA_struct_default_get(ParticleSettings), id);
particle_settings->effector_weights = BKE_effector_add_weights(NULL);
+ particle_settings->pd = BKE_partdeflect_new(PFIELD_NULL);
+ particle_settings->pd2 = BKE_partdeflect_new(PFIELD_NULL);
}
static void particle_settings_copy_data(Main *UNUSED(bmain),
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 63bc8753fc7..948b57578dc 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb);
void pbvh_grow_nodes(PBVH *bvh, int totnode);
bool ray_face_intersection_quad(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *depth);
bool ray_face_intersection_tri(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *depth);
bool ray_face_nearest_quad(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *r_depth,
float *r_dist_sq);
bool ray_face_nearest_tri(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *r_depth,
float *r_dist_sq);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 17434ee8023..be206f8a642 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH
}
for (i = 0; i < BPHYS_TOT_DATA; i++) {
- cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
+ cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
}
return 1;
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index d52e4443ac1..fcc1afbc59b 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
MEM_freeN(ccdm->mima);
MEM_freeN(ccdm);
- ccdm = NULL;
}
}
@@ -830,13 +829,13 @@ static void calculate_collision_balls(Object *ob)
}
/* creates new softbody if didn't exist yet, makes new points and springs arrays */
-static void renew_softbody(Scene *scene, Object *ob, int totpoint, int totspring)
+static void renew_softbody(Object *ob, int totpoint, int totspring)
{
SoftBody *sb;
int i;
short softflag;
if (ob->soft == NULL) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
}
else {
free_softbody_intern(ob->soft);
@@ -2680,7 +2679,7 @@ static void springs_from_mesh(Object *ob)
}
/* makes totally fresh start situation */
-static void mesh_to_softbody(Scene *scene, Object *ob)
+static void mesh_to_softbody(Object *ob)
{
SoftBody *sb;
Mesh *me = ob->data;
@@ -2698,7 +2697,7 @@ static void mesh_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, me->totvert, totedge);
+ renew_softbody(ob, me->totvert, totedge);
/* we always make body points */
sb = ob->soft;
@@ -2910,7 +2909,7 @@ static void makelatticesprings(Lattice *lt, BodySpring *bs, int dostiff, Object
}
/* makes totally fresh start situation */
-static void lattice_to_softbody(Scene *scene, Object *ob)
+static void lattice_to_softbody(Object *ob)
{
Lattice *lt = ob->data;
SoftBody *sb;
@@ -2930,7 +2929,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
bp = sb->bpoint;
@@ -2973,7 +2972,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* makes totally fresh start situation */
-static void curve_surf_to_softbody(Scene *scene, Object *ob)
+static void curve_surf_to_softbody(Object *ob)
{
Curve *cu = ob->data;
SoftBody *sb;
@@ -2994,7 +2993,7 @@ static void curve_surf_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
/* set vars now */
@@ -3118,7 +3117,7 @@ static void sb_new_scratch(SoftBody *sb)
/* ************ Object level, exported functions *************** */
/* allocates and initializes general main data */
-SoftBody *sbNew(Scene *scene)
+SoftBody *sbNew(void)
{
SoftBody *sb;
@@ -3141,12 +3140,6 @@ SoftBody *sbNew(Scene *scene)
/*todo backward file compat should copy inspring to inpush while reading old files*/
sb->inpush = 0.5f;
- sb->interval = 10;
- if (scene != NULL) {
- sb->sfra = scene->r.sfra;
- sb->efra = scene->r.efra;
- }
-
sb->colball = 0.49f;
sb->balldamp = 0.50f;
sb->ballstiff = 1.0f;
@@ -3578,17 +3571,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
switch (ob->type) {
case OB_MESH:
- mesh_to_softbody(scene, ob);
+ mesh_to_softbody(ob);
break;
case OB_LATTICE:
- lattice_to_softbody(scene, ob);
+ lattice_to_softbody(ob);
break;
case OB_CURVE:
case OB_SURF:
- curve_surf_to_softbody(scene, ob);
+ curve_surf_to_softbody(ob);
break;
default:
- renew_softbody(scene, ob, numVerts, 0);
+ renew_softbody(ob, numVerts, 0);
break;
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
new file mode 100644
index 00000000000..11620a30948
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -0,0 +1,343 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_spline.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+Spline::Type Spline::type() const
+{
+ return type_;
+}
+
+void Spline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void Spline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ this->mark_cache_invalid();
+}
+
+int Spline::evaluated_edges_size() const
+{
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return 0;
+ }
+
+ return this->is_cyclic_ ? eval_size : eval_size - 1;
+}
+
+float Spline::length() const
+{
+ Span<float> lengths = this->evaluated_lengths();
+ return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
+}
+
+int Spline::segments_size() const
+{
+ const int points_len = this->size();
+
+ return is_cyclic_ ? points_len : points_len - 1;
+}
+
+bool Spline::is_cyclic() const
+{
+ return is_cyclic_;
+}
+
+void Spline::set_cyclic(const bool value)
+{
+ is_cyclic_ = value;
+}
+
+static void accumulate_lengths(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float> lengths)
+{
+ float length = 0.0f;
+ for (const int i : IndexRange(positions.size() - 1)) {
+ length += float3::distance(positions[i], positions[i + 1]);
+ lengths[i] = length;
+ }
+ if (is_cyclic) {
+ lengths.last() = length + float3::distance(positions.last(), positions.first());
+ }
+}
+
+/**
+ * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
+ * length of the subsequent segment, i.e. the first value is the length of the first segment rather
+ * than 0. This calculation is rather trivial, and only depends on the evaluated positions.
+ * However, the results are used often, so it makes sense to cache it.
+ */
+Span<float> Spline::evaluated_lengths() const
+{
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ std::lock_guard lock{length_cache_mutex_};
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ const int total = evaluated_edges_size();
+ evaluated_lengths_cache_.resize(total);
+
+ Span<float3> positions = this->evaluated_positions();
+ accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
+
+ length_cache_dirty_ = false;
+ return evaluated_lengths_cache_;
+}
+
+static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
+{
+ const float3 dir_prev = (middle - prev).normalized();
+ const float3 dir_next = (next - middle).normalized();
+
+ return (dir_prev + dir_next).normalized();
+}
+
+static void calculate_tangents(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float3> tangents)
+{
+ if (positions.size() == 1) {
+ return;
+ }
+
+ for (const int i : IndexRange(1, positions.size() - 2)) {
+ tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
+ }
+
+ if (is_cyclic) {
+ const float3 &second_to_last = positions[positions.size() - 2];
+ const float3 &last = positions.last();
+ const float3 &first = positions.first();
+ const float3 &second = positions[1];
+ tangents.first() = direction_bisect(last, first, second);
+ tangents.last() = direction_bisect(second_to_last, last, first);
+ }
+ else {
+ tangents.first() = (positions[1] - positions[0]).normalized();
+ tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction of the curve at each evaluated point.
+ */
+Span<float3> Spline::evaluated_tangents() const
+{
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ std::lock_guard lock{tangent_cache_mutex_};
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_tangents_cache_.resize(eval_size);
+
+ Span<float3> positions = this->evaluated_positions();
+
+ if (eval_size == 1) {
+ evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
+ }
+ else {
+ calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
+ this->correct_end_tangents();
+ }
+
+ tangent_cache_dirty_ = false;
+ return evaluated_tangents_cache_;
+}
+
+static float3 rotate_direction_around_axis(const float3 &direction,
+ const float3 &axis,
+ const float angle)
+{
+ BLI_ASSERT_UNIT_V3(direction);
+ BLI_ASSERT_UNIT_V3(axis);
+
+ const float3 axis_scaled = axis * float3::dot(direction, axis);
+ const float3 diff = direction - axis_scaled;
+ const float3 cross = float3::cross(axis, diff);
+
+ return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
+}
+
+static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
+{
+ for (const int i : normals.index_range()) {
+ normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction vectors perpendicular to the tangents at every
+ * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
+ */
+Span<float3> Spline::evaluated_normals() const
+{
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ std::lock_guard lock{normal_cache_mutex_};
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_normals_cache_.resize(eval_size);
+
+ Span<float3> tangents = evaluated_tangents();
+ MutableSpan<float3> normals = evaluated_normals_cache_;
+
+ /* Only Z up normals are supported at the moment. */
+ calculate_normals_z_up(tangents, normals);
+
+ /* Rotate the generated normals with the interpolated tilt data. */
+ blender::fn::GVArray_Typed<float> tilts{
+ this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))};
+ for (const int i : normals.index_range()) {
+ normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
+ }
+
+ normal_cache_dirty_ = false;
+ return evaluated_normals_cache_;
+}
+
+Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
+{
+ return this->lookup_evaluated_length(this->length() * factor);
+}
+
+/**
+ * \note This does not support extrapolation currently.
+ */
+Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
+{
+ BLI_assert(length >= 0.0f && length <= this->length());
+
+ Span<float> lengths = this->evaluated_lengths();
+
+ const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
+ const int index = offset - lengths.begin();
+ const int next_index = (index == this->size() - 1) ? 0 : index + 1;
+
+ const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
+ const float factor = (length - previous_length) / (lengths[index] - previous_length);
+
+ return LookupResult{index, next_index, factor};
+}
+
+/**
+ * Return an array of evenly spaced samples along the length of the spline. The samples are indices
+ * and factors to the next index encoded in floats. The logic for converting from the float values
+ * to interpolation data is in #lookup_data_from_index_factor.
+ */
+Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
+{
+ const Span<float> lengths = this->evaluated_lengths();
+
+ BLI_assert(samples_size > 0);
+ Array<float> samples(samples_size);
+
+ samples[0] = 0.0f;
+ if (samples_size == 1) {
+ return samples;
+ }
+
+ const float total_length = this->length();
+ const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1));
+
+ /* Store the length at the previous evaluated point in a variable so it can
+ * start out at zero (the lengths array doesn't contain 0 for the first point). */
+ float prev_length = 0.0f;
+ int i_sample = 1;
+ for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) {
+ const float length = lengths[i_evaluated];
+
+ /* Add every sample that fits in this evaluated edge. */
+ while ((sample_length * i_sample) < length && i_sample < samples_size) {
+ const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
+ samples[i_sample] = i_evaluated + factor;
+ i_sample++;
+ }
+
+ prev_length = length;
+ }
+
+ if (!is_cyclic_) {
+ /* In rare cases this can prevent overflow of the stored index. */
+ samples.last() = lengths.size();
+ }
+
+ return samples;
+}
+
+Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
+{
+ const int points_len = this->evaluated_points_size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 2, points_len - 1, 1.0f};
+}
+
+void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
+ for (const float3 &position : positions) {
+ minmax_v3v3_v3(min, max, position);
+ }
+}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
new file mode 100644
index 00000000000..4be3ba8576e
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -0,0 +1,584 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr BezierSpline::copy() const
+{
+ return std::make_unique<BezierSpline>(*this);
+}
+
+int BezierSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == handle_types_left_.size());
+ BLI_assert(size == handle_positions_left_.size());
+ BLI_assert(size == handle_types_right_.size());
+ BLI_assert(size == handle_positions_right_.size());
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+int BezierSpline::resolution() const
+{
+ return resolution_;
+}
+
+void BezierSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void BezierSpline::add_point(const float3 position,
+ const HandleType handle_type_start,
+ const float3 handle_position_start,
+ const HandleType handle_type_end,
+ const float3 handle_position_end,
+ const float radius,
+ const float tilt)
+{
+ handle_types_left_.append(handle_type_start);
+ handle_positions_left_.append(handle_position_start);
+ positions_.append(position);
+ handle_types_right_.append(handle_type_end);
+ handle_positions_right_.append(handle_position_end);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::resize(const int size)
+{
+ handle_types_left_.resize(size);
+ handle_positions_left_.resize(size);
+ positions_.resize(size);
+ handle_types_right_.resize(size);
+ handle_positions_right_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> BezierSpline::positions()
+{
+ return positions_;
+}
+Span<float3> BezierSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> BezierSpline::radii()
+{
+ return radii_;
+}
+Span<float> BezierSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> BezierSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> BezierSpline::tilts() const
+{
+ return tilts_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
+{
+ return handle_types_left_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
+{
+ return handle_types_left_;
+}
+Span<float3> BezierSpline::handle_positions_left() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_left()
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
+{
+ return handle_types_right_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
+{
+ return handle_types_right_;
+}
+Span<float3> BezierSpline::handle_positions_right() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_right()
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+
+static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == 0) {
+ if (cyclic) {
+ return positions[positions.size() - 1];
+ }
+ return 2.0f * positions[i] - positions[i + 1];
+ }
+ return positions[i - 1];
+}
+
+static float3 next_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == positions.size() - 1) {
+ if (cyclic) {
+ return positions[0];
+ }
+ return 2.0f * positions[i] - positions[i - 1];
+ }
+ return positions[i + 1];
+}
+
+/**
+ * Recalculate all #Auto and #Vector handles with positions automatically
+ * derived from the neighboring control points.
+ */
+void BezierSpline::ensure_auto_handles() const
+{
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{auto_handle_mutex_};
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ for (const int i : IndexRange(this->size())) {
+ if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
+ const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
+ const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
+ float prev_len = prev_diff.length();
+ float next_len = next_diff.length();
+ if (prev_len == 0.0f) {
+ prev_len = 1.0f;
+ }
+ if (next_len == 0.0f) {
+ next_len = 1.0f;
+ }
+ const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+ /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+ const float len = dir.length() * 2.5614f;
+ if (len != 0.0f) {
+ if (handle_types_left_[i] == HandleType::Auto) {
+ const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+ handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
+ }
+ if (handle_types_right_[i] == HandleType::Auto) {
+ const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+ handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
+ }
+ }
+ }
+
+ if (handle_types_left_[i] == HandleType::Vector) {
+ const float3 prev = previous_position(positions_, is_cyclic_, i);
+ handle_positions_left_[i] = float3::interpolate(positions_[i], prev, 1.0f / 3.0f);
+ }
+
+ if (handle_types_right_[i] == HandleType::Vector) {
+ const float3 next = next_position(positions_, is_cyclic_, i);
+ handle_positions_right_[i] = float3::interpolate(positions_[i], next, 1.0f / 3.0f);
+ }
+ }
+
+ auto_handles_dirty_ = false;
+}
+
+void BezierSpline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position = matrix * handle_position;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position = matrix * handle_position;
+ }
+ this->mark_cache_invalid();
+}
+
+bool BezierSpline::point_is_sharp(const int index) const
+{
+ return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
+ ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
+}
+
+bool BezierSpline::segment_is_vector(const int index) const
+{
+ if (index == this->size() - 1) {
+ if (is_cyclic_) {
+ return handle_types_right_.last() == HandleType::Vector &&
+ handle_types_left_.first() == HandleType::Vector;
+ }
+ /* There is actually no segment in this case, but it's nice to avoid
+ * having a special case for the last segment in calling code. */
+ return true;
+ }
+ return handle_types_right_[index] == HandleType::Vector &&
+ handle_types_left_[index + 1] == HandleType::Vector;
+}
+
+void BezierSpline::mark_cache_invalid()
+{
+ offset_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ mapping_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+ auto_handles_dirty_ = true;
+}
+
+int BezierSpline::evaluated_points_size() const
+{
+ BLI_assert(this->size() > 0);
+ return this->control_point_offsets().last();
+}
+
+/**
+ * If the spline is not cyclic, the direction for the first and last points is just the
+ * direction formed by the corresponding handles and control points. In the unlikely situation
+ * that the handles define a zero direction, fallback to using the direction defined by the
+ * first and last evaluated segments already calculated in #Spline::evaluated_tangents().
+ */
+void BezierSpline::correct_end_tangents() const
+{
+ if (is_cyclic_) {
+ return;
+ }
+
+ MutableSpan<float3> tangents(evaluated_tangents_cache_);
+
+ if (handle_positions_left_.first() != positions_.first()) {
+ tangents.first() = (positions_.first() - handle_positions_left_.first()).normalized();
+ }
+ if (handle_positions_right_.last() != positions_.last()) {
+ tangents.last() = (handle_positions_right_.last() - positions_.last()).normalized();
+ }
+}
+
+static void bezier_forward_difference_3d(const float3 &point_0,
+ const float3 &point_1,
+ const float3 &point_2,
+ const float3 &point_3,
+ MutableSpan<float3> result)
+{
+ BLI_assert(result.size() > 0);
+ const float inv_len = 1.0f / static_cast<float>(result.size());
+ const float inv_len_squared = inv_len * inv_len;
+ const float inv_len_cubed = inv_len_squared * inv_len;
+
+ const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
+ const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
+ const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
+
+ float3 q0 = point_0;
+ float3 q1 = rt1 + rt2 + rt3;
+ float3 q2 = 2.0f * rt2 + 6.0f * rt3;
+ float3 q3 = 6.0f * rt3;
+ for (const int i : result.index_range()) {
+ result[i] = q0;
+ q0 += q1;
+ q1 += q2;
+ q2 += q3;
+ }
+}
+
+void BezierSpline::evaluate_bezier_segment(const int index,
+ const int next_index,
+ MutableSpan<float3> positions) const
+{
+ if (this->segment_is_vector(index)) {
+ BLI_assert(positions.size() == 1);
+ positions.first() = positions_[index];
+ }
+ else {
+ bezier_forward_difference_3d(positions_[index],
+ handle_positions_right_[index],
+ handle_positions_left_[next_index],
+ positions_[next_index],
+ positions);
+ }
+}
+
+/**
+ * Returns access to a cache of offsets into the evaluated point array for each control point.
+ * While most control point edges generate the number of edges specified by the resolution, vector
+ * segments only generate one edge.
+ *
+ * \note The length of the result is one greater than the number of points, so that the last item
+ * is the total number of evaluated points. This is useful to avoid recalculating the size of the
+ * last segment everywhere.
+ */
+Span<int> BezierSpline::control_point_offsets() const
+{
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ std::lock_guard lock{offset_cache_mutex_};
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ const int points_len = this->size();
+ offset_cache_.resize(points_len + 1);
+
+ MutableSpan<int> offsets = offset_cache_;
+
+ int offset = 0;
+ for (const int i : IndexRange(points_len)) {
+ offsets[i] = offset;
+ offset += this->segment_is_vector(i) ? 1 : resolution_;
+ }
+ offsets.last() = offset;
+
+ offset_cache_dirty_ = false;
+ return offsets;
+}
+
+static void calculate_mappings_linear_resolution(Span<int> offsets,
+ const int size,
+ const int resolution,
+ const bool is_cyclic,
+ MutableSpan<float> r_mappings)
+{
+ const float first_segment_len_inv = 1.0f / offsets[1];
+ for (const int i : IndexRange(0, offsets[1])) {
+ r_mappings[i] = i * first_segment_len_inv;
+ }
+
+ const int grain_size = std::max(2048 / resolution, 1);
+ parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
+ for (const int i_control_point : range) {
+ const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
+ const float segment_len_inv = 1.0f / segment_len;
+ for (const int i : IndexRange(segment_len)) {
+ r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int last_segment_len = offsets[size] - offsets[size - 1];
+ const float last_segment_len_inv = 1.0f / last_segment_len;
+ for (const int i : IndexRange(last_segment_len)) {
+ r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
+ }
+ }
+ else {
+ r_mappings.last() = size - 1;
+ }
+}
+
+/**
+ * Returns non-owning access to an array of values containing the information necessary to
+ * interpolate values from the original control points to evaluated points. The control point
+ * index is the integer part of each value, and the factor used for interpolating to the next
+ * control point is the remaining factional part.
+ */
+Span<float> BezierSpline::evaluated_mappings() const
+{
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ std::lock_guard lock{mapping_cache_mutex_};
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_mapping_cache_.resize(eval_size);
+ MutableSpan<float> mappings = evaluated_mapping_cache_;
+
+ if (eval_size == 1) {
+ mappings.first() = 0.0f;
+ mapping_cache_dirty_ = false;
+ return mappings;
+ }
+
+ Span<int> offsets = this->control_point_offsets();
+
+ calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings);
+
+ mapping_cache_dirty_ = false;
+ return mappings;
+}
+
+Span<float3> BezierSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ this->ensure_auto_handles();
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ MutableSpan<float3> positions = evaluated_position_cache_;
+
+ Span<int> offsets = this->control_point_offsets();
+
+ const int grain_size = std::max(512 / resolution_, 1);
+ parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) {
+ for (const int i : range) {
+ this->evaluate_bezier_segment(
+ i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
+ }
+ });
+ if (is_cyclic_) {
+ this->evaluate_bezier_segment(
+ size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1]));
+ }
+ else {
+ /* Since evaluating the bezier segment doesn't add the final point,
+ * it must be added manually in the non-cyclic case. */
+ positions.last() = positions_.last();
+ }
+
+ position_cache_dirty_ = false;
+ return positions;
+}
+
+/**
+ * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
+ * to interpolate data from control points to evaluated points between them. The next control
+ * point index result will not overflow the size of the control point vectors.
+ */
+BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
+ const float index_factor) const
+{
+ const int points_len = this->size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 2, points_len - 1, 1.0f};
+}
+
+/* Use a spline argument to avoid adding this to the header. */
+template<typename T>
+static void interpolate_to_evaluated_points_impl(const BezierSpline &spline,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ Span<float> mappings = spline.evaluated_mappings();
+
+ for (const int i : result_data.index_range()) {
+ BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
+ mappings[i]);
+
+ const T &value = source_data[interp.control_point_index];
+ const T &next_value = source_data[interp.next_control_point_index];
+
+ result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
+ }
+}
+
+blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return source_data.shallow_copy();
+ }
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(eval_size);
+ interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
new file mode 100644
index 00000000000..ae691d26cdb
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -0,0 +1,434 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr NURBSpline::copy() const
+{
+ return std::make_unique<NURBSpline>(*this);
+}
+
+int NURBSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ BLI_assert(size == weights_.size());
+ return size;
+}
+
+int NURBSpline::resolution() const
+{
+ return resolution_;
+}
+
+void NURBSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+uint8_t NURBSpline::order() const
+{
+ return order_;
+}
+
+void NURBSpline::set_order(const uint8_t value)
+{
+ BLI_assert(value >= 2 && value <= 6);
+ order_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void NURBSpline::add_point(const float3 position,
+ const float radius,
+ const float tilt,
+ const float weight)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ weights_.append(weight);
+ knots_dirty_ = true;
+ this->mark_cache_invalid();
+}
+
+void NURBSpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ weights_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> NURBSpline::positions()
+{
+ return positions_;
+}
+Span<float3> NURBSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> NURBSpline::radii()
+{
+ return radii_;
+}
+Span<float> NURBSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> NURBSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> NURBSpline::tilts() const
+{
+ return tilts_;
+}
+MutableSpan<float> NURBSpline::weights()
+{
+ return weights_;
+}
+Span<float> NURBSpline::weights() const
+{
+ return weights_;
+}
+
+void NURBSpline::mark_cache_invalid()
+{
+ basis_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int NURBSpline::evaluated_points_size() const
+{
+ if (!this->check_valid_size_and_order()) {
+ return 0;
+ }
+ return resolution_ * this->segments_size();
+}
+
+void NURBSpline::correct_end_tangents() const
+{
+}
+
+bool NURBSpline::check_valid_size_and_order() const
+{
+ if (this->size() < order_) {
+ return false;
+ }
+
+ if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) {
+ if (order_ == 4) {
+ if (this->size() < 5) {
+ return false;
+ }
+ }
+ else if (order_ != 3) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int NURBSpline::knots_size() const
+{
+ const int size = this->size() + order_;
+ return is_cyclic_ ? size + order_ - 1 : size;
+}
+
+void NURBSpline::calculate_knots() const
+{
+ const KnotsMode mode = this->knots_mode;
+ const int length = this->size();
+ const int order = order_;
+
+ knots_.resize(this->knots_size());
+
+ MutableSpan<float> knots = knots_;
+
+ if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
+ for (const int i : knots.index_range()) {
+ knots[i] = static_cast<float>(i);
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::EndPoint) {
+ float k = 0.0f;
+ for (const int i : IndexRange(1, knots.size())) {
+ knots[i - 1] = k;
+ if (i >= order && i <= length) {
+ k += 1.0f;
+ }
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::Bezier) {
+ BLI_assert(ELEM(order, 3, 4));
+ if (order == 3) {
+ float k = 0.6f;
+ for (const int i : knots.index_range()) {
+ if (i >= order && i <= length) {
+ k += 0.5f;
+ }
+ knots[i] = std::floor(k);
+ }
+ }
+ else {
+ float k = 0.34f;
+ for (const int i : knots.index_range()) {
+ knots[i] = std::floor(k);
+ k += 1.0f / 3.0f;
+ }
+ }
+ }
+
+ if (is_cyclic_) {
+ const int b = length + order - 1;
+ if (order > 2) {
+ for (const int i : IndexRange(1, order - 2)) {
+ if (knots[b] != knots[b - i]) {
+ if (i == order - 1) {
+ knots[length + order - 2] += 1.0f;
+ break;
+ }
+ }
+ }
+ }
+
+ int c = order;
+ for (int i = b; i < this->knots_size(); i++) {
+ knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
+ c--;
+ }
+ }
+}
+
+Span<float> NURBSpline::knots() const
+{
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ std::lock_guard lock{knots_mutex_};
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ this->calculate_knots();
+
+ knots_dirty_ = false;
+
+ return knots_;
+}
+
+static void calculate_basis_for_point(const float parameter,
+ const int points_len,
+ const int order,
+ Span<float> knots,
+ MutableSpan<float> basis_buffer,
+ NURBSpline::BasisCache &basis_cache)
+{
+ /* Clamp parameter due to floating point inaccuracy. */
+ const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
+
+ int start = 0;
+ int end = 0;
+ for (const int i : IndexRange(points_len + order - 1)) {
+ const bool knots_equal = knots[i] == knots[i + 1];
+ if (knots_equal || t < knots[i] || t > knots[i + 1]) {
+ basis_buffer[i] = 0.0f;
+ continue;
+ }
+
+ basis_buffer[i] = 1.0f;
+ start = std::max(i - order - 1, 0);
+ end = i;
+ basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
+ break;
+ }
+ basis_buffer[points_len + order - 1] = 0.0f;
+
+ for (const int i_order : IndexRange(2, order - 1)) {
+ if (end + i_order >= points_len + order) {
+ end = points_len + order - 1 - i_order;
+ }
+ for (const int i : IndexRange(start, end - start + 1)) {
+ float new_basis = 0.0f;
+ if (basis_buffer[i] != 0.0f) {
+ new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
+ }
+
+ if (basis_buffer[i + 1] != 0.0f) {
+ new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
+ (knots[i + i_order] - knots[i + 1]);
+ }
+
+ basis_buffer[i] = new_basis;
+ }
+ }
+
+ /* Shrink the range of calculated values to avoid storing unnecessary zeros. */
+ while (basis_buffer[start] == 0.0f && start < end) {
+ start++;
+ }
+ while (basis_buffer[end] == 0.0f && end > start) {
+ end--;
+ }
+
+ basis_cache.weights.clear();
+ basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
+ basis_cache.start_index = start;
+}
+
+void NURBSpline::calculate_basis_cache() const
+{
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{basis_cache_mutex_};
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ const int points_len = this->size();
+ const int eval_size = this->evaluated_points_size();
+ BLI_assert(this->evaluated_edges_size() > 0);
+ basis_cache_.resize(eval_size);
+
+ const int order = this->order();
+ Span<float> control_weights = this->weights();
+ Span<float> knots = this->knots();
+
+ MutableSpan<BasisCache> basis_cache(basis_cache_);
+
+ /* This buffer is reused by each basis calculation to store temporary values.
+ * Theoretically it could be optimized away in the future. */
+ Array<float> basis_buffer(this->knots_size());
+
+ const float start = knots[order - 1];
+ const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len];
+ const float step = (end - start) / this->evaluated_edges_size();
+ float parameter = start;
+ for (const int i : IndexRange(eval_size)) {
+ BasisCache &basis = basis_cache[i];
+ calculate_basis_for_point(
+ parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis);
+ BLI_assert(basis.weights.size() <= order);
+
+ for (const int j : basis.weights.index_range()) {
+ const int point_index = (basis.start_index + j) % points_len;
+ basis.weights[j] *= control_weights[point_index];
+ }
+
+ parameter += step;
+ }
+
+ basis_cache_dirty_ = false;
+}
+
+template<typename T>
+void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ const int points_len = source_data.size();
+ BLI_assert(result_data.size() == weights.size());
+ blender::attribute_math::DefaultMixer<T> mixer(result_data);
+
+ for (const int i : result_data.index_range()) {
+ Span<float> point_weights = weights[i].weights;
+ const int start_index = weights[i].start_index;
+
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (start_index + j) % points_len;
+ mixer.mix_in(i, source_data[point_index], point_weights[j]);
+ }
+ }
+
+ mixer.finalize();
+}
+
+blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ this->calculate_basis_cache();
+ Span<BasisCache> weights(basis_cache_);
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(this->evaluated_points_size());
+ interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
+
+Span<float3> NURBSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ blender::fn::GVArray_Typed<float3> evaluated_positions{
+ this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span<float3>(positions_))};
+
+ evaluated_positions->materialize(evaluated_position_cache_);
+
+ position_cache_dirty_ = false;
+ return evaluated_position_cache_;
+}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
new file mode 100644
index 00000000000..5c33b0052fc
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -0,0 +1,117 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_spline.hh"
+
+using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr PolySpline::copy() const
+{
+ return std::make_unique<PolySpline>(*this);
+}
+
+int PolySpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void PolySpline::add_point(const float3 position, const float radius, const float tilt)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void PolySpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> PolySpline::positions()
+{
+ return positions_;
+}
+Span<float3> PolySpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> PolySpline::radii()
+{
+ return radii_;
+}
+Span<float> PolySpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> PolySpline::tilts()
+{
+ return tilts_;
+}
+Span<float> PolySpline::tilts() const
+{
+ return tilts_;
+}
+
+void PolySpline::mark_cache_invalid()
+{
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int PolySpline::evaluated_points_size() const
+{
+ return this->size();
+}
+
+void PolySpline::correct_end_tangents() const
+{
+}
+
+Span<float3> PolySpline::evaluated_positions() const
+{
+ return this->positions();
+}
+
+/**
+ * Poly spline interpolation from control points to evaluated points is a special case, since
+ * the result data is the same as the input data. This function returns a GVArray that points to
+ * the original data. Therefore the lifetime of the returned virtual array must not be longer than
+ * the source data.
+ */
+blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ return source_data.shallow_copy();
+}
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 7b03839f659..c0ce57818d1 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -39,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
-static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Volume *volume_input)
+static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- Volume *volume = volume_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if (mti->modifyVolume) {
- /* Ensure we are not modifying the input. */
- if (volume == volume_input) {
- volume = BKE_volume_copy_for_eval(volume, true);
- }
-
- Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
-
- if (volume_next && volume_next != volume) {
- /* If the modifier returned a new volume, release the old one. */
- if (volume != volume_input) {
- BKE_id_free(nullptr, volume);
- }
- volume = volume_next;
- }
+ if (mti->modifyGeometrySet) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
-
- return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
+static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
+{
+ if (!geometry_set.has<VolumeComponent>()) {
+ return nullptr;
+ }
+ VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
+ Volume *volume = volume_component.release();
+ if (volume != nullptr) {
+ /* Add back, but only as read-only non-owning component. */
+ volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
+ }
+ else {
+ /* The component was empty, we can remove it. */
+ geometry_set.remove<VolumeComponent>();
+ }
+ return volume;
+}
+
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
- Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
+ GeometrySet geometry_set;
+ geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
+ volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
+
+ Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
+
+ /* If the geometry set did not contain a volume, we still create an empty one. */
+ if (volume_eval == nullptr) {
+ volume_eval = BKE_volume_new_for_eval(volume);
+ }
/* Assign evaluated object. */
- const bool is_owned = (volume != volume_eval);
- BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
+ const bool eval_is_owned = (volume != volume_eval);
+ BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index e5550cee124..39f65d76e3c 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -56,6 +56,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/imgutils.h>
+# include <libavutil/opt.h>
# include <libavutil/rational.h>
# include <libavutil/samplefmt.h>
# include <libswscale/swscale.h>
@@ -80,6 +81,8 @@ typedef struct FFMpegContext {
int ffmpeg_preset; /* see eFFMpegPreset */
AVFormatContext *outfile;
+ AVCodecContext *video_codec;
+ AVCodecContext *audio_codec;
AVStream *video_stream;
AVStream *audio_stream;
AVFrame *current_frame; /* Image frame in output pixel format. */
@@ -91,10 +94,6 @@ typedef struct FFMpegContext {
uint8_t *audio_input_buffer;
uint8_t *audio_deinterleave_buffer;
int audio_input_samples;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- uint8_t *audio_output_buffer;
- int audio_outbuf_size;
-# endif
double audio_time;
bool audio_deinterleave;
int audio_sample_size;
@@ -141,33 +140,22 @@ static int request_float_audio_buffer(int codec_id)
}
# ifdef WITH_AUDASPACE
+
static int write_audio_frame(FFMpegContext *context)
{
- AVCodecContext *c = NULL;
- AVPacket pkt;
AVFrame *frame = NULL;
- int got_output = 0;
-
- c = context->audio_stream->codec;
-
- av_init_packet(&pkt);
- pkt.size = 0;
- pkt.data = NULL;
+ AVCodecContext *c = context->audio_codec;
AUD_Device_read(
context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
-# ifdef FFMPEG_HAVE_ENCODE_AUDIO2
frame = av_frame_alloc();
- av_frame_unref(frame);
frame->pts = context->audio_time / av_q2d(c->time_base);
frame->nb_samples = context->audio_input_samples;
frame->format = c->sample_fmt;
frame->channels = c->channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
frame->channel_layout = c->channel_layout;
-# endif
if (context->audio_deinterleave) {
int channel, i;
@@ -195,61 +183,49 @@ static int write_audio_frame(FFMpegContext *context)
context->audio_input_samples * c->channels * context->audio_sample_size,
1);
- if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ int success = 0;
- if (!got_output) {
- av_frame_free(&frame);
- return 0;
+ int ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
-# else
- pkt.size = avcodec_encode_audio(c,
- context->audio_output_buffer,
- context->audio_outbuf_size,
- (short *)context->audio_input_buffer);
- if (pkt.size < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ AVPacket *pkt = av_packet_alloc();
- pkt.data = context->audio_output_buffer;
- got_output = 1;
-# endif
+ while (ret >= 0) {
- if (got_output) {
- if (pkt.pts != AV_NOPTS_VALUE) {
- pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base);
+ ret = avcodec_receive_packet(c, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
}
- if (pkt.dts != AV_NOPTS_VALUE) {
- pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base);
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
- if (pkt.duration > 0) {
- pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base);
+
+ av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base);
+ if (pkt->duration > 0) {
+ pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base);
}
- pkt.stream_index = context->audio_stream->index;
+ pkt->stream_index = context->audio_stream->index;
- pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt->flags |= AV_PKT_FLAG_KEY;
- if (av_interleaved_write_frame(context->outfile, &pkt) != 0) {
- fprintf(stderr, "Error writing audio packet!\n");
- if (frame) {
- av_frame_free(&frame);
- }
- return -1;
+ int write_ret = av_interleaved_write_frame(context->outfile, pkt);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing audio packet: %s\n", av_err2str(write_ret));
+ success = -1;
+ break;
}
-
- av_free_packet(&pkt);
}
- if (frame) {
- av_frame_free(&frame);
- }
+ av_packet_free(&pkt);
+ av_frame_free(&frame);
- return 0;
+ return success;
}
# endif /* #ifdef WITH_AUDASPACE */
@@ -265,14 +241,15 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height)
if (!f) {
return NULL;
}
- size = avpicture_get_size(pix_fmt, width, height);
+ size = av_image_get_buffer_size(pix_fmt, width, height, 1);
/* allocate the actual picture buffer */
buf = MEM_mallocN(size, "AVFrame buffer");
if (!buf) {
free(f);
return NULL;
}
- avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height);
+
+ av_image_fill_arrays(f->data, f->linesize, buf, pix_fmt, width, height, 1);
f->format = pix_fmt;
f->width = width;
f->height = height;
@@ -342,58 +319,57 @@ static const char **get_file_extensions(int format)
}
/* Write a frame to the output file */
-static int write_video_frame(
- FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
+static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, ReportList *reports)
{
- int got_output;
int ret, success = 1;
- AVCodecContext *c = context->video_stream->codec;
- AVPacket packet = {0};
+ AVPacket *packet = av_packet_alloc();
- av_init_packet(&packet);
+ AVCodecContext *c = context->video_codec;
frame->pts = cfra;
- ret = avcodec_encode_video2(c, &packet, frame, &got_output);
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ success = -1;
+ }
- if (ret >= 0 && got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets available. */
+ break;
}
- else {
- PRINT("Video Frame DTS: not set\n");
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding frame: %s\n", av_err2str(ret));
+ break;
}
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- success = (ret == 0);
- }
- else if (ret < 0) {
- success = 0;
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+ if (av_interleaved_write_frame(context->outfile, packet) != 0) {
+ success = -1;
+ break;
+ }
}
if (!success) {
BKE_report(reports, RPT_ERROR, "Error writing frame");
+ PRINT("Error writing frame: %s\n", av_err2str(ret));
}
+ av_packet_free(&packet);
+
return success;
}
/* read and encode a frame of audio from the buffer */
-static AVFrame *generate_video_frame(FFMpegContext *context,
- const uint8_t *pixels,
- ReportList *reports)
+static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
{
- AVCodecContext *c = context->video_stream->codec;
- int height = c->height;
+ AVCodecParameters *codec = context->video_stream->codecpar;
+ int height = codec->height;
AVFrame *rgb_frame;
if (context->img_convert_frame != NULL) {
@@ -438,7 +414,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
(const uint8_t *const *)rgb_frame->data,
rgb_frame->linesize,
0,
- c->height,
+ codec->height,
context->current_frame->data,
context->current_frame->linesize);
}
@@ -446,9 +422,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
return context->current_frame;
}
-static void set_ffmpeg_property_option(AVCodecContext *c,
- IDProperty *prop,
- AVDictionary **dictionary)
+static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary)
{
char name[128];
char *param;
@@ -536,7 +510,7 @@ static void set_ffmpeg_properties(RenderData *rd,
for (curr = prop->data.group.first; curr; curr = curr->next) {
if (ffmpeg_proprty_valid(c, prop_name, curr)) {
- set_ffmpeg_property_option(c, curr, dictionary);
+ set_ffmpeg_property_option(curr, dictionary);
}
}
}
@@ -553,7 +527,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -567,7 +540,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
/* Set up the codec context */
- c = st->codec;
+ context->video_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->video_codec;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;
@@ -650,11 +624,9 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- /* Deprecated and not doing anything since July 2015, deleted in recent ffmpeg */
- // c->me_method = ME_EPZS;
-
codec = avcodec_find_encoder(c->codec_id);
if (!codec) {
+ avcodec_free_context(&c);
return NULL;
}
@@ -714,7 +686,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if ((of->oformat->flags & AVFMT_GLOBALHEADER)) {
PRINT("Using global header\n");
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* xasp & yasp got float lately... */
@@ -742,6 +714,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if (avcodec_open2(c, codec, &opts) < 0) {
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
return NULL;
}
av_dict_free(&opts);
@@ -769,6 +742,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
NULL);
}
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
@@ -780,7 +755,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -792,7 +766,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
}
st->id = 1;
- c = st->codec;
+ context->audio_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->audio_codec;
c->thread_count = BLI_system_thread_count();
c->thread_type = FF_THREAD_SLICE;
@@ -804,7 +779,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->channels = rd->ffcodecdata.audio_channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
switch (rd->ffcodecdata.audio_channels) {
case FFM_CHANNELS_MONO:
c->channel_layout = AV_CH_LAYOUT_MONO;
@@ -822,7 +796,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->channel_layout = AV_CH_LAYOUT_7POINT1;
break;
}
-# endif
if (request_float_audio_buffer(codec_id)) {
/* mainly for AAC codec which is experimental */
@@ -833,6 +806,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
codec = avcodec_find_encoder(c->codec_id);
if (!codec) {
// XXX error("Couldn't find a valid audio codec");
+ avcodec_free_context(&c);
return NULL;
}
@@ -844,13 +818,13 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
* Float samples in particular are not always supported. */
const enum AVSampleFormat *p = codec->sample_fmts;
for (; *p != -1; p++) {
- if (*p == st->codec->sample_fmt) {
+ if (*p == c->sample_fmt) {
break;
}
}
if (*p == -1) {
/* sample format incompatible with codec. Defaulting to a format known to work */
- st->codec->sample_fmt = codec->sample_fmts[0];
+ c->sample_fmt = codec->sample_fmts[0];
}
}
@@ -859,18 +833,18 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int best = 0;
int best_dist = INT_MAX;
for (; *p; p++) {
- int dist = abs(st->codec->sample_rate - *p);
+ int dist = abs(c->sample_rate - *p);
if (dist < best_dist) {
best_dist = dist;
best = *p;
}
}
/* best is the closest supported sample rate (same as selected if best_dist == 0) */
- st->codec->sample_rate = best;
+ c->sample_rate = best;
}
if (of->oformat->flags & AVFMT_GLOBALHEADER) {
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
set_ffmpeg_properties(rd, c, "audio", &opts);
@@ -879,32 +853,25 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
// XXX error("Couldn't initialize audio codec");
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
return NULL;
}
av_dict_free(&opts);
/* need to prevent floating point exception when using vorbis audio codec,
* initialize this value in the same way as it's done in FFmpeg itself (sergey) */
- st->codec->time_base.num = 1;
- st->codec->time_base.den = st->codec->sample_rate;
-
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_outbuf_size = FF_MIN_BUFFER_SIZE;
-# endif
+ c->time_base.num = 1;
+ c->time_base.den = c->sample_rate;
if (c->frame_size == 0) {
/* Used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
* not sure if that is needed anymore, so let's try out if there are any
* complaints regarding some FFmpeg versions users might have. */
- context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
+ context->audio_input_samples = AV_INPUT_BUFFER_MIN_SIZE * 8 / c->bits_per_coded_sample /
+ c->channels;
}
else {
context->audio_input_samples = c->frame_size;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) {
- context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
- }
-# endif
}
context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
@@ -913,10 +880,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_input_buffer = (uint8_t *)av_malloc(context->audio_input_samples * c->channels *
context->audio_sample_size);
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_output_buffer = (uint8_t *)av_malloc(context->audio_outbuf_size);
-# endif
-
if (context->audio_deinterleave) {
context->audio_deinterleave_buffer = (uint8_t *)av_malloc(
context->audio_input_samples * c->channels * context->audio_sample_size);
@@ -924,6 +887,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_time = 0.0f;
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
/* essential functions -- start, append, end */
@@ -949,7 +914,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
static void ffmpeg_add_metadata_callback(void *data,
const char *propname,
char *propvalue,
- int len)
+ int UNUSED(len))
{
AVDictionary **metadata = (AVDictionary **)data;
av_dict_set(metadata, propname, propvalue, 0);
@@ -1040,7 +1005,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
fmt->audio_codec = context->ffmpeg_audio_codec;
- BLI_strncpy(of->filename, name, sizeof(of->filename));
+ of->url = av_strdup(name);
/* set the codec to the user's selection */
switch (context->ffmpeg_type) {
case FFMPEG_AVI:
@@ -1105,9 +1070,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->video_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Video stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing video stream");
+ PRINT("Error initializing video stream");
}
goto fail;
}
@@ -1119,9 +1086,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->audio_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Audio stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing audio stream");
+ PRINT("Error initializing audio stream");
}
goto fail;
}
@@ -1129,6 +1098,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!(fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) {
BKE_report(reports, RPT_ERROR, "Could not open file for writing");
+ PRINT("Could not open file for writing\n");
goto fail;
}
}
@@ -1138,10 +1108,12 @@ static int start_ffmpeg_impl(FFMpegContext *context,
&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
}
- if (avformat_write_header(of, NULL) < 0) {
+ int ret = avformat_write_header(of, NULL);
+ if (ret < 0) {
BKE_report(reports,
RPT_ERROR,
"Could not initialize streams, probably unsupported codec combination");
+ PRINT("Could not write media header: %s\n", av_err2str(ret));
goto fail;
}
@@ -1156,13 +1128,11 @@ fail:
avio_close(of->pb);
}
- if (context->video_stream && context->video_stream->codec) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream) {
context->video_stream = NULL;
}
- if (context->audio_stream && context->audio_stream->codec) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream) {
context->audio_stream = NULL;
}
@@ -1190,46 +1160,36 @@ fail:
*/
static void flush_ffmpeg(FFMpegContext *context)
{
- int ret = 0;
+ AVCodecContext *c = context->video_codec;
+ AVPacket *packet = av_packet_alloc();
- AVCodecContext *c = context->video_stream->codec;
- /* get the delayed frames */
- while (1) {
- int got_output;
- AVPacket packet = {0};
- av_init_packet(&packet);
+ avcodec_send_frame(c, NULL);
- ret = avcodec_encode_video2(c, &packet, NULL, &got_output);
- if (ret < 0) {
- fprintf(stderr, "Error encoding delayed frame %d\n", ret);
+ /* Get the packets frames. */
+ int ret = 1;
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
break;
}
- if (!got_output) {
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding delayed frame: %s\n", av_err2str(ret));
break;
}
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
- }
- else {
- PRINT("Video Frame DTS: not set\n");
- }
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- if (ret != 0) {
- fprintf(stderr, "Error writing delayed frame %d\n", ret);
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+
+ int write_ret = av_interleaved_write_frame(context->outfile, packet);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret));
break;
}
}
- avcodec_flush_buffers(context->video_stream->codec);
+
+ av_packet_free(&packet);
}
/* **********************************************************************
@@ -1327,7 +1287,8 @@ int BKE_ffmpeg_start(void *context_v,
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
# ifdef WITH_AUDASPACE
if (context->audio_stream) {
- AVCodecContext *c = context->audio_stream->codec;
+ AVCodecContext *c = context->audio_codec;
+
AUD_DeviceSpecs specs;
specs.channels = c->channels;
@@ -1354,10 +1315,6 @@ int BKE_ffmpeg_start(void *context_v,
specs.rate = rd->ffcodecdata.audio_mixrate;
context->audio_mixdown_device = BKE_sound_mixdown(
scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
-# ifdef FFMPEG_CODEC_TIME_BASE
- c->time_base.den = specs.rate;
- c->time_base.num = 1;
-# endif
}
# endif
return success;
@@ -1398,8 +1355,8 @@ int BKE_ffmpeg_append(void *context_v,
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
if (context->video_stream) {
- avframe = generate_video_frame(context, (unsigned char *)pixels, reports);
- success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports));
+ avframe = generate_video_frame(context, (unsigned char *)pixels);
+ success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports));
if (context->ffmpeg_autosplit) {
if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
@@ -1428,9 +1385,11 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
context->audio_mixdown_device = NULL;
}
}
+# else
+ UNUSED_VARS(is_autosplit);
# endif
- if (context->video_stream && context->video_stream->codec) {
+ if (context->video_stream) {
PRINT("Flushing delayed frames...\n");
flush_ffmpeg(context);
}
@@ -1441,14 +1400,12 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
/* Close the video codec */
- if (context->video_stream != NULL && context->video_stream->codec != NULL) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream != NULL) {
PRINT("zero video stream %p\n", context->video_stream);
context->video_stream = NULL;
}
- if (context->audio_stream != NULL && context->audio_stream->codec != NULL) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream != NULL) {
context->audio_stream = NULL;
}
@@ -1467,6 +1424,16 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
avio_close(context->outfile->pb);
}
}
+
+ if (context->video_codec != NULL) {
+ avcodec_free_context(&context->video_codec);
+ context->video_codec = NULL;
+ }
+ if (context->audio_codec != NULL) {
+ avcodec_free_context(&context->audio_codec);
+ context->audio_codec = NULL;
+ }
+
if (context->outfile != NULL) {
avformat_free_context(context->outfile);
context->outfile = NULL;
@@ -1475,12 +1442,6 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
av_free(context->audio_input_buffer);
context->audio_input_buffer = NULL;
}
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (context->audio_output_buffer != NULL) {
- av_free(context->audio_output_buffer);
- context->audio_output_buffer = NULL;
- }
-# endif
if (context->audio_deinterleave_buffer != NULL) {
av_free(context->audio_deinterleave_buffer);
@@ -1560,12 +1521,12 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd,
switch (o->type) {
case AV_OPT_TYPE_INT:
case AV_OPT_TYPE_INT64:
- val.i = FFMPEG_DEF_OPT_VAL_INT(o);
+ val.i = o->default_val.i64;
idp_type = IDP_INT;
break;
case AV_OPT_TYPE_DOUBLE:
case AV_OPT_TYPE_FLOAT:
- val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o);
+ val.f = o->default_val.dbl;
idp_type = IDP_FLOAT;
break;
case AV_OPT_TYPE_STRING:
@@ -1707,16 +1668,9 @@ static void ffmpeg_set_expert_options(RenderData *rd)
BKE_ffmpeg_property_add_string(rd, "video", "trellis:0");
BKE_ffmpeg_property_add_string(rd, "video", "weightb:1");
-# ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:dct8x8");
- BKE_ffmpeg_property_add_string(rd, "video", "directpred:3");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:fastpskip");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:wpred");
-# else
BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1");
BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1");
BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2");
-# endif
}
else if (codec_id == AV_CODEC_ID_DNXHD) {
if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
@@ -1871,14 +1825,12 @@ bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd)
{
int codec = rd->ffcodecdata.codec;
-# ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
- /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */
- if (codec == AV_CODEC_ID_FFV1) {
- return true;
- }
-# endif
-
- return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV);
+ return ELEM(codec,
+ AV_CODEC_ID_FFV1,
+ AV_CODEC_ID_QTRLE,
+ AV_CODEC_ID_PNG,
+ AV_CODEC_ID_VP9,
+ AV_CODEC_ID_HUFFYUV);
}
void *BKE_ffmpeg_context_create(void)
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 706bcac4f17..71b5a74ddf7 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -82,6 +82,10 @@ typedef struct NlaEvalChannelSnapshot {
/** For an upper snapshot channel, marks values that should be blended. */
NlaValidMask blend_domain;
+ /** Only used for keyframe remapping. Any values not in the \a remap_domain will not be used
+ * for keyframe remapping. */
+ NlaValidMask remap_domain;
+
int length; /* Number of values in the property. */
bool is_base; /* Base snapshot of the channel. */
@@ -196,6 +200,13 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
const float upper_influence,
NlaEvalSnapshot *r_blended_snapshot);
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h
index 680c4bc78da..4b5a7d671f2 100644
--- a/source/blender/blenlib/BLI_compiler_attrs.h
+++ b/source/blender/blenlib/BLI_compiler_attrs.h
@@ -98,3 +98,10 @@
#else
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
#endif
+
+/* Alignment directive */
+#ifdef _WIN64
+# define ALIGN_STRUCT __declspec(align(64))
+#else
+# define ALIGN_STRUCT
+#endif
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
new file mode 100644
index 00000000000..89be4cad848
--- /dev/null
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -0,0 +1,73 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#ifdef WITH_TBB
+# include <tbb/enumerable_thread_specific.h>
+#endif
+
+#include <atomic>
+#include <mutex>
+
+#include "BLI_map.hh"
+#include "BLI_utility_mixins.hh"
+
+namespace blender {
+
+namespace enumerable_thread_specific_utils {
+inline std::atomic<int> next_id = 0;
+inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed);
+} // namespace enumerable_thread_specific_utils
+
+/**
+ * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we
+ * want to be able to build without tbb.
+ *
+ * More features of the tbb version can be wrapped when they are used.
+ */
+template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable {
+#ifdef WITH_TBB
+
+ private:
+ tbb::enumerable_thread_specific<T> values_;
+
+ public:
+ T &local()
+ {
+ return values_.local();
+ }
+
+#else /* WITH_TBB */
+
+ private:
+ std::mutex mutex_;
+ /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that
+ * their addresses do not change when the map grows. */
+ Map<int, std::unique_ptr<T>> values_;
+
+ public:
+ T &local()
+ {
+ const int thread_id = enumerable_thread_specific_utils::thread_id;
+ std::lock_guard lock{mutex_};
+ return *values_.lookup_or_add_cb(thread_id, []() { return std::make_unique<T>(); });
+ }
+
+#endif /* WITH_TBB */
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index df80e720363..bf18dd1070e 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -87,7 +87,7 @@ typedef enum eFileAttributes {
FILE_ATTR_RESTRICTED = 1 << 6, /* Protected by OS. */
FILE_ATTR_TEMPORARY = 1 << 7, /* Used for temporary storage. */
FILE_ATTR_SPARSE_FILE = 1 << 8, /* Sparse File. */
- FILE_ATTR_OFFLINE = 1 << 9, /* Data is not immediately available. */
+ FILE_ATTR_OFFLINE = 1 << 9, /* Contents available after a short delay. */
FILE_ATTR_ALIAS = 1 << 10, /* Mac Alias or Windows LNK. File-based redirection. */
FILE_ATTR_REPARSE_POINT = 1 << 11, /* File has associated re-parse point. */
FILE_ATTR_SYMLINK = 1 << 12, /* Reference to another file. */
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index cbc4d4ed366..04aae375889 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -245,6 +245,13 @@ struct float3 {
return result;
}
+ static float3 cross(const float3 &a, const float3 &b)
+ {
+ float3 result;
+ cross_v3_v3v3(result, a, b);
+ return result;
+ }
+
static float3 project(const float3 &a, const float3 &b)
{
float3 result;
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index f5ba91bc12c..396b0b1bd21 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,37 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_normalized_axis_data(const float3 location,
+ const float3 forward,
+ const float3 up)
+ {
+ BLI_ASSERT_UNIT_V3(forward);
+ BLI_ASSERT_UNIT_V3(up);
+ float4x4 matrix;
+ const float3 cross = float3::cross(forward, up);
+ matrix.values[0][0] = forward.x;
+ matrix.values[1][0] = cross.x;
+ matrix.values[2][0] = up.x;
+ matrix.values[3][0] = location.x;
+
+ matrix.values[0][1] = forward.y;
+ matrix.values[1][1] = cross.y;
+ matrix.values[2][1] = up.y;
+ matrix.values[3][1] = location.y;
+
+ matrix.values[0][2] = forward.z;
+ matrix.values[1][2] = cross.z;
+ matrix.values[2][2] = up.z;
+ matrix.values[3][2] = location.z;
+
+ matrix.values[0][3] = 0.0f;
+ matrix.values[1][3] = 0.0f;
+ matrix.values[2][3] = 0.0f;
+ matrix.values[3][3] = 1.0f;
+
+ return matrix;
+ }
+
static float4x4 identity()
{
float4x4 mat;
@@ -116,6 +147,19 @@ struct float4x4 {
return scale;
}
+ void apply_scale(const float scale)
+ {
+ values[0][0] *= scale;
+ values[0][1] *= scale;
+ values[0][2] *= scale;
+ values[1][0] *= scale;
+ values[1][1] *= scale;
+ values[1][2] *= scale;
+ values[2][0] *= scale;
+ values[2][1] *= scale;
+ values[2][2] *= scale;
+ }
+
float4x4 inverted() const
{
float4x4 result;
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index 4022c2baa1f..fbed321534c 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -85,9 +85,12 @@
namespace blender {
/**
- * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on
- * the value. If there is no such method, this will result in a compiler error. Usually that means
- * that you have to implement a hash function using one of three strategies listed above.
+ * If there is no other specialization of #DefaultHash for a given type, look for a hash function
+ * on the type itself. Implementing a `hash()` method on a type is often significantly easier than
+ * specializing #DefaultHash.
+ *
+ * To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)`
+ * function.
*
* In the case of an enum type, the default hash is just to cast the enum value to an integer.
*/
@@ -95,12 +98,25 @@ template<typename T> struct DefaultHash {
uint64_t operator()(const T &value) const
{
if constexpr (std::is_enum_v<T>) {
+ /* For enums use the value as hash directly. */
return (uint64_t)value;
}
else {
+ /* Try to call the `hash()` function on the value. */
+ /* If this results in a compiler error, no hash function for the type has been found. */
return value.hash();
}
}
+
+ template<typename U> uint64_t operator()(const U &value) const
+ {
+ /* Try calling the static `T::hash_as(value)` function with the given value. The returned hash
+ * should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to
+ * `T` its hash does not change. */
+ /* If this results in a compiler error, no hash function for the heterogeneous lookup has been
+ * found. */
+ return T::hash_as(value);
+ }
};
/**
diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh
index 6aa97d5c5e7..7de6bcfdd98 100644
--- a/source/blender/blenlib/BLI_linear_allocator.hh
+++ b/source/blender/blenlib/BLI_linear_allocator.hh
@@ -129,6 +129,21 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
/**
+ * Construct multiple instances of a type in an array. The constructor of is called with the
+ * given arguments. The caller is responsible for calling the destructor (and not `delete`) on
+ * the constructed elements.
+ */
+ template<typename T, typename... Args>
+ MutableSpan<T> construct_array(int64_t size, Args &&... args)
+ {
+ MutableSpan<T> array = this->allocate_array<T>(size);
+ for (const int64_t i : IndexRange(size)) {
+ new (&array[i]) T(std::forward<Args>(args)...);
+ }
+ return array;
+ }
+
+ /**
* Copy the given array into a memory buffer provided by this allocator.
*/
template<typename T> MutableSpan<T> construct_array_copy(Span<T> src)
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 9fa69853e44..4d254960f34 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -247,11 +247,11 @@ class Map {
{
this->add_new_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new_as(ForwardKey &&key, ForwardValue &&... value)
{
this->add_new__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -277,11 +277,11 @@ class Map {
{
return this->add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -307,11 +307,11 @@ class Map {
{
return this->add_overwrite_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add_overwrite__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -413,12 +413,12 @@ class Map {
{
return this->pop_default_as(key, std::move(default_value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
{
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
if (slot == nullptr) {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
Value value = std::move(*slot->value());
slot->remove();
@@ -525,15 +525,15 @@ class Map {
{
return this->lookup_default_as(key, default_value);
}
- template<typename ForwardKey, typename ForwardValue>
- Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
+ template<typename ForwardKey, typename... ForwardValue>
+ Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
{
const Value *ptr = this->lookup_ptr_as(key);
if (ptr != nullptr) {
return *ptr;
}
else {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
}
@@ -557,11 +557,11 @@ class Map {
{
return this->lookup_or_add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->lookup_or_add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -605,6 +605,37 @@ class Map {
}
/**
+ * Returns the key that is stored in the set that compares equal to the given key. This invokes
+ * undefined behavior when the key is not in the map.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Slot &slot = this->lookup_slot(key, hash_(key));
+ return *slot.key();
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the map that compares equal to the given key.
+ * If the key is not in the map, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return nullptr;
+ }
+ return slot->key();
+ }
+
+ /**
* Calls the provided callback for every key-value-pair in the map. The callback is expected
* to take a `const Key &` as first and a `const Value &` as second parameter.
*/
@@ -621,19 +652,23 @@ class Map {
}
}
- /**
- * A utility iterator that reduces the amount of code when implementing the actual iterators.
- * This uses the "curiously recurring template pattern" (CRTP).
- */
- template<typename SubIterator> struct BaseIterator {
+ /* Common base class for all iterators below. */
+ struct BaseIterator {
+ public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
+ protected:
+ /* We could have separate base iterators for const and non-const iterators, but that would add
+ * more complexity than benefits right now. */
Slot *slots_;
int64_t total_slots_;
int64_t current_slot_;
- BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ friend Map;
+
+ public:
+ BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot)
: slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot)
{
}
@@ -667,11 +702,29 @@ class Map {
return !(a != b);
}
+ protected:
+ Slot &current_slot() const
+ {
+ return slots_[current_slot_];
+ }
+ };
+
+ /**
+ * A utility iterator that reduces the amount of code when implementing the actual iterators.
+ * This uses the "curiously recurring template pattern" (CRTP).
+ */
+ template<typename SubIterator> class BaseIteratorRange : public BaseIterator {
+ public:
+ BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIterator(slots, total_slots, current_slot)
+ {
+ }
+
SubIterator begin() const
{
- for (int64_t i = 0; i < total_slots_; i++) {
- if (slots_[i].is_occupied()) {
- return SubIterator(slots_, total_slots_, i);
+ for (int64_t i = 0; i < this->total_slots_; i++) {
+ if (this->slots_[i].is_occupied()) {
+ return SubIterator(this->slots_, this->total_slots_, i);
}
}
return this->end();
@@ -679,23 +732,18 @@ class Map {
SubIterator end() const
{
- return SubIterator(slots_, total_slots_, total_slots_);
- }
-
- Slot &current_slot() const
- {
- return slots_[current_slot_];
+ return SubIterator(this->slots_, this->total_slots_, this->total_slots_);
}
};
- class KeyIterator final : public BaseIterator<KeyIterator> {
+ class KeyIterator final : public BaseIteratorRange<KeyIterator> {
public:
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<KeyIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot)
{
}
@@ -705,14 +753,14 @@ class Map {
}
};
- class ValueIterator final : public BaseIterator<ValueIterator> {
+ class ValueIterator final : public BaseIteratorRange<ValueIterator> {
public:
using value_type = Value;
using pointer = const Value *;
using reference = const Value &;
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ValueIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot)
{
}
@@ -722,14 +770,14 @@ class Map {
}
};
- class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
+ class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> {
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
- MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
+ MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot)
{
}
@@ -754,14 +802,14 @@ class Map {
}
};
- class ItemIterator final : public BaseIterator<ItemIterator> {
+ class ItemIterator final : public BaseIteratorRange<ItemIterator> {
public:
using value_type = Item;
using pointer = Item *;
using reference = Item &;
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ItemIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot)
{
}
@@ -772,14 +820,14 @@ class Map {
}
};
- class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
+ class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> {
public:
using value_type = MutableItem;
using pointer = MutableItem *;
using reference = MutableItem &;
- MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
+ MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot)
{
}
@@ -840,6 +888,19 @@ class Map {
}
/**
+ * Remove the key-value-pair that the iterator is currently pointing at.
+ * It is valid to call this method while iterating over the map. However, after this method has
+ * been called, the removed element must not be accessed anymore.
+ */
+ void remove(const BaseIterator &iterator)
+ {
+ Slot &slot = iterator.current_slot();
+ BLI_assert(slot.is_occupied());
+ slot.remove();
+ removed_slots_++;
+ }
+
+ /**
* Print common statistics like size and collision count. This is useful for debugging purposes.
*/
void print_stats(StringRef name = "") const
@@ -982,7 +1043,7 @@ class Map {
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
- slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
+ slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value()));
return;
}
}
@@ -996,8 +1057,8 @@ class Map {
new (this) Map(NoExceptConstructor(), allocator);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->contains_as(key));
@@ -1005,7 +1066,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return;
}
@@ -1013,14 +1074,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return true;
}
@@ -1075,7 +1136,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, create_value());
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1086,14 +1147,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1104,15 +1165,15 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
auto create_func = [&](Value *ptr) {
- new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value));
+ new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
return true;
};
auto modify_func = [&](Value *ptr) {
- *ptr = std::forward<ForwardValue>(value);
+ *ptr = Value(std::forward<ForwardValue>(value)...);
return false;
};
return this->add_or_modify__impl(
@@ -1221,16 +1282,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
map_.reserve(n);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new(ForwardKey &&key, ForwardValue &&... value)
{
- map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
+ map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
}
- template<typename ForwardKey, typename ForwardValue>
- bool add(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add(ForwardKey &&key, ForwardValue &&... value)
{
- return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
+ return map_
+ .insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
+ .second;
}
bool contains(const Key &key) const
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index c0cb3091a8e..1b4ca11af41 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot {
* Change the state of this slot from empty/removed to occupied. The key/value has to be
* constructed by calling the constructor with the given key/value as parameter.
*/
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
state_ = Occupied;
}
@@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
return is_equal(key, key_);
}
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
}
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 26d2f1fcb29..28257ba418a 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b);
void minmax_rgb(short c[3]);
void hsv_clamp_v(float hsv[3], float v_max);
-void rgb_float_set_hue_float_offset(float *rgb, float hue_offset);
-void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset);
+void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset);
+void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset);
void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]);
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]);
@@ -143,6 +143,7 @@ MINLINE void rgba_uchar_args_test_set(unsigned char col[4],
MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack);
void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max);
+void wavelength_to_xyz_table(float *r_table, int width);
/********* lift/gamma/gain / ASC-CDL conversion ***********/
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index d767c2924d1..c744c5d13d3 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]);
float dist_to_plane_v3(const float p[3], const float plane[4]);
/* plane3 versions */
-float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_signed_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_to_plane3_v3(const float p[3], const float plane[4]);
+float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_signed_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_to_plane3_v3(const float p[3], const float plane[3]);
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
@@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]);
MINLINE float eval_shv3(float r[9], const float v[3]);
MINLINE float diffuse_shv3(float r[9], const float v[3]);
MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f);
-MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f);
+MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f);
/********************************* Form Factor *******************************/
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 378095589e8..54df88ca541 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -362,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4],
const float size[3]);
void loc_axisangle_size_to_mat4(float R[4][4],
const float loc[3],
- const float axis[4],
+ const float axis[3],
const float angle,
const float size[3]);
diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h
index 13481e27e2a..39a79efc7e2 100644
--- a/source/blender/blenlib/BLI_math_solvers.h
+++ b/source/blender/blenlib/BLI_math_solvers.h
@@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3],
float r_eigen_values[3],
float r_eigen_vectors[3][3]);
-void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]);
+void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]);
/***************************** Simple Solvers ************************************/
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index bb1e1a1c38d..b43f86af670 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]);
MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void mul_v4_fl(float r[4], float f);
MINLINE void mul_v4_v4(float r[4], const float a[4]);
-MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f);
+MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f);
MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]);
MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]);
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4],
@@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]);
MINLINE void negate_v3(float r[3]);
MINLINE void negate_v3_v3(float r[3], const float a[3]);
MINLINE void negate_v4(float r[4]);
-MINLINE void negate_v4_v4(float r[4], const float a[3]);
+MINLINE void negate_v4_v4(float r[4], const float a[4]);
MINLINE void negate_v3_short(short r[3]);
MINLINE void negate_v3_db(double r[3]);
@@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]);
/********************************* Comparison ********************************/
-MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
-bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index fe511793c46..c3876d4eaf8 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -94,7 +94,7 @@ template<typename T> class Span {
using iterator = const T *;
using size_type = int64_t;
- private:
+ protected:
const T *data_ = nullptr;
int64_t size_ = 0;
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
using iterator = T *;
using size_type = int64_t;
- private:
+ protected:
T *data_;
int64_t size_;
@@ -662,6 +662,16 @@ template<typename T> class MutableSpan {
}
/**
+ * Return a reference to the first element in the array. This invokes undefined behavior when the
+ * array is empty.
+ */
+ constexpr T &first() const
+ {
+ BLI_assert(size_ > 0);
+ return data_[0];
+ }
+
+ /**
* Returns a reference to the last element. This invokes undefined behavior when the array is
* empty.
*/
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index 19f77078c5b..d66316a95d9 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -232,13 +232,14 @@ class Stack {
{
this->push_as(std::move(value));
}
- template<typename ForwardT> void push_as(ForwardT &&value)
+ /* This is similar to `std::stack::emplace`. */
+ template<typename... ForwardT> void push_as(ForwardT &&... value)
{
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
try {
- new (top_) T(std::forward<ForwardT>(value));
+ new (top_) T(std::forward<ForwardT>(value)...);
top_++;
size_++;
}
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 328d623787b..8bea2584ca7 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -437,13 +437,17 @@ class Vector {
*/
void append(const T &value)
{
- this->ensure_space_for_one();
- this->append_unchecked(value);
+ this->append_as(value);
}
void append(T &&value)
{
+ this->append_as(std::move(value));
+ }
+ /* This is similar to `std::vector::emplace_back`. */
+ template<typename... ForwardValue> void append_as(ForwardValue &&... value)
+ {
this->ensure_space_for_one();
- this->append_unchecked(std::move(value));
+ this->append_unchecked_as(std::forward<ForwardValue>(value)...);
}
/**
@@ -474,10 +478,18 @@ class Vector {
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
* critical code.
*/
- template<typename ForwardT> void append_unchecked(ForwardT &&value)
+ void append_unchecked(const T &value)
+ {
+ this->append_unchecked_as(value);
+ }
+ void append_unchecked(T &&value)
+ {
+ this->append_unchecked_as(std::move(value));
+ }
+ template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value)
{
BLI_assert(end_ < capacity_end_);
- new (end_) T(std::forward<ForwardT>(value));
+ new (end_) T(std::forward<ForwardT>(value)...);
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -657,6 +669,21 @@ class Vector {
}
/**
+ * Return a reference to the first element in the vector.
+ * This invokes undefined behavior when the vector is empty.
+ */
+ const T &first() const
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+ T &first()
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+
+ /**
* Return how many values are currently stored in the vector.
*/
int64_t size() const
@@ -713,11 +740,12 @@ class Vector {
BLI_assert(index >= 0);
BLI_assert(index < this->size());
T *element_to_remove = begin_ + index;
- if (element_to_remove < end_) {
- *element_to_remove = std::move(*(end_ - 1));
+ T *last_element = end_ - 1;
+ if (element_to_remove < last_element) {
+ *element_to_remove = std::move(*last_element);
}
- end_--;
- end_->~T();
+ end_ = last_element;
+ last_element->~T();
UPDATE_VECTOR_SIZE(this);
}
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index 14b38d564cb..567e4fd8128 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -398,6 +398,55 @@ class VectorSet {
}
/**
+ * Return the index of the key in the vector. If the key is not in the set, add it and return its
+ * index.
+ */
+ int64_t index_of_or_add(const Key &key)
+ {
+ return this->index_of_or_add_as(key);
+ }
+ int64_t index_of_or_add(Key &&key)
+ {
+ return this->index_of_or_add_as(std::move(key));
+ }
+ template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key)
+ {
+ return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key));
+ }
+
+ /**
+ * Returns the key that is stored in the vector set that compares equal to the given key. This
+ * invokes undefined behavior when the key is not in the set.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Key *key_ptr = this->lookup_key_ptr_as(key);
+ BLI_assert(key_ptr != nullptr);
+ return *key_ptr;
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the vector set that compares equal to the given
+ * key. If the key is not in the set, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const int64_t index = this->index_of_try__impl(key, hash_(key));
+ if (index >= 0) {
+ return keys_ + index;
+ }
+ return nullptr;
+ }
+
+ /**
* Get a pointer to the beginning of the array containing all keys.
*/
const Key *data() const
@@ -484,6 +533,14 @@ class VectorSet {
}
/**
+ * Remove all keys from the vector set.
+ */
+ void clear()
+ {
+ this->noexcept_reset();
+ }
+
+ /**
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the set.
*/
@@ -652,6 +709,26 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_END();
}
+ template<typename ForwardKey>
+ int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash)
+ {
+ this->ensure_can_add();
+
+ VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
+ return slot.index();
+ }
+ if (slot.is_empty()) {
+ const int64_t index = this->size();
+ new (keys_ + index) Key(std::forward<ForwardKey>(key));
+ slot.occupy(index, hash);
+ occupied_and_removed_slots_++;
+ return index;
+ }
+ }
+ VECTOR_SET_SLOT_PROBING_END();
+ }
+
Key pop__impl()
{
BLI_assert(this->size() > 0);
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index f9b0aaa7de6..1c02bce8411 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -37,6 +37,8 @@
* see of the increased compile time and binary size is worth it.
*/
+#include "BLI_array.hh"
+#include "BLI_index_mask.hh"
#include "BLI_span.hh"
namespace blender {
@@ -71,6 +73,11 @@ template<typename T> class VArray {
return size_ == 0;
}
+ IndexRange index_range() const
+ {
+ return IndexRange(size_);
+ }
+
/* Returns true when the virtual array is stored as a span internally. */
bool is_span() const
{
@@ -82,13 +89,13 @@ template<typename T> class VArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- Span<T> get_span() const
+ Span<T> get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return {};
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -102,20 +109,46 @@ template<typename T> class VArray {
/* Returns the value that is returned for every index. This invokes undefined behavior if the
* virtual array would not return the same value for every index. */
- T get_single() const
+ T get_internal_single() const
{
BLI_assert(this->is_single());
if (size_ == 1) {
return this->get(0);
}
- return this->get_single_impl();
+ return this->get_internal_single_impl();
}
+ /* Get the element at a specific index. Note that this operator cannot be used to assign values
+ * to an index, because the return value is not a reference. */
T operator[](const int64_t index) const
{
return this->get(index);
}
+ /* Copy the entire virtual array into a span. */
+ void materialize(MutableSpan<T> r_span) const
+ {
+ this->materialize(IndexMask(size_), r_span);
+ }
+
+ /* Copy some indices of the virtual array into a span. */
+ void materialize(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_impl(mask, r_span);
+ }
+
+ void materialize_to_uninitialized(MutableSpan<T> r_span) const
+ {
+ this->materialize_to_uninitialized(IndexMask(size_), r_span);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, r_span);
+ }
+
protected:
virtual T get_impl(const int64_t index) const = 0;
@@ -124,7 +157,7 @@ template<typename T> class VArray {
return false;
}
- virtual Span<T> get_span_impl() const
+ virtual Span<T> get_internal_span_impl() const
{
BLI_assert_unreachable();
return {};
@@ -135,56 +168,196 @@ template<typename T> class VArray {
return false;
}
- virtual T get_single_impl() const
+ virtual T get_internal_single_impl() const
{
/* Provide a default implementation, so that subclasses don't have to provide it. This method
* should never be called because `is_single_impl` returns false by default. */
BLI_assert_unreachable();
return T();
}
+
+ virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { dst[i] = single; });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
+ }
+ }
+
+ virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
+ }
+ }
+};
+
+/* Similar to VArray, but the elements are mutable. */
+template<typename T> class VMutableArray : public VArray<T> {
+ public:
+ VMutableArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ void set(const int64_t index, T value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size_);
+ this->set_impl(index, std::move(value));
+ }
+
+ /* Copy the values from the source span to all elements in the virtual array. */
+ void set_all(Span<T> src)
+ {
+ BLI_assert(src.size() == this->size_);
+ this->set_all_impl(src);
+ }
+
+ MutableSpan<T> get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
+ return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
+ }
+
+ protected:
+ virtual void set_impl(const int64_t index, T value) = 0;
+
+ virtual void set_all_impl(Span<T> src)
+ {
+ if (this->is_span()) {
+ const MutableSpan<T> span = this->get_internal_span();
+ initialized_copy_n(src.data(), this->size_, span.data());
+ }
+ else {
+ const int64_t size = this->size_;
+ for (int64_t i = 0; i < size; i++) {
+ this->set(i, src[i]);
+ }
+ }
+ }
};
+template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
+template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
+
/**
- * A virtual array implementation for a span. This class is final so that it can be devirtualized
- * by the compiler in some cases (e.g. when #devirtualize_varray is used).
+ * A virtual array implementation for a span. Methods in this class are final so that it can be
+ * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
*/
-template<typename T> class VArrayForSpan final : public VArray<T> {
- private:
- const T *data_;
+template<typename T> class VArray_For_Span : public VArray<T> {
+ protected:
+ const T *data_ = nullptr;
public:
- VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
+ VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
{
}
protected:
- T get_impl(const int64_t index) const override
+ VArray_For_Span(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ return data_[index];
+ }
+
+ bool is_span_impl() const final
+ {
+ return true;
+ }
+
+ Span<T> get_internal_span_impl() const final
+ {
+ return Span<T>(data_, this->size_);
+ }
+};
+
+template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
+ protected:
+ T *data_ = nullptr;
+
+ public:
+ VMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : VMutableArray<T>(data.size()), data_(data.data())
+ {
+ }
+
+ protected:
+ VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
{
return data_[index];
}
+ void set_impl(const int64_t index, T value) final
+ {
+ data_[index] = value;
+ }
+
bool is_span_impl() const override
{
return true;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(data_, this->size_);
}
};
/**
+ * A variant of `VArray_For_Span` that owns the underlying data.
+ * The `Container` type has to implement a `size()` and `data()` method.
+ * The `data()` method has to return a pointer to the first element in the continuous array of
+ * elements.
+ */
+template<typename Container, typename T = typename Container::value_type>
+class VArray_For_ArrayContainer : public VArray_For_Span<T> {
+ private:
+ Container container_;
+
+ public:
+ VArray_For_ArrayContainer(Container container)
+ : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
+ {
+ this->data_ = container_.data();
+ }
+};
+
+/**
* A virtual array implementation that returns the same value for every index. This class is final
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
* used).
*/
-template<typename T> class VArrayForSingle final : public VArray<T> {
+template<typename T> class VArray_For_Single final : public VArray<T> {
private:
T value_;
public:
- VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
+ VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
{
}
@@ -199,7 +372,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return this->size_ == 1;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(&value_, 1);
}
@@ -209,13 +382,207 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return true;
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
return value_;
}
};
/**
+ * In many cases a virtual array is a span internally. In those cases, access to individual could
+ * be much more efficient than calling a virtual method. When the underlying virtual array is not a
+ * span, this class allocates a new array and copies the values over.
+ *
+ * This should be used in those cases:
+ * - All elements in the virtual array are accessed multiple times.
+ * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
+ * from faster access.
+ * - An API is called, that does not accept virtual arrays, but only spans.
+ */
+template<typename T> class VArray_Span final : public Span<T> {
+ private:
+ const VArray<T> &varray_;
+ Array<T> owned_data_;
+
+ public:
+ VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ this->data_ = owned_data_.data();
+ }
+ }
+};
+
+/**
+ * Same as VArray_Span, but for a mutable span.
+ * The important thing to note is that when changing this span, the results might not be
+ * immediately reflected in the underlying virtual array (only when the virtual array is a span
+ * internally). The #save method can be used to write all changes to the underlying virtual array,
+ * if necessary.
+ */
+template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
+ private:
+ VMutableArray<T> &varray_;
+ Array<T> owned_data_;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
+ * not, a new array has to be allocated as a wrapper for the underlying virtual array. */
+ VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
+ : MutableSpan<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ if (copy_values_to_span) {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ }
+ else {
+ owned_data_.reinitialize(varray_.size());
+ }
+ this->data_ = owned_data_.data();
+ }
+ }
+
+ ~VMutableArray_Span()
+ {
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ }
+
+ /* Write back all values from a temporary allocated array to the underlying virtual array. */
+ void save()
+ {
+ save_has_been_called_ = true;
+ if (this->data_ != owned_data_.data()) {
+ return;
+ }
+ varray_.set_all(owned_data_);
+ }
+
+ void disable_not_applied_warning()
+ {
+ show_not_saved_warning_ = false;
+ }
+};
+
+/**
+ * This class makes it easy to create a virtual array for an existing function or lambda. The
+ * `GetFunc` should take a single `index` argument and return the value at that index.
+ */
+template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
+ private:
+ GetFunc get_func_;
+
+ public:
+ VArray_For_Func(const int64_t size, GetFunc get_func)
+ : VArray<T>(size), get_func_(std::move(get_func))
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ return get_func_(index);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); });
+ }
+};
+
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class VArray_For_DerivedSpan : public VArray<ElemT> {
+ private:
+ const StructT *data_;
+
+ public:
+ VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
+ private:
+ StructT *data_;
+
+ public:
+ VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : VMutableArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void set_impl(const int64_t index, ElemT value) override
+ {
+ SetFunc(data_[index], std::move(value));
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+/**
* Generate multiple versions of the given function optimized for different virtual arrays.
* One has to be careful with nesting multiple devirtualizations, because that results in an
* exponential number of function instantiations (increasing compile time and binary size).
@@ -229,14 +596,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
/* Support disabling the devirtualization to simplify benchmarking. */
if (enable) {
if (varray.is_single()) {
- /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
+ /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
func(varray_single);
return;
}
if (varray.is_span()) {
- /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSpan<T> varray_span{varray.get_span()};
+ /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Span<T> varray_span{varray.get_internal_span()};
func(varray_span);
return;
}
@@ -262,26 +629,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
const bool is_single1 = varray1.is_single();
const bool is_single2 = varray2.is_single();
if (is_span1 && is_span2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_span, varray2_span);
return;
}
if (is_span1 && is_single2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_span, varray2_single);
return;
}
if (is_single1 && is_span2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_single, varray2_span);
return;
}
if (is_single1 && is_single2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_single, varray2_single);
return;
}
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index e66049c9bd6..ce3515ac153 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -185,6 +185,7 @@ set(SRC
BLI_edgehash.h
BLI_endian_switch.h
BLI_endian_switch_inline.h
+ BLI_enumerable_thread_specific.hh
BLI_expr_pylike_eval.h
BLI_fileops.h
BLI_fileops_types.h
diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c
index b5f1d7818cf..786fdd219a3 100644
--- a/source/blender/blenlib/intern/BLI_dial_2d.c
+++ b/source/blender/blenlib/intern/BLI_dial_2d.c
@@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2])
cosval = dot_v2v2(current_direction, dial->initial_direction);
sinval = cross_v2v2(current_direction, dial->initial_direction);
- /* clamp to avoid nans in #acos */
+ /* Clamp to avoid NAN's in #acos */
angle = atan2f(sinval, cosval);
/* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index 8fd2802a547..abcb3139dc7 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -713,3 +713,75 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl
r_table[i * 4 + 3] = 0.0f;
}
}
+
+/* ****************************** wavelength ******************************** */
+/* Wavelength to RGB. */
+
+/* CIE colour matching functions xBar, yBar, and zBar for
+ * wavelengths from 380 through 780 nanometers, every 5
+ * nanometers.
+ * For a wavelength lambda in this range:
+ * cie_colour_match[(lambda - 380) / 5][0] = xBar
+ * cie_colour_match[(lambda - 380) / 5][1] = yBar
+ * cie_colour_match[(lambda - 380) / 5][2] = zBar */
+
+static float cie_colour_match[81][3] = {
+ {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
+ {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
+ {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
+ {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
+ {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
+ {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
+ {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
+ {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
+ {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
+ {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
+ {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
+ {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
+ {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
+ {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
+ {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
+ {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
+ {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
+ {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
+ {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
+ {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
+ {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
+ {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
+ {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
+ {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
+ {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
+ {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
+ {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}};
+
+static void wavelength_to_xyz(float xyz[3], float lambda_nm)
+{
+ float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */
+ int i = (int)ii;
+
+ if (i < 0 || i >= 80) {
+ xyz[0] = 0.0f;
+ xyz[1] = 0.0f;
+ xyz[2] = 0.0f;
+ }
+ else {
+ ii -= (float)i;
+ const float *c = cie_colour_match[i];
+ xyz[0] = c[0] + ii * (c[3] - c[0]);
+ xyz[1] = c[1] + ii * (c[4] - c[1]);
+ xyz[2] = c[2] + ii * (c[5] - c[2]);
+ }
+}
+
+void wavelength_to_xyz_table(float *r_table, int width)
+{
+ for (int i = 0; i < width; i++) {
+ float temperature = 380 + 400 / (float)width * (float)i;
+
+ float rgb[3];
+ wavelength_to_xyz(rgb, temperature);
+
+ copy_v3_v3(&r_table[i * 4], rgb);
+ r_table[i * 4 + 3] = 0.0f;
+ }
+}
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 01cda6c9e4a..508de506ae8 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2353,7 +2353,7 @@ bool isect_planes_v3_fn(
for (i_test = 0; i_test < planes_len; i_test++) {
const float *np_test = planes[i_test];
if (((dot_v3v3(np_test, co_test) + np_test[3]) > eps_isect)) {
- /* For low epsilon values the point could intersect it's own plane. */
+ /* For low epsilon values the point could intersect its own plane. */
if (!ELEM(i_test, i, j, k)) {
break;
}
@@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos,
void tangent_from_uv_v3(const float uv1[2],
const float uv2[2],
- const float uv3[3],
+ const float uv3[2],
const float co1[3],
const float co2[3],
const float co3[3],
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index a21e0c8f092..fb7b96fde78 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -412,7 +412,7 @@ bool is_finite_v4(const float v[4])
* this would return the angle at the elbow.
*
* note that when v1/v2/v3 represent 3 points along a straight line
- * that the angle returned will be pi (180deg), rather then 0.0
+ * that the angle returned will be pi (180deg), rather than 0.0
*/
float angle_v3v3v3(const float a[3], const float b[3], const float c[3])
{
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index 3558dfad81d..636209883c3 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -1744,9 +1744,7 @@ static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair)
if (pair.first <= pair.second) {
return pair;
}
- else {
- return std::pair<int, int>(pair.second, pair.first);
- }
+ return std::pair<int, int>(pair.second, pair.first);
}
/**
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index cb2634e6fda..5f823396ed9 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -266,7 +266,8 @@ eFileAttributes BLI_file_attributes(const char *path)
if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
ret |= FILE_ATTR_SPARSE_FILE;
}
- if (attr & FILE_ATTRIBUTE_OFFLINE) {
+ if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN ||
+ attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) {
ret |= FILE_ATTR_OFFLINE;
}
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c
index 329c4d48fe8..093d08e643d 100644
--- a/source/blender/blenlib/intern/uvproject.c
+++ b/source/blender/blenlib/intern/uvproject.c
@@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2],
/* 'rotmat' can be `obedit->obmat` when uv project is used.
* 'winx' and 'winy' can be from `scene->r.xsch/ysch` */
-ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy)
+ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy)
{
ProjCameraInfo uci;
Camera *camera = ob->data;
diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
index 977e5dba497..0e0145e592a 100644
--- a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
+++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
@@ -136,4 +136,17 @@ TEST(linear_allocator, ManyAllocations)
}
}
+TEST(linear_allocator, ConstructArray)
+{
+ LinearAllocator<> allocator;
+ MutableSpan<std::string> strings = allocator.construct_array<std::string>(4, "hello");
+ EXPECT_EQ(strings[0], "hello");
+ EXPECT_EQ(strings[1], "hello");
+ EXPECT_EQ(strings[2], "hello");
+ EXPECT_EQ(strings[3], "hello");
+ for (std::string &string : strings) {
+ string.~basic_string();
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index f1ae8fb3921..679a10e9ce0 100644
--- a/source/blender/blenlib/tests/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -604,6 +604,55 @@ TEST(map, GenericAlgorithms)
EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1);
}
+TEST(map, AddAsVariadic)
+{
+ Map<int, StringRef> map;
+ map.add_as(3, "hello", 2);
+ map.add_as(2, "test", 1);
+ EXPECT_EQ(map.lookup(3), "he");
+ EXPECT_EQ(map.lookup(2), "t");
+}
+
+TEST(map, RemoveDuringIteration)
+{
+ Map<int, int> map;
+ map.add(2, 1);
+ map.add(5, 2);
+ map.add(1, 2);
+ map.add(6, 0);
+ map.add(3, 3);
+
+ EXPECT_EQ(map.size(), 5);
+
+ using Iter = Map<int, int>::MutableItemIterator;
+ Iter begin = map.items().begin();
+ Iter end = map.items().end();
+ for (Iter iter = begin; iter != end; ++iter) {
+ Map<int, int>::MutableItem item = *iter;
+ if (item.value == 2) {
+ map.remove(iter);
+ }
+ }
+
+ EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.lookup(2), 1);
+ EXPECT_EQ(map.lookup(6), 0);
+ EXPECT_EQ(map.lookup(3), 3);
+}
+
+TEST(map, LookupKey)
+{
+ Map<std::string, int> map;
+ map.add("a", 0);
+ map.add("b", 1);
+ map.add("c", 2);
+ EXPECT_EQ(map.lookup_key("a"), "a");
+ EXPECT_EQ(map.lookup_key_as("c"), "c");
+ EXPECT_EQ(map.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(map.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
index f1fcdae3a52..b3108381d78 100644
--- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
+++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
@@ -93,6 +93,15 @@ TEST(stack, Push)
EXPECT_EQ(stack.size(), 2);
}
+TEST(stack, PushAs)
+{
+ Stack<StringRef> stack;
+ stack.push_as("hello", 3);
+ stack.push_as("world", 1);
+ EXPECT_EQ(stack.pop(), "w");
+ EXPECT_EQ(stack.pop(), "hel");
+}
+
TEST(stack, PushMultiple)
{
Stack<int> stack;
diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc
index bbbe96f9b7e..c4016ca75e1 100644
--- a/source/blender/blenlib/tests/BLI_vector_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc
@@ -232,4 +232,43 @@ TEST(vector_set, PopExceptions)
EXPECT_EQ(set.size(), 4);
}
+TEST(vector_set, IndexOfOrAdd)
+{
+ VectorSet<int> set;
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(0), 2);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+ EXPECT_EQ(set.index_of_or_add(8), 4);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+}
+
+TEST(vector_set, Clear)
+{
+ VectorSet<int> set = {4, 6, 2, 4};
+ EXPECT_EQ(set.size(), 3);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+ set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3});
+ EXPECT_EQ(set.size(), 6);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+}
+
+TEST(vector_set, LookupKey)
+{
+ VectorSet<std::string> set;
+ set.add("a");
+ set.add("b");
+ set.add("c");
+ EXPECT_EQ(set.lookup_key("a"), "a");
+ EXPECT_EQ(set.lookup_key_as("c"), "c");
+ EXPECT_EQ(set.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(set.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a"));
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 462f13c15ab..e8636168308 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -248,6 +248,15 @@ TEST(vector, Append)
EXPECT_EQ(vec[2], 7);
}
+TEST(vector, AppendAs)
+{
+ Vector<StringRef> vec;
+ vec.append_as("hello", 2);
+ vec.append_as("world", 3);
+ EXPECT_EQ(vec[0], "he");
+ EXPECT_EQ(vec[1], "wor");
+}
+
TEST(vector, AppendAndGetIndex)
{
Vector<int> vec;
diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
index ac25229cd69..a6d2ca10315 100644
--- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
@@ -1,26 +1,29 @@
/* Apache License, Version 2.0 */
+#include "BLI_array.hh"
#include "BLI_strict_flags.h"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
#include "BLI_virtual_array.hh"
#include "testing/testing.h"
namespace blender::tests {
-TEST(virtual_array, ForSpan)
+TEST(virtual_array, Span)
{
std::array<int, 5> data = {3, 4, 5, 6, 7};
- VArrayForSpan<int> varray{data};
+ VArray_For_Span<int> varray{data};
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray.get(0), 3);
EXPECT_EQ(varray.get(4), 7);
EXPECT_TRUE(varray.is_span());
EXPECT_FALSE(varray.is_single());
- EXPECT_EQ(varray.get_span().data(), data.data());
+ EXPECT_EQ(varray.get_internal_span().data(), data.data());
}
-TEST(virtual_array, ForSingle)
+TEST(virtual_array, Single)
{
- VArrayForSingle<int> varray{10, 4};
+ VArray_For_Single<int> varray{10, 4};
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray.get(0), 10);
EXPECT_EQ(varray.get(3), 10);
@@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle)
EXPECT_TRUE(varray.is_single());
}
+TEST(virtual_array, Array)
+{
+ Array<int> array = {1, 2, 3, 5, 8};
+ {
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{std::move(array)};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */
+ EXPECT_TRUE(varray.is_empty());
+ }
+}
+
+TEST(virtual_array, Vector)
+{
+ Vector<int> vector = {9, 8, 7, 6};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 9);
+ EXPECT_EQ(varray[3], 6);
+}
+
+TEST(virtual_array, StdVector)
+{
+ std::vector<int> vector = {5, 6, 7, 8};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 6);
+}
+
+TEST(virtual_array, StdArray)
+{
+ std::array<int, 4> array = {2, 3, 4, 5};
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 2);
+ EXPECT_EQ(varray[1], 3);
+}
+
+TEST(virtual_array, VectorSet)
+{
+ VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1};
+ VArray_For_ArrayContainer varray{std::move(vector_set)};
+ EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 3);
+ EXPECT_EQ(varray[2], 7);
+ EXPECT_EQ(varray[3], 1);
+}
+
+TEST(virtual_array, Func)
+{
+ auto func = [](int64_t index) { return (int)(index * index); };
+ VArray_For_Func<int, decltype(func)> varray{10, func};
+ EXPECT_EQ(varray.size(), 10);
+ EXPECT_EQ(varray[0], 0);
+ EXPECT_EQ(varray[3], 9);
+ EXPECT_EQ(varray[9], 81);
+}
+
+TEST(virtual_array, AsSpan)
+{
+ auto func = [](int64_t index) { return (int)(10 * index); };
+ VArray_For_Func<int, decltype(func)> func_varray{10, func};
+ VArray_Span span_varray{func_varray};
+ EXPECT_EQ(span_varray.size(), 10);
+ Span<int> span = span_varray;
+ EXPECT_EQ(span.size(), 10);
+ EXPECT_EQ(span[0], 0);
+ EXPECT_EQ(span[3], 30);
+ EXPECT_EQ(span[6], 60);
+}
+
+static int get_x(const std::array<int, 3> &item)
+{
+ return item[0];
+}
+
+static void set_x(std::array<int, 3> &item, int value)
+{
+ item[0] = value;
+}
+
+TEST(virtual_array, DerivedSpan)
+{
+ Vector<std::array<int, 3>> vector;
+ vector.append({3, 4, 5});
+ vector.append({1, 1, 1});
+ {
+ VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ }
+ {
+ VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ varray.set(0, 10);
+ varray.set(1, 20);
+ EXPECT_EQ(vector[0][0], 10);
+ EXPECT_EQ(vector[1][0], 20);
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index ee9b9a49768..36802fc8842 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -57,6 +57,7 @@ set(SRC
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
+ intern/versioning_300.c
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 19ae0014bb8..fe7d50bfa15 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -111,6 +111,7 @@
#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_sequencer.h"
+#include "SEQ_utils.h"
#include "readfile.h"
@@ -2696,7 +2697,7 @@ static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map)
{
/* update IDs stored in sequencer clipboard */
- SEQ_iterator_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
+ SEQ_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
}
static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
@@ -3868,6 +3869,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
blo_do_versions_290(fd, lib, main);
+ blo_do_versions_300(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
@@ -3891,6 +3893,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports)
do_versions_after_linking_270(main);
do_versions_after_linking_280(main, reports);
do_versions_after_linking_290(main, reports);
+ do_versions_after_linking_300(main, reports);
do_versions_after_linking_cycles(main);
main->is_locked_for_linking = false;
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 9682b5456d2..d1d4e0b3256 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *
void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain);
+void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain);
void do_versions_after_linking_250(struct Main *bmain);
@@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain);
void do_versions_after_linking_270(struct Main *bmain);
void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports);
+void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_cycles(struct Main *bmain);
/* This is rather unfortunate to have to expose this here, but better use that nasty hack in
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
new file mode 100644
index 00000000000..8c5e86eadd3
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -0,0 +1,135 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup blenloader
+ */
+/* allow readfile to use deprecated functionality */
+#define DNA_DEPRECATED_ALLOW
+
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_genfile.h"
+#include "DNA_modifier_types.h"
+#include "DNA_text_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+
+#include "BLO_readfile.h"
+#include "readfile.h"
+
+void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
+{
+ if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set zero user text objects to have a fake user. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if (text->id.us == 0) {
+ id_fake_user_set(&text->id);
+ }
+ }
+ }
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_300 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+
+ /* Use new texture socket in Attribute Sample Texture node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
+ continue;
+ }
+ if (node->id == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_TEXTURE) {
+ bNodeSocketValueTexture *socket_value = (bNodeSocketValueTexture *)
+ socket->default_value;
+ socket_value->value = (Tex *)node->id;
+ break;
+ }
+ }
+ node->id = NULL;
+ }
+ }
+ }
+}
+
+/* NOLINTNEXTLINE: readability-function-size */
+void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
+{
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set default value for the new bisect_threshold parameter in the mirror modifier. */
+ if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+ /* This was the previous hard-coded value. */
+ mmd->bisect_threshold = 0.001f;
+ }
+ }
+ }
+ }
+ /* Grease Pencil: Set default value for dilate pixels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) {
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->gpencil_settings) {
+ brush->gpencil_settings->dilate_pixels = 1;
+ }
+ }
+ }
+ }
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pose == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale);
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index f2e73e161ca..56b2f18f8a6 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -1278,12 +1278,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (ob->soft->physics_speed == 0.0f) {
ob->soft->physics_speed = 1.0f;
}
-
- if (ob->soft->interval == 0) {
- ob->soft->interval = 2;
- ob->soft->sfra = 1;
- ob->soft->efra = 100;
- }
}
if (ob->soft && ob->soft->vertgroup == 0) {
bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL");
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index dcf9717465c..0754564fa47 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -106,7 +106,7 @@ static void normalize_v2_m3_v3v3(float out[2],
/**
* \note Be sure to update #bm_face_split_edgenet_find_loop_pair_exists
- * when making changed to edge picking logic.
+ * when making changes to edge picking logic.
*/
static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init,
const float face_normal[3],
@@ -456,8 +456,8 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init,
* Splits a face into many smaller faces defined by an edge-net.
* handle customdata and degenerate cases.
*
- * - isolated holes or unsupported face configurations, will be ignored.
- * - customdata calculations aren't efficient
+ * - Isolated holes or unsupported face configurations, will be ignored.
+ * - Customdata calculations aren't efficient
* (need to calculate weights for each vert).
*/
bool BM_face_split_edgenet(BMesh *bm,
@@ -593,7 +593,7 @@ bool BM_face_split_edgenet(BMesh *bm,
BMIter iter;
BMLoop *l_other;
- /* see: #BM_loop_interp_from_face for similar logic */
+ /* See: #BM_loop_interp_from_face for similar logic. */
void **blocks = BLI_array_alloca(blocks, f->len);
float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len);
float *w = BLI_array_alloca(w, f->len);
@@ -1064,7 +1064,7 @@ static int bm_face_split_edgenet_find_connection(const struct EdgeGroup_FindConn
#ifdef USE_PARTIAL_CONNECT
/**
- * Used to identify edges that get split off when making island from partial connection.
+ * Used to identify edges that get split off when making island from partial connection.
* fptr should be a BMFace*, but is a void* for general interface to BM_vert_separate_tested_edges
*/
static bool test_tagged_and_notface(BMEdge *e, void *fptr)
@@ -1211,7 +1211,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
const int v_a_index,
const int v_b_index)
{
- /* connected to eachother */
+ /* Connected to each other. */
if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
return true;
}
@@ -1226,7 +1226,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
* \param use_partial_connect: Support for handling islands connected by only a single edge,
* \note that this is quite slow so avoid using where possible.
* \param mem_arena: Avoids many small allocs & should be cleared after each use.
- * take care since \a r_edge_net_new is stored in \a r_edge_net_new.
+ * take care since \a edge_net_new is stored in \a r_edge_net_new.
*/
bool BM_face_split_edgenet_connect_islands(BMesh *bm,
BMFace *f,
@@ -1246,7 +1246,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
*
* Keep the first part fast since it will run very often for edge-nets that have no holes.
*
- * \note Don't use the mem_arena unless he have holes to fill.
+ * \note Don't use the mem_arena unless we have holes to fill.
* (avoid thrashing the area when the initial check isn't so intensive on the stack).
*/
@@ -1572,7 +1572,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
if (g->has_prev_edge == false) {
BMVert *v_origin = g->vert_span.min;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1598,7 +1598,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
{
BMVert *v_origin = g->vert_span.max;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1660,7 +1660,7 @@ finally:
struct TempVertPair *tvp = temp_vert_pairs.list;
do {
/* its _very_ unlikely the edge exists,
- * however splicing may case this. see: T48012 */
+ * however splicing may cause this. see: T48012 */
if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) {
BM_vert_splice(bm, tvp->v_orig, tvp->v_temp);
}
diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c
index f840a3770ff..e7d0fe6a0c6 100644
--- a/source/blender/bmesh/tools/bmesh_bisect_plane.c
+++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c
@@ -39,12 +39,13 @@
#include "BLI_utildefines_stack.h"
#include "bmesh.h"
-#include "bmesh_bisect_plane.h" /* own include */
+#include "bmesh_bisect_plane.h" /* Own include. */
-#include "BLI_strict_flags.h" /* keep last */
+#include "BLI_strict_flags.h" /* Keep last. */
/* -------------------------------------------------------------------- */
-/* Math utils */
+/** \name Math Functions
+ * \{ */
static short plane_point_test_v3(const float plane[4],
const float co[3],
@@ -63,10 +64,15 @@ static short plane_point_test_v3(const float plane[4],
return 0;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Wrappers to hide internal data-structure abuse,
+/** \name BMesh Element Accessors
+ *
+ * Wrappers to hide internal data-structure abuse,
* later we may want to move this into some hash lookup
- * to a separate struct, but for now we can store in BMesh data */
+ * to a separate struct, but for now we can store in #BMesh data.
+ * \{ */
#define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */
#define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */
@@ -75,12 +81,16 @@ static short plane_point_test_v3(const float plane[4],
#define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \
(*((uint *)(&(v)->no[2])))
-/**
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BMesh Flag Accessors
+ *
* Hide flag access
- * (for more readable code since same flag is used differently for vert/edgeface)...
+ * (for more readable code since same flag is used differently for vert/edge-face).
*/
-/* enable when vertex is in the center and its faces have been added to the stack */
+/** Enable when vertex is in the center and its faces have been added to the stack. */
BLI_INLINE void vert_is_center_enable(BMVert *v)
{
BM_elem_flag_enable(v, BM_ELEM_TAG);
@@ -100,7 +110,7 @@ BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const
return ELEM(delta, 1, (uint)(f_len_orig - 1));
}
-/* enable when the edge can be cut */
+/** Enable when the edge can be cut. */
BLI_INLINE void edge_is_cut_enable(BMEdge *e)
{
BM_elem_flag_enable(e, BM_ELEM_TAG);
@@ -114,7 +124,7 @@ BLI_INLINE bool edge_is_cut_test(BMEdge *e)
return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0);
}
-/* enable when the faces are added to the stack */
+/** Enable when the faces are added to the stack. */
BLI_INLINE void face_in_stack_enable(BMFace *f)
{
BM_elem_flag_disable(f, BM_ELEM_TAG);
@@ -128,8 +138,11 @@ BLI_INLINE bool face_in_stack_test(BMFace *f)
return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BMesh utils */
+/** \name BMesh Face Bisect
+ * \{ */
static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
{
@@ -148,7 +161,7 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
static void bm_face_bisect_verts(
BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
{
- /* unlikely more than 2 verts are needed */
+ /* Unlikely more than 2 verts are needed. */
const uint f_len_orig = (uint)f->len;
BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig);
STACK_DECLARE(vert_split_arr);
@@ -163,16 +176,14 @@ static void bm_face_bisect_verts(
l_first = BM_FACE_FIRST_LOOP(f);
- /* add plane-aligned verts to the stack
- * and check we have verts from both sides in this face,
- * ... that the face doesn't only have boundary verts on the plane for eg. */
+ /* Add plane-aligned verts to the stack and check we have verts from both sides in this face
+ * (that the face doesn't only have boundary verts on the plane for eg). */
l_iter = l_first;
do {
if (vert_is_center_test(l_iter->v)) {
BLI_assert(BM_VERT_DIR(l_iter->v) == 0);
- /* if both are -1 or 1, or both are zero:
- * don't flip 'inside' var while walking */
+ /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */
BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) ==
0);
@@ -195,7 +206,7 @@ static void bm_face_bisect_verts(
l_a = BM_face_vert_share_loop(f, vert_split_arr[0]);
l_b = BM_face_vert_share_loop(f, vert_split_arr[1]);
- /* common case, just cut the face once */
+ /* Common case, just cut the face once. */
BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true);
if (l_new) {
if (oflag_center | oflag_new) {
@@ -207,6 +218,7 @@ static void bm_face_bisect_verts(
}
}
else {
+ /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */
uint i = 0;
@@ -263,7 +275,6 @@ static void bm_face_bisect_verts(
} while ((l_iter = l_iter->next) != l_first_non_center);
}
- /* less common case, _complicated_ we need to calculate how to do multiple cuts */
float(*face_verts_proj_2d)[2] = BLI_array_alloca(face_verts_proj_2d, f_len_orig);
float axis_mat[3][3];
@@ -275,10 +286,8 @@ static void bm_face_bisect_verts(
/* ---- */
/* Calculate the direction to sort verts in the face intersecting the plane */
- /* exact dir isn't so important,
- * just need a dir for sorting verts across face,
- * 'sort_dir' could be flipped either way, it not important, we only need to order the array
- */
+ /* The exact direction isn't important, vertices just need to be sorted across the face.
+ * (`sort_dir` could be flipped either way). */
cross_v3_v3v3(sort_dir, f->no, plane);
if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) {
/* find any 2 verts and get their direction */
@@ -289,8 +298,8 @@ static void bm_face_bisect_verts(
}
}
if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) {
- /* ok, we can't do anything useful here,
- * face has no area or so, bail out, this is highly unlikely but not impossible */
+ /* Ok, we can't do anything useful here,
+ * face has no area or so, bail out, this is highly unlikely but not impossible. */
goto finally;
}
}
@@ -298,7 +307,7 @@ static void bm_face_bisect_verts(
/* ---- */
/* Calculate 2d coords to use for intersection checks */
- /* get the faces 2d coords */
+ /* Get the faces 2d coords. */
BLI_assert(BM_face_is_normal_valid(f));
axis_dominant_v3_to_m3(axis_mat, f->no);
@@ -310,7 +319,7 @@ static void bm_face_bisect_verts(
} while ((l_iter = l_iter->next) != l_first);
/* ---- */
- /* Sort the verts across the face from one side to another */
+ /* Sort the verts across the face from one side to another. */
for (i = 0; i < STACK_SIZE(vert_split_arr); i++) {
BMVert *v = vert_split_arr[i];
BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co);
@@ -320,9 +329,9 @@ static void bm_face_bisect_verts(
vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb);
/* ---- */
- /* Split the face across sorted splits */
+ /* Split the face across sorted splits. */
- /* note: we don't know which face gets which splits,
+ /* NOTE: we don't know which face gets which splits,
* so at the moment we have to search all faces for the vert pair,
* while not all that nice, typically there are < 5 resulting faces,
* so its not _that_ bad. */
@@ -350,8 +359,8 @@ static void bm_face_bisect_verts(
uint j;
for (j = 0; j < STACK_SIZE(face_split_arr); j++) {
- /* would be nice to avoid loop lookup here,
- * but we need to know which face the verts are in */
+ /* It would be nice to avoid loop lookup here,
+ * but we need to know which face the verts are in. */
if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) &&
(l_b = BM_face_vert_share_loop(face_split_arr[j], v_b))) {
found = true;
@@ -359,11 +368,10 @@ static void bm_face_bisect_verts(
}
}
- /* ideally wont happen, but it can for self intersecting faces */
+ /* Ideally wont happen, but it can for self intersecting faces. */
// BLI_assert(found == true);
- /* in fact this simple test is good enough,
- * test if the loops are adjacent */
+ /* In fact this simple test is good enough, test if the loops are adjacent. */
if (found && !BM_loop_is_adjacent(l_a, l_b)) {
BMLoop *l_new;
BMFace *f_tmp;
@@ -397,8 +405,11 @@ finally:
(void)vert_split_arr;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Main logic */
+/** \name Public BMesh Bisect Function
+ * \{ */
/**
* \param use_snap_center: Snap verts onto the plane.
@@ -425,25 +436,25 @@ void BM_mesh_bisect_plane(BMesh *bm,
BMIter iter;
if (use_tag) {
- /* build tagged edge array */
+ /* Build tagged edge array. */
BMEdge *e;
einput_len = 0;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
- /* keep face tags as is */
+ /* Keep face tags as is. */
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
if (edge_is_cut_test(e)) {
edges_arr[einput_len++] = e;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
- /* face tags are set by caller */
+ /* Face tags are set by caller. */
}
else {
BMEdge *e;
@@ -463,7 +474,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
vert_is_center_disable(v);
- /* these should never be accessed */
+ /* These should never be accessed. */
BM_VERT_DIR(v) = 0;
BM_VERT_DIST(v) = 0.0f;
@@ -483,11 +494,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* store a stack of faces to be evaluated for splitting */
+ /* Store a stack of faces to be evaluated for splitting. */
BLI_LINKSTACK_INIT(face_stack);
for (i = 0; i < einput_len; i++) {
- /* we could check edge_is_cut_test(e) but there is no point */
+ /* We could check `edge_is_cut_test(e)` but there is no point. */
BMEdge *e = edges_arr[i];
const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)};
const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)};
@@ -524,8 +535,8 @@ void BM_mesh_bisect_plane(BMesh *bm,
BM_VERT_DIST(v_new) = 0.0f;
}
else if (side[0] == 0 || side[1] == 0) {
- /* check if either edge verts are aligned,
- * if so - tag and push all faces that use it into the stack */
+ /* Check if either edge verts are aligned,
+ * if so - tag and push all faces that use it into the stack. */
uint j;
BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) {
if (side[j] == 0) {
@@ -545,7 +556,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* if both verts are on the center - tag it */
+ /* If both verts are on the center - tag it. */
if (oflag_center) {
if (side[0] == 0 && side[1] == 0) {
BMO_edge_flag_enable(bm, e, oflag_center);
@@ -560,9 +571,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new);
}
- /* Caused by access macros: BM_VERT_DIR, BM_VERT_SKIP. */
+ /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */
bm->elem_index_dirty |= BM_VERT;
- /* now we have all faces to split in the stack */
+ /* Now we have all faces to split in the stack. */
BLI_LINKSTACK_FREE(face_stack);
}
+
+/** \} */
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index c5ff2b3adc6..ef889807030 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -55,7 +55,6 @@ constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType
// configurable items
// chunk size determination
-// #define COM_DEBUG
// chunk order
/**
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
index 79afcc9deea..b8e19fc2c34 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
@@ -30,4 +30,4 @@ double ChunkOrderHotspot::calc_distance(int x, int y)
return result;
}
-} // namespace blender::compositor \ No newline at end of file
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index 648ef7519d1..dfb4f53fee5 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -42,8 +42,6 @@ extern "C" {
namespace blender::compositor {
-#ifdef COM_DEBUG
-
int DebugInfo::m_file_index = 0;
DebugInfo::NodeNameMap DebugInfo::m_node_names;
DebugInfo::OpNameMap DebugInfo::m_op_names;
@@ -69,50 +67,6 @@ std::string DebugInfo::operation_name(const NodeOperation *op)
return "";
}
-void DebugInfo::convert_started()
-{
- m_op_names.clear();
-}
-
-void DebugInfo::execute_started(const ExecutionSystem *system)
-{
- m_file_index = 1;
- m_group_states.clear();
- for (ExecutionGroup *execution_group : system->m_groups) {
- m_group_states[execution_group] = EG_WAIT;
- }
-}
-
-void DebugInfo::node_added(const Node *node)
-{
- m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
-}
-
-void DebugInfo::node_to_operations(const Node *node)
-{
- m_current_node_name = m_node_names[node];
-}
-
-void DebugInfo::operation_added(const NodeOperation *operation)
-{
- m_op_names[operation] = m_current_node_name;
-}
-
-void DebugInfo::operation_read_write_buffer(const NodeOperation *operation)
-{
- m_current_op_name = m_op_names[operation];
-}
-
-void DebugInfo::execution_group_started(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_RUNNING;
-}
-
-void DebugInfo::execution_group_finished(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_FINISHED;
-}
-
int DebugInfo::graphviz_operation(const ExecutionSystem *system,
NodeOperation *operation,
const ExecutionGroup *group,
@@ -442,6 +396,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
void DebugInfo::graphviz(const ExecutionSystem *system)
{
+ if (!COM_EXPORT_GRAPHVIZ) {
+ return;
+ }
char str[1000000];
if (graphviz_system(system, str, sizeof(str) - 1)) {
char basename[FILE_MAX];
@@ -459,44 +416,4 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
}
}
-#else
-
-std::string DebugInfo::node_name(const Node * /*node*/)
-{
- return "";
-}
-std::string DebugInfo::operation_name(const NodeOperation * /*op*/)
-{
- return "";
-}
-void DebugInfo::convert_started()
-{
-}
-void DebugInfo::execute_started(const ExecutionSystem * /*system*/)
-{
-}
-void DebugInfo::node_added(const Node * /*node*/)
-{
-}
-void DebugInfo::node_to_operations(const Node * /*node*/)
-{
-}
-void DebugInfo::operation_added(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::graphviz(const ExecutionSystem * /*system*/)
-{
-}
-
-#endif
-
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index bf7b981fbd5..e1aea69e481 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -21,11 +21,13 @@
#include <map>
#include <string>
+#include "COM_ExecutionSystem.h"
#include "COM_NodeOperation.h"
#include "COM_defines.h"
namespace blender::compositor {
+static constexpr bool COM_EXPORT_GRAPHVIZ = false;
class Node;
class ExecutionSystem;
class ExecutionGroup;
@@ -41,20 +43,81 @@ class DebugInfo {
static std::string node_name(const Node *node);
static std::string operation_name(const NodeOperation *op);
- static void convert_started();
- static void execute_started(const ExecutionSystem *system);
+ private:
+ static int m_file_index;
+ /** Map nodes to usable names for debug output. */
+ static NodeNameMap m_node_names;
+ /** Map operations to usable names for debug output. */
+ static OpNameMap m_op_names;
+ /** Base name for all operations added by a node. */
+ static std::string m_current_node_name;
+ /** Base name for automatic sub-operations. */
+ static std::string m_current_op_name;
+ /** For visualizing group states. */
+ static GroupStateMap m_group_states;
+
+ public:
+ static void convert_started()
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names.clear();
+ }
+ }
+
+ static void execute_started(const ExecutionSystem *system)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_file_index = 1;
+ m_group_states.clear();
+ for (ExecutionGroup *execution_group : system->m_groups) {
+ m_group_states[execution_group] = EG_WAIT;
+ }
+ }
+ };
+
+ static void node_added(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+ }
+ }
- static void node_added(const Node *node);
- static void node_to_operations(const Node *node);
- static void operation_added(const NodeOperation *operation);
- static void operation_read_write_buffer(const NodeOperation *operation);
+ static void node_to_operations(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_node_name = m_node_names[node];
+ }
+ }
- static void execution_group_started(const ExecutionGroup *group);
- static void execution_group_finished(const ExecutionGroup *group);
+ static void operation_added(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names[operation] = m_current_node_name;
+ }
+ };
+
+ static void operation_read_write_buffer(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_op_name = m_op_names[operation];
+ }
+ };
+
+ static void execution_group_started(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_RUNNING;
+ }
+ };
+ static void execution_group_finished(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_FINISHED;
+ }
+ };
static void graphviz(const ExecutionSystem *system);
-#ifdef COM_DEBUG
protected:
static int graphviz_operation(const ExecutionSystem *system,
NodeOperation *operation,
@@ -68,20 +131,6 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend(char *str, int maxlen);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
-
- private:
- static int m_file_index;
- /** Map nodes to usable names for debug output. */
- static NodeNameMap m_node_names;
- /** Map operations to usable names for debug output. */
- static OpNameMap m_op_names;
- /** Base name for all operations added by a node. */
- static std::string m_current_node_name;
- /** Base name for automatic sub-operations. */
- static std::string m_current_op_name;
- /** For visualizing group states. */
- static GroupStateMap m_group_states;
-#endif
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 68e39b19eaf..8c30d3215d7 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -25,29 +25,49 @@ namespace blender::compositor {
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
m_rect = rect;
+ this->m_is_a_single_elem = false;
this->m_memoryProxy = memoryProxy;
this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
+
+ set_strides();
}
-MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect)
+MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem)
{
m_rect = rect;
+ this->m_is_a_single_elem = is_a_single_elem;
this->m_memoryProxy = nullptr;
this->m_num_channels = COM_data_type_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
+
+ set_strides();
}
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
- : MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary)
+ : MemoryBuffer(src.m_datatype, src.m_rect, false)
+{
+ m_memoryProxy = src.m_memoryProxy;
+ /* src may be single elem buffer */
+ fill_from(src);
+}
+
+void MemoryBuffer::set_strides()
{
- memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float));
+ if (m_is_a_single_elem) {
+ this->elem_stride = 0;
+ this->row_stride = 0;
+ }
+ else {
+ this->elem_stride = m_num_channels;
+ this->row_stride = getWidth() * m_num_channels;
+ }
}
void MemoryBuffer::clear()
@@ -100,6 +120,8 @@ MemoryBuffer::~MemoryBuffer()
void MemoryBuffer::fill_from(const MemoryBuffer &src)
{
+ BLI_assert(!this->is_a_single_elem());
+
unsigned int otherY;
unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
@@ -109,10 +131,8 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src)
int otherOffset;
for (otherY = minY; otherY < maxY; otherY++) {
- otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) *
- this->m_num_channels;
- offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) *
- this->m_num_channels;
+ otherOffset = src.get_coords_offset(minX, otherY);
+ offset = this->get_coords_offset(minX, otherY);
memcpy(&this->m_buffer[offset],
&src.m_buffer[otherOffset],
(maxX - minX) * this->m_num_channels * sizeof(float));
@@ -123,8 +143,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels);
}
}
@@ -133,8 +152,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
float *dst = &this->m_buffer[offset];
const float *src = color;
for (int i = 0; i < this->m_num_channels; i++, dst++, src++) {
@@ -151,26 +169,31 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
- BLI_assert(this->m_datatype == DataType::Color);
- float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
- /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
- * but compositor uses pixel space. For now let's just divide the values and
- * switch compositor to normalized space for EWA later.
- */
- float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
- float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
- float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
-
- BLI_ewa_filter(this->getWidth(),
- this->getHeight(),
- false,
- true,
- uv_normal,
- du_normal,
- dv_normal,
- read_ewa_pixel_sampled,
- this,
- result);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_assert(this->m_datatype == DataType::Color);
+ float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
+ /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
+ * but compositor uses pixel space. For now let's just divide the values and
+ * switch compositor to normalized space for EWA later.
+ */
+ float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
+ float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
+ float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
+
+ BLI_ewa_filter(this->getWidth(),
+ this->getHeight(),
+ false,
+ true,
+ uv_normal,
+ du_normal,
+ dv_normal,
+ read_ewa_pixel_sampled,
+ this,
+ result);
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 060a67f8797..97b220508e0 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -50,6 +50,25 @@ class MemoryProxy;
* \brief a MemoryBuffer contains access to the data of a chunk
*/
class MemoryBuffer {
+ public:
+ /**
+ * Offset between elements.
+ *
+ * Should always be used for the x dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int elem_stride;
+
+ /**
+ * Offset between rows.
+ *
+ * Should always be used for the y dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int row_stride;
+
private:
/**
* \brief proxy of the memory (same for all chunks in the same buffer)
@@ -82,6 +101,11 @@ class MemoryBuffer {
*/
uint8_t m_num_channels;
+ /**
+ * Whether buffer is a single element in memory.
+ */
+ bool m_is_a_single_elem;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -91,7 +115,7 @@ class MemoryBuffer {
/**
* \brief construct new temporarily MemoryBuffer for an area
*/
- MemoryBuffer(DataType datatype, const rcti &rect);
+ MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
/**
* Copy constructor
@@ -103,6 +127,102 @@ class MemoryBuffer {
*/
~MemoryBuffer();
+ /**
+ * Whether buffer is a single element in memory independently of its resolution. True for set
+ * operations buffers.
+ */
+ bool is_a_single_elem() const
+ {
+ return m_is_a_single_elem;
+ }
+
+ float &operator[](int index)
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ const float &operator[](int index) const
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ /**
+ * Get offset needed to jump from buffer start to given coordinates.
+ */
+ int get_coords_offset(int x, int y) const
+ {
+ return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ float *get_elem(int x, int y)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ const float *get_elem(int x, int y) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ float &get_value(int x, int y, int channel)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ const float &get_value(int x, int y, int channel) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get the buffer row end.
+ */
+ const float *get_row_end(int y) const
+ {
+ BLI_assert(y >= 0 && y < getHeight());
+ return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
+ }
+
+ /**
+ * Get the number of elements in memory for a row. For single element buffers it will always
+ * be 1.
+ */
+ int get_memory_width() const
+ {
+ return is_a_single_elem() ? 1 : getWidth();
+ }
+
+ /**
+ * Get number of elements in memory for a column. For single element buffers it will
+ * always be 1.
+ */
+ int get_memory_height() const
+ {
+ return is_a_single_elem() ? 1 : getHeight();
+ }
+
uint8_t get_num_channels()
{
return this->m_num_channels;
@@ -216,7 +336,7 @@ class MemoryBuffer {
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * y + x) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -232,7 +352,7 @@ class MemoryBuffer {
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * v + u) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
BLI_assert(offset >= 0);
BLI_assert(offset < this->buffer_len() * this->m_num_channels);
@@ -258,15 +378,20 @@ class MemoryBuffer {
copy_vn_fl(result, this->m_num_channels, 0.0f);
return;
}
- BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
- result,
- getWidth(),
- getHeight(),
- this->m_num_channels,
- u,
- v,
- extend_x == MemoryBufferExtend::Repeat,
- extend_y == MemoryBufferExtend::Repeat);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
+ result,
+ getWidth(),
+ getHeight(),
+ this->m_num_channels,
+ u,
+ v,
+ extend_x == MemoryBufferExtend::Repeat,
+ extend_y == MemoryBufferExtend::Repeat);
+ }
}
void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
@@ -321,9 +446,10 @@ class MemoryBuffer {
float get_max_value(const rcti &rect) const;
private:
+ void set_strides();
const int buffer_len() const
{
- return getWidth() * getHeight();
+ return get_memory_width() * get_memory_height();
}
#ifdef WITH_CXX_GUARDEDALLOC
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc
index ee3a6dedd44..d578ac24a4a 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cc
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cc
@@ -263,10 +263,10 @@ static void opencl_initialize(const bool use_opencl)
if (error2 != CL_SUCCESS) {
printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2));
}
- g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context,
- device,
- g_work_scheduler.opencl.program,
- vendorID));
+ g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context,
+ device,
+ g_work_scheduler.opencl.program,
+ vendorID);
}
}
MEM_freeN(cldevices);
@@ -368,7 +368,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads)
/* Initialize CPU threads. */
if (!g_work_scheduler.queue.initialized) {
for (int index = 0; index < num_cpu_threads; index++) {
- g_work_scheduler.queue.devices.append(CPUDevice(index));
+ g_work_scheduler.queue.devices.append_as(index);
}
BLI_thread_local_create(g_thread_device);
g_work_scheduler.queue.initialized = true;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index ec5037fb29c..77661b4870a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -84,6 +84,7 @@
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
#include "BKE_light.h"
#include "BKE_mask.h"
#include "BKE_material.h"
@@ -114,6 +115,7 @@
#include "intern/builder/deg_builder.h"
#include "intern/depsgraph.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node.h"
@@ -360,7 +362,103 @@ void DepsgraphNodeBuilder::begin_build()
graph_->entry_tags.clear();
}
-void DepsgraphNodeBuilder::end_build()
+/* Util callbacks for `BKE_library_foreach_ID_link`, used to detect when a COW ID is using ID
+ * pointers that are either:
+ * - COW ID pointers that do not exist anymore in current depsgraph.
+ * - Orig ID pointers that do have now a COW version in current depsgraph.
+ * In both cases, it means the COW ID user needs to be flushed, to ensure its pointers are properly
+ * remapped.
+ *
+ * NOTE: This is split in two, a static function and a public method of the node builder, to allow
+ * the code to access the builder's data more easily. */
+
+/* `id_cow_self` is the user of `id_pointer`, see also `LibraryIDLinkCallbackData` struct
+ * definition. */
+int DepsgraphNodeBuilder::foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self,
+ ID *id_pointer)
+{
+ if (id_pointer->orig_id == nullptr) {
+ /* `id_cow_self` uses a non-cow ID, if that ID has a COW copy in current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ IDNode *id_node = find_id_node(id_pointer);
+ if (id_node != nullptr && id_node->id_cow != nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ else {
+ /* `id_cow_self` uses a COW ID, if that COW copy is removed from current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ /* NOTE: at that stage, old existing COW copies that are to be removed from current state of
+ * evaluated depsgraph are still valid pointers, they are freed later (typically during
+ * destruction of the builder itself). */
+ IDNode *id_node = find_id_node(id_pointer->orig_id);
+ if (id_node == nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackData *cb_data)
+{
+ ID *id = *cb_data->id_pointer;
+ if (id == nullptr) {
+ return IDWALK_RET_NOP;
+ }
+
+ DepsgraphNodeBuilder *builder = static_cast<DepsgraphNodeBuilder *>(cb_data->user_data);
+ ID *id_cow_self = cb_data->id_self;
+
+ return builder->foreach_id_cow_detect_need_for_update_callback(id_cow_self, id);
+}
+
+/* Check for IDs that need to be flushed (COW-updated) because the depsgraph itself created or
+ * removed some of their evaluated dependencies.
+ *
+ * NOTE: Currently the only ID types that depsgraph may decide to not evaluate/generate COW
+ * copies for, even though they are referenced by other data-blocks, are Collections and Objects
+ * (through their various visbility flags, and the ones from LayerCollections too). However, this
+ * code is kept generic as it makes it more future-proof, and optimization here would give
+ * neglectable performance improvements in typical cases.
+ *
+ * NOTE: This mechanism may also 'fix' some missing update tagging from non-depsgraph code in
+ * some cases. This is slightly unfortunate (as it may hide issues in other parts of Blender
+ * code), but cannot really be avoided currently.
+ */
+void DepsgraphNodeBuilder::update_invalid_cow_pointers()
+{
+ for (const IDNode *id_node : graph_->id_nodes) {
+ if (id_node->previously_visible_components_mask == 0) {
+ /* Newly added node/ID, no need to check it. */
+ continue;
+ }
+ if (ELEM(id_node->id_cow, id_node->id_orig, nullptr)) {
+ /* Node/ID with no COW data, no need to check it. */
+ continue;
+ }
+ if ((id_node->id_cow->recalc & ID_RECALC_COPY_ON_WRITE) != 0) {
+ /* Node/ID already tagged for COW flush, no need to check it. */
+ continue;
+ }
+ BKE_library_foreach_ID_link(nullptr,
+ id_node->id_cow,
+ deg::foreach_id_cow_detect_need_for_update_callback,
+ this,
+ IDWALK_IGNORE_EMBEDDED_ID | IDWALK_READONLY);
+ }
+}
+
+void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
{
for (const SavedEntryTag &entry_tag : saved_entry_tags_) {
IDNode *id_node = find_id_node(entry_tag.id_orig);
@@ -382,6 +480,12 @@ void DepsgraphNodeBuilder::end_build()
}
}
+void DepsgraphNodeBuilder::end_build()
+{
+ tag_previously_tagged_nodes();
+ update_invalid_cow_pointers();
+}
+
void DepsgraphNodeBuilder::build_id(ID *id)
{
if (id == nullptr) {
@@ -1557,6 +1661,12 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_COLLECTION) {
build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value);
}
+ else if (socket->type == SOCK_TEXTURE) {
+ build_id((ID *)((bNodeSocketValueTexture *)socket->default_value)->value);
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value);
+ }
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index a7033c8c8f3..151e0d844b6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -101,6 +101,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void begin_build();
virtual void end_build();
+ int foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self, ID *id_pointer);
+
IDNode *add_id_node(ID *id);
IDNode *find_id_node(ID *id);
TimeSourceNode *add_time_source();
@@ -276,6 +278,9 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
bool is_reference,
void *user_data);
+ void tag_previously_tagged_nodes();
+ void update_invalid_cow_pointers();
+
/* State which demotes currently built entities. */
Scene *scene_;
ViewLayer *view_layer_;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index 13caba67713..00c78b8edce 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -169,7 +169,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible)
}
/* Speed optimization for animation lookups. */
if (object->pose != nullptr) {
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
@@ -318,7 +318,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visibl
/* Armature. */
build_armature(armature);
/* speed optimization for animation lookups */
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 0f8b613f7ac..8a02228146a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -2401,6 +2401,18 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_collection(nullptr, nullptr, collection);
}
}
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ build_texture(texture);
+ }
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ build_material(material);
+ }
+ }
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 43bcb23a38a..e5d7bd70214 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -606,20 +606,12 @@ void update_lattice_edit_mode_pointers(const Depsgraph * /*depsgraph*/,
void update_mesh_edit_mode_pointers(const ID *id_orig, ID *id_cow)
{
- /* For meshes we need to update edit_mesh to make it to point
- * to the CoW version of object.
- *
- * This is kind of confusing, because actual bmesh is not owned by
- * the CoW object, so need to be accurate about using link from
- * edit_mesh to object. */
const Mesh *mesh_orig = (const Mesh *)id_orig;
Mesh *mesh_cow = (Mesh *)id_cow;
if (mesh_orig->edit_mesh == nullptr) {
return;
}
- mesh_cow->edit_mesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_mesh);
- mesh_cow->edit_mesh->mesh_eval_cage = nullptr;
- mesh_cow->edit_mesh->mesh_eval_final = nullptr;
+ mesh_cow->edit_mesh = mesh_orig->edit_mesh;
}
/* Edit data is stored and owned by original datablocks, copied ones
@@ -1001,11 +993,6 @@ void discard_lattice_edit_mode_pointers(ID *id_cow)
void discard_mesh_edit_mode_pointers(ID *id_cow)
{
Mesh *mesh_cow = (Mesh *)id_cow;
- if (mesh_cow->edit_mesh == nullptr) {
- return;
- }
- BKE_editmesh_free_derivedmesh(mesh_cow->edit_mesh);
- MEM_freeN(mesh_cow->edit_mesh);
mesh_cow->edit_mesh = nullptr;
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 196b46953c4..27167ce839b 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -478,6 +478,7 @@ add_dependencies(bf_draw bf_dna)
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
+ tests/draw_testing.cc
tests/shaders_test.cc
)
set(TEST_INC
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 70d48ea6040..7fe984b4397 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -265,8 +265,7 @@ void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
Object *ob)
{
BLI_assert(ob->type == OB_HAIR);
- Hair *hair = ob->data;
- Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL;
+ Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}
@@ -291,8 +290,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
if (draw_as != PART_DRAW_PATH) {
continue;
}
- Mesh *mesh = ob->data;
- Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1];
+ Material *material = BKE_object_material_get_eval(ob, part->omat);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
}
}
@@ -318,7 +316,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s
if (geom == NULL) {
continue;
}
- Material *material = BKE_object_material_get(ob, i + 1);
+ Material *material = BKE_object_material_get_eval(ob, i + 1);
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, false);
DRW_shgroup_call(grp, geom, ob);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index a4325675ea9..d2e0c8308c5 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -723,7 +723,7 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdou
if (holdout) {
return BKE_material_default_holdout();
}
- Material *ma = BKE_object_material_get(ob, slot + 1);
+ Material *ma = BKE_object_material_get_eval(ob, slot + 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 809d6010f10..eed36221fcb 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -501,7 +501,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
Scene *scene,
Object *ob)
{
- Material *ma = BKE_object_material_get(ob, 1);
+ Material *ma = BKE_object_material_get_eval(ob, 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index ee51b751187..adb70f97585 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -60,7 +60,8 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
/* Check if any material with holdout flag enabled. */
tgp_ob->do_mat_holdout = false;
- for (int i = 0; i < ob->totcol; i++) {
+ const int tot_materials = BKE_object_material_count_eval(ob);
+ for (int i = 0; i < tot_materials; i++) {
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1);
if (((gp_style != NULL) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) ||
((gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT))) {
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 526f553329e..e3e84dd4c8c 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -186,7 +186,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
{
GPENCIL_MaterialPool *matpool = pd->last_material_pool;
- int mat_len = max_ii(1, ob->totcol);
+ int mat_len = max_ii(1, BKE_object_material_count_eval(ob));
bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN);
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
index 0b66141e51a..21d55357a2a 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
@@ -424,22 +424,26 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
GPUShader *sh = GPENCIL_shader_fx_glow_get();
- float ref_col[3];
+ float ref_col[4];
if (fx->mode == eShaderFxGlowMode_Luminance) {
+ /* Only pass in the first value for luminance. */
ref_col[0] = fx->threshold;
ref_col[1] = -1.0f;
ref_col[2] = -1.0f;
+ ref_col[3] = -1.0f;
}
else {
+ /* First three values are the RGB for the selected color, last value the threshold. */
copy_v3_v3(ref_col, fx->select_color);
+ ref_col[3] = fx->threshold;
}
DRWState state = DRW_STATE_WRITE_COLOR;
grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col);
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", ref_col);
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color);
DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under);
DRW_shgroup_uniform_bool_copy(grp, "firstPass", true);
@@ -473,7 +477,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f});
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", (float[4]){-1.0f, -1.0f, -1.0f, -1.0});
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]});
DRW_shgroup_uniform_bool_copy(grp, "firstPass", false);
DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode);
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
index bb905f8694b..269ed49c4d0 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
@@ -145,7 +145,7 @@ void main()
uniform vec4 glowColor;
uniform vec2 offset;
uniform int sampCount;
-uniform vec3 threshold;
+uniform vec4 threshold;
uniform bool firstPass;
uniform bool glowUnder;
uniform int blendMode;
@@ -168,7 +168,7 @@ void main()
vec3 rev = texture(revealBuf, uv).rgb;
if (threshold.x > -1.0) {
if (threshold.y > -1.0) {
- if (any(greaterThan(abs(col - threshold), vec3(0.05)))) {
+ if (any(greaterThan(abs(col - vec3(threshold)), vec3(threshold.w)))) {
weight = 0.0;
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 54224071d23..fbad60ff4ab 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -1031,7 +1031,7 @@ static void pchan_draw_data_init(bPoseChannel *pchan)
static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan)
{
float ebmat[4][4];
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
@@ -1040,23 +1040,23 @@ static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *
* and not be tight to the draw pass creation.
* This would refresh armature without invalidating the draw cache */
if (pchan) {
- length = pchan->bone->length;
bone_mat = pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ copy_v3_fl(bone_scale, pchan->bone->length);
}
else {
eBone->length = len_v3v3(eBone->tail, eBone->head);
ED_armature_ebone_to_mat4(eBone, ebmat);
- length = eBone->length;
+ copy_v3_fl(bone_scale, eBone->length);
bone_mat = ebmat;
disp_mat = eBone->disp_mat;
disp_tail_mat = eBone->disp_tail_mat;
}
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
@@ -1255,19 +1255,27 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc
static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan)
{
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
+ float rot_mat[3][3];
/* See TODO above */
- length = PCHAN_CUSTOM_DRAW_SIZE(pchan);
+ mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan));
bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ);
+
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ translate_m4(disp_mat,
+ pchan->custom_translation[0],
+ pchan->custom_translation[1],
+ pchan->custom_translation[2]);
+ mul_m4_m4m3(disp_mat, disp_mat, rot_mat);
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c
index 891142fe0a2..aa26aa47faa 100644
--- a/source/blender/draw/engines/overlay/overlay_gpencil.c
+++ b/source/blender/draw/engines/overlay/overlay_gpencil.c
@@ -382,7 +382,7 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
void *thunk)
{
Object *ob = (Object *)thunk;
- Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
+ Material *ma = BKE_object_material_get_eval(ob, gps->mat_nr + 1);
if (ma == NULL) {
return;
}
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c
index 89e724bcfcc..d52640ed174 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.c
@@ -48,7 +48,7 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob)
v3d->shading.color_type == V3D_SHADING_MATERIAL_COLOR) {
Mesh *me = ob->data;
for (int i = 0; i < me->totcol; i++) {
- Material *mat = me->mat[i];
+ Material *mat = BKE_object_material_get_eval(ob, i + 1);
if (mat && mat->a < 1.0f) {
return true;
}
diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.c
index 71064e7ff47..5fa74a8c3a6 100644
--- a/source/blender/draw/engines/overlay/overlay_particle.c
+++ b/source/blender/draw/engines/overlay/overlay_particle.c
@@ -186,7 +186,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* TODO(fclem): Here would be a good place for preemptive culling. */
/* NOTE(fclem): Is color even useful in our modern context? */
- Material *ma = BKE_object_material_get(ob, part->omat);
+ Material *ma = BKE_object_material_get_eval(ob, part->omat);
float color[4] = {0.6f, 0.6f, 0.6f, part->draw_size};
if (ma != NULL) {
copy_v3_v3(color, &ma->r);
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 6e9118bfe46..800d1085505 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -93,7 +93,7 @@ void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
/* Return correct material or empty default material if slot is empty. */
BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr)
{
- Material *ma = BKE_object_material_get(ob, mat_nr);
+ Material *ma = BKE_object_material_get_eval(ob, mat_nr);
if (ma == NULL) {
ma = BKE_material_default_empty();
}
diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index 525a81b5581..ddda6d7b58e 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -202,7 +202,7 @@ static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd,
eV3DShadingColorType color_type,
float color[3])
{
- Material *ma = BKE_object_material_get(ob, VOLUME_MATERIAL_NR);
+ Material *ma = BKE_object_material_get_eval(ob, VOLUME_MATERIAL_NR);
WORKBENCH_UBO_Material ubo_data;
workbench_material_ubo_data(wpd, ob, ma, &ubo_data, color_type);
copy_v3_v3(color, ubo_data.base_color);
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index c929fe7dfd3..bbb97fc09a3 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -88,6 +88,10 @@ typedef enum eMRExtractType {
BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
{
+ /* In edit mode, the displayed mesh is stored in the edit-mesh. */
+ if (me->edit_mesh && me->edit_mesh->mesh_eval_final) {
+ return MAX2(1, me->edit_mesh->mesh_eval_final->totcol);
+ }
return MAX2(1, me->totcol);
}
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index c07271a0d33..49b5e0fecd3 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -348,7 +348,14 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts,
}
/* Draw line to first point to complete the loop for cyclic strokes. */
if (is_cyclic) {
- gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false);
+ gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false);
+ /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the
+ * first point. It should be the factor of the last point plus the distance from the last point
+ * to the first.
+ */
+ gpStrokeVert *vert = &verts[v];
+ vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x);
+ v++;
}
/* Last adjacency point (not drawn). */
adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index c09b4719f3a..37f6bbf52b5 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1473,6 +1473,14 @@ void DRW_draw_callbacks_post_scene(void)
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
+ else {
+ if (v3d && ((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0)) {
+ GPU_depth_test(GPU_DEPTH_NONE);
+ /* XXX: as scene->gpd is not copied for COW yet */
+ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ }
+ }
}
struct DRWTextStore *DRW_text_cache_ensure(void)
diff --git a/source/blender/draw/tests/draw_testing.cc b/source/blender/draw/tests/draw_testing.cc
new file mode 100644
index 00000000000..0104437e921
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.cc
@@ -0,0 +1,18 @@
+/* Apache License, Version 2.0 */
+
+#include "draw_testing.hh"
+
+#include "GPU_shader.h"
+
+#include "draw_manager_testing.h"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+void DrawTest::SetUp()
+{
+ GPUTest::SetUp();
+ DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
+}
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/draw_testing.hh b/source/blender/draw/tests/draw_testing.hh
new file mode 100644
index 00000000000..ec0b15b611e
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.hh
@@ -0,0 +1,13 @@
+/* Apache License, Version 2.0 */
+
+#include "gpu_testing.hh"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+class DrawTest : public blender::gpu::GPUTest {
+ public:
+ void SetUp() override;
+};
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 96d544fd855..c96f22859ca 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -2,12 +2,12 @@
#include "testing/testing.h"
+#include "draw_testing.hh"
#include "intern/draw_manager_testing.h"
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "GPU_shader.h"
-#include "gpu_testing.hh"
#include "engines/eevee/eevee_private.h"
#include "engines/gpencil/gpencil_engine.h"
@@ -17,19 +17,9 @@
namespace blender::draw {
-/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
-class DrawTest : public blender::gpu::GPUTest {
- void SetUp() override
- {
- GPUTest::SetUp();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
- }
-};
-
TEST_F(DrawTest, workbench_glsl_shaders)
{
workbench_shader_library_ensure();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
const int MAX_WPD = 6;
WORKBENCH_PrivateData wpds[MAX_WPD];
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 6dd1fe75163..d4941950029 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4674,7 +4674,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void
DEG_id_tag_update(ale_setting->id, ID_RECALC_ANIMATION);
}
if (ale_setting->adt && ale_setting->adt->action) {
- /* Action is it's own datablock, so has to be tagged specifically. */
+ /* Action is its own datablock, so has to be tagged specifically. */
DEG_id_tag_update(&ale_setting->adt->action->id, ID_RECALC_ANIMATION);
}
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 98c050950be..0030e78002b 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -17,6 +17,7 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
../../blentranslation
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 68fff1091af..3902f6613a1 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
}
BKE_pose_channels_hash_free(ob->pose);
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
GHash *name_map = BLI_ghash_str_new(__func__);
@@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) {
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) {
if ((conlist = &pchan->constraints)) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
/* does this constraint have a subtarget in
@@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL ||
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL ||
(conlist = &pchan->constraints) == NULL) {
return;
}
@@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj
return;
}
bPoseChannel *pchan;
- pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name);
+ pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name);
if (pchan->custom != NULL) {
Main *bmain = CTX_data_main(C);
@@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O
if (src_ob->pose) {
bPoseChannel *chanold, *channew;
- chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name);
+ chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name);
if (chanold) {
/* WARNING: this creates a new posechannel, but there will not be an attached bone
* yet as the new bones created here are still 'EditBones' not 'Bones'.
*/
- channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name);
+ channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name);
if (channew) {
BKE_pose_channel_copy_data(channew, chanold);
@@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
* is synchronized. */
bPoseChannel *pchan;
/* Make sure we clean up the old data before overwriting it */
- pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name);
+ pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name);
BKE_pose_channel_free(pchan);
/* Sync pchan data */
copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 93d36abe792..f7b54b79601 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -33,6 +33,7 @@
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_vec_types.h"
#include "BKE_fcurve.h"
#include "BKE_nla.h"
@@ -41,6 +42,7 @@
#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_report.h"
+#include "BKE_screen.h"
#include "BKE_unit.h"
#include "RNA_access.h"
@@ -50,15 +52,28 @@
#include "WM_types.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "ED_armature.h"
#include "ED_keyframes_draw.h"
#include "ED_markers.h"
#include "ED_numinput.h"
#include "ED_screen.h"
+#include "ED_space_api.h"
+
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
#include "armature_intern.h"
+#include "BLF_api.h"
+
+/* Pixel distance from 0% to 100%. */
+#define SLIDE_PIXEL_DISTANCE (300 * U.pixelsize)
+#define OVERSHOOT_RANGE_DELTA 0.2f
+
/* **************************************************** */
/* == POSE 'SLIDING' TOOLS ==
*
@@ -110,15 +125,36 @@ typedef struct tPoseSlideOp {
/** unused for now, but can later get used for storing runtime settings.... */
short flag;
+ /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */
+ int overlay_flag;
+
/** which transforms/channels are affected (ePoseSlide_Channels) */
short channels;
/** axis-limits for transforms (ePoseSlide_AxisLock) */
short axislock;
- /** 0-1 value for determining the influence of whatever is relevant */
+ /* Allow overshoot or clamp between 0% and 100%. */
+ bool overshoot;
+
+ /* Reduces percentage delta from mouse movement. */
+ bool precision;
+
+ /* Move percentage in 10% steps. */
+ bool increments;
+
+ /* Draw callback handler. */
+ void *draw_handle;
+
+ /* Accumulative, unclamped and unrounded percentage. */
+ float raw_percentage;
+
+ /* 0-1 value for determining the influence of whatever is relevant. */
float percentage;
- /** numeric input */
+ /* Last cursor position in screen space used for mouse movement delta calculation. */
+ int last_cursor_x;
+
+ /* Numeric input. */
NumInput num;
struct tPoseSlideObject *ob_data_array;
@@ -187,6 +223,240 @@ static const EnumPropertyItem prop_axis_lock_types[] = {
/* ------------------------------------ */
+static void draw_overshoot_triangle(const uint8_t color[4],
+ const bool facing_right,
+ const float x,
+ const float y)
+{
+ const uint shdr_pos_2d = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_polygon_smooth(true);
+ immUniformColor3ubvAlpha(color, 225);
+ const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
+ const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
+
+ immBegin(GPU_PRIM_TRIS, 3);
+ immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
+ immEnd();
+
+ GPU_polygon_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ immUnbindProgram();
+}
+
+static void draw_ticks(const float start_percentage,
+ const float end_percentage,
+ const struct vec2f line_start,
+ const float base_tick_height,
+ const float line_width,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ /* Use percentage represented as 0-100 int to avoid floating point precision problems. */
+ const int tick_increment = 10;
+
+ /* Round initial_tick_percentage up to the next tick_increment. */
+ int tick_percentage = ceil((start_percentage * 100) / tick_increment) * tick_increment;
+ float tick_height = base_tick_height;
+
+ while (tick_percentage <= (int)(end_percentage * 100)) {
+ /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
+ * smaller and the rest is the minimum size. */
+ if (tick_percentage % 100 == 0) {
+ tick_height = base_tick_height;
+ }
+ else if (tick_percentage % 50 == 0) {
+ tick_height = base_tick_height * 0.8;
+ }
+ else {
+ tick_height = base_tick_height * 0.5;
+ }
+
+ const float x = line_start.x +
+ (((float)tick_percentage / 100) - start_percentage) * SLIDE_PIXEL_DISTANCE;
+ const struct rctf tick_rect = {.xmin = x - (line_width / 2),
+ .xmax = x + (line_width / 2),
+ .ymin = line_start.y - (tick_height / 2),
+ .ymax = line_start.y + (tick_height / 2)};
+
+ if (tick_percentage < 0 || tick_percentage > 100) {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
+ }
+ tick_percentage += tick_increment;
+ }
+}
+
+static void draw_main_line(const struct rctf main_line_rect,
+ const float percentage,
+ const bool overshoot,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ if (overshoot) {
+ /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
+ const float line_zero_percent = main_line_rect.xmin -
+ ((percentage - 0.5f - OVERSHOOT_RANGE_DELTA) *
+ SLIDE_PIXEL_DISTANCE);
+
+ const float clamped_line_zero_percent = clamp_f(
+ line_zero_percent, main_line_rect.xmin, main_line_rect.xmax);
+ const float clamped_line_hundred_percent = clamp_f(
+ line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect.xmin, main_line_rect.xmax);
+
+ const struct rctf left_overshoot_line_rect = {.xmin = main_line_rect.xmin,
+ .xmax = clamped_line_zero_percent,
+ .ymin = main_line_rect.ymin,
+ .ymax = main_line_rect.ymax};
+ const struct rctf right_overshoot_line_rect = {.xmin = clamped_line_hundred_percent,
+ .xmax = main_line_rect.xmax,
+ .ymin = main_line_rect.ymin,
+ .ymax = main_line_rect.ymax};
+ UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
+ UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
+
+ const struct rctf non_overshoot_line_rect = {.xmin = clamped_line_zero_percent,
+ .xmax = clamped_line_hundred_percent,
+ .ymin = main_line_rect.ymin,
+ .ymax = main_line_rect.ymax};
+ UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(&main_line_rect, true, 0, color_line, 255);
+ }
+}
+
+static void draw_backdrop(const int fontid,
+ const struct rctf main_line_rect,
+ const float color_bg[4],
+ const short region_y_size,
+ const float base_tick_height)
+{
+ float string_pixel_size[2];
+ const char *percentage_placeholder = "000%%";
+ BLF_width_and_height(fontid,
+ percentage_placeholder,
+ sizeof(percentage_placeholder),
+ &string_pixel_size[0],
+ &string_pixel_size[1]);
+ const struct vec2f pad = {.x = (region_y_size - base_tick_height) / 2, .y = 2.0f * U.pixelsize};
+ const struct rctf backdrop_rect = {.xmin = main_line_rect.xmin - string_pixel_size[0] - pad.x,
+ .xmax = main_line_rect.xmax + pad.x,
+ .ymin = pad.y,
+ .ymax = region_y_size - pad.y};
+ UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
+}
+
+/* Draw an on screen Slider for a Pose Slide Operator. */
+static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg)
+{
+ tPoseSlideOp *pso = arg;
+
+ /* Only draw in region from which the Operator was started. */
+ if (region != pso->region) {
+ return;
+ }
+
+ uint8_t color_text[4];
+ uint8_t color_line[4];
+ uint8_t color_handle[4];
+ uint8_t color_overshoot[4];
+ float color_bg[4];
+
+ /* Get theme colors. */
+ UI_GetThemeColor4ubv(TH_TEXT, color_text);
+ UI_GetThemeColor4ubv(TH_TEXT, color_line);
+ UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
+ UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
+ UI_GetThemeColor3fv(TH_BACK, color_bg);
+
+ color_bg[3] = 0.5f;
+ color_overshoot[0] = color_overshoot[0] * 0.7;
+ color_overshoot[1] = color_overshoot[1] * 0.7;
+ color_overshoot[2] = color_overshoot[2] * 0.7;
+
+ /* Get the default font. */
+ const uiStyle *style = UI_style_get();
+ const uiFontStyle *fstyle = &style->widget;
+ const int fontid = fstyle->uifont_id;
+ BLF_color3ubv(fontid, color_text);
+ BLF_rotation(fontid, 0.0f);
+
+ const float line_width = 1.5 * U.pixelsize;
+ const float base_tick_height = 12.0 * U.pixelsize;
+ const float line_y = region->winy / 2;
+
+ struct rctf main_line_rect = {.xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
+ .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
+ .ymin = line_y - line_width / 2,
+ .ymax = line_y + line_width / 2};
+ float line_start_percentage = 0;
+ int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->percentage;
+
+ if (pso->overshoot) {
+ main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ line_start_percentage = pso->percentage - 0.5f - OVERSHOOT_RANGE_DELTA;
+ handle_pos_x = region->winx / 2;
+ }
+
+ draw_backdrop(fontid, main_line_rect, color_bg, pso->region->winy, base_tick_height);
+
+ draw_main_line(main_line_rect, pso->percentage, pso->overshoot, color_overshoot, color_line);
+
+ const float percentage_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
+ const struct vec2f line_start_position = {.x = main_line_rect.xmin, .y = line_y};
+ draw_ticks(line_start_percentage,
+ line_start_percentage + percentage_range,
+ line_start_position,
+ base_tick_height,
+ line_width,
+ color_overshoot,
+ color_line);
+
+ /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
+ * range.*/
+ if (pso->overshoot) {
+ if (pso->percentage > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
+ draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
+ }
+ if (pso->percentage < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
+ draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
+ }
+ }
+
+ char percentage_string[256];
+
+ /* Draw handle indicating current percentage. */
+ const struct rctf handle_rect = {.xmin = handle_pos_x - (line_width),
+ .xmax = handle_pos_x + (line_width),
+ .ymin = line_y - (base_tick_height / 2),
+ .ymax = line_y + (base_tick_height / 2)};
+
+ UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
+ BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->percentage * 100);
+
+ /* Draw percentage string. */
+ float percentage_pixel_size[2];
+ BLF_width_and_height(fontid,
+ percentage_string,
+ sizeof(percentage_string),
+ &percentage_pixel_size[0],
+ &percentage_pixel_size[1]);
+
+ BLF_position(fontid,
+ main_line_rect.xmin - 24.0 * U.pixelsize - percentage_pixel_size[0] / 2,
+ (region->winy / 2) - percentage_pixel_size[1] / 2,
+ 0.0f);
+ BLF_draw(fontid, percentage_string, sizeof(percentage_string));
+}
+
/* operator init */
static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
{
@@ -205,6 +475,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
/* set range info from property values - these may get overridden for the invoke() */
pso->percentage = RNA_float_get(op->ptr, "percentage");
+ pso->raw_percentage = pso->percentage;
pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
@@ -257,6 +528,14 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */
+ /* Register UI drawing callback. */
+ ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER);
+ if (region_header != NULL) {
+ pso->region = region_header;
+ pso->draw_handle = ED_region_draw_cb_activate(
+ region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL);
+ }
+
/* return status is whether we've got all the data we were requested to get */
return 1;
}
@@ -266,6 +545,13 @@ static void pose_slide_exit(wmOperator *op)
{
tPoseSlideOp *pso = op->customdata;
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag = pso->overlay_flag;
+
+ /* Remove UI drawing callback. */
+ ED_region_draw_cb_exit(pso->region->type, pso->draw_handle);
+
/* if data exists, clear its data and exit */
if (pso) {
/* free the temp pchan links and their data */
@@ -602,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
const int lock = pso->axislock;
for (int idx = 0; idx < 3; idx++) {
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
@@ -621,7 +907,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo
static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f};
if (!quat) {
/* Axis Angle */
@@ -789,14 +1075,18 @@ static void pose_slide_reset(tPoseSlideOp *pso)
/* ------------------------------------ */
-/* draw percentage indicator in header */
+/* Draw percentage indicator in workspace footer. */
/* TODO: Include hints about locks here... */
-static void pose_slide_draw_status(tPoseSlideOp *pso)
+static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
{
char status_str[UI_MAX_DRAW_STR];
char limits_str[UI_MAX_DRAW_STR];
char axis_str[50];
char mode_str[32];
+ char overshoot_str[50];
+ char precision_str[50];
+ char increments_str[50];
+ char bone_vis_str[50];
switch (pso->mode) {
case POSESLIDE_PUSH:
@@ -871,25 +1161,51 @@ static void pose_slide_draw_status(tPoseSlideOp *pso)
break;
}
+ if (pso->overshoot) {
+ BLI_strncpy(overshoot_str, TIP_("[E] - Disable overshoot"), sizeof(overshoot_str));
+ }
+ else {
+ BLI_strncpy(overshoot_str, TIP_("E - Enable overshoot"), sizeof(overshoot_str));
+ }
+
+ if (pso->precision) {
+ BLI_strncpy(precision_str, TIP_("[Shift] - Precision active"), sizeof(precision_str));
+ }
+ else {
+ BLI_strncpy(precision_str, TIP_("Shift - Hold for precision"), sizeof(precision_str));
+ }
+
+ if (pso->increments) {
+ BLI_strncpy(increments_str, TIP_("[Ctrl] - Increments active"), sizeof(increments_str));
+ }
+ else {
+ BLI_strncpy(increments_str, TIP_("Ctrl - Hold for 10% increments"), sizeof(increments_str));
+ }
+
+ BLI_strncpy(bone_vis_str, TIP_("[H] - Toggle bone visibility"), sizeof(increments_str));
+
if (hasNumInput(&pso->num)) {
Scene *scene = pso->scene;
- char str_ofs[NUM_STR_REP_LEN];
+ char str_offs[NUM_STR_REP_LEN];
- outputNumInput(&pso->num, str_ofs, &scene->unit);
+ outputNumInput(&pso->num, str_offs, &scene->unit);
- BLI_snprintf(
- status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str);
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str);
}
else {
BLI_snprintf(status_str,
sizeof(status_str),
- "%s: %d %% | %s",
+ "%s: %s | %s | %s | %s | %s",
mode_str,
- (int)(pso->percentage * 100.0f),
- limits_str);
+ limits_str,
+ overshoot_str,
+ precision_str,
+ increments_str,
+ bone_vis_str);
}
- ED_area_status_text(pso->area, status_str);
+ ED_workspace_status_text(C, status_str);
+ ED_area_status_text(pso->area, "");
}
/* common code for invoke() methods */
@@ -975,21 +1291,40 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
/* header print */
- pose_slide_draw_status(pso);
+ pose_slide_draw_status(C, pso);
/* add a modal handler for this operator */
WM_event_add_modal_handler(C, op);
+
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ pso->overlay_flag = v3d->overlay.flag;
+ v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES;
+
return OPERATOR_RUNNING_MODAL;
}
-/* calculate percentage based on position of mouse (we only use x-axis for now.
- * since this is more convenient for users to do), and store new percentage value
+/* Calculate percentage based on mouse movement, clamp or round to increments if
+ * enabled by the user. Store the new percentage value.
*/
static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso,
wmOperator *op,
const wmEvent *event)
{
- pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx);
+ const float percentage_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE));
+ /* Reduced percentage delta in precision mode (shift held). */
+ pso->raw_percentage += pso->precision ? (percentage_delta / 8) : percentage_delta;
+ pso->percentage = pso->raw_percentage;
+ pso->last_cursor_x = event->x;
+
+ if (!pso->overshoot) {
+ pso->percentage = clamp_f(pso->percentage, 0, 1);
+ }
+
+ if (pso->increments) {
+ pso->percentage = round(pso->percentage * 10) / 10;
+ }
+
RNA_float_set(op->ptr, "percentage", pso->percentage);
}
@@ -1056,9 +1391,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EVT_PADENTER: {
if (event->val == KM_PRESS) {
/* return to normal cursor and header status */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
+ /* Depsgraph updates + redraws. Redraw needed to remove UI. */
+ pose_slide_refresh(C, pso);
+
/* insert keyframes as required... */
pose_slide_autoKeyframe(C, pso);
pose_slide_exit(op);
@@ -1073,13 +1412,14 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
case RIGHTMOUSE: {
if (event->val == KM_PRESS) {
/* return to normal cursor and header status */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
/* reset transforms back to original state */
pose_slide_reset(pso);
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws.*/
pose_slide_refresh(C, pso);
/* clean up temp data */
@@ -1178,10 +1518,58 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
+ /* Overshoot. */
+ case EVT_EKEY: {
+ pso->overshoot = !pso->overshoot;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Precision mode. */
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Increments mode. */
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Toggle Bone visibility. */
+ case EVT_HKEY: {
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES;
+ }
+
default: /* Some other unhandled key... */
break;
}
}
+ /* Precision and stepping only active while button is held. */
+ else if (event->val == KM_RELEASE) {
+ switch (event->type) {
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = false;
+ do_pose_update = true;
+ break;
+ }
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = false;
+ do_pose_update = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
else {
/* unhandled event - maybe it was some view manipulation? */
/* allow to pass through */
@@ -1193,8 +1581,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Perform pose updates - in response to some user action
* (e.g. pressing a key or moving the mouse). */
if (do_pose_update) {
+ pose_slide_mouse_update_percentage(pso, op, event);
+
/* update percentage indicator in header */
- pose_slide_draw_status(pso);
+ pose_slide_draw_status(C, pso);
/* reset transforms (to avoid accumulation errors) */
pose_slide_reset(pso);
@@ -1247,11 +1637,11 @@ static void pose_slide_opdef_properties(wmOperatorType *ot)
PropertyRNA *prop;
prop = RNA_def_float_factor(ot->srna,
- "factor",
+ "percentage",
0.5f,
0.0f,
1.0f,
- "Factor",
+ "Percentage",
"Weighting factor for which keyframe is favored more",
0.0,
1.0);
@@ -1310,6 +1700,8 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev
pso = op->customdata;
+ pso->last_cursor_x = event->x;
+
/* Initialize percentage so that it won't pop on first mouse move. */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1349,7 +1741,7 @@ void POSE_OT_push(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1370,6 +1762,8 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e
pso = op->customdata;
+ pso->last_cursor_x = event->x;
+
/* Initialize percentage so that it won't pop on first mouse move. */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1409,7 +1803,7 @@ void POSE_OT_relax(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1429,6 +1823,8 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
+ pso->last_cursor_x = event->x;
+
/* Initialize percentage so that it won't pop on first mouse move. */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1468,7 +1864,7 @@ void POSE_OT_push_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1489,6 +1885,8 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve
pso = op->customdata;
+ pso->last_cursor_x = event->x;
+
/* Initialize percentage so that it won't pop on first mouse move. */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1528,7 +1926,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1549,6 +1947,8 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
+ pso->last_cursor_x = event->x;
+
/* Initialize percentage so that it won't pop on first mouse move. */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1588,7 +1988,7 @@ void POSE_OT_breakdown(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 767d822aa39..535ccaa06fd 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -5576,7 +5576,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
},
mval,
NULL,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 364444f99ae..68322ed56af 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -292,7 +292,7 @@ static int gizmo_move_modal(bContext *C,
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index 3cff07dde3f..b2d3a2e1576 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -68,7 +68,9 @@ typedef struct SnapGizmo3D {
struct {
int x;
int y;
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
short shift, ctrl, alt, oskey;
+#endif
} last_eventstate;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
@@ -99,28 +101,30 @@ static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManage
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
if ((event->x != snap_gizmo->last_eventstate.x) ||
- (event->y != snap_gizmo->last_eventstate.y) ||
- (event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
- (event->shift != snap_gizmo->last_eventstate.shift) ||
- (event->alt != snap_gizmo->last_eventstate.alt) ||
- (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ (event->y != snap_gizmo->last_eventstate.y)) {
return true;
}
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
+ (event->shift != snap_gizmo->last_eventstate.shift) ||
+ (event->alt != snap_gizmo->last_eventstate.alt) ||
+ (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ return true;
+ }
+ }
+#endif
}
return false;
}
/* Copies the current eventstate. */
-static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
+static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
{
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
snap_gizmo->last_eventstate.x = event->x;
snap_gizmo->last_eventstate.y = event->y;
- snap_gizmo->last_eventstate.ctrl = event->ctrl;
- snap_gizmo->last_eventstate.shift = event->shift;
- snap_gizmo->last_eventstate.alt = event->alt;
- snap_gizmo->last_eventstate.oskey = event->oskey;
}
}
@@ -140,6 +144,12 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
return snap_gizmo->invert_snap;
}
+ /* Save new eventstate. */
+ snap_gizmo->last_eventstate.ctrl = event->ctrl;
+ snap_gizmo->last_eventstate.shift = event->shift;
+ snap_gizmo->last_eventstate.alt = event->alt;
+ snap_gizmo->last_eventstate.oskey = event->oskey;
+
const int snap_on = snap_gizmo->snap_on;
wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap);
@@ -328,23 +338,17 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
Scene *scene = DEG_get_input_scene(depsgraph);
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- if ((snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE) == 0) {
- bool invert_snap_toggle = invert_snap(snap_gizmo, wm);
- if (invert_snap_toggle != snap_gizmo->invert_snap) {
- snap_gizmo->invert_snap = invert_snap_toggle;
-
- /* Status has changed, be sure to save before early return. */
- eventstate_save(snap_gizmo, wm);
- }
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
const ToolSettings *ts = scene->toolsettings;
- if (invert_snap_toggle != !(ts->snap_flag & SCE_SNAP)) {
+ if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) {
snap_gizmo->snap_elem = 0;
return 0;
}
}
#endif
- eventstate_save(snap_gizmo, wm);
+ eventstate_save_xy(snap_gizmo, wm);
snap_gizmo->is_enabled = true;
@@ -364,25 +368,39 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
}
+ eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ?
+ SNAP_ONLY_ACTIVE :
+ SNAP_ALL;
+
+ eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ?
+ SNAP_GEOM_FINAL :
+ (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ?
+ SNAP_GEOM_CAGE :
+ SNAP_GEOM_EDIT;
+
+ bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false :
+ true;
+
float dist_px = 12.0f * U.pixelsize;
ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz);
- snap_elem = ED_transform_snap_object_project_view3d_ex(snap_gizmo->snap_context_v3d,
- depsgraph,
- snap_elements,
- &(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
- .use_occlusion_test = true,
- },
- mval_fl,
- prev_co,
- &dist_px,
- co,
- no,
- &index,
- NULL,
- NULL);
+ snap_elem = ED_transform_snap_object_project_view3d_ex(
+ snap_gizmo->snap_context_v3d,
+ depsgraph,
+ snap_elements,
+ &(const struct SnapObjectParams){
+ .snap_select = snap_select,
+ .edit_mode_type = edit_mode_type,
+ .use_occlusion_test = use_occlusion_test,
+ },
+ mval_fl,
+ prev_co,
+ &dist_px,
+ co,
+ no,
+ &index,
+ NULL,
+ NULL);
}
if (snap_elem == 0) {
@@ -596,8 +614,8 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
GPU_line_width(1.0f);
- const float *prev_point = snap_gizmo_snap_elements(snap_gizmo) &
- SCE_SNAP_MODE_EDGE_PERPENDICULAR ?
+ const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) &
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR) ?
snap_gizmo->prevpoint :
NULL;
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 47ae90acb74..f7ab72a8a43 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -36,6 +36,7 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
+ gpencil_add_blank.c
gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 5c40bc8e418..c155587e95a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1548,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (p->gpl == NULL) {
/* tag for annotations */
p->gpd->flag |= GP_DATA_ANNOTATIONS;
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false);
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
new file mode 100644
index 00000000000..3aa16e54597
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_gpencil.h"
+
+/* Definition of the most important info from a color */
+typedef struct ColorTemplate {
+ const char *name;
+ float line[4];
+ float fill[4];
+} ColorTemplate;
+
+/* Add color an ensure duplications (matched by name) */
+static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct)
+{
+ int index;
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+
+ copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
+ srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
+
+ copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
+ srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
+
+ return index;
+}
+
+/* ***************************************************************** */
+/* Stroke Geometry */
+
+/* ***************************************************************** */
+/* Color Data */
+
+static const ColorTemplate gp_stroke_material_black = {
+ "Black",
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 0.0f, 0.0f},
+};
+
+/* ***************************************************************** */
+/* Blank API */
+
+/* Add a Simple empty object with one layer and one color. */
+void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4]))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ /* create colors */
+ int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black);
+
+ /* set first color as active and in brushes */
+ ob->actcol = color_black + 1;
+
+ /* layers */
+ bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
+
+ /* frames */
+ BKE_gpencil_frame_addnew(layer, CFRA);
+
+ /* update depsgraph */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
index 6b28c6ec13e..ac0da0ad1db 100644
--- a/source/blender/editors/gpencil/gpencil_add_lineart.c
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob)
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
BKE_gpencil_frame_addnew(lines, 0);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 4497d963c6d..d8734c4ae6b 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* layers */
/* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */
- bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false);
- bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false);
+ bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
/* NOTE: No need to check for existing, as this will take care of it for us */
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 26237636526..e95496b51ee 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false);
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA);
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index ac75ae44c8a..8ab413e907c 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
/* Add layer and frame. */
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index b269fd84d5f..d9a807d17ab 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op)
gpd->flag |= GP_DATA_ANNOTATIONS;
/* add new layer (i.e. a "note") */
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
/* mark as annotation */
(*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS;
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
gpd = *gpd_ptr;
}
else {
@@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if ((ob != NULL) && (ob->type == OB_GPENCIL)) {
gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
/* Add a new frame to make it visible in Dopesheet. */
if (gpl != NULL) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
@@ -524,7 +524,6 @@ enum {
static bool gpencil_layer_duplicate_object_poll(bContext *C)
{
- ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
return false;
@@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C)
return false;
}
- /* check there are more grease pencil objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if ((base->object != ob) && (base->object->type == OB_GPENCIL)) {
- return true;
- }
- }
-
- return false;
+ return true;
}
static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "object", name);
-
- if (name[0] == '\0') {
- return OPERATOR_CANCELLED;
- }
-
- Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name);
-
- int mode = RNA_enum_get(op->ptr, "mode");
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ const int mode = RNA_enum_get(op->ptr, "mode");
Object *ob_src = CTX_data_active_object(C);
bGPdata *gpd_src = (bGPdata *)ob_src->data;
- bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src);
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src);
- /* Sanity checks. */
- if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) {
- return OPERATOR_CANCELLED;
- }
- /* Cannot copy itself and check destination type. */
- if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) {
- return OPERATOR_CANCELLED;
- }
-
- bGPdata *gpd_dst = (bGPdata *)ob_dst->data;
-
- /* Create new layer. */
- bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true);
- /* Need to copy some variables (not all). */
- gpl_dst->onion_flag = gpl_src->onion_flag;
- gpl_dst->thickness = gpl_src->thickness;
- gpl_dst->line_change = gpl_src->line_change;
- copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
- gpl_dst->opacity = gpl_src->opacity;
-
- /* Create all frames. */
- LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
-
- if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
continue;
}
+ bGPdata *gpd_dst = (bGPdata *)ob->data;
+ LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ if ((only_active) && (gpl_src != gpl_active)) {
+ continue;
+ }
+ /* Create new layer (adding at head of the list). */
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true);
+ /* Need to copy some variables (not all). */
+ gpl_dst->onion_flag = gpl_src->onion_flag;
+ gpl_dst->thickness = gpl_src->thickness;
+ gpl_dst->line_change = gpl_src->line_change;
+ copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
+ gpl_dst->opacity = gpl_src->opacity;
+
+ /* Create all frames. */
+ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
+
+ if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ continue;
+ }
- /* Create new frame. */
- bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
+ /* Create new frame. */
+ bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Copy strokes. */
- LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
+ /* Copy strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
- /* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
- /* Check if material is in destination object,
- * otherwise add the slot with the material. */
- Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
- if (ma_src != NULL) {
- int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
+ /* Check if material is in destination object,
+ * otherwise add the slot with the material. */
+ Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
+ if (ma_src != NULL) {
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
- /* Reassign the stroke material to the right slot in destination object. */
- gps_dst->mat_nr = idx;
- }
+ /* Reassign the stroke material to the right slot in destination object. */
+ gps_dst->mat_nr = idx;
+ }
- /* Add new stroke to frame. */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Add new stroke to frame. */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+ }
+ }
}
+ /* notifiers */
+ DEG_id_tag_update(&gpd_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
+ CTX_DATA_END;
- /* notifiers */
- DEG_id_tag_update(&gpd_dst->id,
- ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem copy_mode[] = {
{GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""},
{GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""},
@@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* identifiers */
ot->name = "Duplicate Layer to New Object";
ot->idname = "GPENCIL_OT_layer_duplicate_object";
- ot->description = "Make a copy of the active Grease Pencil layer to new object";
+ ot->description = "Make a copy of the active Grease Pencil layer to selected object";
/* callbacks */
ot->exec = gpencil_layer_duplicate_object_exec;
@@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- ot->prop = RNA_def_string(
- ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object");
- RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
- RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Copy only active Layer, uncheck to append all layers");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ********************* Duplicate Frame ************************** */
@@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else {
/* Try to get layer */
@@ -3589,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ********************* Append Materials in a new object ************************** */
+static bool gpencil_materials_copy_to_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ short *totcolp = BKE_object_material_len_p(ob);
+ if (*totcolp == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ Object *ob_src = CTX_data_active_object(C);
+ Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol);
+
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
+ continue;
+ }
+ /* Duplicate materials. */
+ for (int i = 0; i < ob_src->totcol; i++) {
+ Material *ma_src = BKE_object_material_get(ob_src, i + 1);
+ if (only_active && ma_src != ma_active) {
+ continue;
+ }
+
+ if (ma_src != NULL) {
+ BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
+ }
+ }
+
+ /* notifiers */
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Copy Materials to Selected Object";
+ ot->idname = "GPENCIL_OT_materials_copy_to_object";
+ ot->description = "Append Materials of the active Grease Pencil to other object";
+
+ /* callbacks */
+ ot->exec = gpencil_materials_copy_to_object_exec;
+ ot->poll = gpencil_materials_copy_to_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Append only active material, uncheck to append all materials");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
/* Parent GPencil object to Lattice */
bool ED_gpencil_add_lattice_modifier(const bContext *C,
ReportList *reports,
@@ -3745,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot)
ot->exec = gpencil_layer_mask_remove_exec;
ot->poll = gpencil_active_layer_poll;
}
+
+static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl)) {
+ return OPERATOR_CANCELLED;
+ }
+ if (gpl->act_mask > 0) {
+ bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1);
+ if (mask != NULL) {
+ BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
+ if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) {
+ gpl->act_mask += direction;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_mask_move(wmOperatorType *ot)
+{
+ static const EnumPropertyItem slot_move[] = {
+ {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Move Grease Pencil Layer Mask";
+ ot->idname = "GPENCIL_OT_layer_mask_move";
+ ot->description = "Move the active Grease Pencil mask layer up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gpencil_layer_mask_move_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 66d50e2fd12..e65afa1abff 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
if (gpl == NULL) {
/* no active layer - let's just create one */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
@@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- Scene *scene = CTX_data_scene(C);
bGPDlayer *target_layer = NULL;
ListBase strokes = {NULL, NULL};
int layer_num = RNA_int_get(op->ptr, "layer");
const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* if autolock enabled, disabled now */
+ /* If autolock enabled, disabled now. */
if (use_autolock) {
gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
}
@@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Create a new layer. */
- target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true);
+ target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
}
if (target_layer == NULL) {
@@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Extract all strokes to move to this layer
- * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
- * getting repeatedly moved
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
-
- /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
- if ((gpl == target_layer) || (gpf == NULL)) {
+ /* Extract all strokes to move to this layer. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) {
+ /* Skip if this is the layer we're moving strokes to. */
+ if (gpl_src == target_layer) {
continue;
}
+ bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe;
+ for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) {
+ if ((gpf_src == gpl_src->actframe) ||
+ ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf_src == NULL) {
+ continue;
+ }
- /* make copies of selected strokes, and deselect these once we're done */
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDstroke *gpsn = NULL;
+ BLI_listbase_clear(&strokes);
+ for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf_src->strokes, gps);
+ BLI_addtail(&strokes, gps);
+ }
+ }
+ /* Paste them all in one go. */
+ if (strokes.first) {
+ bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get(
+ target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW);
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
+ BLI_movelisttolist(&gpf_dst->strokes, &strokes);
+ BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
+ }
}
-
- /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
- if (gps->flag & GP_STROKE_SELECT) {
- BLI_remlink(&gpf->strokes, gps);
- BLI_addtail(&strokes, gps);
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
-
- /* if new layer and autolock, lock old layer */
+ /* If new layer and autolock, lock old layer. */
if ((layer_num == -1) && (use_autolock)) {
- gpl->flag |= GP_LAYER_LOCKED;
+ gpl_src->flag |= GP_LAYER_LOCKED;
}
}
CTX_DATA_END;
- /* Paste them all in one go */
- if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW);
-
- BLI_movelisttolist(&gpf->strokes, &strokes);
- BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
- }
-
/* back autolock status */
if (use_autolock) {
gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
@@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
SnapObjectContext *sctx = NULL;
@@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int cfra_prv = INT_MIN;
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
- }
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (gps->flag & GP_STROKE_SELECT || curve_select) {
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ bool curve_select = false;
+ if (is_curve_edit && gps->editcurve != NULL) {
+ curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ }
- /* update frame to get the new location of objects */
- if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
- cfra_prv = gpf_->framenum;
- CFRA = gpf_->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
+ if (gps->flag & GP_STROKE_SELECT || curve_select) {
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+ }
- if (is_curve_edit && gps->editcurve != NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- /* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
+ ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
- changed = true;
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
+ }
+ }
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ CTX_DATA_END;
/* return frame state and DB to original state */
CFRA = oldframe;
@@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"VIEW",
0,
"View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
"using 'Cursor' Stroke Placement"},
{GP_REPROJECT_SURFACE,
"SURFACE",
@@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->name = "Reproject Strokes";
ot->idname = "GPENCIL_OT_reproject";
ot->description =
- "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
+ "Reproject the selected strokes from the current viewpoint as if they had been newly "
+ "drawn "
"(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
"or for matching deforming geometry)";
@@ -4208,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
ot->name = "Subdivide Stroke";
ot->idname = "GPENCIL_OT_stroke_subdivide";
ot->description =
- "Subdivide between continuous selected points of the stroke adding a point half way between "
+ "Subdivide between continuous selected points of the stroke adding a point half way "
+ "between "
"them";
/* api callbacks */
@@ -4593,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
/* add layer if not created before */
if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
}
/* add frame if not created before */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index bb0a2916d2d..f74e211dd65 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1247,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf)
static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
{
ImBuf *ibuf;
+ Brush *brush = tgpf->brush;
float rgba[4];
void *lock;
int v[2];
@@ -1279,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
/* Dilate. */
if (dilate) {
- dilate_shape(ibuf);
+ for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) {
+ dilate_shape(ibuf);
+ }
}
for (int idx = imagesize - 1; idx != 0; idx--) {
@@ -1686,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->gpd = gpd;
tgpf->gpl = BKE_gpencil_layer_active_get(gpd);
if (tgpf->gpl == NULL) {
- tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true);
+ tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false);
}
tgpf->lock_axis = ts->gp_sculpt.lock_axis;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index c6f74c39beb..9f48c81c8f1 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot);
void GPENCIL_OT_hide(struct wmOperatorType *ot);
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
@@ -537,6 +538,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot);
void GPENCIL_OT_material_select(struct wmOperatorType *ot);
void GPENCIL_OT_material_set(struct wmOperatorType *ot);
void GPENCIL_OT_set_active_material(struct wmOperatorType *ot);
+void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot);
/* convert old 2.7 files to 2.8 */
void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 7ca53779522..0062e363cdf 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -1434,36 +1434,32 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
uiLayout *col, *row;
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE);
if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) {
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
}
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE);
if (type == GP_IPO_CURVEMAP) {
/* Get an RNA pointer to ToolSettings to give to the custom curve. */
@@ -1477,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
}
else if (type != GP_IPO_LINEAR) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE);
if (type == GP_IPO_BACK) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE);
}
else if (type == GP_IPO_ELASTIC) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 1a6cb5670c4..698d784c3b0 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_layer_mask_add);
WM_operatortype_append(GPENCIL_OT_layer_mask_remove);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_move);
WM_operatortype_append(GPENCIL_OT_hide);
WM_operatortype_append(GPENCIL_OT_reveal);
@@ -650,6 +651,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
+ WM_operatortype_append(GPENCIL_OT_materials_copy_to_object);
WM_operatortype_append(GPENCIL_OT_transform_fill);
WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 9bd929aa2af..993065a3a70 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
/* only erase stroke points that are visible */
static bool gpencil_stroke_eraser_is_occluded(
- tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y)
+ tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded(
mul_v3_m4v3(fpt, diff_mat, &pt->x);
const float depth_pt = view3d_point_depth(rv3d, fpt);
+ /* Checked occlusion flag. */
+ pt->flag |= GP_SPOINT_TEMP_TAG;
if (depth_pt > depth_mval) {
+ /* Is occluded. */
+ pt->flag |= GP_SPOINT_TEMP_TAG2;
return true;
}
}
@@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
for (i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_TAG;
+ /* Occlusion already checked. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG;
+ /* Point is occluded. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG2;
}
/* First Pass: Loop over the points in the stroke
@@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
* - this assumes that linewidth is irrelevant
*/
if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) {
+
+ bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true;
+ is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ?
+ ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]);
+ if (is_occluded_pt0) {
+ is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]);
+ if (is_occluded_pt1) {
+ is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]);
+ }
+ }
+
+ if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
/* get active layer (or add a new one if non-existent) */
p->gpl = BKE_gpencil_layer_active_get(p->gpd);
if (p->gpl == NULL) {
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false);
changed = true;
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 3d20e32ed49..1b0a40b1be1 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* if layer doesn't exist, create a new one */
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
+ gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false);
}
tgpi->gpl = gpl;
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index d2e5fa3db32..cd75f9313ad 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job)
trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data;
trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd);
if (trace_job->gpl == NULL) {
- trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true);
+ trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false);
}
}
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 983ae94b637..8118e3c6c69 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
- const int temp_win_size[],
+ const int temp_win_size[2],
const bool is_maximized);
void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region);
diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h
index 58b2cb80bbe..571519e52f7 100644
--- a/source/blender/editors/include/ED_gizmo_library.h
+++ b/source/blender/editors/include/ED_gizmo_library.h
@@ -263,6 +263,11 @@ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *sce
typedef enum {
ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */
+ ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5,
} eSnapGizmo;
void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index d4ff374dc38..bad080e1609 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -251,6 +251,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y);
/* ----------- Add Primitive Utilities -------------- */
+void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob);
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 12d6f1fce54..179c9d5b30d 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -511,6 +511,7 @@ bool ED_autokeyframe_property(struct bContext *C,
#define ANIM_KS_ROTATION_ID "Rotation"
#define ANIM_KS_SCALING_ID "Scaling"
#define ANIM_KS_LOC_ROT_SCALE_ID "LocRotScale"
+#define ANIM_KS_LOC_ROT_SCALE_CPROP_ID "LocRotScaleCProp"
#define ANIM_KS_AVAILABLE_ID "Available"
#define ANIM_KS_WHOLE_CHARACTER_ID "WholeCharacter"
#define ANIM_KS_WHOLE_CHARACTER_SELECTED_ID "WholeCharacterSelected"
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 78c4c2a8eba..bdd7ec571dc 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area);
void ED_area_tag_redraw_regiontype(ScrArea *area, int type);
void ED_area_tag_refresh(ScrArea *area);
void ED_area_do_refresh(struct bContext *C, ScrArea *area);
-struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]);
+struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]);
void ED_area_status_text(ScrArea *area, const char *str);
void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit);
void ED_area_prevspace(struct bContext *C, ScrArea *area);
@@ -200,8 +200,6 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/* screens */
void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm);
void ED_screen_draw_edges(struct wmWindow *win);
-void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2);
-void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac);
void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win);
void ED_screen_ensure_updated(struct wmWindowManager *wm,
struct wmWindow *win,
@@ -450,10 +448,10 @@ enum {
};
/* SCREEN_OT_space_context_cycle direction */
-enum {
+typedef enum eScreenCycle {
SPACE_CONTEXT_CYCLE_PREV,
SPACE_CONTEXT_CYCLE_NEXT,
-};
+} eScreenCycle;
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index b7174964ef6..42e73bbf744 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -39,12 +39,19 @@ struct View3D;
/* ED_transform_snap_object_*** API */
-typedef enum eSnapSelect {
+typedef enum {
SNAP_ALL = 0,
SNAP_NOT_SELECTED = 1,
SNAP_NOT_ACTIVE = 2,
+ SNAP_ONLY_ACTIVE = 3,
} eSnapSelect;
+typedef enum {
+ SNAP_GEOM_FINAL = 0,
+ SNAP_GEOM_CAGE = 1,
+ SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */
+} eSnapEditType;
+
/** used for storing multiple hits */
struct SnapObjectHitDepth {
struct SnapObjectHitDepth *next, *prev;
@@ -54,7 +61,7 @@ struct SnapObjectHitDepth {
float no[3];
int index;
- struct Object *ob;
+ struct Object *ob_eval;
float obmat[4][4];
/* needed to tell which ray-cast this was part of,
@@ -64,10 +71,10 @@ struct SnapObjectHitDepth {
/** parameters that define which objects will be used to snap. */
struct SnapObjectParams {
- /* special context sensitive handling for the active or selected object */
+ /* Special context sensitive handling for the active or selected object. */
char snap_select;
- /* use editmode cage */
- unsigned int use_object_edit_cage : 1;
+ /* Geometry for snapping in edit mode. */
+ char edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
unsigned int use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 499f28beb60..66ec57c8a31 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -454,7 +454,7 @@ bool ED_view3d_calc_render_border(const struct Scene *scene,
struct ARegion *region,
struct rcti *rect);
-void ED_view3d_clipping_calc_from_boundbox(float clip[6][4],
+void ED_view3d_clipping_calc_from_boundbox(float clip[4][4],
const struct BoundBox *clipbb,
const bool is_flip);
void ED_view3d_clipping_calc(struct BoundBox *bb,
@@ -690,7 +690,7 @@ float ED_view3d_grid_scale(const struct Scene *scene,
void ED_view3d_grid_steps(const struct Scene *scene,
struct View3D *v3d,
struct RegionView3D *rv3d,
- float *r_grid_steps);
+ float r_grid_steps[8]);
float ED_view3d_grid_view_scale(struct Scene *scene,
struct View3D *v3d,
struct ARegion *region,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 81872428038..338b12f7985 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -186,17 +186,17 @@ enum {
UI_RETURN_POPUP_OK = 1 << 5,
};
-/* but->flag - general state flags. */
+/** #uiBut.flag general state flags. */
enum {
- /** Warning, the first 6 flags are internal. */
- UI_BUT_ICON_SUBMENU = 1 << 6,
- UI_BUT_ICON_PREVIEW = 1 << 7,
+ /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */
+ UI_BUT_ICON_SUBMENU = 1 << 7,
+ UI_BUT_ICON_PREVIEW = 1 << 8,
- UI_BUT_NODE_LINK = 1 << 8,
- UI_BUT_NODE_ACTIVE = 1 << 9,
- UI_BUT_DRAG_LOCK = 1 << 10,
+ UI_BUT_NODE_LINK = 1 << 9,
+ UI_BUT_NODE_ACTIVE = 1 << 10,
+ UI_BUT_DRAG_LOCK = 1 << 11,
/** Grayed out and un-editable. */
- UI_BUT_DISABLED = 1 << 11,
+ UI_BUT_DISABLED = 1 << 12,
UI_BUT_ANIMATED = 1 << 13,
UI_BUT_ANIMATED_KEY = 1 << 14,
@@ -2137,7 +2137,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
struct PointerRNA *ptr,
const char *propname,
const char *name);
-void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
+void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]);
void uiTemplateCacheFile(uiLayout *layout,
const struct bContext *C,
struct PointerRNA *ptr,
@@ -2405,9 +2405,12 @@ void uiItemS_ex(uiLayout *layout, float factor);
void uiItemSpacer(uiLayout *layout);
void uiItemPopoverPanel_ptr(
- uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon);
-void uiItemPopoverPanel(
- uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon);
+ uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon);
+void uiItemPopoverPanel(uiLayout *layout,
+ const struct bContext *C,
+ const char *panel_type,
+ const char *name,
+ int icon);
void uiItemPopoverPanelFromGroup(uiLayout *layout,
struct bContext *C,
int space_id,
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 1820c2f133c..c99c7f681b3 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void);
/* only for buttons in theme editor! */
const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid);
-void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis);
+void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 90d604b3190..a31efefd99c 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -2327,6 +2327,14 @@ bool ui_but_is_float(const uiBut *but)
return false;
}
+PropertyScaleType ui_but_scale_type(const uiBut *but)
+{
+ if (but->rnaprop) {
+ return RNA_property_ui_scale(but->rnaprop);
+ }
+ return PROP_SCALE_LINEAR;
+}
+
bool ui_but_is_bool(const uiBut *but)
{
if (ELEM(but->type,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index a5a5a69728e..282d470c7ea 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -126,6 +126,24 @@
#define UI_MAX_PASSWORD_STR 128
/**
+ * This is a lower limit on the soft minimum of the range.
+ * Usually the derived lower limit from the visible precision is higher,
+ * so this number is the backup minimum.
+ *
+ * Logarithmic scale does not work with a minimum value of zero,
+ * but we want to support it anyway. It is set to 0.5e... for
+ * correct rounding since when the tweaked value is lower than
+ * the log minimum (lower limit), it will snap to 0.
+ */
+#define UI_PROP_SCALE_LOG_MIN 0.5e-8f
+/**
+ * This constant defines an offset for the precision change in
+ * snap rounding, when going to higher values. It is set to
+ * `0.5 - log10(3) = 0.03` to make the switch at `0.3` values.
+ */
+#define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f
+
+/**
* When #USER_CONTINUOUS_MOUSE is disabled or tablet input is used,
* Use this as a maximum soft range for mapping cursor motion to the value.
* Otherwise min/max of #FLT_MAX, #INT_MAX cause small adjustments to jump to large numbers.
@@ -477,6 +495,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C,
static void ui_do_but_extra_operator_icons_mousemove(uiBut *but,
uiHandleButtonData *data,
const wmEvent *event);
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data);
#ifdef USE_DRAG_MULTINUM
static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block);
@@ -1114,6 +1133,13 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
{
if (data->str) {
+ double value;
+ /* Check if the string value is a number and cancel if it's equal to the startvalue. */
+ if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) {
+ data->cancel = true;
+ return;
+ }
+
if (ui_but_string_set(C, but, data->str)) {
data->value = ui_but_value_get(but);
}
@@ -3354,6 +3380,8 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
if (is_num_but) {
BLI_assert(data->is_str_dynamic == false);
ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen);
+
+ ui_numedit_begin_set_values(but, data);
}
/* won't change from now on */
@@ -3890,6 +3918,14 @@ static void ui_do_but_textedit_select(
/** \name Button Number Editing (various types)
* \{ */
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data)
+{
+ data->startvalue = ui_but_value_get(but);
+ data->origvalue = data->startvalue;
+ data->value = data->origvalue;
+ but->editval = &data->value;
+}
+
static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
{
if (but->type == UI_BTYPE_CURVE) {
@@ -3915,19 +3951,21 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
but->editvec = data->vec;
}
else {
- float softrange, softmin, softmax;
+ ui_numedit_begin_set_values(but, data);
- data->startvalue = ui_but_value_get(but);
- data->origvalue = data->startvalue;
- data->value = data->origvalue;
- but->editval = &data->value;
+ float softmin = but->softmin;
+ float softmax = but->softmax;
+ float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
- softmin = but->softmin;
- softmax = but->softmax;
- softrange = softmax - softmin;
+ float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f;
if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) {
uiButNumber *number_but = (uiButNumber *)but;
+
+ if (scale_type == PROP_SCALE_LOG) {
+ log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f);
+ }
/* Use a minimum so we have a predictable range,
* otherwise some float buttons get a large range. */
const float value_step_float_min = 0.1f;
@@ -3976,7 +4014,31 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
}
}
- data->dragfstart = (softrange == 0.0f) ? 0.0f : ((float)data->value - softmin) / softrange;
+ if (softrange == 0.0f) {
+ data->dragfstart = 0.0f;
+ }
+ else {
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ data->dragfstart = ((float)data->value - softmin) / softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ BLI_assert(log_min != 0.0f);
+ const float base = softmax / log_min;
+ data->dragfstart = logf((float)data->value / log_min) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubic_min = cube_f(softmin);
+ const float cubic_max = cube_f(softmax);
+ const float cubic_range = cubic_max - cubic_min;
+ const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min;
+ data->dragfstart = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
+ }
+ }
data->dragf = data->dragfstart;
data->drag_map_soft_min = softmin;
@@ -4694,6 +4756,7 @@ static float ui_numedit_apply_snapf(
/* pass */
}
else {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
float softrange = softmax - softmin;
float fac = 1.0f;
@@ -4731,31 +4794,30 @@ static float ui_numedit_apply_snapf(
}
}
- if (snap == SNAP_ON) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf);
- }
- else {
- tempf = roundf(tempf * 0.1f) * 10.0f;
- }
- }
- else if (snap == SNAP_ON_SMALL) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 100.0f) * 0.01f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
+ BLI_assert(ELEM(snap, SNAP_ON, SNAP_ON_SMALL));
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR:
+ case PROP_SCALE_CUBIC: {
+ const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f);
+ if (softrange < 2.10f) {
+ tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac;
+ }
+ else if (softrange < 21.0f) {
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ }
+ else {
+ tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac;
+ }
+ break;
}
- else {
- tempf = roundf(tempf);
+ case PROP_SCALE_LOG: {
+ const float snap_fac = powf(10.0f,
+ roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) -
+ (snap == SNAP_ON_SMALL ? 2.0f : 1.0f));
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ break;
}
}
- else {
- BLI_assert(0);
- }
if (fac != 1.0f) {
tempf *= fac;
@@ -4800,6 +4862,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
int lvalue, temp;
bool changed = false;
const bool is_float = ui_but_is_float(but);
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
/* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) {
@@ -4811,21 +4874,74 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
const float softmax = but->softmax;
const float softrange = softmax - softmin;
+ const float log_min = (scale_type == PROP_SCALE_LOG) ?
+ max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10, -number_but->precision) * 0.5f) :
+ 0;
+
/* Mouse location isn't screen clamped to the screen so use a linear mapping
* 2px == 1-int, or 1px == 1-ClickStep */
if (is_float) {
fac *= 0.01f * number_but->step_size;
- tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac);
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float startvalue = max_ff((float)data->startvalue, log_min);
+ tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac;
+ tempf *= tempf * tempf;
+ break;
+ }
+ }
+
tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap);
#if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
- if (tempf < softmin) {
- data->dragstartx -= (softmin - tempf) / fac;
- tempf = softmin;
- }
- else if (tempf > softmax) {
- data->dragstartx += (tempf - softmax) / fac;
- tempf = softmax;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (tempf < softmin) {
+ data->dragstartx -= (softmin - tempf) / fac;
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= (softmax - tempf) / fac;
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ if (tempf < log_min) {
+ data->dragstartx -= logf(log_min / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= logf(softmax / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ if (tempf < softmin) {
+ data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmax;
+ }
+ break;
+ }
}
#else
CLAMP(tempf, softmin, softmax);
@@ -4932,7 +5048,31 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
}
data->draglastx = mx;
- tempf = (softmin + data->dragf * softrange);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (softmin + data->dragf * softrange);
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10.0f, -number_but->precision) * 0.5f);
+ const float base = softmax / log_min;
+ tempf = powf(base, data->dragf) * log_min;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = (softmin + data->dragf * softrange);
+ tempf *= tempf * tempf;
+ float cubic_min = softmin * softmin * softmin;
+ float cubic_max = softmax * softmax * softmax;
+ tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin;
+ break;
+ }
+ }
if (!is_float) {
temp = round_fl_to_int(tempf);
@@ -5179,9 +5319,19 @@ static int ui_do_but_NUM(
else {
/* Float Value. */
if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
- const double value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ double value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f,
+ (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) +
+ log10f(number_but->step_size));
+ }
+ else {
+ value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ }
BLI_assert(value_step > 0.0f);
const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
(double)max_ff(but->softmin,
@@ -5229,6 +5379,8 @@ static bool ui_numedit_but_SLI(uiBut *but,
return changed;
}
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
softmin = but->softmin;
softmax = but->softmax;
softrange = softmax - softmin;
@@ -5270,7 +5422,24 @@ static bool ui_numedit_but_SLI(uiBut *but,
#endif
/* done correcting mouse */
- tempf = softmin + f * softrange;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = softmin + f * softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ tempf = powf(softmax / softmin, f) * softmin;
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ tempf = cube_f(softmin + f * softrange);
+ tempf = (tempf - cubicmin) / cubicrange * softrange + softmin;
+ break;
+ }
+ }
temp = round_fl_to_int(tempf);
if (snap) {
@@ -5464,6 +5633,8 @@ static int ui_do_but_SLI(
if (click) {
if (click == 2) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
/* nudge slider to the left or right */
float f, tempf, softmin, softmax, softrange;
int temp;
@@ -5488,14 +5659,20 @@ static int ui_do_but_SLI(
f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect));
}
- f = softmin + f * softrange;
+ if (scale_type == PROP_SCALE_LOG) {
+ f = powf(softmax / softmin, f) * softmin;
+ }
+ else {
+ f = softmin + f * softrange;
+ }
if (!ui_but_is_float(but)) {
+ int value_step = 1;
if (f < temp) {
- temp--;
+ temp -= value_step;
}
else {
- temp++;
+ temp += value_step;
}
if (temp >= softmin && temp <= softmax) {
@@ -5506,14 +5683,23 @@ static int ui_do_but_SLI(
}
}
else {
- if (f < tempf) {
- tempf -= 0.01f;
- }
- else {
- tempf += 0.01f;
- }
-
if (tempf >= softmin && tempf <= softmax) {
+ float value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f);
+ }
+ else {
+ value_step = 0.01f;
+ }
+
+ if (f < tempf) {
+ tempf -= value_step;
+ }
+ else {
+ tempf += value_step;
+ }
+
+ CLAMP(tempf, softmin, softmax);
data->value = tempf;
}
else {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 4c96512b4f3..23856c41ceb 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -62,7 +62,7 @@ struct wmTimer;
#define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y)
#define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9)
-/* some extra padding added to menus containing submenu icons */
+/** Some extra padding added to menus containing sub-menu icons. */
#define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC)
/* menu scrolling */
@@ -74,28 +74,31 @@ struct wmTimer;
#define UI_PANEL_MINX 100
#define UI_PANEL_MINY 70
-/* popover width (multiplied by 'U.widget_unit') */
+/** Popover width (multiplied by #U.widget_unit) */
#define UI_POPOVER_WIDTH_UNITS 10
-/* uiBut->flag */
+/** #uiBut.flag */
enum {
- UI_SELECT = (1 << 0), /* use when the button is pressed */
- UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */
+ /** Use when the button is pressed. */
+ UI_SELECT = (1 << 0),
+ /** Temporarily hidden (scrolled out of the view). */
+ UI_SCROLLED = (1 << 1),
UI_ACTIVE = (1 << 2),
UI_HAS_ICON = (1 << 3),
UI_HIDDEN = (1 << 4),
- UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */
+ /** Display selected, doesn't impact interaction. */
+ UI_SELECT_DRAW = (1 << 5),
/** Property search filter is active and the button does not match. */
- UI_SEARCH_FILTER_NO_MATCH = (1 << 12),
- /* warn: rest of uiBut->flag in UI_interface.h */
+ UI_SEARCH_FILTER_NO_MATCH = (1 << 6),
+ /* WARNING: rest of #uiBut.flag in UI_interface.h */
};
-/* uiBut->dragflag */
+/** #uiBut.dragflag */
enum {
UI_BUT_DRAGPOIN_FREE = (1 << 0),
};
-/* but->pie_dir */
+/** #uiBut.pie_dir */
typedef enum RadialDirection {
UI_RADIAL_NONE = -1,
UI_RADIAL_N = 0,
@@ -126,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8];
#define UI_BITBUT_ROW(min, max) \
(((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0))
-/* split numbuts by ':' and align l/r */
+/** Split number-buttons by ':' and align left/right. */
#define USE_NUMBUTS_LR_ALIGN
-/* Use new 'align' computation code. */
+/** Use new 'align' computation code. */
#define USE_UIBUT_SPATIAL_ALIGN
-/* PieMenuData->flags */
+/** #PieMenuData.flags */
enum {
/** pie menu item collision is detected at 90 degrees */
UI_PIE_DEGREES_RANGE_LARGE = (1 << 0),
@@ -152,13 +155,13 @@ enum {
#define PIE_CLICK_THRESHOLD_SQ 50.0f
-/* max amount of items a radial menu (pie menu) can contain */
+/** The maximum number of items a radial menu (pie menu) can contain. */
#define PIE_MAX_ITEMS 8
struct uiBut {
struct uiBut *next, *prev;
- /* Pointer back to the layout item holding this button. */
+ /** Pointer back to the layout item holding this button. */
uiLayout *layout;
int flag, drawflag;
eButType type;
@@ -235,10 +238,10 @@ struct uiBut {
short modifier_key;
short iconadd;
- /* UI_BTYPE_BLOCK data */
+ /** #UI_BTYPE_BLOCK data */
uiBlockCreateFunc block_create_func;
- /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */
+ /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */
uiMenuCreateFunc menu_create_func;
uiMenuStepFunc menu_step_func;
@@ -252,9 +255,11 @@ struct uiBut {
struct wmOperatorType *optype;
struct PointerRNA *opptr;
short opcontext;
- uchar menu_key; /* 'a'-'z', always lower case */
- ListBase extra_op_icons; /* uiButExtraOpIcon */
+ /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */
+ uchar menu_key;
+
+ ListBase extra_op_icons; /** #uiButExtraOpIcon */
/* Draggable data, type is WM_DRAG_... */
char dragtype;
@@ -263,10 +268,10 @@ struct uiBut {
struct ImBuf *imb;
float imb_scale;
- /* active button data */
+ /** Active button data (set when the user is hovering or interacting with a button). */
struct uiHandleButtonData *active;
- /* Custom button data. */
+ /** Custom button data (borrowed, not owned). */
void *custom_data;
char *editstr;
@@ -429,7 +434,7 @@ struct PieMenuData {
float alphafac;
};
-/* uiBlock.content_hints */
+/** #uiBlock.content_hints */
enum eBlockContentHints {
/** In a menu block, if there is a single sub-menu button, we add some
* padding to the right to put nicely aligned triangle icons there. */
@@ -463,7 +468,8 @@ struct uiBlock {
struct Panel *panel;
uiBlock *oldblock;
- ListBase butstore; /* UI_butstore_* runtime function */
+ /** Used for `UI_butstore_*` runtime function. */
+ ListBase butstore;
ListBase button_groups; /* #uiButtonGroup. */
@@ -479,7 +485,8 @@ struct uiBlock {
rctf rect;
float aspect;
- uint puphash; /* popup menu hash for memory */
+ /** Unique hash used to implement popup menu memory. */
+ uint puphash;
uiButHandleFunc func;
void *func_arg1;
@@ -494,10 +501,10 @@ struct uiBlock {
uiBlockHandleFunc handle_func;
void *handle_func_arg;
- /* custom extra handling */
+ /** Custom extra event handling. */
int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *);
- /* extra draw function for custom blocks */
+ /** Custom extra draw function for custom blocks. */
void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect);
void *drawextra_arg1;
void *drawextra_arg2;
@@ -507,7 +514,7 @@ struct uiBlock {
/** Hints about the buttons of this block. Used to avoid iterating over
* buttons to find out if some criteria is met by any. Instead, check this
* criteria when adding the button and set a flag here if it's met. */
- short content_hints; /* eBlockContentHints */
+ short content_hints; /* #eBlockContentHints */
char direction;
/** UI_BLOCK_THEME_STYLE_* */
@@ -521,11 +528,11 @@ struct uiBlock {
const char *lockstr;
bool lock;
- /** to keep blocks while drawing and free them afterwards */
+ /** To keep blocks while drawing and free them afterwards. */
bool active;
- /** to avoid tooltip after click */
+ /** To avoid tool-tip after click. */
bool tooltipdisabled;
- /** UI_block_end done? */
+ /** True when #UI_block_end has been called. */
bool endblock;
/** for doing delayed */
@@ -535,12 +542,12 @@ struct uiBlock {
/** for doing delayed */
int bounds, minbounds;
- /** pull-downs, to detect outside, can differ per case how it is created. */
+ /** Pull-downs, to detect outside, can differ per case how it is created. */
rctf safety;
- /** uiSafetyRct list */
+ /** #uiSafetyRct list */
ListBase saferct;
- uiPopupBlockHandle *handle; /* handle */
+ uiPopupBlockHandle *handle;
/** use so presets can find the operator,
* across menus and from nested popups which fail for operator context. */
@@ -555,10 +562,12 @@ struct uiBlock {
/** \note only accessed by color picker templates. */
ColorPickerData color_pickers;
- bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */
+ /** Block for color picker with gamma baked in. */
+ bool is_color_gamma_picker;
- /** display device name used to display this block,
- * used by color widgets to transform colors from/to scene linear
+ /**
+ * Display device name used to display this block,
+ * used by color widgets to transform colors from/to scene linear.
*/
char display_device[64];
@@ -651,6 +660,7 @@ bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot,
extern void ui_but_update(uiBut *but);
extern void ui_but_update_edited(uiBut *but);
+extern PropertyScaleType ui_but_scale_type(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_float(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_bool(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -671,9 +681,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]);
/* interface_regions.c */
struct uiKeyNavLock {
- /* Set when we're using key-input. */
+ /** Set when we're using keyboard-input. */
bool is_keynav;
- /* only used to check if we've moved the cursor */
+ /** Only used to check if we've moved the cursor. */
int event_xy[2];
};
@@ -689,7 +699,7 @@ struct uiPopupBlockCreate {
int event_xy[2];
- /* when popup is initialized from a button */
+ /** Set when popup is initialized from a button. */
struct ARegion *butregion;
uiBut *but;
};
@@ -698,7 +708,7 @@ struct uiPopupBlockHandle {
/* internal */
struct ARegion *region;
- /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */
+ /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */
float towards_xy[2];
double towardstime;
bool dotowards;
@@ -708,9 +718,9 @@ struct uiPopupBlockHandle {
void (*cancel_func)(struct bContext *C, void *arg);
void *popup_arg;
- /* store data for refreshing popups */
+ /** Store data for refreshing popups. */
struct uiPopupBlockCreate popup_create_vars;
- /* true if we can re-create the popup using 'popup_create_vars' */
+ /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */
bool can_refresh;
bool refresh;
@@ -730,7 +740,7 @@ struct uiPopupBlockHandle {
int retvalue;
float retvec[4];
- /* menu direction */
+ /** Menu direction. */
int direction;
/* Previous values so we don't resize or reposition on refresh. */
@@ -924,9 +934,7 @@ extern void ui_but_execute_end(struct bContext *C,
void *active_back);
extern void ui_but_active_free(const struct bContext *C, uiBut *but);
extern int ui_but_menu_direction(uiBut *but);
-extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR],
- uiBut *but,
- const bool restore);
+extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore);
extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction);
bool ui_but_is_editing(const uiBut *but);
float ui_block_calc_pie_segment(struct uiBlock *block, const float event_xy[2]);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index cabd98902a6..8f2871ce18b 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -3038,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i
/* popover */
void uiItemPopoverPanel_ptr(
- uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
+ uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon)
{
if (!name) {
name = CTX_IFACE_(pt->translation_context, pt->label);
@@ -3067,7 +3067,7 @@ void uiItemPopoverPanel_ptr(
}
void uiItemPopoverPanel(
- uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon)
+ uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon)
{
PanelType *pt = WM_paneltype_find(panel_type, true);
if (pt == NULL) {
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 1fb7a931c08..0cf3ad59903 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1495,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root,
static int edittranslation_exec(bContext *C, wmOperator *op)
{
uiBut *but = UI_context_active_but_get(C);
- int ret = OPERATOR_CANCELLED;
-
- if (but) {
- wmOperatorType *ot;
- PointerRNA ptr;
- char popath[FILE_MAX];
- const char *root = U.i18ndir;
- const char *uilng = BLT_lang_get();
-
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
- uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
- uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
-
- if (!BLI_is_dir(root)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Please set your Preferences' 'Translation Branches "
- "Directory' path to a valid directory");
- return OPERATOR_CANCELLED;
- }
- ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
- if (ot == NULL) {
- BKE_reportf(op->reports,
- RPT_ERROR,
- "Could not find operator '%s'! Please enable ui_translate add-on "
- "in the User Preferences",
- EDTSRC_I18N_OP_NAME);
- return OPERATOR_CANCELLED;
- }
- /* Try to find a valid po file for current language... */
- edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
- /* printf("po path: %s\n", popath); */
- if (popath[0] == '\0') {
- BKE_reportf(
- op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
- return OPERATOR_CANCELLED;
- }
-
- UI_but_string_info_get(C,
- but,
- &but_label,
- &rna_label,
- &enum_label,
- &but_tip,
- &rna_tip,
- &enum_tip,
- &rna_struct,
- &rna_prop,
- &rna_enum,
- &rna_ctxt,
- NULL);
-
- WM_operator_properties_create_ptr(&ptr, ot);
- RNA_string_set(&ptr, "lang", uilng);
- RNA_string_set(&ptr, "po_file", popath);
- RNA_string_set(&ptr, "but_label", but_label.strinfo);
- RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
- RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
- RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
- RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
- RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
- RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
- RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
- RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
- RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
- ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
-
- /* Clean up */
- if (but_label.strinfo) {
- MEM_freeN(but_label.strinfo);
- }
- if (rna_label.strinfo) {
- MEM_freeN(rna_label.strinfo);
- }
- if (enum_label.strinfo) {
- MEM_freeN(enum_label.strinfo);
- }
- if (but_tip.strinfo) {
- MEM_freeN(but_tip.strinfo);
- }
- if (rna_tip.strinfo) {
- MEM_freeN(rna_tip.strinfo);
- }
- if (enum_tip.strinfo) {
- MEM_freeN(enum_tip.strinfo);
- }
- if (rna_struct.strinfo) {
- MEM_freeN(rna_struct.strinfo);
- }
- if (rna_prop.strinfo) {
- MEM_freeN(rna_prop.strinfo);
- }
- if (rna_enum.strinfo) {
- MEM_freeN(rna_enum.strinfo);
- }
- if (rna_ctxt.strinfo) {
- MEM_freeN(rna_ctxt.strinfo);
- }
+ if (but == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Active button not found");
+ return OPERATOR_CANCELLED;
+ }
- return ret;
+ wmOperatorType *ot;
+ PointerRNA ptr;
+ char popath[FILE_MAX];
+ const char *root = U.i18ndir;
+ const char *uilng = BLT_lang_get();
+
+ uiStringInfo but_label = {BUT_GET_LABEL, NULL};
+ uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
+ uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
+
+ if (!BLI_is_dir(root)) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Please set your Preferences' 'Translation Branches "
+ "Directory' path to a valid directory");
+ return OPERATOR_CANCELLED;
+ }
+ ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
+ if (ot == NULL) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Could not find operator '%s'! Please enable ui_translate add-on "
+ "in the User Preferences",
+ EDTSRC_I18N_OP_NAME);
+ return OPERATOR_CANCELLED;
+ }
+ /* Try to find a valid po file for current language... */
+ edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
+ /* printf("po path: %s\n", popath); */
+ if (popath[0] == '\0') {
+ BKE_reportf(
+ op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
+ return OPERATOR_CANCELLED;
}
- BKE_report(op->reports, RPT_ERROR, "Active button not found");
- return OPERATOR_CANCELLED;
+ UI_but_string_info_get(C,
+ but,
+ &but_label,
+ &rna_label,
+ &enum_label,
+ &but_tip,
+ &rna_tip,
+ &enum_tip,
+ &rna_struct,
+ &rna_prop,
+ &rna_enum,
+ &rna_ctxt,
+ NULL);
+
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_string_set(&ptr, "lang", uilng);
+ RNA_string_set(&ptr, "po_file", popath);
+ RNA_string_set(&ptr, "but_label", but_label.strinfo);
+ RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
+ RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
+ RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
+ RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
+ RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
+ RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
+ RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
+ RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
+ RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
+ const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+
+ /* Clean up */
+ if (but_label.strinfo) {
+ MEM_freeN(but_label.strinfo);
+ }
+ if (rna_label.strinfo) {
+ MEM_freeN(rna_label.strinfo);
+ }
+ if (enum_label.strinfo) {
+ MEM_freeN(enum_label.strinfo);
+ }
+ if (but_tip.strinfo) {
+ MEM_freeN(but_tip.strinfo);
+ }
+ if (rna_tip.strinfo) {
+ MEM_freeN(rna_tip.strinfo);
+ }
+ if (enum_tip.strinfo) {
+ MEM_freeN(enum_tip.strinfo);
+ }
+ if (rna_struct.strinfo) {
+ MEM_freeN(rna_struct.strinfo);
+ }
+ if (rna_prop.strinfo) {
+ MEM_freeN(rna_prop.strinfo);
+ }
+ if (rna_enum.strinfo) {
+ MEM_freeN(rna_enum.strinfo);
+ }
+ if (rna_ctxt.strinfo) {
+ MEM_freeN(rna_ctxt.strinfo);
+ }
+
+ return ret;
}
static void UI_OT_edittranslation_init(wmOperatorType *ot)
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index f234f0fbbf5..58a74a3473e 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction)
return 0;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Popup Menu Memory
+ *
+ * Support menu-memory, a feature that positions the cursor
+ * over the previously used menu item.
+ *
+ * \note This is stored for each unique menu title.
+ * \{ */
+
static uint ui_popup_string_hash(const char *str, const bool use_sep)
{
/* sometimes button contains hotkey, sometimes not, strip for proper compare */
@@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C,
pup->layout = UI_block_layout(
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
- /* note, this intentionally differs from the menu & submenu default because many operators
+ /* note, this intentionally differs from the menu & sub-menu default because many operators
* use popups like this to select one of their options -
* where having invoke doesn't make sense */
uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index b11a727b173..accfb78ab94 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
/* button is disabled, we may be able to tell user why */
if (but->flag & UI_BUT_DISABLED) {
const char *disabled_msg = NULL;
+ bool disabled_msg_free = false;
/* if operator poll check failed, it can give pretty precise info why */
if (but->optype) {
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
WM_operator_poll_context(C, but->optype, but->opcontext);
- disabled_msg = CTX_wm_operator_poll_msg_get(C);
+ disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
}
/* alternatively, buttons can store some reasoning too */
else if (but->disabled_info) {
@@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
});
field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg);
}
+ if (disabled_msg_free) {
+ MEM_freeN((void *)disabled_msg);
+ }
}
if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) {
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 0484565ee2e..e3df9704826 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -7195,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
/** \name Node Socket Icon Template
* \{ */
-void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
+void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4])
{
uiBlock *block = uiLayoutGetBlock(layout);
UI_block_align_begin(block);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index c9c1f56dc98..d2e6165a20f 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -3752,12 +3752,35 @@ static void widget_numslider(
float factor, factor_ui;
float factor_discard = 1.0f; /* No discard. */
const float value = (float)ui_but_value_get(but);
-
- if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
- factor = value / but->softmax;
- }
- else {
- factor = (value - but->softmin) / (but->softmax - but->softmin);
+ const float softmin = but->softmin;
+ const float softmax = but->softmax;
+ const float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
+ factor = value / softmax;
+ }
+ else {
+ factor = (value - softmin) / softrange;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float logmin = fmaxf(softmin, 0.5e-8f);
+ const float base = softmax / logmin;
+ factor = logf(value / logmin) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ const float f = (value - softmin) * cubicrange / softrange + cubicmin;
+ factor = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
}
const float width = (float)BLI_rcti_size_x(rect);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 7453cd17868..40c510af7e5 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -757,7 +757,7 @@ typedef struct v2dViewZoomData {
} v2dViewZoomData;
/**
- * Clamp by convention rather then locking flags,
+ * Clamp by convention rather than locking flags,
* for ndof and +/- keys
*/
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 1f1165a464b..28838d677f0 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -121,6 +121,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.uvs = RNA_boolean_get(op->ptr, "uvs"),
.normals = RNA_boolean_get(op->ptr, "normals"),
.vcolors = RNA_boolean_get(op->ptr, "vcolors"),
+ .orcos = RNA_boolean_get(op->ptr, "orcos"),
.apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"),
.curves_as_mesh = RNA_boolean_get(op->ptr, "curves_as_mesh"),
.flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"),
@@ -210,6 +211,7 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "normals", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "vcolors", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "orcos", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "face_sets", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE);
@@ -240,22 +242,17 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_alembic_export_draw(bContext *C, wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
/* Conveniently set start and end frame to match the scene's frame range. */
Scene *scene = CTX_data_scene(C);
- if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) {
- RNA_int_set(&ptr, "start", SFRA);
- RNA_int_set(&ptr, "end", EFRA);
+ if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) {
+ RNA_int_set(op->ptr, "start", SFRA);
+ RNA_int_set(op->ptr, "end", EFRA);
- RNA_boolean_set(&ptr, "init_scene_frame_range", false);
+ RNA_boolean_set(op->ptr, "init_scene_frame_range", false);
}
- ui_alembic_export_settings(op->layout, &ptr);
+ ui_alembic_export_settings(op->layout, op->ptr);
}
static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -383,6 +380,12 @@ void WM_OT_alembic_export(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors");
+ RNA_def_boolean(ot->srna,
+ "orcos",
+ true,
+ "Generated Coordinates",
+ "Export undeformed mesh vertex coordinates");
+
RNA_def_boolean(
ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments");
@@ -594,13 +597,9 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
}
-static void wm_alembic_import_draw(bContext *C, wmOperator *op)
+static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
- ui_alembic_import_settings(op->layout, &ptr);
+ ui_alembic_import_settings(op->layout, op->ptr);
}
/* op->invoke, opens fileselect if path property not set, otherwise executes */
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index c8e3e1814fc..859c12d7e52 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -400,13 +400,9 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
}
}
-static void wm_collada_export_draw(bContext *C, wmOperator *op)
+static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
- uiCollada_exportSettings(op->layout, &ptr);
+ uiCollada_exportSettings(op->layout, op->ptr);
}
static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -798,13 +794,9 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(box, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
}
-static void wm_collada_import_draw(bContext *C, wmOperator *op)
+static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
- uiCollada_importSettings(op->layout, &ptr);
+ uiCollada_importSettings(op->layout, op->ptr);
}
void WM_OT_collada_import(wmOperatorType *ot)
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 7b4b59902f9..b49be324372 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -215,14 +215,9 @@ static void ui_gpencil_export_svg_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "use_clip_camera", 0, NULL, ICON_NONE);
}
-static void wm_gpencil_export_svg_draw(bContext *C, wmOperator *op)
+static void wm_gpencil_export_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_svg_settings(op->layout, &ptr);
+ ui_gpencil_export_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_svg_poll(bContext *C)
@@ -378,14 +373,9 @@ static void ui_gpencil_export_pdf_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(sub, imfptr, "use_normalized_thickness", 0, NULL, ICON_NONE);
}
-static void wm_gpencil_export_pdf_draw(bContext *C, wmOperator *op)
+static void wm_gpencil_export_pdf_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_pdf_settings(op->layout, &ptr);
+ ui_gpencil_export_pdf_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_pdf_poll(bContext *C)
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index e4fabc0c5de..a9911f1cef2 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -136,13 +136,9 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "scale", 0, NULL, ICON_NONE);
}
-static void wm_gpencil_import_svg_draw(bContext *C, wmOperator *op)
+static void wm_gpencil_import_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_import_svg_settings(op->layout, &ptr);
+ ui_gpencil_import_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_import_svg_poll(bContext *C)
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index 1eb6613d8fe..81bf66da72c 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
}
/*
- * - loop over selected shapekeys.
- * - find firstsel/lastsel pairs.
+ * - loop over selected shape-keys.
+ * - find first-selected/last-selected pairs.
* - move these into a temp list.
* - re-key all the original shapes.
* - copy unselected values back from the original.
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 51190268c4c..43492cd57af 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -911,76 +911,73 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void edbm_bevel_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
uiLayout *col, *row;
- PointerRNA ptr, toolsettings_ptr;
+ PointerRNA toolsettings_ptr;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- int profile_type = RNA_enum_get(&ptr, "profile_type");
- int offset_type = RNA_enum_get(&ptr, "offset_type");
- bool affect_type = RNA_enum_get(&ptr, "affect");
+ int profile_type = RNA_enum_get(op->ptr, "profile_type");
+ int offset_type = RNA_enum_get(op->ptr, "offset_type");
+ bool affect_type = RNA_enum_get(op->ptr, "affect");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_type", 0, NULL, ICON_NONE);
if (offset_type == BEVEL_AMT_PERCENT) {
- uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_pct", 0, NULL, ICON_NONE);
}
else {
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
}
- uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "segments", 0, NULL, ICON_NONE);
if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) {
uiItemR(layout,
- &ptr,
+ op->ptr,
"profile",
UI_ITEM_R_SLIDER,
(profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"),
ICON_NONE);
}
- uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "material", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "harden_normals", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "clamp_overlap", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "loop_slide", 0, NULL, ICON_NONE);
col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
- uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
- uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
- if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
- uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
+ uiItemR(col, op->ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
+ if (RNA_enum_get(op->ptr, "miter_inner") == BEVEL_MITER_ARC) {
+ uiItemR(col, op->ptr, "spread", 0, NULL, ICON_NONE);
}
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
+ uiItemR(col, op->ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
- uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
+ uiItemR(layout, op->ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
if (profile_type == BEVEL_PROFILE_CUSTOM) {
/* Get an RNA pointer to ToolSettings to give to the curve profile template code. */
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 141f120396a..d1f228e951a 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -251,32 +251,28 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static void edbm_intersect_ui(bContext *C, wmOperator *op)
+static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
uiLayout *row;
- PointerRNA ptr;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
@@ -418,32 +414,28 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static void edbm_intersect_boolean_ui(bContext *C, wmOperator *op)
+static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 33e4691bfb5..bb332a4094c 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3694,21 +3694,18 @@ static const EnumPropertyItem *shape_itemf(bContext *C,
static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
Object *obedit = CTX_data_edit_object(C);
Mesh *me = obedit->data;
PointerRNA ptr_key;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
RNA_id_pointer_create((ID *)me->key, &ptr_key);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
- uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE);
+ uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
+ uiItemR(layout, op->ptr, "blend", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "add", 0, NULL, ICON_NONE);
}
void MESH_OT_blend_from_shape(wmOperatorType *ot)
@@ -5612,29 +5609,25 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
return true;
}
-static void edbm_decimate_ui(bContext *C, wmOperator *op)
+static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout, *row, *col, *sub;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "ratio", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_vertex_group", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group"));
- uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
+ uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
+ uiItemR(col, op->ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
- uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE);
+ uiItemR(row, op->ptr, "use_symmetry", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry"));
- uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
+ uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
void MESH_OT_decimate(wmOperatorType *ot)
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 2e98f0558f3..19c9909039c 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1721,7 +1721,7 @@ void EDBM_project_snap_verts(
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval,
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 77b5379ddd4..4338b043fc9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -87,7 +87,7 @@ if(WITH_INTERNATIONAL)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 4c28b24b8d9..8cf7d60e6c4 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -117,6 +117,7 @@
#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_view3d.h"
@@ -1322,12 +1323,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
return OPERATOR_CANCELLED;
}
- /* add new object if not currently editing a GP object,
- * or if "empty" was chosen (i.e. user wants a blank GP canvas)
- */
- if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) {
+ /* Add new object if not currently editing a GP object. */
+ if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) {
const char *ob_name = NULL;
switch (type) {
+ case GP_EMPTY: {
+ ob_name = "GPencil";
+ break;
+ }
case GP_MONKEY: {
ob_name = "Suzanne";
break;
@@ -1358,6 +1361,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* create relevant geometry */
switch (type) {
+ case GP_EMPTY: {
+ float mat[4][4];
+
+ ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ ED_gpencil_create_blank(C, ob, mat);
+ break;
+ }
case GP_STROKE: {
float radius = RNA_float_get(op->ptr, "radius");
float mat[4][4];
@@ -1434,10 +1444,6 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
break;
}
- case GP_EMPTY:
- /* do nothing */
- break;
-
default:
BKE_report(op->reports, RPT_WARNING, "Not implemented");
break;
@@ -2680,6 +2686,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
int a, mballConverted = 0;
bool gpencilConverted = false;
+ bool gpencilCurveConverted = false;
/* don't forget multiple users! */
@@ -2892,6 +2899,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
me_eval = BKE_mesh_copy_for_eval(me_eval, false);
/* Full (edge-angle based) draw calculation should ideally be performed. */
BKE_mesh_edges_set_draw_render(me_eval);
+ BKE_object_material_from_eval_data(bmain, newob, &me_eval->id);
BKE_mesh_nomain_to_mesh(me_eval, newob->data, newob, &CD_MASK_MESH, true);
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
}
@@ -2964,6 +2972,16 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* meshes doesn't use displist */
BKE_object_free_curve_cache(newob);
}
+ else if (target == OB_GPENCIL) {
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits);
+ copy_v3_v3(ob_gpencil->rot, newob->rot);
+ copy_v3_v3(ob_gpencil->scale, newob->scale);
+ BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f);
+ gpencilConverted = true;
+ gpencilCurveConverted = true;
+ basen = NULL;
+ }
}
else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
ob->flag |= OB_DONE;
@@ -3145,6 +3163,17 @@ static int object_convert_exec(bContext *C, wmOperator *op)
FOREACH_SCENE_OBJECT_END;
}
}
+ else {
+ /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */
+ if (gpencilCurveConverted) {
+ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) {
+ if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) {
+ ED_object_base_free_and_unlink(bmain, scene, ob_delete);
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+ }
+ }
// XXX ED_object_editmode_enter(C, 0);
// XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */
@@ -3168,24 +3197,21 @@ static int object_convert_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static void object_convert_ui(bContext *C, wmOperator *op)
+static void object_convert_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
uiLayoutSetPropSep(layout, true);
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
- uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE);
- if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) {
- uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE);
+ if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) {
+ uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE);
}
}
@@ -3465,7 +3491,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
/* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or
* BKE_view_layer_base_deselect_all(). */
- ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT);
+ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_activate(C, basen);
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index d3b4b91881c..5be572baec5 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1592,25 +1592,8 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C,
const Object *ob = CTX_data_active_object(C);
if (ob) {
- const bool use_mode_particle_edit = ED_object_particle_edit_mode_supported(ob);
while (input->identifier) {
- if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) ||
- (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) ||
- (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
- (ELEM(input->value,
- OB_MODE_SCULPT,
- OB_MODE_VERTEX_PAINT,
- OB_MODE_WEIGHT_PAINT,
- OB_MODE_TEXTURE_PAINT) &&
- (ob->type == OB_MESH)) ||
- (ELEM(input->value,
- OB_MODE_EDIT_GPENCIL,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL) &&
- (ob->type == OB_GPENCIL)) ||
- (input->value == OB_MODE_OBJECT)) {
+ if (ED_object_mode_compat_test(ob, input->value)) {
RNA_enum_item_add(&item, &totitem, input);
}
input++;
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index ededcbfdba8..5bf04e195fe 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C,
const bool is_liboverride_allowed);
void edit_modifier_properties(struct wmOperatorType *ot);
bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op);
-bool edit_modifier_invoke_properties_with_hover(struct bContext *C,
- struct wmOperator *op,
- const struct wmEvent *event,
- int *r_retval);
struct ModifierData *edit_modifier_property_get(struct wmOperator *op,
struct Object *ob,
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index ff1855cafc5..dcb294bcb12 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -115,43 +115,46 @@ static const char *object_mode_op_string(eObjectMode mode)
*/
bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
{
- if (ob) {
- if (mode == OB_MODE_OBJECT) {
- return true;
- }
+ if (mode == OB_MODE_OBJECT) {
+ return true;
+ }
- switch (ob->type) {
- case OB_MESH:
- if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
- OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT)) {
- return true;
- }
- break;
- case OB_CURVE:
- case OB_SURF:
- case OB_FONT:
- case OB_MBALL:
- if (mode & OB_MODE_EDIT) {
- return true;
- }
- break;
- case OB_LATTICE:
- if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
- return true;
- }
- break;
- case OB_ARMATURE:
- if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
- return true;
- }
- break;
- case OB_GPENCIL:
- if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL |
- OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ switch (ob->type) {
+ case OB_MESH:
+ if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
+ OB_MODE_TEXTURE_PAINT)) {
+ return true;
+ }
+ if (mode & OB_MODE_PARTICLE_EDIT) {
+ if (ED_object_particle_edit_mode_supported(ob)) {
return true;
}
- break;
- }
+ }
+ break;
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_FONT:
+ case OB_MBALL:
+ if (mode & OB_MODE_EDIT) {
+ return true;
+ }
+ break;
+ case OB_LATTICE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
+ return true;
+ }
+ break;
+ case OB_ARMATURE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
+ return true;
+ }
+ break;
+ case OB_GPENCIL:
+ if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL |
+ OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ return true;
+ }
+ break;
}
return false;
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 49c07b28f07..e14e5cbd44b 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -63,6 +63,7 @@
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
@@ -210,7 +211,7 @@ ModifierData *ED_object_modifier_add(
/* special cases */
if (type == eModifierType_Softbody) {
if (!ob->soft) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
}
}
@@ -772,6 +773,8 @@ static bool modifier_apply_obdata(
return false;
}
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id);
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
if (md_eval->type == eModifierType_Multires) {
@@ -1120,10 +1123,10 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
* with a UI panel below the mouse cursor, unless a specific modifier is set with a context
* pointer. Used in order to apply modifier operators on hover over their panels.
*/
-bool edit_modifier_invoke_properties_with_hover(bContext *C,
- wmOperator *op,
- const wmEvent *event,
- int *r_retval)
+static bool edit_modifier_invoke_properties_with_hover(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
{
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
return true;
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index a7308002e76..b685a93f27b 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -801,7 +801,7 @@ bool ED_object_parent_set(ReportList *reports,
* so we check this by assuming that the parent is selected too.
*/
/* XXX currently this should only happen for meshes, curves, surfaces,
- * and lattices - this stuff isn't available for metas yet */
+ * and lattices - this stuff isn't available for meta-balls yet. */
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
ModifierData *md;
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 10e0a143d9b..e28fe8a5d04 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -70,6 +70,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -692,8 +693,9 @@ struct ObjectPreviewData {
int sizey;
};
-static Object *object_preview_camera_create(
- Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+static Object *object_preview_camera_create(Main *preview_main,
+ ViewLayer *view_layer,
+ Object *preview_object)
{
Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
@@ -701,18 +703,17 @@ static Object *object_preview_camera_create(
float dummyscale[3];
mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat);
- /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */
+ /* Camera is Y up, so needs additional rotations to obliquely face the front. */
float drotmat[3][3];
- axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f};
+ eul_to_mat3(drotmat, eul);
mul_m3_m3_post(rotmat, drotmat);
camera->rotmode = ROT_MODE_QUAT;
mat3_to_quat(camera->quat, rotmat);
- /* shader_preview_render() does this too. */
- if (sizex > sizey) {
- ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex;
- }
+ /* Nice focal length for close portraiture. */
+ ((Camera *)camera->data)->lens = 85;
return camera;
}
@@ -730,11 +731,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object);
- Object *camera_object = object_preview_camera_create(preview_data->pr_main,
- view_layer,
- preview_data->object,
- preview_data->sizex,
- preview_data->sizey);
+ Object *camera_object = object_preview_camera_create(
+ preview_data->pr_main, view_layer, preview_data->object);
scene->camera = camera_object;
scene->r.xsch = preview_data->sizex;
@@ -779,16 +777,21 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
U.pixelsize = 2.0f;
+ View3DShading shading;
+ BKE_screen_view3d_shading_init(&shading);
+ /* Enable shadows, makes it a bit easier to see the shape. */
+ shading.flag |= V3D_SHADING_SHADOW;
+
ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
depsgraph,
DEG_get_evaluated_scene(depsgraph),
- NULL,
- OB_SOLID,
+ &shading,
+ OB_TEXTURE,
DEG_get_evaluated_object(depsgraph, scene->camera),
preview_sized->sizex,
preview_sized->sizey,
IB_rect,
- V3D_OFSDRAW_NONE,
+ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS,
R_ALPHAPREMUL,
NULL,
NULL,
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index bd2b1c4c553..2814a4c9351 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1172,12 +1172,12 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg
}
/* dir is direction to check, not the splitting edge direction! */
-static int rct_fits(const rcti *rect, char dir, int size)
+static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size)
{
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
return BLI_rcti_size_x(rect) + 1 - size;
}
- /* 'v' */
+ /* Vertical. */
return BLI_rcti_size_y(rect) + 1 - size;
}
@@ -1398,7 +1398,8 @@ static void region_rect_recursive(
region->flag |= RGN_FLAG_TOO_SMALL;
}
}
- else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
+ else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 ||
+ rct_fits(remainder, SCREEN_AXIS_H, 1) < 0) {
/* remainder is too small for any usage */
region->flag |= RGN_FLAG_TOO_SMALL;
}
@@ -1410,11 +1411,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) {
+ if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'v', prefsizey);
+ int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey);
if (fac < 0) {
prefsizey += fac;
@@ -1436,11 +1437,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) {
+ if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'h', prefsizex);
+ int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex);
if (fac < 0) {
prefsizex += fac;
@@ -1464,7 +1465,7 @@ static void region_rect_recursive(
region->winrct = *remainder;
if (alignment == RGN_ALIGN_HSPLIT) {
- if (rct_fits(remainder, 'h', prefsizex) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) {
region->winrct.xmax = BLI_rcti_cent_x(remainder);
remainder->xmin = region->winrct.xmax + 1;
}
@@ -1473,7 +1474,7 @@ static void region_rect_recursive(
}
}
else {
- if (rct_fits(remainder, 'v', prefsizey) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) {
region->winrct.ymax = BLI_rcti_cent_y(remainder);
remainder->ymin = region->winrct.ymax + 1;
}
@@ -1526,8 +1527,8 @@ static void region_rect_recursive(
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
- /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see
- * T72200). */
+ /* Fix any negative dimensions. This can happen when a quad split 3d view gets too small.
+ * (see T72200). */
BLI_rcti_sanitize(&region->winrct);
quad++;
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 2ba7ef8f972..2c45524ef94 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -33,184 +33,11 @@
#include "WM_api.h"
+#include "UI_interface.h"
#include "UI_resources.h"
#include "screen_intern.h"
-/**
- * Draw horizontal shape visualizing future joining
- * (left as well right direction of future joining).
- */
-static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 8;
- w = height / 4;
- }
- else {
- h = width / 8;
- w = width / 4;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x;
- points[0].y = area->v1->vec.y + height / 2;
-
- points[1].x = area->v1->vec.x;
- points[1].y = area->v1->vec.y;
-
- points[2].x = area->v4->vec.x - w;
- points[2].y = area->v4->vec.y;
-
- points[3].x = area->v4->vec.x - w;
- points[3].y = area->v4->vec.y + height / 2 - 2 * h;
-
- points[4].x = area->v4->vec.x - 2 * w;
- points[4].y = area->v4->vec.y + height / 2;
-
- points[5].x = area->v4->vec.x - w;
- points[5].y = area->v4->vec.y + height / 2 + 2 * h;
-
- points[6].x = area->v3->vec.x - w;
- points[6].y = area->v3->vec.y;
-
- points[7].x = area->v2->vec.x;
- points[7].y = area->v2->vec.y;
-
- points[8].x = area->v4->vec.x;
- points[8].y = area->v4->vec.y + height / 2 - h;
-
- points[9].x = area->v4->vec.x;
- points[9].y = area->v4->vec.y + height / 2 + h;
-
- if (dir == 'l') {
- /* when direction is left, then we flip direction of arrow */
- float cx = area->v1->vec.x + width;
- for (int i = 0; i < 10; i++) {
- points[i].x -= cx;
- points[i].x = -points[i].x;
- points[i].x += area->v1->vec.x;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw vertical shape visualizing future joining (up/down direction).
- */
-static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 4;
- w = height / 8;
- }
- else {
- h = width / 4;
- w = width / 8;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x + width / 2;
- points[0].y = area->v3->vec.y;
-
- points[1].x = area->v2->vec.x;
- points[1].y = area->v2->vec.y;
-
- points[2].x = area->v1->vec.x;
- points[2].y = area->v1->vec.y + h;
-
- points[3].x = area->v1->vec.x + width / 2 - 2 * w;
- points[3].y = area->v1->vec.y + h;
-
- points[4].x = area->v1->vec.x + width / 2;
- points[4].y = area->v1->vec.y + 2 * h;
-
- points[5].x = area->v1->vec.x + width / 2 + 2 * w;
- points[5].y = area->v1->vec.y + h;
-
- points[6].x = area->v4->vec.x;
- points[6].y = area->v4->vec.y + h;
-
- points[7].x = area->v3->vec.x;
- points[7].y = area->v3->vec.y;
-
- points[8].x = area->v1->vec.x + width / 2 - w;
- points[8].y = area->v1->vec.y;
-
- points[9].x = area->v1->vec.x + width / 2 + w;
- points[9].y = area->v1->vec.y;
-
- if (dir == 'u') {
- /* when direction is up, then we flip direction of arrow */
- float cy = area->v1->vec.y + height;
- for (int i = 0; i < 10; i++) {
- points[i].y -= cy;
- points[i].y = -points[i].y;
- points[i].y += area->v1->vec.y;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw join shape due to direction of joining.
- */
-static void draw_join_shape(ScrArea *area, char dir, uint pos)
-{
- if (ELEM(dir, 'u', 'd')) {
- draw_vertical_join_shape(area, dir, pos);
- }
- else {
- draw_horizontal_join_shape(area, dir, pos);
- }
-}
-
#define CORNER_RESOLUTION 3
static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i)
@@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len)
#undef CORNER_RESOLUTION
-/**
- * Draw screen area darker with arrow (visualization of future joining).
- */
-static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(0, 0, 0, 50);
-
- draw_join_shape(area, dir, pos);
-}
-
-/**
- * Draw screen area lighter with arrow shape ("eraser" of previous dark shape).
- */
-static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(255, 255, 255, 25);
-
- immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y);
-}
-
static void drawscredge_area_draw(
int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness)
{
@@ -427,53 +232,97 @@ void ED_screen_draw_edges(wmWindow *win)
}
/**
- * The blended join arrows.
+ * Visual indication of the two areas involved in a proposed join.
*
* \param sa1: Area from which the resultant originates.
* \param sa2: Target area that will be replaced.
*/
-void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2)
+void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return;
+ }
+
+ /* Rect of the combined areas.*/
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const rctf combined = {
+ .xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) :
+ MIN2(sa1->totrct.xmin, sa2->totrct.xmin),
+ .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) :
+ MAX2(sa1->totrct.xmax, sa2->totrct.xmax),
+ .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) :
+ MAX2(sa1->totrct.ymin, sa2->totrct.ymin),
+ .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) :
+ MIN2(sa1->totrct.ymax, sa2->totrct.ymax),
+ };
+
+ uint pos_id = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_width(1);
-
- /* blended join arrow */
- int dir = area_getorientation(sa1, sa2);
- int dira = -1;
- if (dir != -1) {
- switch (dir) {
- case 0: /* W */
- dir = 'r';
- dira = 'l';
- break;
- case 1: /* N */
- dir = 'd';
- dira = 'u';
- break;
- case 2: /* E */
- dir = 'l';
- dira = 'r';
- break;
- case 3: /* S */
- dir = 'u';
- dira = 'd';
- break;
+ /* Highlight source (sa1) within combined area. */
+ immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f});
+ immRectf(pos_id,
+ MAX2(sa1->totrct.xmin, combined.xmin),
+ MAX2(sa1->totrct.ymin, combined.ymin),
+ MIN2(sa1->totrct.xmax, combined.xmax),
+ MIN2(sa1->totrct.ymax, combined.ymax));
+
+ /* Highlight destination (sa2) within combined area. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f});
+ immRectf(pos_id,
+ MAX2(sa2->totrct.xmin, combined.xmin),
+ MAX2(sa2->totrct.ymin, combined.ymin),
+ MIN2(sa2->totrct.xmax, combined.xmax),
+ MIN2(sa2->totrct.ymax, combined.ymax));
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+ if (offset1 < 0 || offset2 > 0) {
+ /* Show partial areas that will be closed. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f});
+ if (vertical) {
+ if (sa1->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax);
+ }
+ if (sa1->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax);
+ }
+ }
+ else {
+ if (sa1->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin);
+ }
+ if (sa2->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin);
+ }
+ if (sa1->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax);
+ }
+ if (sa2->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax);
+ }
}
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- scrarea_draw_shape_dark(sa2, dir, pos);
- scrarea_draw_shape_light(sa1, dira, pos);
-
- GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+
+ /* Outline the combined area. */
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f});
}
-void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
+void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -485,7 +334,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immBegin(GPU_PRIM_LINES, 2);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax;
immVertex2f(pos, area->totrct.xmin, y);
@@ -503,7 +352,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immEnd();
}
else {
- BLI_assert(dir == 'v');
+ BLI_assert(dir_axis == SCREEN_AXIS_V);
const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax;
immVertex2f(pos, x, area->totrct.ymin);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 7ad8eada3b9..6fb5f33d836 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -104,8 +104,12 @@ static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area)
MEM_freeN(area);
}
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge)
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge)
{
ScrArea *newa = NULL;
@@ -116,7 +120,7 @@ ScrArea *area_split(
rcti window_rect;
WM_window_rect_calc(win, &window_rect);
- short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac);
+ short split = screen_geom_find_area_split_point(area, &window_rect, dir_axis, fac);
if (split == 0) {
return NULL;
}
@@ -125,7 +129,7 @@ ScrArea *area_split(
* normally it shouldn't matter which is used since the copy should match the original
* however with viewport rendering and python console this isn't the case. - campbell */
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
/* new vertices */
ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split);
ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split);
@@ -279,47 +283,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
screen_new->do_draw = true;
}
-/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
-/* -1 = not valid check */
-/* used with join operator */
-int area_getorientation(ScrArea *area, ScrArea *sb)
+/**
+ * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S
+ * -1 = not valid check.
+ * used with join operator.
+ */
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
{
- if (area == NULL || sb == NULL) {
- return -1;
+ if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) {
+ return SCREEN_DIR_NONE;
}
- ScrVert *saBL = area->v1;
- ScrVert *saTL = area->v2;
- ScrVert *saTR = area->v3;
- ScrVert *saBR = area->v4;
+ const vec2s *sa_bl = &sa_a->v1->vec;
+ const vec2s *sa_tl = &sa_a->v2->vec;
+ const vec2s *sa_tr = &sa_a->v3->vec;
+ const vec2s *sa_br = &sa_a->v4->vec;
- ScrVert *sbBL = sb->v1;
- ScrVert *sbTL = sb->v2;
- ScrVert *sbTR = sb->v3;
- ScrVert *sbBR = sb->v4;
+ const vec2s *sb_bl = &sa_b->v1->vec;
+ const vec2s *sb_tl = &sa_b->v2->vec;
+ const vec2s *sb_tr = &sa_b->v3->vec;
+ const vec2s *sb_br = &sa_b->v4->vec;
- if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */
- if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) {
+ if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */
+ if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) {
return 0;
}
}
- else if (saTL->vec.y == sbBL->vec.y &&
- saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */
- if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */
+ if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) {
return 1;
}
}
- else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */
- if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) {
+ else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */
+ if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) {
return 2;
}
}
- else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/
- if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */
+ if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) {
return 3;
}
}
@@ -327,6 +328,39 @@ int area_getorientation(ScrArea *area, ScrArea *sb)
return -1;
}
+/**
+ * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation().
+ */
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2)
+{
+ if (sa_a == NULL || sa_b == NULL) {
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+ else if (dir == SCREEN_DIR_W) { /* West: sa on right and sa_b to the left. */
+ *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y;
+ *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y;
+ }
+ else if (dir == SCREEN_DIR_N) { /* North: sa below and sa_b above. */
+ *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x;
+ *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x;
+ }
+ else if (dir == SCREEN_DIR_E) { /* East: sa on left and sa_b to the right. */
+ *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y;
+ *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y;
+ }
+ else if (dir == SCREEN_DIR_S) { /* South: sa above and sa_b below. */
+ *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x;
+ *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x;
+ }
+ else {
+ BLI_assert(dir == SCREEN_DIR_NONE);
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+}
+
/* Screen verts with horizontal position equal to from_x are moved to to_x. */
static void screen_verts_halign(const wmWindow *win,
const bScreen *screen,
@@ -358,11 +392,11 @@ static void screen_verts_valign(const wmWindow *win,
/* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
*/
static void screen_areas_align(
- bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir)
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir)
{
wmWindow *win = CTX_wm_window(C);
- if (ELEM(dir, 0, 2)) {
+ if (SCREEN_DIR_IS_HORIZONTAL(dir)) {
/* horizontal join, use average for new top and bottom. */
int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2;
int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2;
@@ -390,41 +424,47 @@ static void screen_areas_align(
}
}
-/* Helper function to join 2 areas, it has a return value, 0=failed 1=success
- * used by the split, join operators
- */
-int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+/* Simple join of two areas without any splitting. Will return false if not possible. */
+static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
{
- int dir = area_getorientation(sa1, sa2);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
- if (dir == -1) {
- return 0;
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
+ if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
+ return false;
}
- /* Align areas if they are not. Do sanity checking before getting here. */
+ /* Align areas if they are not. */
screen_areas_align(C, screen, sa1, sa2, dir);
- if (dir == 0) { /* sa1 to right of sa2 = W */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v2 = sa2->v2; /* TL */
+ if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v2 = sa2->v2; /* TL */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 1) { /* sa1 to bottom of sa2 = N */
- sa1->v2 = sa2->v2; /* TL */
- sa1->v3 = sa2->v3; /* TR */
+ else if (dir == SCREEN_DIR_N) { /* sa1 to bottom of sa2 = North. */
+ sa1->v2 = sa2->v2; /* TL */
+ sa1->v3 = sa2->v3; /* TR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
- else if (dir == 2) { /* sa1 to left of sa2 = E */
- sa1->v3 = sa2->v3; /* TR */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_E) { /* sa1 to left of sa2 = East. */
+ sa1->v3 = sa2->v3; /* TR */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 3) { /* sa1 on top of sa2 = S */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_S) { /* sa1 on top of sa2 = South. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
@@ -434,7 +474,104 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
/* Update preview thumbnail */
BKE_icon_changed(screen->id.icon_id);
- return 1;
+ return true;
+}
+
+/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */
+static ScrArea *screen_area_trim(
+ bContext *C, bScreen *screen, ScrArea **area, int size, eScreenDir dir, bool reverse)
+{
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) {
+ return NULL;
+ }
+
+ /* Measurement with ScrVerts because winx and winy might not be correct at this time. */
+ float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) :
+ ((*area)->v3->vec.y - (*area)->v1->vec.y));
+ fac = (reverse == vertical) ? 1.0f - fac : fac;
+ ScrArea *newsa = area_split(
+ CTX_wm_window(C), screen, *area, vertical ? SCREEN_AXIS_V : SCREEN_AXIS_H, fac, true);
+
+ /* area_split always returns smallest of the two areas, so might have to swap. */
+ if (((fac > 0.5f) == vertical) != reverse) {
+ ScrArea *temp = *area;
+ *area = newsa;
+ newsa = temp;
+ }
+
+ return newsa;
+}
+
+/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */
+static bool screen_area_join_ex(
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders)
+{
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ /* Split Left/Top into new area if overhanging. */
+ ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false);
+
+ /* Split Right/Bottom into new area if overhanging. */
+ ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true);
+
+ /* The two areas now line up, so join them. */
+ screen_area_join_aligned(C, screen, sa1, sa2);
+
+ if (close_all_remainders || offset1 < 0 || offset2 > 0) {
+ /* Close both if trimming `sa1`. */
+ screen_area_close(C, screen, side1);
+ screen_area_close(C, screen, side2);
+ }
+
+ BKE_icon_changed(screen->id.icon_id);
+ return true;
+}
+
+/* Join any two neighboring areas. Might involve complex changes. */
+int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+{
+ return screen_area_join_ex(C, screen, sa1, sa2, false);
+}
+
+/* Close a screen area, allowing most-aligned neighbor to take its place. */
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area)
+{
+ if (area == NULL) {
+ return false;
+ }
+
+ ScrArea *sa2 = NULL;
+ float best_alignment = 0.0f;
+
+ LISTBASE_FOREACH (ScrArea *, neighbor, &screen->areabase) {
+ const eScreenDir dir = area_getorientation(area, neighbor);
+ /* Must at least partially share an edge and not be a global area. */
+ if ((dir != SCREEN_DIR_NONE) && (neighbor->global == NULL)) {
+ /* Winx/Winy might not be updated yet, so get lengths from verts. */
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const int area_length = vertical ? (area->v3->vec.x - area->v1->vec.x) :
+ (area->v3->vec.y - area->v1->vec.y);
+ const int ar_length = vertical ? (neighbor->v3->vec.x - neighbor->v1->vec.x) :
+ (neighbor->v3->vec.y - neighbor->v1->vec.y);
+ /* Calculate the ratio of the lengths of the shared edges. */
+ float alignment = MIN2(area_length, ar_length) / (float)MAX2(area_length, ar_length);
+ if (alignment > best_alignment) {
+ best_alignment = alignment;
+ sa2 = neighbor;
+ }
+ }
+ }
+
+ /* Join from neighbor into this area to close it. */
+ return screen_area_join_ex(C, screen, sa2, area, true);
}
/* ****************** EXPORTED API TO OTHER MODULES *************************** */
diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c
index ac159f4d633..51edad0332b 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -304,7 +304,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
*/
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac)
{
const int cur_area_width = screen_geom_area_width(area);
@@ -313,17 +313,21 @@ short screen_geom_find_area_split_point(const ScrArea *area,
const short area_min_y = ED_area_headersize();
/* area big enough? */
- if ((dir == 'v') && (cur_area_width <= 2 * area_min_x)) {
- return 0;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (cur_area_width <= 2 * area_min_x) {
+ return 0;
+ }
}
- if ((dir == 'h') && (cur_area_height <= 2 * area_min_y)) {
- return 0;
+ else if (dir_axis == SCREEN_AXIS_H) {
+ if (cur_area_height <= 2 * area_min_y) {
+ return 0;
+ }
}
/* to be sure */
CLAMP(fac, 0.0f, 1.0f);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height);
int area_min = area_min_y;
@@ -373,13 +377,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
{
bScreen *screen = WM_window_get_active_screen(win);
- /* 'dir' is the direction of EDGE */
- char dir;
+ /* 'dir_axis' is the direction of EDGE */
+ eScreenAxis dir_axis;
if (edge->v1->vec.x == edge->v2->vec.x) {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
}
else {
- dir = 'h';
+ dir_axis = SCREEN_AXIS_H;
}
ED_screen_verts_iter(win, screen, sv)
@@ -396,13 +400,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
oneselected = false;
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
if (se->v1->flag + se->v2->flag == 1) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
if (se->v1->vec.y == se->v2->vec.y) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
}
}
- if (dir == 'v') {
+ else if (dir_axis == SCREEN_AXIS_V) {
if (se->v1->vec.x == se->v2->vec.x) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index c51ff559786..683f2844371 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -29,12 +29,37 @@ struct bContextDataResult;
/* internal exports only */
+typedef enum eScreenDir {
+ /** This can mean unset, unknown or invalid. */
+ SCREEN_DIR_NONE = -1,
+ /** West/Left. */
+ SCREEN_DIR_W = 0,
+ /** North/Up. */
+ SCREEN_DIR_N = 1,
+ /** East/Right. */
+ SCREEN_DIR_E = 2,
+ /** South/Down. */
+ SCREEN_DIR_S = 3,
+} eScreenDir;
+
+#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S))
+#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E))
+
+typedef enum eScreenAxis {
+ /** Horizontal. */
+ SCREEN_AXIS_H = 'h',
+ /** Vertical. */
+ SCREEN_AXIS_V = 'v',
+} eScreenAxis;
+
#define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */
#define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */
#define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */
-#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */
+/* Edges must be within these to allow joining. */
+#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac)
+#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac)
/* Expanded interaction influence of area borders. */
#define BORDERPADDING (U.dpi_fac + U.pixelsize)
@@ -44,6 +69,10 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
void region_toggle_hidden(struct bContext *C, ARegion *region, const bool do_fade);
+/* screen_draw.c */
+void screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2);
+void screen_draw_split_preview(struct ScrArea *area, const eScreenAxis dir_axis, const float fac);
+
/* screen_edit.c */
bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect);
void screen_data_copy(bScreen *to, bScreen *from);
@@ -54,11 +83,17 @@ void screen_change_prepare(bScreen *screen_old,
struct Main *bmain,
struct bContext *C,
wmWindow *win);
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge);
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge);
int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2);
-int area_getorientation(ScrArea *area, ScrArea *sb);
-
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b);
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2);
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area);
struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
/* screen_geometry.c */
@@ -80,7 +115,7 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win,
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen);
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac);
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 765877d6a8e..6b8d4e73f12 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -694,7 +694,9 @@ static bool screen_active_editable(bContext *C)
typedef struct sActionzoneData {
ScrArea *sa1, *sa2;
AZone *az;
- int x, y, gesture_dir, modifier;
+ int x, y;
+ eScreenDir gesture_dir;
+ int modifier;
} sActionzoneData;
/* quick poll to save operators to be created and handled */
@@ -1045,16 +1047,16 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Calculate gesture cardinal direction. */
if (delta_y > abs(delta_x)) {
- sad->gesture_dir = 'n';
+ sad->gesture_dir = SCREEN_DIR_N;
}
else if (delta_x >= abs(delta_y)) {
- sad->gesture_dir = 'e';
+ sad->gesture_dir = SCREEN_DIR_E;
}
else if (delta_y < -abs(delta_x)) {
- sad->gesture_dir = 's';
+ sad->gesture_dir = SCREEN_DIR_S;
}
else {
- sad->gesture_dir = 'w';
+ sad->gesture_dir = SCREEN_DIR_W;
}
bool is_gesture;
@@ -1071,22 +1073,24 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Are we still in same area? */
if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) {
/* Same area, so possible split. */
- WM_cursor_set(
- win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ WM_cursor_set(win,
+ SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT :
+ WM_CURSOR_V_SPLIT);
is_gesture = (delta_max > split_threshold);
}
else {
/* Different area, so possible join. */
- if (sad->gesture_dir == 'n') {
+ if (sad->gesture_dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (sad->gesture_dir == 's') {
+ else if (sad->gesture_dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (sad->gesture_dir == 'e') {
+ else if (sad->gesture_dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
else {
+ BLI_assert(sad->gesture_dir == SCREEN_DIR_W);
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
is_gesture = (delta_max > join_threshold);
@@ -1395,6 +1399,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Area Close Operator
+ *
+ * Close selected area, replace by expanding a neighbor
+ * \{ */
+
+/* operator callback */
+static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+{
+ ScrArea *area = CTX_wm_area(C);
+ if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) {
+ WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool area_close_poll(bContext *C)
+{
+ if (!ED_operator_areaactive(C)) {
+ return false;
+ }
+
+ ScrArea *area = CTX_wm_area(C);
+
+ if (ED_area_is_global(area)) {
+ return false;
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+
+ /* Can this area join with ANY other area? */
+ LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
+ if (area_getorientation(ar, area) != -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void SCREEN_OT_area_close(wmOperatorType *ot)
+{
+ ot->name = "Close Area";
+ ot->description = "Close selected area";
+ ot->idname = "SCREEN_OT_area_close";
+ ot->invoke = area_close_invoke;
+ ot->poll = area_close_poll;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Move Area Edge Operator
* \{ */
@@ -1428,7 +1484,7 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
typedef struct sAreaMoveData {
int bigger, smaller, origval, step;
- char dir;
+ eScreenAxis dir_axis;
enum AreaMoveSnapType {
/* Snapping disabled */
SNAP_NONE = 0,
@@ -1447,7 +1503,7 @@ typedef struct sAreaMoveData {
* need window bounds in order to get correct limits */
static void area_move_set_limits(wmWindow *win,
bScreen *screen,
- int dir,
+ const eScreenAxis dir_axis,
int *bigger,
int *smaller,
bool *use_bigger_smaller_snap)
@@ -1500,7 +1556,7 @@ static void area_move_set_limits(wmWindow *win,
WM_window_rect_calc(win, &window_rect);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
int areamin = ED_area_headersize();
if (area->v1->vec.y > window_rect.ymin) {
@@ -1563,8 +1619,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
sAreaMoveData *md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
op->customdata = md;
- md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v';
- if (md->dir == 'h') {
+ md->dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_H : SCREEN_AXIS_V;
+ if (md->dir_axis == SCREEN_AXIS_H) {
md->origval = actedge->v1->vec.y;
}
else {
@@ -1579,7 +1635,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
}
bool use_bigger_smaller_snap = false;
- area_move_set_limits(win, screen, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
+ area_move_set_limits(
+ win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID;
@@ -1590,7 +1647,7 @@ static int area_snap_calc_location(const bScreen *screen,
const enum AreaMoveSnapType snap_type,
const int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller)
{
@@ -1615,7 +1672,7 @@ static int area_snap_calc_location(const bScreen *screen,
break;
case SNAP_FRACTION_AND_ADJACENT: {
- const int axis = (dir == 'v') ? 0 : 1;
+ const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
int snap_dist_best = INT_MAX;
{
const float div_array[] = {
@@ -1683,7 +1740,7 @@ static int area_snap_calc_location(const bScreen *screen,
static void area_move_apply_do(const bContext *C,
int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller,
const enum AreaMoveSnapType snap_type)
@@ -1701,11 +1758,12 @@ static void area_move_apply_do(const bContext *C,
final_loc = origval + delta;
}
else {
- final_loc = area_snap_calc_location(screen, snap_type, delta, origval, dir, bigger, smaller);
+ final_loc = area_snap_calc_location(
+ screen, snap_type, delta, origval, dir_axis, bigger, smaller);
}
BLI_assert(final_loc != -1);
- short axis = (dir == 'v') ? 0 : 1;
+ short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
ED_screen_verts_iter(win, screen, v1)
{
@@ -1761,7 +1819,7 @@ static void area_move_apply(bContext *C, wmOperator *op)
sAreaMoveData *md = op->customdata;
int delta = RNA_int_get(op->ptr, "delta");
- area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type);
+ area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type);
}
static void area_move_exit(bContext *C, wmOperator *op)
@@ -1826,7 +1884,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
int x = RNA_int_get(op->ptr, "x");
int y = RNA_int_get(op->ptr, "y");
- int delta = (md->dir == 'v') ? event->x - x : event->y - y;
+ const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y;
RNA_int_set(op->ptr, "delta", delta);
area_move_apply(C, op);
@@ -1892,7 +1950,7 @@ static void SCREEN_OT_area_move(wmOperatorType *ot)
/*
* operator state vars:
* fac spit point
- * dir direction 'v' or 'h'
+ * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H
*
* operator customdata:
* area pointer to (active) area
@@ -1929,7 +1987,7 @@ typedef struct sAreaSplitData {
int delta; /* delta move edge */
int origmin, origsize; /* to calculate fac, for property storage */
int previewmode; /* draw previewline, then split */
- void *draw_callback; /* call `ED_screen_draw_split_preview` */
+ void *draw_callback; /* call `screen_draw_split_preview` */
bool do_snap;
ScrEdge *nedge; /* new edge */
@@ -1944,10 +2002,10 @@ static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdat
sAreaSplitData *sd = op->customdata;
if (sd->sarea) {
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
float fac = RNA_float_get(op->ptr, "factor");
- ED_screen_draw_split_preview(sd->sarea, dir, fac);
+ screen_draw_split_preview(sd->sarea, dir_axis, fac);
}
}
@@ -1974,14 +2032,18 @@ static bool area_split_init(bContext *C, wmOperator *op)
}
/* required properties */
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
/* minimal size */
- if (dir == 'v' && area->winx < 2 * AREAMINX) {
- return false;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (area->winx < 2 * AREAMINX) {
+ return false;
+ }
}
- if (dir == 'h' && area->winy < 2 * ED_area_headersize()) {
- return false;
+ else {
+ if (area->winy < 2 * ED_area_headersize()) {
+ return false;
+ }
}
/* custom data */
@@ -1989,7 +2051,7 @@ static bool area_split_init(bContext *C, wmOperator *op)
op->customdata = sd;
sd->sarea = area;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2038,9 +2100,9 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
float fac = RNA_float_get(op->ptr, "factor");
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
- sd->narea = area_split(win, screen, sd->sarea, dir, fac, 0); /* 0 = no merge */
+ sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */
if (sd->narea == NULL) {
return false;
@@ -2057,7 +2119,7 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sd->nedge->v1->editflag = 1;
sd->nedge->v2->editflag = 1;
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
sd->origval = sd->nedge->v1->vec.y;
}
else {
@@ -2106,8 +2168,8 @@ static void area_split_exit(bContext *C, wmOperator *op)
static void area_split_preview_update_cursor(bContext *C, wmOperator *op)
{
wmWindow *win = CTX_wm_window(C);
- int dir = RNA_enum_get(op->ptr, "direction");
- WM_cursor_set(win, dir == 'h' ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
+ WM_cursor_set(win, (dir_axis == SCREEN_AXIS_H) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
}
/* UI callback, adds new handler */
@@ -2123,7 +2185,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor");
PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor");
- int dir;
+ eScreenAxis dir_axis;
if (event->type == EVT_ACTIONZONE_AREA) {
sActionzoneData *sad = event->customdata;
@@ -2151,12 +2213,12 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float factor;
/* Prepare operator state vars. */
- if (ELEM(sad->gesture_dir, 'n', 's')) {
- dir = 'h';
+ if (SCREEN_DIR_IS_VERTICAL(sad->gesture_dir)) {
+ dir_axis = SCREEN_AXIS_H;
factor = factor_h;
}
else {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
factor = factor_v;
}
@@ -2166,7 +2228,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_property_float_set(op->ptr, prop_factor, factor);
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* general init, also non-UI case, adds customdata, sets area and defaults */
if (!area_split_init(C, op)) {
@@ -2178,8 +2240,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (area == NULL) {
return OPERATOR_CANCELLED;
}
- dir = RNA_property_enum_get(op->ptr, prop_dir);
- if (dir == 'h') {
+ dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ if (dir_axis == SCREEN_AXIS_H) {
RNA_property_float_set(
op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx);
}
@@ -2212,9 +2274,9 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h';
+ dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_V : SCREEN_AXIS_H;
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* special case, adds customdata, sets defaults */
if (!area_split_menu_init(C, op)) {
@@ -2227,7 +2289,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (event->type == EVT_ACTIONZONE_AREA) {
/* do the split */
if (area_split_apply(C, op)) {
- area_move_set_limits(win, screen, dir, &sd->bigger, &sd->smaller, NULL);
+ area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, NULL);
/* add temp handler for edge move or cancel */
G.moving |= G_TRANSFORM_WM;
@@ -2315,8 +2377,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
else {
if (event->val == KM_PRESS) {
if (sd->sarea) {
- int dir = RNA_property_enum_get(op->ptr, prop_dir);
- RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v');
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ RNA_property_enum_set(
+ op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V);
area_split_preview_update_cursor(C, op);
update_factor = true;
}
@@ -2337,9 +2400,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (update_factor) {
- const int dir = RNA_property_enum_get(op->ptr, prop_dir);
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
- sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
+ sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval;
if (sd->previewmode == 0) {
if (sd->do_snap) {
@@ -2347,12 +2410,12 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->bigger,
sd->smaller);
sd->delta = snap_loc - sd->origval;
}
- area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE);
+ area_move_apply_do(C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE);
}
else {
if (sd->sarea) {
@@ -2363,7 +2426,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (sd->sarea) {
ScrArea *area = sd->sarea;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2379,7 +2442,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->origmin + sd->origsize,
-sd->origmin);
@@ -2401,8 +2464,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
static const EnumPropertyItem prop_direction_items[] = {
- {'h', "HORIZONTAL", 0, "Horizontal", ""},
- {'v', "VERTICAL", 0, "Vertical", ""},
+ {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""},
+ {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2423,7 +2486,7 @@ static void SCREEN_OT_area_split(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* rna */
- RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
+ RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", "");
RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
RNA_def_int_vector(
ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
@@ -3218,9 +3281,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
*/
typedef struct sAreaJoinData {
- ScrArea *sa1; /* first area to be considered */
- ScrArea *sa2; /* second area to be considered */
- void *draw_callback; /* call `ED_screen_draw_join_shape` */
+ ScrArea *sa1; /* Potential source area (kept). */
+ ScrArea *sa2; /* Potential target area (removed or reduced). */
+ eScreenDir dir; /* Direction of potential join. */
+ void *draw_callback; /* call #screen_draw_join_highlight */
} sAreaJoinData;
@@ -3229,8 +3293,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata
const wmOperator *op = userdata;
sAreaJoinData *sd = op->customdata;
- if (sd->sa1 && sd->sa2) {
- ED_screen_draw_join_shape(sd->sa1, sd->sa2);
+ if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) {
+ screen_draw_join_highlight(sd->sa1, sd->sa2);
}
}
@@ -3252,6 +3316,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
jd->sa1 = sa1;
jd->sa2 = sa2;
+ jd->dir = SCREEN_DIR_NONE;
op->customdata = jd;
@@ -3264,7 +3329,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
static bool area_join_apply(bContext *C, wmOperator *op)
{
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
- if (!jd) {
+ if (!jd || (jd->dir == SCREEN_DIR_NONE)) {
return false;
}
@@ -3366,61 +3431,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
case MOUSEMOVE: {
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y);
- int dir = -1;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
- if (area) {
- if (jd->sa1 != area) {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- else {
- /* we are not bordering on the previously selected area
- * we check if area has common border with the one marked for removal
- * in this case we can swap areas.
- */
- dir = area_getorientation(area, jd->sa2);
- if (dir != -1) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- }
- else {
- jd->sa2 = NULL;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
- else {
- /* we are back in the area previously selected for keeping
- * we swap the areas if possible to allow user to choose */
- if (jd->sa2 != NULL) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- dir = area_getorientation(jd->sa1, jd->sa2);
- if (dir == -1) {
- printf("oops, didn't expect that!\n");
- }
- }
- else {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
+ if (area == jd->sa1) {
+ /* Hovering current source, so change direction. */
+ jd->sa1 = jd->sa2;
+ jd->sa2 = area;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
+ }
+ else if (area != jd->sa2) {
+ jd->dir = SCREEN_DIR_NONE;
}
- if (dir == 1) {
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ if (jd->dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (dir == 3) {
+ else if (jd->dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (dir == 2) {
+ else if (jd->dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
- else if (dir == 0) {
+ else if (jd->dir == SCREEN_DIR_W) {
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
else {
@@ -3431,6 +3465,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
+ if (jd->dir == SCREEN_DIR_NONE) {
+ area_join_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
ED_area_tag_redraw(jd->sa1);
ED_area_tag_redraw(jd->sa2);
@@ -3501,7 +3539,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'v');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
/* Horizontal Split */
uiItemFullO(layout,
@@ -3514,7 +3552,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'h');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
if (sa1 && sa2) {
uiItemS(layout);
@@ -4077,6 +4115,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \name Region Context Menu Operator (Header/Footer/Navbar)
* \{ */
+static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
+{
+ if (ED_area_is_global(area)) {
+ return;
+ }
+
+ PointerRNA ptr;
+
+ /* Mouse position as if in middle of area. */
+ const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)};
+
+ /* Vertical Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Vertical Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", loc);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
+
+ /* Horizontal Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Horizontal Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", &loc[0]);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
+
+ uiItemS(layout);
+
+ if (area->spacetype != SPACE_FILE) {
+ uiItemO(layout,
+ area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
+ ICON_NONE,
+ "SCREEN_OT_screen_full_area");
+
+ if (!area->full) {
+ uiItemFullO(layout,
+ "SCREEN_OT_screen_full_area",
+ IFACE_("Full Screen Area"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ RNA_boolean_set(&ptr, "use_hide_panels", true);
+ }
+ }
+
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli");
+ uiItemS(layout);
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close");
+}
+
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
ScrArea *area = CTX_wm_area(C);
@@ -4110,17 +4211,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN
if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
uiItemS(layout);
-
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- }
-
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored. */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
+ screen_area_menu_items(area, layout);
}
}
@@ -4142,14 +4235,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored... */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
- uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
- }
+ uiItemS(layout);
+ screen_area_menu_items(area, layout);
}
void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
@@ -5344,7 +5431,7 @@ static void context_cycle_prop_get(bScreen *screen,
static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
PointerRNA ptr;
PropertyRNA *prop;
@@ -5393,7 +5480,7 @@ static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEve
}
Main *bmain = CTX_data_main(C);
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
WorkSpace *workspace_src = WM_window_get_active_workspace(win);
WorkSpace *workspace_dst = NULL;
@@ -5469,6 +5556,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_move);
WM_operatortype_append(SCREEN_OT_area_split);
WM_operatortype_append(SCREEN_OT_area_join);
+ WM_operatortype_append(SCREEN_OT_area_close);
WM_operatortype_append(SCREEN_OT_area_options);
WM_operatortype_append(SCREEN_OT_area_dupli);
WM_operatortype_append(SCREEN_OT_area_swap);
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 3df63d423e2..6df96b1e30f 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -199,10 +199,9 @@ static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr),
return !(STREQ(prop_id, "filepath"));
}
-static void screenshot_draw(bContext *C, wmOperator *op)
+static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
ScreenshotData *scd = op->customdata;
uiLayoutSetPropSep(layout, true);
@@ -214,9 +213,8 @@ static void screenshot_draw(bContext *C, wmOperator *op)
uiTemplateImageSettings(layout, &ptr, false);
/* main draw call */
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
}
static bool screenshot_poll(bContext *C)
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 7740fb42c37..3829aeebbeb 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1324,6 +1324,13 @@ static bool paint_cursor_context_init(bContext *C,
copy_v3_fl(pcontext->outline_col, 0.8f);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(C);
+ if (!is_brush_tool) {
+ /* Use a default color for tools that are not brushes. */
+ pcontext->outline_alpha = 0.8f;
+ copy_v3_fl(pcontext->outline_col, 0.8f);
+ }
+
pcontext->is_stroke_active = pcontext->ups->stroke_active;
return true;
@@ -1610,9 +1617,11 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
pcontext->radius);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(pcontext->C);
+
/* Pose brush updates and rotation origins. */
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
/* Just after switching to the Pose Brush, the active vertex can be the same and the
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
@@ -1645,7 +1654,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
2);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
paint_cursor_preview_boundary_data_update(pcontext, update_previews);
paint_cursor_preview_boundary_data_pivot_draw(pcontext);
}
@@ -1666,17 +1675,18 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
GPU_matrix_mul(pcontext->vc.obact->obmat);
/* Drawing Cursor overlays in 3D object space. */
- if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB &&
+ (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius);
sculpt_geometry_preview_lines_draw(
pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss);
}
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
paint_cursor_pose_brush_segments_draw(pcontext);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
SCULPT_boundary_edges_preview_draw(
pcontext->pos, pcontext->ss, pcontext->outline_col, pcontext->outline_alpha);
SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss);
@@ -1692,7 +1702,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_draw_main_inactive_cursor(pcontext);
/* Cloth brush local simulation areas. */
- if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) {
const float white[3] = {1.0f, 1.0f, 1.0f};
const float zero_v[3] = {0.0f};
@@ -1704,7 +1714,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
}
/* Layer brush height. */
- if (brush->sculpt_tool == SCULPT_TOOL_LAYER) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_LAYER) {
SCULPT_layer_brush_height_preview_draw(pcontext->pos,
brush,
pcontext->radius,
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 3ca0d853d6a..7341d984c91 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -87,7 +87,7 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
void *paint_stroke_mode_data(struct PaintStroke *stroke);
float paint_stroke_distance_get(struct PaintStroke *stroke);
void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
-bool paint_poll(struct bContext *C);
+bool PAINT_brush_tool_poll(struct bContext *C);
void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
void paint_cursor_delete_textures(void);
@@ -304,7 +304,7 @@ bool paint_curve_poll(struct bContext *C);
bool facemask_paint_poll(struct bContext *C);
void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
-void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
+void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm);
/* stroke operator */
typedef enum BrushStrokeMode {
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index e1dc8fa30b9..fed89e02e8f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -139,13 +139,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
+ const bool is_gpencil = (brush && brush->gpencil_settings != NULL);
// Object *ob = CTX_data_active_object(C);
float scalar = RNA_float_get(op->ptr, "scalar");
if (brush) {
/* pixel radius */
{
- const int old_size = BKE_brush_size_get(scene, brush);
+ const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size;
int size = (int)(scalar * old_size);
if (abs(old_size - size) < U.pixelsize) {
@@ -156,6 +157,12 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
size -= U.pixelsize;
}
}
+ /* Grease Pencil does not use unified size. */
+ if (is_gpencil) {
+ brush->size = max_ii(size, 1);
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
+ return OPERATOR_FINISHED;
+ }
BKE_brush_size_set(scene, brush, size);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 49ddf2f82d8..b093f07226e 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -1462,7 +1462,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (paint_supports_smooth_stroke(br, mode)) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_smooth_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_smooth_cursor, stroke);
}
stroke->stroke_init = true;
@@ -1489,7 +1489,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (br->flag & BRUSH_LINE) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_line_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_line_cursor, stroke);
}
first_dab = true;
@@ -1659,7 +1659,7 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
stroke->mode_data = mode_data;
}
-bool paint_poll(bContext *C)
+bool PAINT_brush_tool_poll(bContext *C)
{
Paint *p = BKE_paint_get_active_from_context(C);
Object *ob = CTX_data_active_object(C);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 964e5bdaa90..2e1dd928f96 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -780,6 +780,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
iter->neighbors = iter->neighbors_fixed;
for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (ss->face_sets[vert_map->indices[i]] < 0) {
+ /* Skip connectivity from hidden faces. */
+ continue;
+ }
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
uint f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
@@ -6602,7 +6606,7 @@ bool SCULPT_poll_view3d(bContext *C)
bool SCULPT_poll(bContext *C)
{
- return SCULPT_mode_poll(C) && paint_poll(C);
+ return SCULPT_mode_poll(C) && PAINT_brush_tool_poll(C);
}
static const char *sculpt_tool_name(Sculpt *sd)
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 85616f6356d..fe0a53ae964 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -52,6 +52,7 @@
#include "RNA_enum_types.h"
#include "SEQ_iterator.h"
+#include "SEQ_utils.h"
#include "UI_interface.h"
@@ -258,7 +259,7 @@ static void sound_update_animation_flags(Scene *scene)
scene->id.tag |= LIB_TAG_DOIT;
SEQ_ALL_BEGIN (scene->ed, seq) {
- SEQ_iterator_recursive_apply(seq, sound_update_animation_flags_fn, scene);
+ SEQ_recursive_apply(seq, sound_update_animation_flags_fn, scene);
}
SEQ_ALL_END;
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c71e5e49d8d..b5f6874fcfc 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -50,7 +50,7 @@ if(WITH_FREESTYLE)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 1699e704a4d..aeb2c04656e 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -975,7 +975,8 @@ int /*eContextResult*/ buttons_context(const bContext *C,
if (matnr < 0) {
matnr = 0;
}
- CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, &ob->mat[matnr]);
+ /* Keep aligned with rna_Object_material_slots_get. */
+ CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1));
}
}
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 74e7bc11c26..7564fa4b930 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -35,6 +35,7 @@ struct bContext;
struct bContextDataResult;
struct bNode;
struct bNodeTree;
+struct bNodeSocket;
struct wmOperatorType;
struct SpaceProperties_Runtime {
@@ -66,6 +67,7 @@ typedef struct ButsTextureUser {
struct bNodeTree *ntree;
struct bNode *node;
+ struct bNodeSocket *socket;
const char *category;
int icon;
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 43128ed00fa..97e3cb750c1 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -75,15 +75,16 @@ static SpaceProperties *find_space_properties(const bContext *C);
/************************* Texture User **************************/
-static void buttons_texture_user_node_property_add(ListBase *users,
- ID *id,
- PointerRNA ptr,
- PropertyRNA *prop,
- bNodeTree *ntree,
- bNode *node,
- const char *category,
- int icon,
- const char *name)
+static void buttons_texture_user_socket_property_add(ListBase *users,
+ ID *id,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *socket,
+ const char *category,
+ int icon,
+ const char *name)
{
ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
@@ -92,6 +93,7 @@ static void buttons_texture_user_node_property_add(ListBase *users,
user->prop = prop;
user->ntree = ntree;
user->node = node;
+ user->socket = socket;
user->category = category;
user->icon = icon;
user->name = name;
@@ -181,25 +183,29 @@ static void buttons_texture_modifier_geonodes_users_add(Object *ob,
/* Recurse into the node group */
buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users);
}
- else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
- RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr);
- prop = RNA_struct_find_property(&ptr, "texture");
- if (prop == NULL) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (socket->type != SOCK_TEXTURE) {
continue;
}
+ RNA_pointer_create(&node_tree->id, &RNA_NodeSocket, socket, &ptr);
+ prop = RNA_struct_find_property(&ptr, "default_value");
PointerRNA texptr = RNA_property_pointer_get(&ptr, prop);
Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL;
if (tex != NULL) {
- buttons_texture_user_node_property_add(users,
- &ob->id,
- ptr,
- prop,
- node_tree,
- node,
- N_("Geometry Nodes"),
- RNA_struct_ui_icon(ptr.type),
- nmd->modifier.name);
+ buttons_texture_user_socket_property_add(users,
+ &ob->id,
+ ptr,
+ prop,
+ node_tree,
+ node,
+ socket,
+ N_("Geometry Nodes"),
+ RNA_struct_ui_icon(ptr.type),
+ nmd->modifier.name);
}
}
}
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index c1dcf2e56d3..189b9b4c874 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -323,6 +323,7 @@ static void file_draw_preview(uiBlock *block,
int ex, ey;
bool show_outline = !is_icon &&
(file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ const bool is_offline = (file->attributes & FILE_ATTR_OFFLINE);
BLI_assert(imb != NULL);
@@ -419,14 +420,14 @@ static void file_draw_preview(uiBlock *block,
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
- if (is_link) {
- /* Arrow icon to indicate it is a shortcut, link, or alias. */
+ if (is_link || is_offline) {
+ /* Icon at bottom to indicate it is a shortcut, link, alias, or offline. */
float icon_x, icon_y;
icon_x = xco + (2.0f * UI_DPI_FAC);
icon_y = yco + (2.0f * UI_DPI_FAC);
- const int arrow = ICON_LOOP_FORWARDS;
+ const int arrow = is_link ? ICON_LOOP_FORWARDS : ICON_URL;
if (!is_icon) {
- /* Arrow at very bottom-left if preview style. */
+ /* At very bottom-left if preview style. */
const uchar dark[4] = {0, 0, 0, 255};
const uchar light[4] = {255, 255, 255, 255};
UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 309b280177c..f1d0197b9ae 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot);
void FILE_OT_reset_recent(wmOperatorType *ot);
void FILE_OT_hidedot(struct wmOperatorType *ot);
void FILE_OT_execute(struct wmOperatorType *ot);
+void FILE_OT_mouse_execute(struct wmOperatorType *ot);
void FILE_OT_cancel(struct wmOperatorType *ot);
void FILE_OT_parent(struct wmOperatorType *ot);
void FILE_OT_directory_new(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 856bd5b1bc3..61f3c046550 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot)
/** \name Select Pick Operator
* \{ */
+static rcti file_select_mval_to_select_rect(const int mval[2])
+{
+ rcti rect;
+ rect.xmin = rect.xmax = mval[0];
+ rect.ymin = rect.ymax = mval[1];
+ return rect;
+}
+
static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
@@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- rect.xmin = rect.xmax = event->mval[0];
- rect.ymin = rect.ymax = event->mval[1];
+ rect = file_select_mval_to_select_rect(event->mval);
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
@@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile)
/** \name Execute File Window Operator
* \{ */
-static int file_exec(bContext *C, wmOperator *exec_op)
+/**
+ * Execute the active file, as set in the file select params.
+ */
+static bool file_execute(bContext *C, SpaceFile *sfile)
{
Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- struct FileDirEntry *file = filelist_file(sfile->files, params->active_file);
- char filepath[FILE_MAX];
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file && file->redirection_path) {
/* redirection_path is an absolute path that takes precedence
@@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op)
/* opening file - sends events now, so things get handled on windowqueue level */
else if (sfile->op) {
wmOperator *op = sfile->op;
-
- /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */
- if (RNA_boolean_get(exec_op->ptr, "need_active")) {
- const int numfiles = filelist_files_ensure(sfile->files);
- int i, active = 0;
-
- for (i = 0; i < numfiles; i++) {
- if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
- active = 1;
- break;
- }
- }
- if (active == 0) {
- return OPERATOR_CANCELLED;
- }
- }
+ char filepath[FILE_MAX];
sfile->op = NULL;
@@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op)
BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
BLENDER_BOOKMARK_FILE);
fsmenu_write_file(ED_fsmenu_get(), filepath);
- WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
+ WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC);
}
return OPERATOR_FINISHED;
}
-static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int file_exec(bContext *C, wmOperator *UNUSED(op))
{
- ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!ED_fileselect_layout_is_inside_pt(
- sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
}
- return file_exec(C, op);
+ return OPERATOR_FINISHED;
}
void FILE_OT_execute(struct wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Execute File Window";
ot->description = "Execute selected file";
ot->idname = "FILE_OT_execute";
/* api callbacks */
- ot->invoke = file_exec_invoke;
ot->exec = file_exec;
/* Important since handler is on window level.
*
* Avoid using #file_operator_poll since this is also used for entering directories
* which is used even when the file manager doesn't have an operator. */
ot->poll = ED_operator_file_active;
+}
- /* properties */
- prop = RNA_def_boolean(ot->srna,
- "need_active",
- 0,
- "Need Active",
- "Only execute if there's an active selected file in the file list");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+/**
+ * \returns false if the mouse doesn't hover a selectable item.
+ */
+static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event)
+{
+ rcti rect = file_select_mval_to_select_rect(event->mval);
+ if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) {
+ return false;
+ }
+
+ return true;
+}
+
+static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ if (!ED_fileselect_layout_is_inside_pt(
+ sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ /* Note that this isn't needed practically, because the keymap already activates the hovered item
+ * on mouse-press. This execute operator is called afterwards on the double-click event then.
+ * However relying on this would be fragile and could break with keymap changes, so better to
+ * have this mouse-execute operator that makes sure once more that the hovered file is active. */
+ if (!file_ensure_hovered_is_active(C, event)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+/**
+ * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls
+ * the same logic.
+ */
+void FILE_OT_mouse_execute(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Execute File";
+ ot->description =
+ "Perform the current execute action for the file under the cursor (e.g. open the file)";
+ ot->idname = "FILE_OT_mouse_execute";
+
+ /* api callbacks */
+ ot->invoke = file_execute_mouse_invoke;
+ ot->poll = ED_operator_file_active;
+
+ ot->flag = OPTYPE_INTERNAL;
}
/** \} */
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index c2b3a7ac0ee..37a32164cfc 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1601,37 +1601,51 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
- (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
- FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
- FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
- FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
+ if (!entry->preview_icon_id && (entry->attributes & FILE_ATTR_OFFLINE)) {
+ entry->flags |= FILE_ENTRY_INVALID_PREVIEW;
+ return;
+ }
- if (entry->redirection_path) {
- BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
- }
- else {
- BLI_join_dirfile(
- preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
- }
+ if (entry->preview_icon_id) {
+ return;
+ }
- preview->index = index;
- preview->flags = entry->typeflag;
- preview->in_memory_preview = intern_entry->local_data.preview_image;
- preview->icon_id = 0;
- // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) {
+ return;
+ }
+
+ if (!(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
+ FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
+ return;
+ }
- filelist_cache_preview_ensure_running(cache);
+ FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
- FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
- __func__);
- preview_taskdata->preview = preview;
- BLI_task_pool_push(cache->previews_pool,
- filelist_cache_preview_runf,
- preview_taskdata,
- true,
- filelist_cache_preview_freef);
+ if (entry->redirection_path) {
+ BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
+ }
+ else {
+ BLI_join_dirfile(
+ preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
}
+
+ preview->index = index;
+ preview->flags = entry->typeflag;
+ preview->in_memory_preview = intern_entry->local_data.preview_image;
+ preview->icon_id = 0;
+ // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+
+ filelist_cache_preview_ensure_running(cache);
+
+ FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
+ __func__);
+ preview_taskdata->preview = preview;
+ BLI_task_pool_push(cache->previews_pool,
+ filelist_cache_preview_runf,
+ preview_taskdata,
+ true,
+ filelist_cache_preview_freef);
}
static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
@@ -2360,17 +2374,19 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
// printf("Re-queueing previews...\n");
- /* Note we try to preview first images around given index - i.e. assumed visible ones. */
if (cache->flags & FLC_PREVIEWS_ACTIVE) {
- for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) {
- if ((index - i) >= start_index) {
- const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i);
- }
- if ((index + i) < end_index) {
- const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i);
- }
+ /* Note we try to preview first images around given index - i.e. assumed visible ones. */
+ int block_index = cache->block_cursor + (index - start_index);
+ int offs_max = max_ii(end_index - index, index - start_index);
+ for (i = 0; i <= offs_max; i++) {
+ int offs = i;
+ do {
+ int offs_idx = index + offs;
+ if (start_index <= offs_idx && offs_idx < end_index) {
+ int offs_block_idx = (block_index + offs) % (int)cache_size;
+ filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx);
+ }
+ } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */
}
}
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 993b1d9b69c..12bc0a68ca6 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
break;
}
break;
+ case NC_ID: {
+ switch (wmn->action) {
+ case NA_RENAME:
+ /* Force list to update sorting (with a full reset for now). */
+ file_reset_filelist_showing_main_data(area, sfile);
+ break;
+ }
+ break;
+ }
case NC_ASSET: {
switch (wmn->action) {
case NA_SELECTED:
@@ -654,6 +663,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_highlight);
WM_operatortype_append(FILE_OT_sort_column_ui_context);
WM_operatortype_append(FILE_OT_execute);
+ WM_operatortype_append(FILE_OT_mouse_execute);
WM_operatortype_append(FILE_OT_cancel);
WM_operatortype_append(FILE_OT_parent);
WM_operatortype_append(FILE_OT_previous);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 83dd5098a30..6053253790a 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1479,18 +1479,16 @@ static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr),
return !(STR_ELEM(prop_id, "filepath", "directory", "filename"));
}
-static void image_open_draw(bContext *C, wmOperator *op)
+static void image_open_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
ImageOpenData *iod = op->customdata;
ImageFormatData *imf = &iod->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* main draw call */
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* image template */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
@@ -2001,12 +1999,11 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr,
((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy")));
}
-static void image_save_as_draw(bContext *C, wmOperator *op)
+static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
ImageSaveData *isd = op->customdata;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
/* image template */
@@ -2014,9 +2011,8 @@ static void image_save_as_draw(bContext *C, wmOperator *op)
uiTemplateImageSettings(layout, &imf_ptr, false);
/* main draw call */
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* multiview template */
if (is_multiview) {
@@ -2614,38 +2610,34 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
return WM_operator_props_dialog_popup(C, op, 300);
}
-static void image_new_draw(bContext *C, wmOperator *op)
+static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
#if 0
Scene *scene = CTX_data_scene(C);
const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
#endif
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
/* copy of WM_operator_props_dialog_popup() layout */
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "name", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "color", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "alpha", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "generated_type", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "float", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "tiled", 0, NULL, ICON_NONE);
#if 0
if (is_multiview) {
uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
+ uiItemR(col[1], op->ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
}
#endif
}
@@ -3991,26 +3983,22 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
return WM_operator_props_dialog_popup(C, op, 300);
}
-static void tile_add_draw(bContext *C, wmOperator *op)
+static void tile_add_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
-
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "number", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "count", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "label", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "fill", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "fill")) {
- draw_fill_tile(&ptr, layout);
+ if (RNA_boolean_get(op->ptr, "fill")) {
+ draw_fill_tile(op->ptr, layout);
}
}
@@ -4128,13 +4116,9 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
return WM_operator_props_dialog_popup(C, op, 300);
}
-static void tile_fill_draw(bContext *C, wmOperator *op)
+static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- PointerRNA ptr;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
- draw_fill_tile(&ptr, op->layout);
+ draw_fill_tile(op->ptr, op->layout);
}
void IMAGE_OT_tile_fill(wmOperatorType *ot)
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index 0583628be43..aaf9852e212 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -56,7 +56,9 @@
#include "info_intern.h"
-/********************* pack blend file libraries operator *********************/
+/* -------------------------------------------------------------------- */
+/** \name Pack Blend File Libraries Operator
+ * \{ */
static int pack_libraries_exec(bContext *C, wmOperator *op)
{
@@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op)
void FILE_OT_pack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack Blender Libraries";
+ ot->name = "Pack Linked Libraries";
ot->idname = "FILE_OT_pack_libraries";
- ot->description = "Pack all used Blender library files into the current .blend";
+ ot->description =
+ "Store all data-blocks linked from other .blend files in the current .blend file. "
+ "Library references are preserved so the linked data-blocks can be unpacked again";
/* api callbacks */
ot->exec = pack_libraries_exec;
@@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Blend File Libraries Operator
+ * \{ */
+
static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
return WM_operator_confirm_message(
- C, op, "Unpack Blender Libraries - creates directories, all new paths should work");
+ C, op, "Unpack Linked Libraries - creates directories, all new paths should work");
}
void FILE_OT_unpack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack Blender Libraries";
+ ot->name = "Unpack Linked Libraries";
ot->idname = "FILE_OT_unpack_libraries";
- ot->description = "Unpack all used Blender library files from this .blend file";
+ ot->description = "Restore all packed linked data-blocks to their original locations";
/* api callbacks */
ot->invoke = unpack_libraries_invoke;
@@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* toggle auto-pack operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Toggle Auto-Pack Operator
+ * \{ */
static int autopack_toggle_exec(bContext *C, wmOperator *op)
{
@@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op)
void FILE_OT_autopack_toggle(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Automatically Pack Into .blend";
+ ot->name = "Automatically Pack Resources";
ot->idname = "FILE_OT_autopack_toggle";
ot->description = "Automatically pack all external files into the .blend file";
@@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* pack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pack All Operator
+ * \{ */
static int pack_all_exec(bContext *C, wmOperator *op)
{
@@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
void FILE_OT_pack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack All Into .blend";
+ ot->name = "Pack Resources";
ot->idname = "FILE_OT_pack_all";
- ot->description = "Pack all used external files into the .blend";
+ ot->description = "Pack all used external files into this .blend";
/* api callbacks */
ot->exec = pack_all_exec;
@@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* unpack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack All Operator
+ * \{ */
static const EnumPropertyItem unpack_all_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""},
@@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
void FILE_OT_unpack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack All Into Files";
+ ot->name = "Unpack Resources";
ot->idname = "FILE_OT_unpack_all";
ot->description = "Unpack all files packed into this .blend to external ones";
@@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot)
ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack");
}
-/********************* unpack single item operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Single Item Operator
+ * \{ */
static const EnumPropertyItem unpack_item_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""},
@@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot)
INT_MAX);
}
-/********************* make paths relative operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Relative Operator
+ * \{ */
static int make_paths_relative_exec(bContext *C, wmOperator *op)
{
@@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_relative(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Relative";
+ ot->name = "Make Paths Relative";
ot->idname = "FILE_OT_make_paths_relative";
ot->description = "Make all paths to external files relative to current .blend";
@@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* make paths absolute operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Absolute Operator
+ * \{ */
static int make_paths_absolute_exec(bContext *C, wmOperator *op)
{
@@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_absolute(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Absolute";
+ ot->name = "Make Paths Absolute";
ot->idname = "FILE_OT_make_paths_absolute";
ot->description = "Make all paths to external files absolute";
@@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* report missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Missing Files Operator
+ * \{ */
static int report_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot)
ot->flag = 0; /* only reports so no need to undo/register */
}
-/********************* find missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Find Missing Files Operator
+ * \{ */
static int find_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot)
FILE_SORT_DEFAULT);
}
-/********************* report box operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Box Operator
+ * \{ */
/* Hard to decide whether to keep this as an operator,
* or turn it into a hardcoded ui control feature,
@@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot)
}
/* report operators */
+
+/** \} */
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index ffac5c982d6..0bdfceb36b6 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -697,7 +697,8 @@ void ED_info_draw_stats(
UI_FontThemeColor(font_id, TH_TEXT_HI);
BLF_enable(font_id, BLF_SHADOW);
- BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f});
+ const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ BLF_shadow(font_id, 5, shadow_color);
BLF_shadow_offset(font_id, 1, -1);
/* Translated labels for each stat row. */
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index dfc0abee704..e56bb44b1e6 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP
struct wmMsgBus *mbus = params->message_bus;
ARegion *region = params->region;
- wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
- .owner = region,
- .user_data = region,
- .notify = ED_region_do_msg_notify_tag_redraw,
- };
+ wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL};
+ msg_sub_value_region_tag_redraw.owner = region;
+ msg_sub_value_region_tag_redraw.user_data = region;
+ msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 8bf6f2698e0..7d4011e0812 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -266,7 +266,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col
}
else if (strip->type == NLASTRIP_TYPE_META) {
/* Meta Clip */
- /* TODO: should temporary metas get different colors too? */
+ /* TODO: should temporary meta-strips get different colors too? */
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* selected - use a bold purple color */
UI_GetThemeColor3fv(TH_NLA_META_SEL, color);
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 5110c14ef4d..6b4366b2966 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -456,7 +456,9 @@ static void node_draw_frame(const bContext *C,
}
/* label */
- node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ if (node->label[0] != '\0') {
+ node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ }
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
@@ -3332,12 +3334,14 @@ static const float std_node_socket_colors[][4] = {
{0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */
{0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */
{0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */
- {0.25, 0.75, 0.26, 1.0}, /* SOCK_INT */
+ {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */
{0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */
{0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */
- {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */
+ {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */
{0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */
{0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
+ {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */
+ {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
};
/* common color callbacks for standard types */
@@ -3478,6 +3482,14 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_TEXTURE: {
+ uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index d4780534a83..50fa8b28468 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -1713,8 +1713,6 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
snode_notify(C, snode);
if (do_tag_update) {
snode_dag_update(C, snode);
@@ -1755,8 +1753,6 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index 91fe8f5ec89..28c660b0632 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -852,8 +852,6 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1291,8 +1289,6 @@ static int cut_links_exec(bContext *C, wmOperator *op)
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1399,8 +1395,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
link->flag &= ~NODE_LINK_TEST;
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1882,28 +1876,63 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
-/* assumes sockets in list */
-static bNodeSocket *socket_best_match(ListBase *sockets)
-{
- /* find type range */
- int maxtype = 0;
+static int get_main_socket_priority(const bNodeSocket *socket)
+{
+ switch ((eNodeSocketDatatype)socket->type) {
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ return -1;
+ case SOCK_BOOLEAN:
+ return 0;
+ case SOCK_INT:
+ return 1;
+ case SOCK_FLOAT:
+ return 2;
+ case SOCK_VECTOR:
+ return 3;
+ case SOCK_RGBA:
+ return 4;
+ case SOCK_STRING:
+ case SOCK_SHADER:
+ case SOCK_OBJECT:
+ case SOCK_IMAGE:
+ case SOCK_GEOMETRY:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL:
+ return 5;
+ }
+ return -1;
+}
+
+/** Get the "main" socket of a socket list using a heuristic based on socket types. */
+static bNodeSocket *get_main_socket(ListBase *sockets)
+{
+ /* find priority range */
+ int maxpriority = -1;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- maxtype = max_ii(sock->type, maxtype);
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
}
- /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
- for (int type = maxtype; type >= 0; type--) {
+ /* try all priorities, starting from 'highest' */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (!nodeSocketIsHidden(sock) && type == sock->type) {
+ if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) {
return sock;
}
}
}
- /* no visible sockets, unhide first of highest type */
- for (int type = maxtype; type >= 0; type--) {
+ /* no visible sockets, unhide first of highest priority */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (type == sock->type) {
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (priority == get_main_socket_priority(sock)) {
sock->flag &= ~SOCK_HIDDEN;
return sock;
}
@@ -2248,8 +2277,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
}
if (link) {
- bNodeSocket *best_input = socket_best_match(&select->inputs);
- bNodeSocket *best_output = socket_best_match(&select->outputs);
+ bNodeSocket *best_input = get_main_socket(&select->inputs);
+ bNodeSocket *best_output = get_main_socket(&select->outputs);
if (best_input && best_output) {
bNode *node = link->tonode;
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 92cd50560e4..328a787c768 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -2106,7 +2106,7 @@ static void outliner_draw_mode_column_toggle(uiBlock *block,
tip);
UI_but_func_set(but, outliner_mode_toggle_fn, tselem, NULL);
UI_but_flag_enable(but, UI_BUT_DRAG_LOCK);
- /* Mode toggling handles it's own undo state because undo steps need to be grouped. */
+ /* Mode toggling handles its own undo state because undo steps need to be grouped. */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (ID_IS_LINKED(&ob->id)) {
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 456c2079f27..c532dd8accd 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -902,7 +902,7 @@ static void id_override_library_reset_fn(bContext *C,
}
static void id_override_library_resync_fn(bContext *C,
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *UNUSED(tsep),
@@ -931,7 +931,7 @@ static void id_override_library_resync_fn(bContext *C,
}
BKE_lib_override_library_resync(
- bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true);
+ bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports);
WM_event_add_notifier(C, NC_WINDOW, NULL);
}
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 5d774049e3e..728be1ccaaf 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params)
case NC_OBJECT:
switch (wmn->data) {
case ND_TRANSFORM:
+ ED_region_tag_redraw_no_rebuild(region);
+ break;
case ND_BONE_ACTIVE:
case ND_BONE_SELECT:
case ND_DRAW:
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index a61260acfb2..cd75bc98b15 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -754,18 +754,16 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
return OPERATOR_RUNNING_MODAL;
}
-static void sequencer_add_draw(bContext *C, wmOperator *op)
+static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
SequencerAddData *sad = op->customdata;
ImageFormatData *imf = &sad->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* Main draw call. */
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* Image template. */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 5d616969a4f..ebd4c0090b4 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -290,7 +290,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
snap_frame = RNA_int_get(op->ptr, "frame");
- /* Check metas. */
+ /* Check meta-strips. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) &&
SEQ_transform_sequence_can_be_translated(seq)) {
@@ -348,7 +348,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1443,7 +1443,7 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
}
if (changed) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1485,26 +1485,22 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev
return sequencer_split_exec(C, op);
}
-static void sequencer_split_ui(bContext *C, wmOperator *op)
+static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- wmWindowManager *wm = CTX_wm_manager(C);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- PointerRNA ptr;
- RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
-
uiLayout *row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "frame", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "side", 0, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "use_cursor_position")) {
- uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_cursor_position", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(op->ptr, "use_cursor_position")) {
+ uiItemR(layout, op->ptr, "channel", 0, NULL, ICON_NONE);
}
}
@@ -1581,17 +1577,6 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot)
/** \name Duplicate Strips Operator
* \{ */
-static int apply_unique_name_fn(Sequence *seq, void *arg_pt)
-{
- Scene *scene = (Scene *)arg_pt;
- char name[sizeof(seq->name) - 2];
-
- BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
- SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
- SEQ_dupe_animdata(scene, name, seq->name + 2);
- return 1;
-}
-
static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
@@ -1612,7 +1597,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
BLI_movelisttolist(ed->seqbasep, &nseqbase);
for (; seq; seq = seq->next) {
- SEQ_iterator_recursive_apply(seq, apply_unique_name_fn, scene);
+ SEQ_ensure_unique_name(seq, scene);
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1833,7 +1818,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1871,13 +1856,13 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
Sequence *active_seq = SEQ_select_active_get(scene);
if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) {
- /* Enter metastrip. */
+ /* Enter meta-strip. */
SEQ_meta_stack_alloc(ed, active_seq);
SEQ_seqbase_active_set(ed, &active_seq->seqbase);
SEQ_select_active_set(scene, NULL);
}
else {
- /* Exit metastrip if possible. */
+ /* Exit meta-strip if possible. */
if (BLI_listbase_is_empty(&ed->metastack)) {
return OPERATOR_CANCELLED;
}
@@ -1899,7 +1884,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Toggle Meta Strip";
ot->idname = "SEQUENCER_OT_meta_toggle";
- ot->description = "Toggle a metastrip (to edit enclosed strips)";
+ ot->description = "Toggle a meta-strip (to edit enclosed strips)";
/* Api callbacks. */
ot->exec = sequencer_meta_toggle_exec;
@@ -1967,7 +1952,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Make Meta Strip";
ot->idname = "SEQUENCER_OT_meta_make";
- ot->description = "Group selected strips into a metastrip";
+ ot->description = "Group selected strips into a meta-strip";
/* Api callbacks. */
ot->exec = sequencer_meta_make_exec;
@@ -2018,7 +2003,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- SEQ_sort(scene);
+ SEQ_sort(active_seqbase);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2030,7 +2015,7 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot)
/* Identifiers. */
ot->name = "UnMeta Strip";
ot->idname = "SEQUENCER_OT_meta_separate";
- ot->description = "Put the contents of a metastrip back in the sequencer";
+ ot->description = "Put the contents of a meta-strip back in the sequencer";
/* Api callbacks. */
ot->exec = sequencer_meta_separate_exec;
@@ -2241,7 +2226,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2278,43 +2263,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot)
static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op))
{
- int retval = OPERATOR_CANCELLED;
Scene *scene = CTX_data_scene(C);
Sequence *active_seq = SEQ_select_active_get(scene);
StripElem *se = NULL;
- if (active_seq == NULL) {
+ if (active_seq == NULL || active_seq->strip == NULL) {
return OPERATOR_CANCELLED;
}
- if (active_seq->strip) {
- switch (active_seq->type) {
- case SEQ_TYPE_IMAGE:
- se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
- break;
- case SEQ_TYPE_MOVIE:
- se = active_seq->strip->stripdata;
- break;
- case SEQ_TYPE_SCENE:
- case SEQ_TYPE_META:
- case SEQ_TYPE_SOUND_RAM:
- case SEQ_TYPE_SOUND_HD:
- default:
- break;
- }
+ switch (active_seq->type) {
+ case SEQ_TYPE_IMAGE:
+ se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
+ break;
+ case SEQ_TYPE_MOVIE:
+ se = active_seq->strip->stripdata;
+ break;
+ default:
+ return OPERATOR_CANCELLED;
}
- if (se) {
- /* Prevent setting the render size if sequence values aren't initialized. */
- if ((se->orig_width > 0) && (se->orig_height > 0)) {
- scene->r.xsch = se->orig_width;
- scene->r.ysch = se->orig_height;
- WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
- retval = OPERATOR_FINISHED;
- }
+ if (se == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Prevent setting the render size if sequence values aren't initialized. */
+ if (se->orig_width <= 0 || se->orig_height <= 0) {
+ return OPERATOR_CANCELLED;
}
- return retval;
+ scene->r.xsch = se->orig_width;
+ scene->r.ysch = se->orig_height;
+
+ active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f;
+ active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f;
+
+ SEQ_relations_invalidate_cache_preprocessed(scene, active_seq);
+ WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ return OPERATOR_FINISHED;
}
void SEQUENCER_OT_rendersize(wmOperatorType *ot)
@@ -2467,7 +2454,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
for (iseq = iseq_first; iseq; iseq = iseq->next) {
/* Make sure, that pasted strips have unique names. */
- SEQ_iterator_recursive_apply(iseq, apply_unique_name_fn, scene);
+ SEQ_ensure_unique_name(iseq, scene);
/* Translate after name has been changed, otherwise this will affect animdata of original
* strip. */
SEQ_transform_translate_sequence(scene, iseq, ofs);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index a9f8a70d61e..cfc11afce13 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -26,7 +26,6 @@
#include <string.h>
#include "BLI_blenlib.h"
-#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -1187,7 +1186,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
case 1:
test = (timeline_frame <= seq->startdisp);
break;
- case 0:
+ case 2:
test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp);
break;
}
@@ -1210,6 +1209,7 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
static const EnumPropertyItem sequencer_select_left_right_types[] = {
{-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
{1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
+ {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"},
{0, NULL, 0, NULL, NULL},
};
@@ -1622,64 +1622,47 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
return changed;
}
-static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel)
+/* Query strips that are in lower channel and intersect in time with seq_reference. */
+static void query_lower_channel_strips(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- bool changed = false;
- const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq));
- int startdisp = actseq->startdisp;
- int enddisp = actseq->enddisp;
- int machine = actseq->machine;
- SeqIterator iter;
-
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- seq->tmp = NULL;
- }
-
- actseq->tmp = POINTER_FROM_INT(true);
-
- Sequence *seq = NULL;
- for (SEQ_iterator_begin(ed, &iter, true); iter.valid; SEQ_iterator_next(&iter)) {
- seq = iter.seq;
-
- /* Ignore all seqs already selected. */
- /* Ignore all seqs not sharing some time with active one. */
- /* Ignore all seqs of incompatible types (audio vs video). */
- if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) ||
- (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) ||
- (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) {
- continue;
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->machine > seq_reference->machine) {
+ continue; /* Not lower channel. */
}
+ if (seq_test->enddisp <= seq_reference->startdisp ||
+ seq_test->startdisp >= seq_reference->enddisp) {
+ continue; /* Not intersecting in time. */
+ }
+ SEQ_collection_append_strip(seq_test, collection);
+ }
+}
- /* If the seq is an effect one, we need extra checking. */
- if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) ||
- (seq->seq3 && seq->seq3->tmp))) {
- if (startdisp > seq->startdisp) {
- startdisp = seq->startdisp;
- }
- if (enddisp < seq->enddisp) {
- enddisp = seq->enddisp;
- }
- if (machine < seq->machine) {
- machine = seq->machine;
- }
-
- seq->tmp = POINTER_FROM_INT(true);
+/* Select all strips within time range and with lower channel of initial selection. Then select
+ * effect chains of these strips. */
+static bool select_grouped_effect_link(Editing *ed,
+ Sequence *UNUSED(actseq),
+ const int UNUSED(channel))
+{
+ ListBase *seqbase = SEQ_active_seqbase_get(ed);
- seq->flag |= SELECT;
- changed = true;
+ /* Get collection of strips. */
+ SeqCollection *collection = SEQ_query_selected_strips(seqbase);
+ const int selected_strip_count = BLI_gset_len(collection->set);
+ SEQ_collection_expand(seqbase, collection, query_lower_channel_strips);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Unfortunately, we must restart checks from the beginning. */
- SEQ_iterator_end(&iter);
- SEQ_iterator_begin(ed, &iter, true);
- }
+ /* Check if other strips will be affected. */
+ const bool changed = BLI_gset_len(collection->set) > selected_strip_count;
- /* Video strips below active one, or any strip for audio (order doesn't matter here). */
- else if (seq->machine < machine || is_audio) {
- seq->flag |= SELECT;
- changed = true;
- }
+ /* Actual logic. */
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ seq->flag |= SELECT;
}
- SEQ_iterator_end(&iter);
+
+ SEQ_collection_free(collection);
return changed;
}
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index acdf30d0ea7..1f0b5d5d13e 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -256,7 +256,7 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C)
return {};
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
+ if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 018431225e5..02ffa1259fc 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -57,44 +57,45 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
{
std::lock_guard lock{mutex_};
- bke::ReadAttributePtr attribute_ptr = component_->attribute_try_get_for_read(column_id.name);
- if (!attribute_ptr) {
+ bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
+ if (!attribute) {
return {};
}
- const bke::ReadAttribute *attribute = scope_.add(std::move(attribute_ptr), __func__);
- if (attribute->domain() != domain_) {
+ const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__);
+ if (attribute.domain != domain_) {
return {};
}
- int domain_size = attribute->size();
- switch (attribute->custom_data_type()) {
+ int domain_size = varray->size();
+ const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
+ switch (type) {
case CD_PROP_FLOAT:
return column_values_from_function(
- column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
float value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_float = value;
});
case CD_PROP_INT32:
return column_values_from_function(
- column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
int value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_int = value;
});
case CD_PROP_BOOL:
return column_values_from_function(
- column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) {
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
bool value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_bool = value;
});
case CD_PROP_FLOAT2: {
return column_values_from_function(
column_id.name,
domain_size,
- [attribute](int index, CellValue &r_cell_value) {
+ [varray](int index, CellValue &r_cell_value) {
float2 value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_float2 = value;
},
default_float2_column_width);
@@ -103,9 +104,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
return column_values_from_function(
column_id.name,
domain_size,
- [attribute](int index, CellValue &r_cell_value) {
+ [varray](int index, CellValue &r_cell_value) {
float3 value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_float3 = value;
},
default_float3_column_width);
@@ -114,9 +115,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
return column_values_from_function(
column_id.name,
domain_size,
- [attribute](int index, CellValue &r_cell_value) {
+ [varray](int index, CellValue &r_cell_value) {
Color4f value;
- attribute->get(index, &value);
+ varray->get(index, &value);
r_cell_value.value_color = value;
},
default_color_column_width);
@@ -260,7 +261,7 @@ void InstancesDataSource::foreach_default_column_ids(
SpreadsheetColumnID column_id;
column_id.name = (char *)"Name";
fn(column_id);
- for (const char *name : {"Position", "Rotation", "Scale"}) {
+ for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
column_id.name = (char *)name;
fn(column_id);
}
@@ -275,25 +276,31 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
const int size = this->tot_rows();
if (STREQ(column_id.name, "Name")) {
- Span<InstancedData> instance_data = component_->instanced_data();
+ Span<int> reference_handles = component_->instance_reference_handles();
+ Span<InstanceReference> references = component_->references();
std::unique_ptr<ColumnValues> values = column_values_from_function(
- "Name", size, [instance_data](int index, CellValue &r_cell_value) {
- const InstancedData &data = instance_data[index];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- if (data.data.object != nullptr) {
- r_cell_value.value_object = ObjectCellValue{data.data.object};
+ "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) {
+ const InstanceReference &reference = references[reference_handles[index]];
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ r_cell_value.value_object = ObjectCellValue{&object};
+ break;
}
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- if (data.data.collection != nullptr) {
- r_cell_value.value_collection = CollectionCellValue{data.data.collection};
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ r_cell_value.value_collection = CollectionCellValue{&collection};
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
});
values->default_width = 8.0f;
return values;
}
- Span<float4x4> transforms = component_->transforms();
+ Span<float4x4> transforms = component_->instance_transforms();
if (STREQ(column_id.name, "Position")) {
return column_values_from_function(
column_id.name,
@@ -321,6 +328,15 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
},
default_float3_column_width);
}
+ Span<int> ids = component_->instance_ids();
+ if (STREQ(column_id.name, "ID")) {
+ /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
+ 5.5f);
+ }
return {};
}
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index e6916c34a88..b5274c2357e 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1595,7 +1595,6 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area))
}
const char *view3d_context_dir[] = {
- "active_base",
"active_object",
NULL,
};
@@ -1608,20 +1607,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, view3d_context_dir);
}
- else if (CTX_data_equals(member, "active_base")) {
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- if (view_layer->basact) {
- Object *ob = view_layer->basact->object;
- /* if hidden but in edit mode, we still display, can happen with animation */
- if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 ||
- (ob->mode != OB_MODE_OBJECT)) {
- CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact);
- }
- }
-
- return 1;
- }
else if (CTX_data_equals(member, "active_object")) {
/* In most cases the active object is the `view_layer->basact->object`.
* For the 3D view however it can be NULL when hidden.
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index f3a279ee12b..0a89b7d0292 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1288,6 +1288,11 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
name_array[name_array_len++] = IFACE_(" (Local)");
}
+ /* Indicate that clipping region is enabled. */
+ if (rv3d->rflag & RV3D_CLIPPING) {
+ name_array[name_array_len++] = IFACE_(" (Clipped)");
+ }
+
if (name_array_len > 1) {
BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len);
name = tmpstr;
@@ -2017,7 +2022,6 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
- v3d.shading.type = drawtype;
if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
@@ -2027,6 +2031,12 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
v3d.shading.flag = V3D_SHADING_SCENE_WORLD_RENDER | V3D_SHADING_SCENE_LIGHTS_RENDER;
v3d.shading.render_pass = SCE_PASS_COMBINED;
}
+ else if (drawtype == OB_TEXTURE) {
+ drawtype = OB_SOLID;
+ v3d.shading.light = V3D_LIGHTING_STUDIO;
+ v3d.shading.color_type = V3D_SHADING_TEXTURE_COLOR;
+ }
+ v3d.shading.type = drawtype;
v3d.flag2 = V3D_HIDE_OVERLAYS;
@@ -2067,7 +2077,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
return ED_view3d_draw_offscreen_imbuf(depsgraph,
scene,
- drawtype,
+ v3d.shading.type,
&v3d,
&region,
width,
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 5f3d71cc190..967ad966320 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region,
madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
}
-static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert)
+static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert)
{
float zfac = 1.0;
@@ -5065,7 +5065,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index e202276831c..0d568363b00 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -338,7 +338,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
mval_fl,
NULL,
@@ -352,7 +352,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
ray_start,
ray_normal,
@@ -446,7 +446,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
gpl = view3d_ruler_layer_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false);
+ gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false);
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
gpl->thickness = 1;
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
@@ -1124,12 +1124,13 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup)
const wmGizmoType *gzt_snap;
gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true);
gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL);
+
RNA_enum_set(gizmo->ptr,
"snap_elements_force",
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
/* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT));
-
+ ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE);
WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true);
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 7347e528edc..e602521f6a2 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -252,7 +252,7 @@ static int dot_v3_array_find_max_index(const float dirs[][3],
}
/**
- * Re-order \a mat so \a axis_align uses it's own axis which is closest to \a v.
+ * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v.
*/
static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3])
{
@@ -323,7 +323,7 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -941,7 +941,7 @@ static void view3d_interactive_add_calc_plane(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -1762,7 +1762,7 @@ static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup
gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP;
}
- /* Sets the gizmos custom-data which has it's own free callback. */
+ /* Sets the gizmos custom-data which has its own free callback. */
preview_plane_cursor_setup(gzgroup);
}
@@ -2061,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata)
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
- const float scale_mod = U.gizmo_size * 2 * U.dpi_fac;
+ const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize;
float final_scale = (scale_mod * pixel_size);
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index cbd65e3175d..ab4cf0c2135 100644
--- a/source/blender/editors/space_view3d/view3d_walk.c
+++ b/source/blender/editors/space_view3d/view3d_walk.c
@@ -407,7 +407,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
/* Avoid having to convert the edit-mesh to a regular mesh. */
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
ray_start,
ray_normal,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 6ef2eaf67d6..ba8e5c1e2c6 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -74,7 +74,7 @@
static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg);
-static void initSnapSpatial(TransInfo *t, float r_snap[3]);
+static void initSnapSpatial(TransInfo *t, float r_snap[2]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
@@ -1619,7 +1619,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
/**
* \note caller needs to free 't' on a 0 return
- * \warning \a event might be NULL (when tweaking from redo panel)
+ * \warning \a event might be NULL (when tweaking from redo panel)
* \see #saveTransform which writes these values back.
*/
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 673ceba7a65..0533c08bdc2 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -88,12 +88,11 @@ typedef enum {
CTX_TEXTURE_SPACE = (1 << 9),
CTX_NO_PET = (1 << 10),
- CTX_NO_MIRROR = (1 << 11),
- CTX_AUTOCONFIRM = (1 << 12),
+ CTX_AUTOCONFIRM = (1 << 11),
/** When transforming object's, adjust the object data so it stays in the same place. */
- CTX_OBMODE_XFORM_OBDATA = (1 << 13),
+ CTX_OBMODE_XFORM_OBDATA = (1 << 12),
/** Transform object parents without moving their children. */
- CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14),
+ CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13),
} eTContext;
/** #TransInfo.flag */
@@ -105,51 +104,50 @@ typedef enum {
/** restrictions flags */
T_NO_CONSTRAINT = 1 << 2,
T_NULL_ONE = 1 << 3,
- T_NO_ZERO = 1 << 4,
- T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO,
+ T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE,
- T_PROP_EDIT = 1 << 5,
- T_PROP_CONNECTED = 1 << 6,
- T_PROP_PROJECTED = 1 << 7,
+ T_PROP_EDIT = 1 << 4,
+ T_PROP_CONNECTED = 1 << 5,
+ T_PROP_PROJECTED = 1 << 6,
T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED,
- T_V3D_ALIGN = 1 << 8,
+ T_V3D_ALIGN = 1 << 7,
/** For 2D views such as UV or f-curve. */
- T_2D_EDIT = 1 << 9,
- T_CLIP_UV = 1 << 10,
+ T_2D_EDIT = 1 << 8,
+ T_CLIP_UV = 1 << 9,
/** Auto-IK is on. */
- T_AUTOIK = 1 << 11,
+ T_AUTOIK = 1 << 10,
/** Don't use mirror even if the data-block option is set. */
- T_NO_MIRROR = 1 << 12,
+ T_NO_MIRROR = 1 << 11,
/** To indicate that the value set in the `value` parameter is the final
* value of the transformation, modified only by the constrain. */
- T_INPUT_IS_VALUES_FINAL = 1 << 13,
+ T_INPUT_IS_VALUES_FINAL = 1 << 12,
/** To specify if we save back settings at the end. */
- T_MODAL = 1 << 14,
+ T_MODAL = 1 << 13,
/** No re-topology (projection). */
- T_NO_PROJECT = 1 << 15,
+ T_NO_PROJECT = 1 << 14,
- T_RELEASE_CONFIRM = 1 << 16,
+ T_RELEASE_CONFIRM = 1 << 15,
/** Alternative transformation. used to add offset to tracking markers. */
- T_ALT_TRANSFORM = 1 << 17,
+ T_ALT_TRANSFORM = 1 << 16,
/** #TransInfo.center has been set, don't change it. */
- T_OVERRIDE_CENTER = 1 << 18,
+ T_OVERRIDE_CENTER = 1 << 17,
- T_MODAL_CURSOR_SET = 1 << 19,
+ T_MODAL_CURSOR_SET = 1 << 18,
- T_CLNOR_REBUILD = 1 << 20,
+ T_CLNOR_REBUILD = 1 << 19,
/** Merges unselected into selected after transforming (runs after transforming). */
- T_AUTOMERGE = 1 << 21,
+ T_AUTOMERGE = 1 << 20,
/** Runs auto-merge & splits. */
- T_AUTOSPLIT = 1 << 22,
+ T_AUTOSPLIT = 1 << 21,
} eTFlag;
/** #TransInfo.modifiers */
@@ -432,14 +430,14 @@ typedef struct TransCustomDataContainer {
/**
* Container for Transform Data
*
- * Used to implement multi-object modes, so each object can have it's
+ * Used to implement multi-object modes, so each object can have its
* own data array as well as object matrix, local center etc.
*
* Anything that can't be shared between all objects
* and doesn't make sense to store for every vertex (in the #TransDataContainer.data).
*
* \note at some point this could be used to store non object containers
- * although this only makes sense if each container has it's own matrices,
+ * although this only makes sense if each container has its own matrices,
* otherwise all elements may as well be stored in one array (#TransDataContainer.data),
* as is already done for curve-objects, f-curves. etc.
*/
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 7239bed1eeb..b4175faacf4 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -1147,8 +1147,7 @@ static void init_TransDataContainers(TransInfo *t,
for (int i = 0; i < objects_len; i++) {
TransDataContainer *tc = &t->data_container[i];
- if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) &&
- (objects[i]->type == OB_MESH)) {
+ if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) {
tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0;
tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0;
tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0;
@@ -1497,91 +1496,89 @@ void createTransData(bContext *C, TransInfo *t)
/** \name Transform Data Recalc/Flush
* \{ */
-void clipMirrorModifier(TransInfo *t)
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- Object *ob = tc->obedit;
- ModifierData *md = ob->modifiers.first;
- float tolerance[3] = {0.0f, 0.0f, 0.0f};
- int axis = 0;
-
- for (; md; md = md->next) {
- if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
- MirrorModifierData *mmd = (MirrorModifierData *)md;
-
- if (mmd->flag & MOD_MIR_CLIPPING) {
- axis = 0;
- if (mmd->flag & MOD_MIR_AXIS_X) {
- axis |= 1;
- tolerance[0] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Y) {
- axis |= 2;
- tolerance[1] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Z) {
- axis |= 4;
- tolerance[2] = mmd->tolerance;
- }
- if (axis) {
- float mtx[4][4], imtx[4][4];
- int i;
+ Object *ob = tc->obedit;
+ ModifierData *md = ob->modifiers.first;
+ float tolerance[3] = {0.0f, 0.0f, 0.0f};
+ int axis = 0;
+
+ for (; md; md = md->next) {
+ if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+
+ if (mmd->flag & MOD_MIR_CLIPPING) {
+ axis = 0;
+ if (mmd->flag & MOD_MIR_AXIS_X) {
+ axis |= 1;
+ tolerance[0] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Y) {
+ axis |= 2;
+ tolerance[1] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Z) {
+ axis |= 4;
+ tolerance[2] = mmd->tolerance;
+ }
+ if (axis) {
+ float mtx[4][4], imtx[4][4];
+ int i;
- if (mmd->mirror_ob) {
- float obinv[4][4];
+ if (mmd->mirror_ob) {
+ float obinv[4][4];
- invert_m4_m4(obinv, mmd->mirror_ob->obmat);
- mul_m4_m4m4(mtx, obinv, ob->obmat);
- invert_m4_m4(imtx, mtx);
- }
+ invert_m4_m4(obinv, mmd->mirror_ob->obmat);
+ mul_m4_m4m4(mtx, obinv, ob->obmat);
+ invert_m4_m4(imtx, mtx);
+ }
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- int clip;
- float loc[3], iloc[3];
+ TransData *td = tc->data;
+ for (i = 0; i < tc->data_len; i++, td++) {
+ int clip;
+ float loc[3], iloc[3];
- if (td->loc == NULL) {
- break;
- }
+ if (td->loc == NULL) {
+ break;
+ }
- if (td->flag & TD_SKIP) {
- continue;
- }
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
- copy_v3_v3(loc, td->loc);
- copy_v3_v3(iloc, td->iloc);
+ copy_v3_v3(loc, td->loc);
+ copy_v3_v3(iloc, td->iloc);
- if (mmd->mirror_ob) {
- mul_m4_v3(mtx, loc);
- mul_m4_v3(mtx, iloc);
- }
+ if (mmd->mirror_ob) {
+ mul_m4_v3(mtx, loc);
+ mul_m4_v3(mtx, iloc);
+ }
- clip = 0;
- if (axis & 1) {
- if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
- loc[0] = 0.0f;
- clip = 1;
- }
+ clip = 0;
+ if (axis & 1) {
+ if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
+ loc[0] = 0.0f;
+ clip = 1;
}
+ }
- if (axis & 2) {
- if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
- loc[1] = 0.0f;
- clip = 1;
- }
+ if (axis & 2) {
+ if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
+ loc[1] = 0.0f;
+ clip = 1;
}
- if (axis & 4) {
- if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
- loc[2] = 0.0f;
- clip = 1;
- }
+ }
+ if (axis & 4) {
+ if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
+ loc[2] = 0.0f;
+ clip = 1;
}
- if (clip) {
- if (mmd->mirror_ob) {
- mul_m4_v3(imtx, loc);
- }
- copy_v3_v3(td->loc, loc);
+ }
+ if (clip) {
+ if (mmd->mirror_ob) {
+ mul_m4_v3(imtx, loc);
}
+ copy_v3_v3(td->loc, loc);
}
}
}
@@ -1669,38 +1666,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob)
}
}
-static void recalcData_cursor_image(TransInfo *t)
-{
- TransDataContainer *tc = t->data_container;
- TransData *td = tc->data;
- float aspect_inv[2];
-
- aspect_inv[0] = 1.0f / t->aspect[0];
- aspect_inv[1] = 1.0f / t->aspect[1];
-
- td->loc[0] = td->loc[0] * aspect_inv[0];
- td->loc[1] = td->loc[1] * aspect_inv[1];
-
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_cursor(TransInfo *t)
-{
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_obedit(TransInfo *t)
-{
- if (t->state != TRANS_CANCEL) {
- applyProject(t);
- }
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
- }
- }
-}
-
/* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t)
{
@@ -1767,7 +1732,7 @@ void recalcData(TransInfo *t)
recalcData_tracking(t);
break;
case TC_MBALL_VERTS:
- recalcData_obedit(t);
+ recalcData_mball(t);
break;
case TC_LATTICE_VERTS:
recalcData_lattice(t);
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 36a51d57f64..11550ec8803 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
/* transform_convert_mesh.c */
-void mesh_customdatacorrect_init(TransInfo *t);
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
/* transform_convert_sequencer.c */
int transform_convert_sequencer_get_snap_bound(TransInfo *t);
@@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic);
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe);
bool FrameOnMouseSide(char side, float frame, float cframe);
-void clipMirrorModifier(TransInfo *t);
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc);
void animrecord_check_state(TransInfo *t, struct Object *ob);
/* transform_convert_action.c */
@@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t);
/* transform_convert_cursor.c */
void createTransCursor_image(TransInfo *t);
void createTransCursor_view3d(TransInfo *t);
+void recalcData_cursor_image(TransInfo *t);
+void recalcData_cursor(TransInfo *t);
/* transform_convert_curve.c */
void createTransCurveVerts(TransInfo *t);
@@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t);
/* transform_convert_mball.c */
void createTransMBallVerts(TransInfo *t);
+void recalcData_mball(TransInfo *t);
/* transform_convert_mesh.c */
struct TransIslandData {
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 7f24a0fa5f8..aaea9d05f84 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -1268,9 +1268,6 @@ void recalcData_edit_armature(TransInfo *t)
restoreBones(tc);
}
}
-
- /* Tag for redraw/invalidate overlay cache. */
- DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
}
}
diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c
index 67d85f9610b..1f3eff31205 100644
--- a/source/blender/editors/transform/transform_convert_cursor.c
+++ b/source/blender/editors/transform/transform_convert_cursor.c
@@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Cursor
+ * \{ */
+
+void recalcData_cursor_image(TransInfo *t)
+{
+ TransDataContainer *tc = t->data_container;
+ TransData *td = tc->data;
+ float aspect_inv[2];
+
+ aspect_inv[0] = 1.0f / t->aspect[0];
+ aspect_inv[1] = 1.0f / t->aspect[1];
+
+ td->loc[0] = td->loc[0] * aspect_inv[0];
+ td->loc[1] = td->loc[1] * aspect_inv[1];
+
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+void recalcData_cursor(TransInfo *t)
+{
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index 3ff83e00f43..e57fd85470f 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t)
void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- clipMirrorModifier(t);
applyProject(t);
}
@@ -450,7 +449,7 @@ void recalcData_curve(TransInfo *t)
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
Nurb *nu = nurbs->first;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (t->state == TRANS_CANCEL) {
while (nu) {
@@ -460,7 +459,10 @@ void recalcData_curve(TransInfo *t)
}
}
else {
- /* Normal updating */
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ /* Normal updating. */
BKE_curve_dimension_update(cu);
}
}
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index 20ac7dcb998..fbfce41d555 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -122,7 +122,7 @@ void recalcData_lattice(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
Lattice *la = tc->obedit->data;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (la->editlatt->latt->flag & LT_OUTSIDE) {
outside_lattice(la->editlatt->latt);
}
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 6f5c0318054..f38f3ccf421 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -30,6 +30,8 @@
#include "BKE_context.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
@@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Meta Ball
+ * \{ */
+
+void recalcData_mball(TransInfo *t)
+{
+ if (t->state != TRANS_CANCEL) {
+ applyProject(t);
+ }
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->data_len) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 5c05e35feb4..f715228e25e 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -48,7 +48,575 @@
#include "transform_convert.h"
+/* -------------------------------------------------------------------- */
+/** \name Container TransCustomData Creation
+ * \{ */
+
+struct TransCustomDataMergeGroup {
+ /** map {BMVert: TransCustomDataLayerVert} */
+ struct LinkNode **cd_loop_groups;
+};
+
+struct TransCustomDataLayer {
+ BMesh *bm;
+ struct MemArena *arena;
+
+ struct GHash *origfaces;
+ struct BMesh *bm_origfaces;
+
+ /* Special handle for multi-resolution. */
+ int cd_loop_mdisp_offset;
+
+ /* Optionally merge custom-data groups (this keeps UVs connected for example). */
+ struct {
+ /** map {BMVert: TransDataBasic} */
+ struct GHash *origverts;
+ struct TransCustomDataMergeGroup *data;
+ int data_len;
+ /** Array size of 'layer_math_map_len'
+ * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
+ int *customdatalayer_map;
+ /** Number of math BMLoop layers. */
+ int customdatalayer_map_len;
+ } merge_group;
+
+ bool use_merge_group;
+};
+
+static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t),
+ struct TransDataContainer *UNUSED(tc),
+ struct TransCustomData *custom_data)
+{
+ struct TransCustomDataLayer *tcld = custom_data->data;
+ bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+
+ if (tcld->bm_origfaces) {
+ BM_mesh_free(tcld->bm_origfaces);
+ }
+ if (tcld->origfaces) {
+ BLI_ghash_free(tcld->origfaces, NULL, NULL);
+ }
+ if (tcld->merge_group.origverts) {
+ BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
+ }
+ if (tcld->arena) {
+ BLI_memarena_free(tcld->arena);
+ }
+ if (tcld->merge_group.customdatalayer_map) {
+ MEM_freeN(tcld->merge_group.customdatalayer_map);
+ }
+
+ MEM_freeN(tcld);
+ custom_data->data = NULL;
+}
+
#define USE_FACE_SUBSTITUTE
+#ifdef USE_FACE_SUBSTITUTE
+# define FACE_SUBSTITUTE_INDEX INT_MIN
+
+/**
+ * Search for a neighboring face with area and preferably without selected vertex.
+ * Used to replace area-less faces in custom-data correction.
+ */
+static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
+{
+ BMFace *best_face = NULL;
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BMLoop *l_radial_next = l->radial_next;
+ BMFace *f_test = l_radial_next->f;
+ if (f_test == f) {
+ continue;
+ }
+ if (is_zero_v3(f_test->no)) {
+ continue;
+ }
+
+ /* Check the loops edge isn't selected. */
+ if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
+ !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
+ /* Prefer edges with unselected vertices.
+ * Useful for extrude. */
+ best_face = f_test;
+ break;
+ }
+ if (best_face == NULL) {
+ best_face = f_test;
+ }
+ }
+ return best_face;
+}
+
+static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
+ BMFace *f,
+ BMFace *f_copy)
+{
+ BLI_assert(is_zero_v3(f->no));
+ BMesh *bm = tcld->bm;
+ /* It is impossible to calculate the loops weights of a face without area.
+ * Find a substitute. */
+ BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f);
+ if (f_substitute) {
+ /* Copy the custom-data from the substitute face. */
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Use the substitute face as the reference during the transformation. */
+ BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
+
+ /* Hack: reference substitute face in `f_copy->no`.
+ * `tcld->origfaces` is already used to restore the initial value. */
+ BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
+ *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
+ }
+}
+
+static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+{
+ BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
+ return *((BMFace **)&f_copy->no[0]);
+}
+
+#endif /* USE_FACE_SUBSTITUTE */
+
+static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ const int index)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMLoop *l = BM_iter_step(&liter);
+ BMLoop *l_prev, *l_next;
+
+ /* Generic custom-data correction. Copy face data. */
+ void **val_p;
+ if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
+ BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
+ *val_p = f_copy;
+#ifdef USE_FACE_SUBSTITUTE
+ if (is_zero_v3(l->f->no)) {
+ tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
+ }
+#endif
+ }
+
+ if (tcld->use_merge_group) {
+ if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
+ (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
+ loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ /* Store cd_loop_groups. */
+ struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
+ if (l_num != 0) {
+ merge_data->cd_loop_groups = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ const int layer_nr = tcld->merge_group.customdatalayer_map[j];
+ merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
+ bm, v, layer_nr, loop_weights, tcld->arena);
+ }
+ }
+ else {
+ merge_data->cd_loop_groups = NULL;
+ }
+
+ BLI_ghash_insert(tcld->merge_group.origverts, v, td);
+ }
+}
+
+static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+
+ struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
+ struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ /* We need to have matching loop custom-data. */
+ BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
+
+ tcld->origfaces = origfaces;
+ tcld->bm_origfaces = bm_origfaces;
+
+ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+ tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+}
+
+static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+ BLI_assert(CustomData_has_math(&bm->ldata));
+
+ /* TODO: We don't need `layer_math_map` when there are no loops linked
+ * to one of the sliding vertices. */
+
+ /* Over allocate, only 'math' layers are indexed. */
+ int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
+ int layer_math_map_len = 0;
+ for (int i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ customdatalayer_map[layer_math_map_len++] = i;
+ }
+ }
+ BLI_assert(layer_math_map_len != 0);
+
+ tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
+ tcld->merge_group.customdatalayer_map = customdatalayer_map;
+ tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
+ tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
+ tcld->merge_group.data = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
+}
+
+static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc,
+ const bool use_merge_group)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BMesh *bm = em->bm;
+
+ if (bm->shapenr > 1) {
+ /* Don't do this at all for non-basis shape keys, too easy to
+ * accidentally break uv maps or vertex colors then */
+ /* create copies of faces for custom-data projection. */
+ return NULL;
+ }
+ if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
+ /* There is no custom-data to correct. */
+ return NULL;
+ }
+
+ struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
+ tcld->bm = bm;
+ tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
+ tcld->cd_loop_mdisp_offset = -1;
+ tcld->use_merge_group = use_merge_group;
+
+ tc_mesh_customdatacorrect_init_container_generic(tc, tcld);
+
+ if (tcld->use_merge_group) {
+ tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld);
+ }
+
+ {
+ /* Setup Verts. */
+ int i = 0;
+
+ TransData *tob = tc->data;
+ for (int j = tc->data_len; j--; tob++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
+ }
+ }
+
+ return tcld;
+}
+
+static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group)
+{
+ struct TransCustomDataLayer *customdatacorrect;
+ customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group);
+
+ if (!customdatacorrect) {
+ return;
+ }
+
+ BLI_assert(tc->custom.type.data == NULL);
+ tc->custom.type.data = customdatacorrect;
+ tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn;
+}
+
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t)
+{
+ bool use_merge_group = false;
+ if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = true;
+ }
+ else if (ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN)) {
+ {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ }
+ }
+ else {
+ return;
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->custom.type.data != NULL) {
+ tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type);
+ }
+
+ tc_mesh_customdata_create(tc, use_merge_group);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Apply
+ * \{ */
+
+/**
+ * If we're sliding the vert, return its original location, if not, the current location is good.
+ */
+static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
+{
+ TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
+ return td ? td->iloc : v->co;
+}
+
+static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ struct TransCustomDataMergeGroup *merge_data,
+ bool do_loop_mdisps)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ const float *co_orig_3d = td->iloc;
+
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+ const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
+ const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
+ const float *v_proj_axis = v->no;
+ /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
+ float v_proj[3][3];
+
+ if (do_loop_weight) {
+ project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
+ }
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMFace *f_copy; /* the copy of 'f' */
+ BMLoop *l = BM_iter_step(&liter);
+
+ f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+
+#ifdef USE_FACE_SUBSTITUTE
+ /* In some faces it is not possible to calculate interpolation,
+ * so we use a substitute. */
+ if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
+ f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy);
+ }
+#endif
+
+ /* only loop data, no vertex data since that contains shape keys,
+ * and we do not want to mess up other shape keys */
+ BM_loop_interp_from_face(bm, l, f_copy, false, false);
+
+ /* weight the loop */
+ if (do_loop_weight) {
+ const float eps = 1.0e-8f;
+ const BMLoop *l_prev = l->prev;
+ const BMLoop *l_next = l->next;
+ const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ bool co_prev_ok;
+ bool co_next_ok;
+
+ /* In the unlikely case that we're next to a zero length edge -
+ * walk around the to the next.
+ *
+ * Since we only need to check if the vertex is in this corner,
+ * its not important _which_ loop - as long as its not overlapping
+ * 'sv->co_orig_3d', see: T45096. */
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
+ ((l_prev = l_prev->prev) != l->next))) {
+ co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ }
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
+ ((l_next = l_next->next) != l->prev))) {
+ co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ }
+
+ if (co_prev_ok && co_next_ok) {
+ const float dist = dist_signed_squared_to_corner_v3v3v3(
+ v->co, UNPACK3(v_proj), v_proj_axis);
+
+ loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
+ if (UNLIKELY(!isfinite(loop_weights[j]))) {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
+ if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
+ if (do_loop_weight) {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge_weights(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ }
+ }
+ else {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ }
+ }
+ }
+ }
+
+ /* Special handling for multires
+ *
+ * Interpolate from every other loop (not ideal)
+ * However values will only be taken from loops which overlap other mdisps.
+ */
+ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
+ if (update_loop_mdisps) {
+ float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
+ BMLoop *l;
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BM_face_calc_center_median(l->f, faces_center[j]);
+ }
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+ float f_copy_center[3];
+ BMIter liter_other;
+ BMLoop *l_other;
+ int j_other;
+
+ BM_face_calc_center_median(f_copy, f_copy_center);
+
+ BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
+ BM_face_interp_multires_ex(bm,
+ l_other->f,
+ f_copy,
+ faces_center[j_other],
+ f_copy_center,
+ tcld->cd_loop_mdisp_offset);
+ }
+ }
+ }
+}
+
+static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final)
+{
+ if (!tc->custom.type.data) {
+ return;
+ }
+ struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ const bool use_merge_group = tcld->use_merge_group;
+
+ struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
+ TransData *tob = tc->data;
+ for (int i = tc->data_len; i--; tob++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = tc->data_mirror_len; i--; td_mirror++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Restore
+ * \{ */
+
+static void tc_mesh_customdatacorrect_restore(struct TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ if (!tcld) {
+ continue;
+ }
+
+ BMesh *bm = tcld->bm;
+ BMesh *bm_copy = tcld->bm_origfaces;
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tcld->origfaces) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
+ BLI_assert(f->len == f_copy->len);
+
+ BMLoop *l_iter, *l_first, *l_copy;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ l_copy = BM_FACE_FIRST_LOOP(f_copy);
+ do {
+ /* TODO: Restore only the elements that transform. */
+ BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
+ l_copy = l_copy->next;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+}
+
+/** \} */
/* -------------------------------------------------------------------- */
/** \name Island Creation
@@ -741,10 +1309,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr
/** \name Edit Mesh Verts Transform Creation
* \{ */
-static void transdata_center_get(const struct TransIslandData *island_data,
- const int island_index,
- const float iloc[3],
- float r_center[3])
+static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data,
+ const int island_index,
+ const float iloc[3],
+ float r_center[3])
{
if (island_data->center && island_index != -1) {
copy_v3_v3(r_center, island_data->center[island_index]);
@@ -783,7 +1351,7 @@ static void VertsToTransData(TransInfo *t,
no = eve->no;
}
- transdata_center_get(island_data, island_index, td->iloc, td->center);
+ tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center);
if ((island_index != -1) && island_data->axismtx) {
copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
@@ -973,7 +1541,8 @@ void createTransEditVerts(TransInfo *t)
copy_v3_v3(td_mirror->iloc, eve->co);
td_mirror->flag = mirror_data.vert_map[a].flag;
td_mirror->loc_src = v_src->co;
- transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center);
+ tc_mesh_transdata_center_copy(
+ &island_data, island_index, td_mirror->iloc, td_mirror->center);
td_mirror++;
}
@@ -1045,590 +1614,39 @@ void createTransEditVerts(TransInfo *t)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name CustomData Layer Correction
+/** \name Recalc Mesh Data
* \{ */
-struct TransCustomDataMergeGroup {
- /** map {BMVert: TransCustomDataLayerVert} */
- struct LinkNode **cd_loop_groups;
-};
-
-struct TransCustomDataLayer {
- BMesh *bm;
- struct MemArena *arena;
-
- struct GHash *origfaces;
- struct BMesh *bm_origfaces;
-
- /* Special handle for multi-resolution. */
- int cd_loop_mdisp_offset;
-
- /* Optionally merge custom-data groups (this keeps UVs connected for example). */
- struct {
- /** map {BMVert: TransDataBasic} */
- struct GHash *origverts;
- struct TransCustomDataMergeGroup *data;
- int data_len;
- /** Array size of 'layer_math_map_len'
- * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
- int *customdatalayer_map;
- /** Number of math BMLoop layers. */
- int customdatalayer_map_len;
- } merge_group;
-
- bool use_merge_group;
-};
-
-static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t),
- struct TransDataContainer *UNUSED(tc),
- struct TransCustomData *custom_data)
-{
- struct TransCustomDataLayer *tcld = custom_data->data;
- bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
-
- if (tcld->bm_origfaces) {
- BM_mesh_free(tcld->bm_origfaces);
- }
- if (tcld->origfaces) {
- BLI_ghash_free(tcld->origfaces, NULL, NULL);
- }
- if (tcld->merge_group.origverts) {
- BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
- }
- if (tcld->arena) {
- BLI_memarena_free(tcld->arena);
- }
- if (tcld->merge_group.customdatalayer_map) {
- MEM_freeN(tcld->merge_group.customdatalayer_map);
- }
-
- MEM_freeN(tcld);
- custom_data->data = NULL;
-}
-
-#ifdef USE_FACE_SUBSTITUTE
-
-# define FACE_SUBSTITUTE_INDEX INT_MIN
-
-/**
- * Search for a neighboring face with area and preferably without selected vertex.
- * Used to replace area-less faces in custom-data correction.
- */
-static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
-{
- BMFace *best_face = NULL;
- BMLoop *l;
- BMIter liter;
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- BMLoop *l_radial_next = l->radial_next;
- BMFace *f_test = l_radial_next->f;
- if (f_test == f) {
- continue;
- }
- if (is_zero_v3(f_test->no)) {
- continue;
- }
-
- /* Check the loops edge isn't selected. */
- if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
- !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
- /* Prefer edges with unselected vertices.
- * Useful for extrude. */
- best_face = f_test;
- break;
- }
- if (best_face == NULL) {
- best_face = f_test;
- }
- }
- return best_face;
-}
-
-static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
- BMFace *f,
- BMFace *f_copy)
-{
- BLI_assert(is_zero_v3(f->no));
- BMesh *bm = tcld->bm;
- /* It is impossible to calculate the loops weights of a face without area.
- * Find a substitute. */
- BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f);
- if (f_substitute) {
- /* Copy the custom-data from the substitute face. */
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
- } while ((l_iter = l_iter->next) != l_first);
-
- /* Use the substitute face as the reference during the transformation. */
- BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
-
- /* Hack: reference substitute face in `f_copy->no`.
- * `tcld->origfaces` is already used to restore the initial value. */
- BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
- *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
- }
-}
-
-static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
-{
- BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
- return *((BMFace **)&f_copy->no[0]);
-}
-
-#endif /* USE_FACE_SUBSTITUTE */
-
-static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- const int index)
-{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- BMIter liter;
- int j, l_num;
- float *loop_weights;
-
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMLoop *l = BM_iter_step(&liter);
- BMLoop *l_prev, *l_next;
-
- /* Generic custom-data correction. Copy face data. */
- void **val_p;
- if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
- BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
- *val_p = f_copy;
-#ifdef USE_FACE_SUBSTITUTE
- if (is_zero_v3(l->f->no)) {
- mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
- }
-#endif
- }
-
- if (tcld->use_merge_group) {
- if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
- (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
- loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
- }
- else {
- loop_weights[j] = 0.0f;
- }
- }
- }
-
- if (tcld->use_merge_group) {
- /* Store cd_loop_groups. */
- struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
- if (l_num != 0) {
- merge_data->cd_loop_groups = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- const int layer_nr = tcld->merge_group.customdatalayer_map[j];
- merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
- bm, v, layer_nr, loop_weights, tcld->arena);
- }
- }
- else {
- merge_data->cd_loop_groups = NULL;
- }
-
- BLI_ghash_insert(tcld->merge_group.origverts, v, td);
- }
-}
-
-static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
-
- struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
- struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- /* We need to have matching loop custom-data. */
- BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
-
- tcld->origfaces = origfaces;
- tcld->bm_origfaces = bm_origfaces;
-
- bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
- tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
-}
-
-static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
- BLI_assert(CustomData_has_math(&bm->ldata));
-
- /* TODO: We don't need `layer_math_map` when there are no loops linked
- * to one of the sliding vertices. */
-
- /* Over allocate, only 'math' layers are indexed. */
- int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
- int layer_math_map_len = 0;
- for (int i = 0; i < bm->ldata.totlayer; i++) {
- if (CustomData_layer_has_math(&bm->ldata, i)) {
- customdatalayer_map[layer_math_map_len++] = i;
- }
- }
- BLI_assert(layer_math_map_len != 0);
-
- tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
- tcld->merge_group.customdatalayer_map = customdatalayer_map;
- tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
- tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
- tcld->merge_group.data = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
-}
-
-static void mesh_customdatacorrect_init_container(TransDataContainer *tc,
- const bool use_merge_group)
-{
- if (tc->custom.type.data) {
- /* The custom-data correction has been initiated before.
- * Free since some modes have different settings. */
- mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type);
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- BMesh *bm = em->bm;
-
- if (bm->shapenr > 1) {
- /* Don't do this at all for non-basis shape keys, too easy to
- * accidentally break uv maps or vertex colors then */
- /* create copies of faces for custom-data projection. */
- return;
- }
- if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
- /* There is no custom-data to correct. */
- return;
- }
-
- struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
- tcld->bm = bm;
- tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
-
- /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
- tcld->cd_loop_mdisp_offset = -1;
- tcld->use_merge_group = use_merge_group;
-
- mesh_customdatacorrect_init_container_generic(tc, tcld);
-
- if (tcld->use_merge_group) {
- mesh_customdatacorrect_init_container_merge_group(tc, tcld);
- }
-
- {
- /* Setup Verts. */
- int i = 0;
-
- TransData *tob = tc->data;
- for (int j = tc->data_len; j--; tob++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
- }
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
- }
- }
-
- tc->custom.type.data = tcld;
- tc->custom.type.free_cb = mesh_customdatacorrect_free_cb;
-}
-
-void mesh_customdatacorrect_init(TransInfo *t)
+static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
{
- bool use_merge_group = false;
- if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = true;
- }
- else if (ELEM(t->mode,
- TFM_TRANSLATION,
- TFM_ROTATION,
- TFM_RESIZE,
- TFM_TOSPHERE,
- TFM_SHEAR,
- TFM_BEND,
- TFM_SHRINKFATTEN,
- TFM_TRACKBALL,
- TFM_PUSHPULL,
- TFM_ALIGN)) {
- {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
- }
- }
- else {
- return;
- }
-
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- mesh_customdatacorrect_init_container(tc, use_merge_group);
- }
-}
-
-/**
- * If we're sliding the vert, return its original location, if not, the current location is good.
- */
-static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
-{
- TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
- return td ? td->iloc : v->co;
-}
-
-static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- struct TransCustomDataMergeGroup *merge_data,
- bool do_loop_mdisps)
-{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- const float *co_orig_3d = td->iloc;
-
- BMIter liter;
- int j, l_num;
- float *loop_weights;
- const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
- const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
- const float *v_proj_axis = v->no;
- /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
- float v_proj[3][3];
-
- if (do_loop_weight) {
- project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
- }
-
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMFace *f_copy; /* the copy of 'f' */
- BMLoop *l = BM_iter_step(&liter);
-
- f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
-
-#ifdef USE_FACE_SUBSTITUTE
- /* In some faces it is not possible to calculate interpolation,
- * so we use a substitute. */
- if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
- f_copy = mesh_customdatacorrect_face_substitute_get(f_copy);
- }
-#endif
-
- /* only loop data, no vertex data since that contains shape keys,
- * and we do not want to mess up other shape keys */
- BM_loop_interp_from_face(bm, l, f_copy, false, false);
-
- /* weight the loop */
- if (do_loop_weight) {
- const float eps = 1.0e-8f;
- const BMLoop *l_prev = l->prev;
- const BMLoop *l_next = l->next;
- const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- const float *co_next = trans_vert_orig_co_get(tcld, l_next->v);
- bool co_prev_ok;
- bool co_next_ok;
-
- /* In the unlikely case that we're next to a zero length edge -
- * walk around the to the next.
- *
- * Since we only need to check if the vertex is in this corner,
- * its not important _which_ loop - as long as its not overlapping
- * 'sv->co_orig_3d', see: T45096. */
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
- ((l_prev = l_prev->prev) != l->next))) {
- co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- }
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
- ((l_next = l_next->next) != l->prev))) {
- co_next = trans_vert_orig_co_get(tcld, l_next->v);
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- }
-
- if (co_prev_ok && co_next_ok) {
- const float dist = dist_signed_squared_to_corner_v3v3v3(
- v->co, UNPACK3(v_proj), v_proj_axis);
-
- loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
- if (UNLIKELY(!isfinite(loop_weights[j]))) {
- loop_weights[j] = 0.0f;
+ if (tc->use_mirror_axis_any) {
+ int i;
+ TransData *td;
+ for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
+ if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
+ if (td->flag & TD_MIRROR_EDGE_X) {
+ td->loc[0] = 0.0f;
}
- }
- else {
- loop_weights[j] = 0.0f;
- }
- }
- }
-
- if (tcld->use_merge_group) {
- struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
- if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
- if (do_loop_weight) {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge_weights(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ if (td->flag & TD_MIRROR_EDGE_Y) {
+ td->loc[1] = 0.0f;
}
- }
- else {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ if (td->flag & TD_MIRROR_EDGE_Z) {
+ td->loc[2] = 0.0f;
}
}
}
- }
-
- /* Special handling for multires
- *
- * Interpolate from every other loop (not ideal)
- * However values will only be taken from loops which overlap other mdisps.
- */
- const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
- if (update_loop_mdisps) {
- float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
- BMLoop *l;
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BM_face_calc_center_median(l->f, faces_center[j]);
- }
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
- float f_copy_center[3];
- BMIter liter_other;
- BMLoop *l_other;
- int j_other;
-
- BM_face_calc_center_median(f_copy, f_copy_center);
-
- BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
- BM_face_interp_multires_ex(bm,
- l_other->f,
- f_copy,
- faces_center[j_other],
- f_copy_center,
- tcld->cd_loop_mdisp_offset);
- }
- }
- }
-}
-
-static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (!tc->custom.type.data) {
- continue;
- }
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- const bool use_merge_group = tcld->use_merge_group;
-
- struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
- TransData *tob = tc->data;
- for (int i = tc->data_len; i--; tob++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
- }
- }
TransDataMirror *td_mirror = tc->data_mirror;
- for (int i = tc->data_mirror_len; i--; td_mirror++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
+ for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
+ if (td_mirror->flag & TD_MIRROR_X) {
+ td_mirror->loc[0] *= -1;
}
- }
- }
-}
-
-static void mesh_customdatacorrect_restore(struct TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- if (!tcld) {
- continue;
- }
-
- BMesh *bm = tcld->bm;
- BMesh *bm_copy = tcld->bm_origfaces;
-
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tcld->origfaces) {
- BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
- BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
- BLI_assert(f->len == f_copy->len);
-
- BMLoop *l_iter, *l_first, *l_copy;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- l_copy = BM_FACE_FIRST_LOOP(f_copy);
- do {
- /* TODO: Restore only the elements that transform. */
- BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
- l_copy = l_copy->next;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Recalc Mesh Data
- * \{ */
-
-static void mesh_apply_to_mirror(TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->use_mirror_axis_any) {
- int i;
- TransData *td;
- for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
- if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
- if (td->flag & TD_MIRROR_EDGE_X) {
- td->loc[0] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Y) {
- td->loc[1] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Z) {
- td->loc[2] = 0.0f;
- }
- }
+ if (td_mirror->flag & TD_MIRROR_Y) {
+ td_mirror->loc[1] *= -1;
}
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
- copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
- if (td_mirror->flag & TD_MIRROR_X) {
- td_mirror->loc[0] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Y) {
- td_mirror->loc[1] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Z) {
- td_mirror->loc[2] *= -1;
- }
+ if (td_mirror->flag & TD_MIRROR_Z) {
+ td_mirror->loc[2] *= -1;
}
}
}
@@ -1637,24 +1655,28 @@ static void mesh_apply_to_mirror(TransInfo *t)
void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
- /* mirror modifier clipping? */
+ /* Apply corrections. */
if (!is_canceling) {
- /* apply clipping after so we never project past the clip plane T25423. */
applyProject(t);
- clipMirrorModifier(t);
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_apply_to_mirror(t);
- }
+ bool do_mirror = !(t->flag & T_NO_MIRROR);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ if (do_mirror) {
+ tc_mesh_transdata_mirror_apply(tc);
+ }
- mesh_customdatacorrect_apply(t, false);
+ tc_mesh_customdatacorrect_apply(tc, false);
+ }
}
else {
- mesh_customdatacorrect_restore(t);
+ tc_mesh_customdatacorrect_restore(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
BKE_editmesh_looptri_calc(em);
@@ -1674,7 +1696,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
/* NOTE(joeedh): Handle multi-res re-projection,
* done on transform completion since it's really slow. */
- mesh_customdatacorrect_apply(t, true);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ tc_mesh_customdatacorrect_apply(tc, true);
+ }
}
if (use_automerge) {
diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c
index 5dbb1947773..7c61da31f72 100644
--- a/source/blender/editors/transform/transform_convert_mesh_skin.c
+++ b/source/blender/editors/transform/transform_convert_mesh_skin.c
@@ -47,9 +47,9 @@
/** \name Edit Mesh #CD_MVERT_SKIN Transform Creation
* \{ */
-static float *mesh_skin_transdata_center(const struct TransIslandData *island_data,
- const int island_index,
- BMVert *eve)
+static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data,
+ const int island_index,
+ BMVert *eve)
{
if (island_data->center && island_index != -1) {
return island_data->center[island_index];
@@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da
return eve->co;
}
-static void mesh_skin_transdata_create(TransDataBasic *td,
- BMEditMesh *em,
- BMVert *eve,
- const struct TransIslandData *island_data,
- const int island_index)
+static void tc_mesh_skin_transdata_create(TransDataBasic *td,
+ BMEditMesh *em,
+ BMVert *eve,
+ const struct TransIslandData *island_data,
+ const int island_index)
{
BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN);
@@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td,
td->flag |= TD_SELECTED;
}
- copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve));
+ copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve));
td->extra = eve;
}
@@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t)
}
if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
- mesh_skin_transdata_create(
+ tc_mesh_skin_transdata_create(
(TransDataBasic *)td_mirror, em, eve, &island_data, island_index);
int elem_index = mirror_data.vert_map[a].index;
@@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t)
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
+ tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
createSpaceNormal(td->axismtx, eve->no);
@@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t)
/** \name Recalc Mesh Data
* \{ */
-static void mesh_skin_apply_to_mirror(TransInfo *t)
+static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->use_mirror_axis_any) {
@@ -292,13 +292,13 @@ void recalcData_mesh_skin(TransInfo *t)
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
if (!is_canceling) {
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_skin_apply_to_mirror(t);
+ if (!(t->flag & T_NO_MIRROR)) {
+ tc_mesh_skin_apply_to_mirror(t);
}
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
BKE_editmesh_looptri_calc(em);
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index a5f90e9ac5f..d91a2a8be4b 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -475,7 +475,7 @@ void recalcData_uv(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0);
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 30418471d6d..34be89e5ed9 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -491,7 +491,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
}
}
- SEQ_sort(t->scene);
+ SEQ_sort(seqbasep);
}
else {
/* Canceled, need to update the strips display */
@@ -707,7 +707,7 @@ static void flushTransSeq(TransInfo *t)
/* originally TFM_TIME_EXTEND, transform changes */
if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
- /* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */
+ /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */
/* calc all meta's then effects T27953. */
for (seq = seqbasep->first; seq; seq = seq->next) {
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index f00467d3dfc..71c91221fbb 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -527,7 +527,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Avoid mirroring for unsupported contexts. */
- t->options |= CTX_NO_MIRROR;
+ t->flag |= T_NO_MIRROR;
}
/* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index ad0cb5f27aa..d61e7bb867c 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -1264,7 +1264,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
if (t->data_type == TC_MESH_VERTS) {
/* Init Custom Data correction.
* Ideally this should be called when creating the TransData. */
- mesh_customdatacorrect_init(t);
+ transform_convert_mesh_customdatacorrect_init(t);
}
/* TODO(germano): Some of these operations change the `t->mode`.
@@ -1273,7 +1273,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
}
/**
- * When in modal nad not set, initializes a default orientation for the mode.
+ * When in modal and not set, initializes a default orientation for the mode.
*/
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
{
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index ee63bf4be6f..68416c780ef 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index e67c6c03481..7c496d271ef 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 95e3d89d2b7..608a49f38b1 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 3019984d70b..857ee37f0ad 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c
index 9891af8b9a4..f225f1a7c06 100644
--- a/source/blender/editors/transform/transform_mode_mirror.c
+++ b/source/blender/editors/transform/transform_mode_mirror.c
@@ -235,8 +235,5 @@ void initMirror(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_NONE);
t->flag |= T_NULL_ONE;
- if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
- }
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index a98cee81915..0d7d0be3c0e 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -176,7 +176,6 @@ void initResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 8beacb844b9..77e57484bef 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index b3ed294845d..d97bcba161f 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -445,7 +445,7 @@ int BIF_countTransformOrientation(const bContext *C)
return BLI_listbase_count(transform_orientations);
}
-void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name)
+void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
{
if (r_name) {
BLI_strncpy(r_name, ts->name, MAX_NAME);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index d0f91802fff..193954fffb6 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -345,7 +345,7 @@ void applyProject(TransInfo *t)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = false,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1167,7 +1167,7 @@ short snapObjectsTransform(
t->settings->snap_mode,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1201,7 +1201,7 @@ bool peelObjectsTransform(TransInfo *t,
t->depsgraph,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
},
mval,
-1.0f,
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index e5f6f207e3c..512f912a532 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -44,6 +44,7 @@
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_geometry_set.h"
+#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -145,9 +146,37 @@ struct SnapObjectContext {
/** \name Utilities
* \{ */
-static bool editmesh_eval_final_is_bmesh(const BMEditMesh *em)
+/* Mesh used for snapping.
+ * If NULL the BMesh should be used. */
+static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
{
- return (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH);
+ Mesh *me_eval = ob_eval->data;
+ bool use_hide = false;
+ if (BKE_object_is_in_editmode(ob_eval)) {
+ if (edit_mode_type == SNAP_GEOM_EDIT) {
+ return NULL;
+ }
+
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) {
+ if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_final;
+ use_hide = true;
+ }
+ else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) {
+ if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_cage;
+ use_hide = true;
+ }
+ }
+ if (r_use_hide) {
+ *r_use_hide = use_hide;
+ }
+ return me_eval;
}
/** \} */
@@ -209,30 +238,69 @@ static void snap_object_data_clear(SnapObjectData *sod)
memset(&sod->type, 0x0, sizeof(*sod) - offsetof(SnapObjectData, type));
}
-static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob_eval)
{
- SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
if (sod == NULL) {
if (sctx->cache.data_to_object_map != NULL) {
- ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data);
+ ob_eval = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data);
/* Could be NULl when mixing edit-mode and non edit-mode objects. */
- if (ob != NULL) {
- sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ if (ob_eval != NULL) {
+ sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
}
}
}
return sod;
}
-static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ Mesh *me_eval,
+ bool use_hide)
{
SnapObjectData *sod;
void **sod_p;
bool init = false;
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
+ bool is_dirty = false;
if (sod->type != SNAP_MESH) {
+ is_dirty = true;
+ }
+ else if (sod->treedata_mesh.tree && sod->treedata_mesh.cached &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[0] && sod->cached[0] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[1] && sod->cached[1] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.looptri_allocated &&
+ sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.vert_allocated && sod->treedata_mesh.vert != me_eval->mvert) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.loop_allocated && sod->treedata_mesh.loop != me_eval->mloop) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.edge_allocated && sod->treedata_mesh.edge != me_eval->medge) {
+ is_dirty = true;
+ }
+ else if (sod->poly != me_eval->mpoly) {
+ is_dirty = true;
+ }
+
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -244,8 +312,32 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
if (init) {
sod->type = SNAP_MESH;
- /* start assuming that it has each of these element types */
- sod->has_looptris = true;
+
+ /* The BVHTree from looptris is always required. */
+ BLI_assert(sod->treedata_mesh.tree == NULL);
+ BKE_bvhtree_from_mesh_get(&sod->treedata_mesh,
+ me_eval,
+ use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI,
+ 4);
+
+ if (sod->treedata_mesh.tree == NULL) {
+ sod->treedata_mesh.vert = me_eval->mvert;
+ sod->treedata_mesh.loop = me_eval->mloop;
+ sod->treedata_mesh.looptri = BKE_mesh_runtime_looptri_ensure(me_eval);
+ BLI_assert(sod->has_looptris == false);
+ }
+ else {
+ BLI_assert(sod->treedata_mesh.vert != NULL);
+ BLI_assert(sod->treedata_mesh.loop != NULL);
+ BLI_assert(sod->treedata_mesh.looptri != NULL);
+ sod->has_looptris = true;
+ }
+
+ /* Required for snapping with occlusion. */
+ sod->treedata_mesh.edge = me_eval->medge;
+ sod->poly = me_eval->mpoly;
+
+ /* Start assuming that it has each of these element types. */
sod->has_loose_edge = true;
sod->has_loose_vert = true;
}
@@ -253,26 +345,26 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
return sod;
}
-static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob)
+static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval)
{
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- return &em->mesh_eval_final->runtime;
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if (em_eval->mesh_eval_final) {
+ return &em_eval->mesh_eval_final->runtime;
}
- if (em->mesh_eval_cage) {
- return &em->mesh_eval_cage->runtime;
+ if (em_eval->mesh_eval_cage) {
+ return &em_eval->mesh_eval_cage->runtime;
}
- return &((Mesh *)ob->data)->runtime;
+ return &((Mesh *)ob_eval->data)->runtime;
}
static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em)
{
SnapObjectData *sod;
void **sod_p;
- bool init = false, init_min_max = true, clear_cache = false;
+ bool init = false;
{
/* Use object-data as the key in ghash since the editmesh
@@ -281,48 +373,53 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__);
}
void **ob_p;
- if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) {
- ob = *ob_p;
+ if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) {
+ ob_eval = *ob_p;
}
else {
- *ob_p = ob;
+ *ob_p = ob_eval;
}
}
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
- bool clear = false;
+ bool is_dirty = false;
/* Check if the geometry has changed. */
if (sod->type != SNAP_EDIT_MESH) {
- clear = true;
+ is_dirty = true;
}
else if (sod->treedata_editmesh.em != em) {
- clear_cache = true;
- init = true;
+ is_dirty = true;
}
else if (sod->mesh_runtime) {
- if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob)) {
- clear_cache = true;
- init = true;
+ if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
+ if (G.moving) {
+ /* Hack to avoid updating while transforming. */
+ BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ }
+ else {
+ is_dirty = true;
+ }
}
else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree)) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[0] && sod->cached[0] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[1] && sod->cached[1] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
}
- if (clear) {
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -335,27 +432,8 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
if (init) {
sod->type = SNAP_EDIT_MESH;
sod->treedata_editmesh.em = em;
-
- if (clear_cache) {
- /* Only init min and max when you have a non-custom bvhtree pending. */
- init_min_max = false;
- if (sod->treedata_editmesh.cached) {
- sod->treedata_editmesh.tree = NULL;
- init_min_max = true;
- }
- for (int i = 0; i < ARRAY_SIZE(sod->bvhtree); i++) {
- if (sod->cached[i]) {
- sod->bvhtree[i] = NULL;
- init_min_max = true;
- }
- }
- }
-
- if (init_min_max) {
- bm_mesh_minmax(em->bm, sod->min, sod->max);
- }
-
- sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ bm_mesh_minmax(em->bm, sod->min, sod->max);
}
return sod;
@@ -368,9 +446,9 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
* \{ */
typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data);
@@ -387,10 +465,17 @@ static void iter_snap_objects(SnapObjectContext *sctx,
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
const View3D *v3d = sctx->v3d_data.v3d;
const eSnapSelect snap_select = params->snap_select;
- const bool use_object_edit_cage = params->use_object_edit_cage;
+ const eSnapEditType edit_mode_type = params->edit_mode_type;
const bool use_backface_culling = params->use_backface_culling;
Base *base_act = view_layer->basact;
+ if (snap_select == SNAP_ONLY_ACTIVE) {
+ Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object);
+ sob_callback(
+ sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data);
+ return;
+ }
+
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
if (!BASE_VISIBLE(v3d, base)) {
continue;
@@ -425,7 +510,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
sob_callback(sctx,
dupli_ob->ob,
dupli_ob->mat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -436,7 +521,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
sob_callback(sctx,
obj_eval,
obj_eval->obmat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -464,7 +549,7 @@ struct RayCastAll_Data {
float len_diff;
float local_scale;
- Object *ob;
+ Object *ob_eval;
uint ob_uuid;
/* output data */
@@ -476,7 +561,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
const float co[3],
const float no[3],
int index,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
uint ob_uuid)
{
@@ -487,7 +572,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
copy_v3_v3(hit->no, no);
hit->index = index;
- hit->ob = ob;
+ hit->ob_eval = ob_eval;
copy_m4_m4(hit->obmat, (float(*)[4])obmat);
hit->ob_uuid = ob_uuid;
@@ -529,7 +614,7 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
normalize_v3(normal);
struct SnapObjectHitDepth *hit_item = hit_depth_create(
- depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid);
+ depth, location, normal, hit->index, data->ob_eval, data->obmat, data->ob_uuid);
BLI_addtail(data->hit_list, hit_item);
}
}
@@ -601,8 +686,8 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
static bool raycastMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
@@ -617,7 +702,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
{
bool retval = false;
- if (me->totpoly == 0) {
+ if (me_eval->totpoly == 0) {
return retval;
}
@@ -641,7 +726,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
@@ -661,53 +746,18 @@ static bool raycastMesh(SnapObjectContext *sctx,
len_diff = 0.0f;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used. */
- if (treedata->tree) {
- BLI_assert(treedata->cached);
- if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- else {
- /* Update Pointers. */
- if (treedata->vert && treedata->vert_allocated == false) {
- treedata->vert = me->mvert;
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop;
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
- }
- }
-
if (treedata->tree == NULL) {
- if (use_hide) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI_NO_HIDDEN, 4);
- }
- else {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- }
-
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
-
- if (treedata->tree == NULL) {
- return retval;
- }
+ return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
+ BLI_assert(treedata->raycast_callback != NULL);
if (r_hit_list) {
struct RayCastAll_Data data;
@@ -717,7 +767,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -776,7 +826,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
static bool raycastEditMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
@@ -813,7 +863,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
local_depth *= local_scale;
}
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -839,7 +889,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
if (treedata->tree == NULL) {
/* Operators only update the editmesh looptris of the original mesh. */
- BLI_assert(sod->treedata_editmesh.em == BKE_editmesh_from_object(DEG_get_original_object(ob)));
+ BLI_assert(sod->treedata_editmesh.em ==
+ BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em = sod->treedata_editmesh.em;
if (sctx->callbacks.edit_mesh.test_face_fn) {
@@ -886,7 +937,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -967,9 +1018,9 @@ struct RaycastObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void raycast_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data)
@@ -982,53 +1033,46 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
bool retval = false;
if (use_occlusion_test) {
- if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
+ if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d &&
+ XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
/* Use of occlude geometry in editing mode disabled. */
return;
}
- if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) {
+ if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) {
/* Do not hit objects that are in wire or bounding box
* display mode. */
return;
}
}
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
bool use_hide = false;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = raycastEditMesh(sctx,
- dt->ray_start,
- dt->ray_dir,
- ob,
- em_orig,
- obmat,
- ob_index,
- use_backface_culling,
- ray_depth,
- dt->r_loc,
- dt->r_no,
- dt->r_index,
- dt->r_hit_list);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- use_hide = true;
- }
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = raycastEditMesh(sctx,
+ dt->ray_start,
+ dt->ray_dir,
+ ob_eval,
+ em_orig,
+ obmat,
+ ob_index,
+ use_backface_culling,
+ ray_depth,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index,
+ dt->r_hit_list);
+ break;
}
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
ob_index,
use_hide,
@@ -1044,12 +1088,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
case OB_SURF:
case OB_FONT: {
if (!is_object_active) {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
+ ob_eval,
mesh_eval,
obmat,
ob_index,
@@ -1068,7 +1112,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -1323,6 +1367,33 @@ typedef struct Nearest2dUserData {
bool use_backface_culling;
} Nearest2dUserData;
+static void nearest2d_data_init(SnapObjectData *sod,
+ bool is_persp,
+ bool use_backface_culling,
+ Nearest2dUserData *r_nearest2d)
+{
+ if (sod->type == SNAP_MESH) {
+ r_nearest2d->userdata = &sod->treedata_mesh;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
+ r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get;
+ r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get;
+ }
+ else {
+ BLI_assert(sod->type == SNAP_EDIT_MESH);
+ r_nearest2d->userdata = sod->treedata_editmesh.em;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
+ r_nearest2d->get_tri_verts_index = NULL;
+ r_nearest2d->get_tri_edges_index = NULL;
+ }
+
+ r_nearest2d->is_persp = is_persp;
+ r_nearest2d->use_backface_culling = use_backface_culling;
+}
+
static void cb_snap_vert(void *userdata,
int index,
const struct DistProjectedAABBPrecalc *precalc,
@@ -1470,7 +1541,7 @@ static void cb_snap_tri_verts(void *userdata,
static short snap_mesh_polygon(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_backface_culling,
/* read/write args */
@@ -1495,28 +1566,21 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
}
- Nearest2dUserData nearest2d = {
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
-
BVHTreeNearest nearest = {
.index = -1,
.dist_sq = square_f(*dist_px),
};
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
+
if (sod->type == SNAP_MESH) {
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- nearest2d.userdata = treedata;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
-
const MPoly *mp = &sod->poly[*r_index];
const MLoop *ml = &treedata->loop[mp->loopstart];
if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
@@ -1547,11 +1611,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
BLI_assert(sod->type == SNAP_EDIT_MESH);
BMEditMesh *em = sod->treedata_editmesh.em;
- nearest2d.userdata = em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
-
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, *r_index);
BMLoop *l_iter, *l_first;
@@ -1608,7 +1667,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
float original_dist_px,
const float prev_co[3],
@@ -1622,32 +1681,16 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
{
short elem = SCE_SNAP_MODE_EDGE;
- if (ob->type != OB_MESH) {
+ if (ob_eval->type != OB_MESH) {
return elem;
}
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
Nearest2dUserData nearest2d;
- {
- nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- nearest2d.use_backface_culling = use_backface_culling;
- if (sod->type == SNAP_MESH) {
- nearest2d.userdata = &sod->treedata_mesh;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
- }
- else {
- BLI_assert(sod->type == SNAP_EDIT_MESH);
- nearest2d.userdata = sod->treedata_editmesh.em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
- }
- }
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
int vindex[2];
nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata);
@@ -1772,9 +1815,8 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
}
static short snapArmature(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
- bool use_obedit,
/* read/write args */
float *dist_px,
/* return args */
@@ -1795,11 +1837,11 @@ static short snapArmature(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL;
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_armature_boundbox_get(ob);
+ BoundBox *bb = BKE_armature_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return retval;
@@ -1814,7 +1856,7 @@ static short snapArmature(SnapData *snapdata,
bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- bArmature *arm = ob->data;
+ bArmature *arm = ob_eval->data;
if (arm->edbo) {
LISTBASE_FOREACH (EditBone *, eBone, arm->edbo) {
if (eBone->layer & arm->layer) {
@@ -1858,8 +1900,8 @@ static short snapArmature(SnapData *snapdata,
}
}
}
- else if (ob->pose && ob->pose->chanbase.first) {
- LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ else if (ob_eval->pose && ob_eval->pose->chanbase.first) {
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) {
Bone *bone = pchan->bone;
/* skip hidden bones */
if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
@@ -1917,7 +1959,7 @@ static short snapArmature(SnapData *snapdata,
}
static short snapCurve(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_obedit,
/* read/write args */
@@ -1934,7 +1976,7 @@ static short snapCurve(SnapData *snapdata,
return 0;
}
- Curve *cu = ob->data;
+ Curve *cu = ob_eval->data;
float dist_px_sq = square_f(*dist_px);
float lpmat[4][4];
@@ -1944,11 +1986,11 @@ static short snapCurve(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval);
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_curve_boundbox_get(ob);
+ BoundBox *bb = BKE_curve_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
@@ -2068,7 +2110,7 @@ static short snapCurve(SnapData *snapdata,
/* may extend later (for now just snaps to empty center) */
static short snap_object_center(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
/* read/write args */
float *dist_px,
@@ -2079,7 +2121,7 @@ static short snap_object_center(SnapData *snapdata,
{
short retval = 0;
- if (ob->transflag & OB_DUPLI) {
+ if (ob_eval->transflag & OB_DUPLI) {
return retval;
}
@@ -2222,9 +2264,10 @@ static short snapCamera(const SnapObjectContext *sctx,
static short snapMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
+ bool use_hide,
bool use_backface_culling,
/* read/write args */
float *dist_px,
@@ -2234,10 +2277,10 @@ static short snapMesh(SnapObjectContext *sctx,
int *r_index)
{
BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE);
- if (me->totvert == 0) {
+ if (me_eval->totvert == 0) {
return 0;
}
- if (me->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
+ if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
return 0;
}
@@ -2247,65 +2290,46 @@ static short snapMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
- BVHTreeFromMesh *treedata, dummy_treedata;
+ BVHTreeFromMesh *treedata, treedata_tmp;
treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used! */
- if (treedata->cached && treedata->tree &&
- !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- if (sod->cached[0] && sod->bvhtree[0] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[0])) {
- sod->bvhtree[0] = NULL;
- }
- if (sod->cached[1] && sod->bvhtree[1] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[1])) {
- sod->bvhtree[1] = NULL;
- }
-
- if (sod->has_looptris && treedata->tree == NULL) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- sod->has_looptris = (treedata->tree != NULL);
- if (sod->has_looptris) {
- /* Make sure that the array of edges is referenced in the callbacks. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- }
if (sod->has_loose_edge && sod->bvhtree[0] == NULL) {
- sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2);
- sod->has_loose_edge = sod->bvhtree[0] != NULL;
- sod->cached[0] = dummy_treedata.cached;
-
- if (sod->has_loose_edge) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
-
- BLI_assert(treedata->edge_allocated == false);
- treedata->edge = dummy_treedata.edge;
- treedata->edge_allocated = dummy_treedata.edge_allocated;
+ sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
+ if (sod->bvhtree[0] == NULL) {
+ sod->has_loose_edge = false;
}
+ sod->cached[0] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
+
if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) {
if (sod->has_loose_vert && sod->bvhtree[1] == NULL) {
- sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2);
- sod->has_loose_vert = sod->bvhtree[1] != NULL;
- sod->cached[1] = dummy_treedata.cached;
-
- if (sod->has_loose_vert) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
+ sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
+ if (sod->bvhtree[1] == NULL) {
+ sod->has_loose_vert = false;
}
+ sod->cached[1] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
}
else {
@@ -2313,33 +2337,9 @@ static short snapMesh(SnapObjectContext *sctx,
sod->has_loose_vert = false;
}
- /* Update pointers. */
- if (treedata->vert_allocated == false) {
- treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */
- }
- if (treedata->tree || sod->bvhtree[0]) {
- if (treedata->edge_allocated == false) {
- /* If raycast has been executed before, `treedata->edge` can be NULL. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- }
-
- Nearest2dUserData nearest2d = {
- .userdata = treedata,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get,
- .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get,
- .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2455,7 +2455,7 @@ static short snapMesh(SnapObjectContext *sctx,
static short snapEditMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
bool use_backface_culling,
@@ -2484,7 +2484,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -2558,14 +2558,9 @@ static short snapEditMesh(SnapObjectContext *sctx,
}
}
- Nearest2dUserData nearest2d = {
- .userdata = em,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2659,9 +2654,9 @@ struct SnapObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void snap_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool UNUSED(is_object_active),
void *data)
@@ -2669,41 +2664,36 @@ static void snap_obj_fn(SnapObjectContext *sctx,
struct SnapObjUserData *dt = data;
short retval = 0;
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = snapEditMesh(sctx,
- dt->snapdata,
- ob,
- em_orig,
- obmat,
- use_backface_culling,
- dt->dist_px,
- dt->r_loc,
- dt->r_no,
- dt->r_index);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- }
+ bool use_hide;
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = snapEditMesh(sctx,
+ dt->snapdata,
+ ob_eval,
+ em_orig,
+ obmat,
+ use_backface_culling,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ break;
}
- else if (ob->dt == OB_BOUNDBOX) {
+ if (ob_eval->dt == OB_BOUNDBOX) {
/* Do not snap to objects that are in bounding box display mode */
return;
}
retval = snapMesh(sctx,
dt->snapdata,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
+ use_hide,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2713,21 +2703,28 @@ static void snap_obj_fn(SnapObjectContext *sctx,
}
case OB_ARMATURE:
retval = snapArmature(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CURVE:
- retval = snapCurve(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ retval = snapCurve(dt->snapdata,
+ ob_eval,
+ obmat,
+ edit_mode_type == SNAP_GEOM_EDIT,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */
case OB_SURF:
case OB_FONT: {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval |= snapMesh(sctx,
dt->snapdata,
- ob,
+ ob_eval,
mesh_eval,
obmat,
+ false,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2740,17 +2737,17 @@ static void snap_obj_fn(SnapObjectContext *sctx,
case OB_GPENCIL:
case OB_LAMP:
retval = snap_object_center(
- dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CAMERA:
retval = snapCamera(
- sctx, dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
}
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -3023,7 +3020,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
short retval = 0;
bool has_hit = false;
- Object *ob = NULL;
+ Object *ob_eval = NULL;
float loc[3];
/* Not all snapping callbacks set the normal,
* initialize this since any hit copies both the `loc` and `no`. */
@@ -3060,7 +3057,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
loc,
no,
&index,
- &ob,
+ &ob_eval,
obmat,
NULL);
@@ -3072,7 +3069,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
@@ -3108,9 +3105,10 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.has_occlusion_plane = false;
/* By convention we only snap to the original elements of a curve. */
- if (has_hit && ob->type != OB_CURVE) {
+ if (has_hit && ob_eval->type != OB_CURVE) {
/* Compute the new clip_pane but do not add it yet. */
float new_clipplane[4];
+ BLI_ASSERT_UNIT_V3(no);
plane_from_point_normal_v3(new_clipplane, loc, no);
if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) {
/* The plane is facing the wrong direction. */
@@ -3121,8 +3119,15 @@ static short transform_snap_context_project_view3d_mixed_impl(
new_clipplane[3] += 0.01f;
/* Try to snap only to the polygon. */
- elem_test = snap_mesh_polygon(
- sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index);
+ elem_test = snap_mesh_polygon(sctx,
+ &snapdata,
+ ob_eval,
+ obmat,
+ params->use_backface_culling,
+ &dist_px_tmp,
+ loc,
+ no,
+ &index);
if (elem_test) {
elem = elem_test;
}
@@ -3137,7 +3142,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
}
elem_test = snapObjectsRay(
- sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob, obmat);
+ sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat);
if (elem_test) {
elem = elem_test;
}
@@ -3148,7 +3153,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.snap_to_flag = snap_to_flag;
elem = snap_mesh_edge_verts_mixed(sctx,
&snapdata,
- ob,
+ ob_eval,
obmat,
*dist_px,
prev_co,
@@ -3167,7 +3172,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index ad45686dc75..c1d0dcdb095 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -54,7 +54,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
const int mode = tvs->mode;
BLI_assert(ED_transverts_check_obedit(obedit) == true);
- DEG_id_tag_update(obedit->data, 0);
+ DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
if (obedit->type == OB_MESH) {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index 54ea0103fe5..cd1597a742c 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -666,7 +666,7 @@ class CPPType : NonCopyable, NonMovable {
template<typename T> bool is() const
{
- return this == &CPPType::get<T>();
+ return this == &CPPType::get<std::decay_t<T>>();
}
};
diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/functions/FN_generic_pointer.hh
index 2bd66daa7fe..f88ff09f916 100644
--- a/source/blender/functions/FN_generic_pointer.hh
+++ b/source/blender/functions/FN_generic_pointer.hh
@@ -66,6 +66,16 @@ class GMutablePointer {
return type_ != nullptr && type_->is<T>();
}
+ template<typename T> T relocate_out()
+ {
+ BLI_assert(this->is_type<T>());
+ T value;
+ type_->relocate_to_initialized(data_, &value);
+ data_ = nullptr;
+ type_ = nullptr;
+ return value;
+ }
+
void destruct()
{
BLI_assert(data_ != nullptr);
diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh
index 31b67dd3d70..e2c49697ba9 100644
--- a/source/blender/functions/FN_generic_span.hh
+++ b/source/blender/functions/FN_generic_span.hh
@@ -30,7 +30,7 @@ namespace blender::fn {
* A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time.
*/
class GSpan {
- private:
+ protected:
const CPPType *type_;
const void *data_;
int64_t size_;
@@ -85,6 +85,14 @@ class GSpan {
BLI_assert(type_->is<T>());
return Span<T>(static_cast<const T *>(data_), size_);
}
+
+ GSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
/**
@@ -92,7 +100,7 @@ class GSpan {
* known at run-time.
*/
class GMutableSpan {
- private:
+ protected:
const CPPType *type_;
void *data_;
int64_t size_;
@@ -153,6 +161,14 @@ class GMutableSpan {
BLI_assert(type_->is<T>());
return MutableSpan<T>(static_cast<T *>(data_), size_);
}
+
+ GMutableSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh
index 68cb945f1af..4e7fe298874 100644
--- a/source/blender/functions/FN_generic_value_map.hh
+++ b/source/blender/functions/FN_generic_value_map.hh
@@ -93,6 +93,11 @@ template<typename Key> class GValueMap {
return values_.pop_as(key);
}
+ template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const
+ {
+ return values_.lookup_as(key);
+ }
+
/* Remove the value for the given name from the container and remove it. */
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
{
diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh
index ae6eb8a614f..b02ed471875 100644
--- a/source/blender/functions/FN_generic_vector_array.hh
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef {
void extend(const int64_t index, const VArray<T> &values)
{
- GVArrayForVArray<T> array{values};
+ GVArray_For_VArray<T> array{values};
this->extend(index, array);
}
@@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef {
};
/* A generic virtual vector array implementation for a `GVectorArray`. */
-class GVVectorArrayForGVectorArray : public GVVectorArray {
+class GVVectorArray_For_GVectorArray : public GVVectorArray {
private:
const GVectorArray &vector_array_;
public:
- GVVectorArrayForGVectorArray(const GVectorArray &vector_array)
+ GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index c6230730a8d..d530d10b3c8 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -23,12 +23,23 @@
* the data type is only known at runtime.
*/
+#include <optional>
+
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
namespace blender::fn {
+template<typename T> class GVArray_Typed;
+template<typename T> class GVMutableArray_Typed;
+
+class GVArray;
+class GVMutableArray;
+
+using GVArrayPtr = std::unique_ptr<GVArray>;
+using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>;
+
/* A generically typed version of `VArray<T>`. */
class GVArray {
protected:
@@ -86,13 +97,13 @@ class GVArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- GSpan get_span() const
+ GSpan get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return GSpan(*type_);
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -107,57 +118,151 @@ class GVArray {
/* Copies the value that is used for every element into `r_value`, which is expected to point to
* initialized memory. This invokes undefined behavior if the virtual array would not return the
* same value for every index. */
- void get_single(void *r_value) const
+ void get_internal_single(void *r_value) const
{
BLI_assert(this->is_single());
if (size_ == 1) {
this->get(0, r_value);
+ return;
}
- this->get_single_impl(r_value);
+ this->get_internal_single_impl(r_value);
}
- /* Same as `get_single`, but `r_value` points to initialized memory. */
+ /* Same as `get_internal_single`, but `r_value` points to initialized memory. */
void get_single_to_uninitialized(void *r_value) const
{
type_->construct_default(r_value);
- this->get_single(r_value);
+ this->get_internal_single(r_value);
}
+ void materialize(void *dst) const;
+ void materialize(const IndexMask mask, void *dst) const;
+
+ void materialize_to_uninitialized(void *dst) const;
void materialize_to_uninitialized(const IndexMask mask, void *dst) const;
+ template<typename T> const VArray<T> *try_get_internal_varray() const
+ {
+ BLI_assert(type_->is<T>());
+ return (const VArray<T> *)this->try_get_internal_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVArray_Typed<T> typed() const
+ {
+ return GVArray_Typed<T>(*this);
+ }
+
+ GVArrayPtr shallow_copy() const;
+
protected:
virtual void get_impl(const int64_t index, void *r_value) const;
virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0;
virtual bool is_span_impl() const;
- virtual GSpan get_span_impl() const;
+ virtual GSpan get_internal_span_impl() const;
virtual bool is_single_impl() const;
- virtual void get_single_impl(void *UNUSED(r_value)) const;
+ virtual void get_internal_single_impl(void *UNUSED(r_value)) const;
+
+ virtual void materialize_impl(const IndexMask mask, void *dst) const;
+ virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const;
+
+ virtual const void *try_get_internal_varray_impl() const;
+};
+
+/* Similar to GVArray, but supports changing the elements in the virtual array. */
+class GVMutableArray : public GVArray {
+ public:
+ GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
+ void set_by_copy(const int64_t index, const void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_copy_impl(index, value);
+ }
+
+ void set_by_move(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_move_impl(index, value);
+ }
+
+ void set_by_relocate(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_relocate_impl(index, value);
+ }
+
+ GMutableSpan get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ GSpan span = static_cast<const GVArray *>(this)->get_internal_span();
+ return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
+ }
+
+ template<typename T> VMutableArray<T> *try_get_internal_mutable_varray()
+ {
+ BLI_assert(type_->is<T>());
+ return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVMutableArray_Typed<T> typed()
+ {
+ return GVMutableArray_Typed<T>(*this);
+ }
+
+ void fill(const void *value);
+
+ /* Copy the values from the source buffer to all elements in the virtual array. */
+ void set_all(const void *src)
+ {
+ this->set_all_impl(src);
+ }
+
+ protected:
+ virtual void set_by_copy_impl(const int64_t index, const void *value);
+ virtual void set_by_relocate_impl(const int64_t index, void *value);
+ virtual void set_by_move_impl(const int64_t index, void *value) = 0;
+
+ virtual void set_all_impl(const void *src);
+
+ virtual void *try_get_internal_mutable_varray_impl();
};
-class GVArrayForGSpan : public GVArray {
+class GVArray_For_GSpan : public GVArray {
protected:
- const void *data_;
+ const void *data_ = nullptr;
const int64_t element_size_;
public:
- GVArrayForGSpan(const GSpan span)
+ GVArray_For_GSpan(const GSpan span)
: GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size())
{
}
protected:
+ GVArray_For_GSpan(const CPPType &type, const int64_t size)
+ : GVArray(type, size), element_size_(type.size())
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
};
-class GVArrayForEmpty : public GVArray {
+class GVArray_For_Empty : public GVArray {
public:
- GVArrayForEmpty(const CPPType &type) : GVArray(type, 0)
+ GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
{
}
@@ -168,108 +273,642 @@ class GVArrayForEmpty : public GVArray {
}
};
-class GVArrayForSingleValueRef : public GVArray {
- private:
- const void *value_;
+class GVMutableArray_For_GMutableSpan : public GVMutableArray {
+ protected:
+ void *data_ = nullptr;
+ const int64_t element_size_;
public:
- GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value)
+ GVMutableArray_For_GMutableSpan(const GMutableSpan span)
+ : GVMutableArray(span.type(), span.size()),
+ data_(span.data()),
+ element_size_(span.type().size())
+ {
+ }
+
+ protected:
+ GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size)
+ : GVMutableArray(type, size), element_size_(type.size())
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override;
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
+
+ void set_by_copy_impl(const int64_t index, const void *value) override;
+ void set_by_move_impl(const int64_t index, void *value) override;
+ void set_by_relocate_impl(const int64_t index, void *value) override;
+
+ bool is_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
+};
+
+/* Generic virtual array where each element has the same value. The value is not owned. */
+class GVArray_For_SingleValueRef : public GVArray {
+ protected:
+ const void *value_ = nullptr;
+
+ public:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
: GVArray(type, size), value_(value)
{
}
protected:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
bool is_single_impl() const override;
- void get_single_impl(void *r_value) const override;
+ void get_internal_single_impl(void *r_value) const override;
};
-template<typename T> class GVArrayForVArray : public GVArray {
- private:
- const VArray<T> &array_;
+/* Same as GVArray_For_SingleValueRef, but the value is owned. */
+class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
+ public:
+ GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value);
+ ~GVArray_For_SingleValue();
+};
+
+/* Used to convert a typed virtual array into a generic one. */
+template<typename T> class GVArray_For_VArray : public GVArray {
+ protected:
+ const VArray<T> *varray_ = nullptr;
public:
- GVArrayForVArray(const VArray<T> &array)
- : GVArray(CPPType::get<T>(), array.size()), array_(array)
+ GVArray_For_VArray(const VArray<T> &varray)
+ : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
{
}
protected:
+ GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override
{
- *(T *)r_value = array_.get(index);
+ *(T *)r_value = varray_->get(index);
}
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
{
- new (r_value) T(array_.get(index));
+ new (r_value) T(varray_->get(index));
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- GSpan get_span_impl() const override
+ GSpan get_internal_span_impl() const override
{
- return GSpan(array_.get_span());
+ return GSpan(varray_->get_internal_span());
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- void get_single_impl(void *r_value) const override
+ void get_internal_single_impl(void *r_value) const override
{
- *(T *)r_value = array_.get_single();
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return varray_;
}
};
-template<typename T> class VArrayForGVArray : public VArray<T> {
- private:
- const GVArray &array_;
+/* Used to convert any generic virtual array into a typed one. */
+template<typename T> class VArray_For_GVArray : public VArray<T> {
+ protected:
+ const GVArray *varray_ = nullptr;
public:
- VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array)
+ VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
{
- BLI_assert(array_.type().template is<T>());
+ BLI_assert(varray_->type().template is<T>());
}
protected:
+ VArray_For_GVArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
T get_impl(const int64_t index) const override
{
T value;
- array_.get(index, &value);
+ varray_->get(index, &value);
return value;
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
- return array_.get_span().template typed<T>();
+ return varray_->get_internal_span().template typed<T>();
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
T value;
- array_.get_single(&value);
+ varray_->get_internal_single(&value);
return value;
}
};
+/* Used to convert an generic mutable virtual array into a typed one. */
+template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> {
+ protected:
+ GVMutableArray *varray_ = nullptr;
+
+ public:
+ VMutableArray_For_GVMutableArray(GVMutableArray &varray)
+ : VMutableArray<T>(varray.size()), varray_(&varray)
+ {
+ BLI_assert(varray.type().template is<T>());
+ }
+
+ VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ T value;
+ varray_->get(index, &value);
+ return value;
+ }
+
+ void set_impl(const int64_t index, T value) override
+ {
+ varray_->set_by_relocate(index, &value);
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ Span<T> get_internal_span_impl() const override
+ {
+ return varray_->get_internal_span().template typed<T>();
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ T get_internal_single_impl() const override
+ {
+ T value;
+ varray_->get_internal_single(&value);
+ return value;
+ }
+};
+
+/* Used to convert any typed virtual mutable array into a generic one. */
+template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
+ protected:
+ VMutableArray<T> *varray_ = nullptr;
+
+ public:
+ GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
+ : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray)
+ {
+ }
+
+ protected:
+ GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ *(T *)r_value = varray_->get(index);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(varray_->get(index));
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ GSpan get_internal_span_impl() const override
+ {
+ Span<T> span = varray_->get_internal_span();
+ return span;
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ void get_internal_single_impl(void *r_value) const override
+ {
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void set_by_copy_impl(const int64_t index, const void *value) override
+ {
+ const T &value_ = *(const T *)value;
+ varray_->set(index, value_);
+ }
+
+ void set_by_relocate_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ value_.~T();
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ }
+
+ void set_all_impl(const void *src) override
+ {
+ varray_->set_all(Span((T *)src, size_));
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return (const VArray<T> *)varray_;
+ }
+
+ void *try_get_internal_mutable_varray_impl() override
+ {
+ return varray_;
+ }
+};
+
+/* A generic version of VArray_Span. */
+class GVArray_GSpan : public GSpan {
+ private:
+ const GVArray &varray_;
+ void *owned_data_ = nullptr;
+
+ public:
+ GVArray_GSpan(const GVArray &varray);
+ ~GVArray_GSpan();
+};
+
+/* A generic version of VMutableArray_Span. */
+class GVMutableArray_GSpan : public GMutableSpan {
+ private:
+ GVMutableArray &varray_;
+ void *owned_data_ = nullptr;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true);
+ ~GVMutableArray_GSpan();
+
+ void save();
+ void disable_not_applied_warning();
+};
+
+/* Similar to GVArray_GSpan, but the resulting span is typed. */
+template<typename T> class GVArray_Span : public Span<T> {
+ private:
+ GVArray_GSpan varray_gspan_;
+
+ public:
+ GVArray_Span(const GVArray &varray) : varray_gspan_(varray)
+ {
+ BLI_assert(varray.type().is<T>());
+ this->data_ = (const T *)varray_gspan_.data();
+ this->size_ = varray_gspan_.size();
+ }
+};
+
+template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVArray_For_OwnedVArray(VArrayPtr<T> varray)
+ : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> {
+ private:
+ GVArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VArray_For_OwnedGVArray(GVArrayPtr varray)
+ : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray)
+ : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> {
+ private:
+ GVMutableArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray)
+ : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give
+ * the compiler more opportunity to optimize the generic virtual array. */
+template<typename T, typename VArrayT>
+class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args)
+ : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */
+template<typename T, typename VMutableArrayT>
+class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args)
+ : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */
+template<typename Container, typename T = typename Container::value_type>
+class GVArray_For_ArrayContainer
+ : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> {
+ public:
+ GVArray_For_ArrayContainer(Container container)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>(
+ container.size(), std::move(container))
+ {
+ }
+};
+
+/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class GVArray_For_DerivedSpan
+ : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> {
+ public:
+ GVArray_For_DerivedSpan(const Span<StructT> data)
+ : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class GVMutableArray_For_DerivedSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> {
+ public:
+ GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VArray_For_Span, but for a generic virtual array. */
+template<typename T>
+class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> {
+ public:
+ GVArray_For_Span(const Span<T> data)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */
+template<typename T>
+class GVMutableArray_For_MutableSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> {
+ public:
+ GVMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(),
+ data)
+ {
+ }
+};
+
+/**
+ * Utility class to create the "best" typed virtual array for a given generic virtual array.
+ * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional
+ * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is
+ * just a span or single value).
+ *
+ * This is not a virtual array itself, but is used to get a virtual array.
+ */
+template<typename T> class GVArray_Typed {
+ private:
+ const VArray<T> *varray_;
+ /* Of these optional virtual arrays, at most one is constructed at any time. */
+ std::optional<VArray_For_Span<T>> varray_span_;
+ std::optional<VArray_For_Single<T>> varray_single_;
+ std::optional<VArray_For_GVArray<T>> varray_any_;
+ GVArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVArray_Typed(const GVArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (gvarray.is_single()) {
+ T single_value;
+ gvarray.get_internal_single(&single_value);
+ varray_single_.emplace(single_value, gvarray.size());
+ varray_ = &*varray_single_;
+ }
+ else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ /* Same as the constructor above, but also takes ownership of the passed in virtual array. */
+ explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ const VArray<T> &operator*() const
+ {
+ return *varray_;
+ }
+
+ const VArray<T> *operator->() const
+ {
+ return varray_;
+ }
+
+ /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is
+ * used within an expression. */
+ operator const VArray<T> &() const
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(this->size());
+ }
+};
+
+/* Same as GVArray_Typed, but for mutable virtual arrays. */
+template<typename T> class GVMutableArray_Typed {
+ private:
+ VMutableArray<T> *varray_;
+ std::optional<VMutableArray_For_MutableSpan<T>> varray_span_;
+ std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_;
+ GVMutableArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVMutableArray_Typed(GVMutableArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GMutableSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ VMutableArray<T> &operator*()
+ {
+ return *varray_;
+ }
+
+ VMutableArray<T> *operator->()
+ {
+ return varray_;
+ }
+
+ operator VMutableArray<T> &()
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+};
+
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh
index ef3f53b5c25..4155a55a801 100644
--- a/source/blender/functions/FN_generic_virtual_vector_array.hh
+++ b/source/blender/functions/FN_generic_virtual_vector_array.hh
@@ -100,13 +100,13 @@ class GVVectorArray {
}
};
-class GVArrayForGVVectorArrayIndex : public GVArray {
+class GVArray_For_GVVectorArrayIndex : public GVArray {
private:
const GVVectorArray &vector_array_;
const int64_t index_;
public:
- GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
+ GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
: GVArray(vector_array.type(), vector_array.get_vector_size(index)),
vector_array_(vector_array),
index_(index)
@@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray {
void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override;
};
-class GVVectorArrayForSingleGVArray : public GVVectorArray {
+class GVVectorArray_For_SingleGVArray : public GVVectorArray {
private:
const GVArray &array_;
public:
- GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size)
+ GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size)
: GVVectorArray(array.type(), size), array_(array)
{
}
@@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-class GVVectorArrayForSingleGSpan : public GVVectorArray {
+class GVVectorArray_For_SingleGSpan : public GVVectorArray {
private:
const GSpan span_;
public:
- GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size)
+ GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size)
: GVVectorArray(span.type(), size), span_(span)
{
}
@@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> {
+template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> {
private:
const GVVectorArray &vector_array_;
public:
- VVectorArrayForGVVectorArray(const GVVectorArray &vector_array)
+ VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array)
: VVectorArray<T>(vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh
index 72ebc0d9b94..e292d11def7 100644
--- a/source/blender/functions/FN_multi_function_params.hh
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -27,6 +27,7 @@
#include "BLI_resource_scope.hh"
+#include "FN_generic_pointer.hh"
#include "FN_generic_vector_array.hh"
#include "FN_generic_virtual_vector_array.hh"
#include "FN_multi_function_signature.hh"
@@ -55,13 +56,19 @@ class MFParamsBuilder {
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
{
- this->add_readonly_single_input(scope_.construct<GVArrayForSingleValueRef>(
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
__func__, CPPType::get<T>(), min_array_size_, value),
expected_name);
}
void add_readonly_single_input(const GSpan span, StringRef expected_name = "")
{
- this->add_readonly_single_input(scope_.construct<GVArrayForGSpan>(__func__, span),
+ this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span),
+ expected_name);
+ }
+ void add_readonly_single_input(GPointer value, StringRef expected_name = "")
+ {
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
+ __func__, *value.type(), min_array_size_, value.get()),
expected_name);
}
void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "")
@@ -74,7 +81,7 @@ class MFParamsBuilder {
void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "")
{
this->add_readonly_vector_input(
- scope_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name);
+ scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name);
}
void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "")
{
@@ -177,7 +184,7 @@ class MFParams {
template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "")
{
const GVArray &array = this->readonly_single_input(param_index, name);
- return builder_->scope_.construct<VArrayForGVArray<T>>(__func__, array);
+ return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array);
}
const GVArray &readonly_single_input(int param_index, StringRef name = "")
{
@@ -202,7 +209,7 @@ class MFParams {
const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "")
{
const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name);
- return builder_->scope_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array);
+ return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array);
}
const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "")
{
diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc
index b3c5517cc43..3335b07e559 100644
--- a/source/blender/functions/intern/generic_vector_array.cc
+++ b/source/blender/functions/intern/generic_vector_array.cc
@@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values)
void GVectorArray::extend(const int64_t index, const GSpan values)
{
- GVArrayForGSpan varray{values};
+ GVArray_For_GSpan varray{values};
this->extend(index, varray);
}
void GVectorArray::extend(IndexMask mask, const GVVectorArray &values)
{
for (const int i : mask) {
- GVArrayForGVVectorArrayIndex array{values, i};
+ GVArray_For_GVVectorArrayIndex array{values, i};
this->extend(i, array);
}
}
void GVectorArray::extend(IndexMask mask, const GVectorArray &values)
{
- GVVectorArrayForGVectorArray virtual_values{values};
+ GVVectorArray_For_GVectorArray virtual_values{values};
this->extend(mask, virtual_values);
}
diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc
index 9380eb257b2..87dae06ccdc 100644
--- a/source/blender/functions/intern/generic_virtual_array.cc
+++ b/source/blender/functions/intern/generic_virtual_array.cc
@@ -18,8 +18,72 @@
namespace blender::fn {
+/* --------------------------------------------------------------------
+ * GVArray_For_ShallowCopy.
+ */
+
+class GVArray_For_ShallowCopy : public GVArray {
+ private:
+ const GVArray &varray_;
+
+ public:
+ GVArray_For_ShallowCopy(const GVArray &varray)
+ : GVArray(varray.type(), varray.size()), varray_(varray)
+ {
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get(index, r_value);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get_to_uninitialized(index, r_value);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_.materialize_to_uninitialized(mask, dst);
+ }
+};
+
+/* --------------------------------------------------------------------
+ * GVArray.
+ */
+
+void GVArray::materialize(void *dst) const
+{
+ this->materialize(IndexMask(size_), dst);
+}
+
+void GVArray::materialize(const IndexMask mask, void *dst) const
+{
+ this->materialize_impl(mask, dst);
+}
+
+void GVArray::materialize_impl(const IndexMask mask, void *dst) const
+{
+ for (const int64_t i : mask) {
+ void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
+ this->get(i, elem_dst);
+ }
+}
+
+void GVArray::materialize_to_uninitialized(void *dst) const
+{
+ this->materialize_to_uninitialized(IndexMask(size_), dst);
+}
+
void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, dst);
+}
+
+void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const
+{
for (const int64_t i : mask) {
void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
this->get_to_uninitialized(i, elem_dst);
@@ -37,7 +101,7 @@ bool GVArray::is_span_impl() const
return false;
}
-GSpan GVArray::get_span_impl() const
+GSpan GVArray::get_internal_span_impl() const
{
BLI_assert(false);
return GSpan(*type_);
@@ -48,60 +112,279 @@ bool GVArray::is_single_impl() const
return false;
}
-void GVArray::get_single_impl(void *UNUSED(r_value)) const
+void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const
{
BLI_assert(false);
}
-void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const
+const void *GVArray::try_get_internal_varray_impl() const
+{
+ return nullptr;
+}
+
+/**
+ * Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`.
+ * The lifetime of the returned virtual array must not be longer than the lifetime of this virtual
+ * array.
+ */
+GVArrayPtr GVArray::shallow_copy() const
+{
+ if (this->is_span()) {
+ return std::make_unique<GVArray_For_GSpan>(this->get_internal_span());
+ }
+ if (this->is_single()) {
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ this->get_internal_single(buffer);
+ std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer);
+ type_->destruct(buffer);
+ return new_varray;
+ }
+ return std::make_unique<GVArray_For_ShallowCopy>(*this);
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray.
+ */
+
+void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value)
+{
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ type_->copy_to_uninitialized(value, buffer);
+ this->set_by_move_impl(index, buffer);
+ type_->destruct(buffer);
+}
+
+void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value)
+{
+ this->set_by_move_impl(index, value);
+ type_->destruct(value);
+}
+
+void GVMutableArray::set_all_impl(const void *src)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->copy_to_initialized_n(src, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, POINTER_OFFSET(src, type_->size() * i));
+ }
+ }
+}
+
+void *GVMutableArray::try_get_internal_mutable_varray_impl()
+{
+ return nullptr;
+}
+
+void GVMutableArray::fill(const void *value)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->fill_initialized(value, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, value);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_GSpan.
+ */
+
+void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const
{
type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
+void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
{
type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-bool GVArrayForGSpan::is_span_impl() const
+bool GVArray_For_GSpan::is_span_impl() const
{
return true;
}
-GSpan GVArrayForGSpan::get_span_impl() const
+GSpan GVArray_For_GSpan::get_internal_span_impl() const
{
return GSpan(*type_, data_, size_);
}
-void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
+/* --------------------------------------------------------------------
+ * GVMutableArray_For_GMutableSpan.
+ */
+
+void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const
+{
+ type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index,
+ void *r_value) const
+{
+ type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value)
+{
+ type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value)
+{
+ type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value)
+{
+ type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+bool GVMutableArray_For_GMutableSpan::is_span_impl() const
+{
+ return true;
+}
+
+GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const
+{
+ return GSpan(*type_, data_, size_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValueRef.
+ */
+
+void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
-void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
- void *r_value) const
+void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
+ void *r_value) const
{
type_->copy_to_uninitialized(value_, r_value);
}
-bool GVArrayForSingleValueRef::is_span_impl() const
+bool GVArray_For_SingleValueRef::is_span_impl() const
{
return size_ == 1;
}
-GSpan GVArrayForSingleValueRef::get_span_impl() const
+GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const
{
return GSpan{*type_, value_, 1};
}
-bool GVArrayForSingleValueRef::is_single_impl() const
+bool GVArray_For_SingleValueRef::is_single_impl() const
{
return true;
}
-void GVArrayForSingleValueRef::get_single_impl(void *r_value) const
+void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValue.
+ */
+
+GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type,
+ const int64_t size,
+ const void *value)
+ : GVArray_For_SingleValueRef(type, size)
+{
+ value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_to_uninitialized(value, (void *)value_);
+}
+
+GVArray_For_SingleValue::~GVArray_For_SingleValue()
+{
+ type_->destruct((void *)value_);
+ MEM_freeN((void *)value_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_GSpan.
+ */
+
+GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ data_ = owned_data_;
+ }
+}
+
+GVArray_GSpan::~GVArray_GSpan()
+{
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray_GSpan.
+ */
+
+GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span)
+ : GMutableSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ if (copy_values_to_span) {
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ }
+ else {
+ type_->construct_default_n(owned_data_, size_);
+ }
+ data_ = owned_data_;
+ }
+}
+
+GVMutableArray_GSpan::~GVMutableArray_GSpan()
+{
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+void GVMutableArray_GSpan::save()
+{
+ save_has_been_called_ = true;
+ if (data_ != owned_data_) {
+ return;
+ }
+ const int64_t element_size = type_->size();
+ for (int64_t i : IndexRange(size_)) {
+ varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i));
+ }
+}
+
+void GVMutableArray_GSpan::disable_not_applied_warning()
+{
+ show_not_saved_warning_ = false;
+}
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc
index f6504cee41e..aa3d90883c6 100644
--- a/source/blender/functions/intern/generic_virtual_vector_array.cc
+++ b/source/blender/functions/intern/generic_virtual_vector_array.cc
@@ -18,48 +18,48 @@
namespace blender::fn {
-void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
{
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
- void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
+ void *r_value) const
{
type_->construct_default(r_value);
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return array_.size();
}
-void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
array_.get(index_in_vector, r_value);
}
-bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const
{
return true;
}
-int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return span_.size();
}
-void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
type_->copy_to_initialized(span_[index_in_vector], r_value);
}
-bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const
{
return true;
}
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
index 86ac4f6a179..9a0cb0c35ce 100644
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ b/source/blender/functions/intern/multi_function_network_evaluation.cc
@@ -974,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
if (value->is_single_allocated) {
- return scope.construct<GVArrayForSingleValueRef>(
+ return scope.construct<GVArray_For_SingleValueRef>(
__func__, value->span.type(), min_array_size_, value->span.data());
}
- return scope.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -987,11 +987,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OutputSingle) {
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
- return scope.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
@@ -1004,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
BLI_assert(value->span.size() == 1);
- return scope.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -1015,11 +1015,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
BLI_assert(value->span.size() == 1);
- return scope.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
@@ -1033,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
if (value->vector_array->size() == 1) {
GSpan span = (*value->vector_array)[0];
- return scope.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
}
- return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1044,11 +1044,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
}
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
@@ -1061,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1071,11 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
/** \} */
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
index 51e116b5983..7b9738e5ca4 100644
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_network_test.cc
@@ -223,7 +223,7 @@ TEST(multi_function_network, Test2)
Array<int> output_value_2(5, -1);
MFParamsBuilder params(network_fn, 5);
- GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5};
+ GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
params.add_readonly_vector_input(inputs_1);
params.add_readonly_single_input(&input_value_2);
params.add_vector_output(output_value_1);
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 9690f47c862..f39306ac9d0 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRC
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
intern/MOD_gpencillattice.c
+ intern/MOD_gpencillength.c
intern/MOD_gpencillineart.c
intern/MOD_gpencilmirror.c
intern/MOD_gpencilmultiply.c
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index e6ce7983a0f..f8a28f2e5cb 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Array;
extern GpencilModifierTypeInfo modifierType_Gpencil_Build;
extern GpencilModifierTypeInfo modifierType_Gpencil_Opacity;
extern GpencilModifierTypeInfo modifierType_Gpencil_Lattice;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Length;
extern GpencilModifierTypeInfo modifierType_Gpencil_Mirror;
extern GpencilModifierTypeInfo modifierType_Gpencil_Smooth;
extern GpencilModifierTypeInfo modifierType_Gpencil_Hook;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
index a156fca5b7b..94285b5032e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -203,6 +203,20 @@ void gpencil_modifier_curve_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiTemplateCurveMapping(layout, ptr, "curve", 0, false, false, false, false);
}
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE);
+ uiLayout *sub = uiLayoutColumn(layout, true);
+ uiItemR(sub, ptr, "fading_start", 0, NULL, ICON_NONE);
+ uiItemR(sub, ptr, "fading_end", 0, IFACE_("End"), ICON_NONE);
+ uiItemR(layout, ptr, "fading_end_factor", 0, NULL, ICON_NONE);
+}
+
/**
* Draw modifier error message.
*/
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
index 782b36d47ed..75907aaa781 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
@@ -37,6 +37,8 @@ void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool u
void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel);
void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel);
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel);
+
void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr);
struct PointerRNA *gpencil_modifier_panel_get_property_pointers(struct Panel *panel,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 2dc00079f91..b28a44a0521 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -54,6 +54,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Build);
INIT_GP_TYPE(Opacity);
INIT_GP_TYPE(Lattice);
+ INIT_GP_TYPE(Length);
INIT_GP_TYPE(Mirror);
INIT_GP_TYPE(Smooth);
INIT_GP_TYPE(Hook);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index d7fc08e38f3..d9f0fc9bddd 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -455,8 +455,10 @@ static void generate_geometry(
/* Compute start and end frames for the animation effect
* By default, the upper bound is given by the "maximum length" setting
*/
- float start_frame = gpf->framenum + mmd->start_delay;
- float end_frame = start_frame + mmd->length;
+ float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay;
+ /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last
+ * frame. */
+ float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length;
if (gpf->next) {
/* Use the next frame or upper bound as end frame, whichever is lower/closer */
@@ -547,6 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
int mode = RNA_enum_get(ptr, "mode");
+ const bool use_percentage = RNA_boolean_get(ptr, "use_percentage");
uiLayoutSetPropSep(layout, true);
@@ -558,8 +561,12 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemS(layout);
uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "start_delay", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
uiItemS(layout);
@@ -567,7 +574,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropDecorate(row, false);
uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_percentage"));
+ uiLayoutSetActive(sub, use_percentage);
uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE);
uiItemDecoratorR(row, ptr, "percentage_factor", 0);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
new file mode 100644
index 00000000000..fd94ac92bc3
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -0,0 +1,223 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_defaults.h"
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_query.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
+#include "MOD_gpencil_util.h"
+
+#include "DEG_depsgraph.h"
+
+static void initData(GpencilModifierData *md)
+{
+ LengthGpencilModifierData *gpmd = (LengthGpencilModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(LengthGpencilModifierData), modifier);
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copydata_generic(md, target);
+}
+
+static bool gpencil_modify_stroke(bGPDstroke *gps,
+ float length,
+ const float overshoot_fac,
+ const short len_mode)
+{
+ bool changed = false;
+ if (length == 0.0f) {
+ return changed;
+ }
+
+ if (length > 0.0f) {
+ BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode);
+ }
+ else {
+ changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode);
+ }
+
+ return changed;
+}
+
+static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke *gps)
+{
+ bool changed = false;
+ const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f :
+ BKE_gpencil_stroke_length(gps, true);
+ if (len < FLT_EPSILON) {
+ return;
+ }
+
+ changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1);
+ changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2);
+
+ if (changed) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *UNUSED(depsgraph),
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ applyLength(lmd, gpd, gps);
+ }
+ }
+ }
+}
+
+/* -------------------------------- */
+
+/* Generic "generateStrokes" callback */
+static void deformStroke(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ bGPdata *gpd = ob->data;
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ if (is_stroke_affected_by_modifier(ob,
+ lmd->layername,
+ lmd->material,
+ lmd->pass_index,
+ lmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ lmd->flag & GP_LENGTH_INVERT_LAYER,
+ lmd->flag & GP_LENGTH_INVERT_PASS,
+ lmd->flag & GP_LENGTH_INVERT_LAYERPASS,
+ lmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
+ applyLength(lmd, gpd, gps);
+ }
+}
+
+static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ LengthGpencilModifierData *mmd = (LengthGpencilModifierData *)md;
+
+ walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+ uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+
+ uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE);
+ uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE);
+
+ uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, ptr);
+}
+
+static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Length, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Length = {
+ /* name */ "Length",
+ /* structName */ "LengthGpencilModifierData",
+ /* structSize */ sizeof(LengthGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ deformStroke,
+ /* generateStrokes */ NULL,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ initData,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index c1a791d460b..cc79810d2a2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -345,7 +345,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
if (use_multiple_levels) {
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE);
- uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "level_end", 0, IFACE_("End"), ICON_NONE);
}
else {
uiItemR(layout, ptr, "level_start", 0, IFACE_("Level"), ICON_NONE);
@@ -377,7 +377,7 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetPropDecorate(row, false);
- uiLayout *sub = uiLayoutRow(row, true);
+ uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks"));
char text[2] = "0";
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_transparency_mask");
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index bffb324f07f..cd29a006aae 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -26,7 +26,11 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
@@ -71,7 +75,7 @@ static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
Object *ob,
bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
+ bGPDframe *gpf,
bGPDstroke *gps)
{
OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
@@ -94,6 +98,46 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_OFFSET_INVERT_MATERIAL)) {
return;
}
+
+ int seed = mmd->seed;
+ /* Make sure different modifiers get different seeds. */
+ seed += BLI_hash_string(ob->id.name + 2);
+ seed += BLI_hash_string(md->name);
+
+ float rand[3][3];
+ float rand_offset = BLI_hash_int_01(seed);
+
+ /* Get stroke index for random offset. */
+ int rnd_index = BLI_findindex(&gpf->strokes, gps);
+ for (int j = 0; j < 3; j++) {
+ const uint primes[3] = {2, 3, 7};
+ double offset[3] = {0.0f, 0.0f, 0.0f};
+ double r[3];
+ /* To ensure a nice distribution, we use halton sequence and offset using the seed. */
+ BLI_halton_3d(primes, offset, rnd_index, r);
+
+ if ((mmd->flag & GP_OFFSET_UNIFORM_RANDOM_SCALE) && j == 2) {
+ float rand_value;
+ rand_value = fmodf(r[0] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand_value = fmodf(sin(rand_value * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ copy_v3_fl(rand[j], rand_value);
+ }
+ else {
+ for (int i = 0; i < 3; i++) {
+ rand[j][i] = fmodf(r[i] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand[j][i] = fmodf(sin(rand[j][i] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ }
+ }
+ }
+ /* Calculate Random matrix. */
+ float mat_rnd[4][4];
+ float rnd_loc[3], rnd_rot[3];
+ float rnd_scale[3] = {1.0f, 1.0f, 1.0f};
+ mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rand[0]);
+ mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rand[1]);
+ madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rand[2]);
+ loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale);
+
bGPdata *gpd = ob->data;
for (int i = 0; i < gps->totpoints; i++) {
@@ -106,6 +150,9 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
+ /* Apply randomness matrix. */
+ mul_m4_v3(mat_rnd, &pt->x);
+
/* Calculate matrix. */
mul_v3_v3fl(loc, mmd->loc, weight);
mul_v3_v3fl(rot, mmd->rot, weight);
@@ -161,6 +208,21 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void random_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE);
+ uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE);
+ uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
gpencil_modifier_masking_panel_draw(panel, true, true);
@@ -171,6 +233,8 @@ static void panelRegister(ARegionType *region_type)
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Offset, panel_draw);
gpencil_modifier_subpanel_register(
+ region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index c193fc49362..ea37558fa7f 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -47,6 +47,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -84,6 +86,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity);
}
+static float give_opacity_fading_factor(OpacityGpencilModifierData *mmd,
+ Object *ob_this,
+ float *pos,
+ bool apply_obmat)
+{
+ float factor_depth = 1.0f;
+
+ if (((mmd->flag & GP_OPACITY_FADING) == 0) || ((mmd->object) == NULL)) {
+ return factor_depth;
+ }
+
+ float gvert[3];
+ if (apply_obmat) {
+ mul_v3_m4v3(gvert, ob_this->obmat, pos);
+ }
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+
+ return factor_depth;
+}
+
/* opacity strokes */
static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
@@ -138,6 +173,9 @@ static void deformStroke(GpencilModifierData *md,
factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
}
+ float factor_depth = give_opacity_fading_factor(mmd, ob, &pt->x, true);
+ factor_curve = interpf(factor_curve, mmd->fading_end_factor, factor_depth);
+
if (def_nr < 0) {
if (mmd->flag & GP_OPACITY_NORMALIZE) {
pt->strength = factor_curve;
@@ -167,6 +205,10 @@ static void deformStroke(GpencilModifierData *md,
/* Fill using opacity factor. */
if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) {
gps->fill_opacity_fac = mmd->factor;
+
+ float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true);
+ gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth);
+
CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
}
}
@@ -201,6 +243,18 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -228,6 +282,20 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
@@ -266,6 +334,9 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Opacity, panel_draw);
+
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
@@ -289,7 +360,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index a13f8d64755..512e3af063a 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -43,6 +43,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -128,6 +130,30 @@ static void deformStroke(GpencilModifierData *md,
}
float curvef = 1.0f;
+
+ float factor_depth = 1.0f;
+
+ if (mmd->flag & GP_THICK_FADING) {
+ if (mmd->object) {
+ float gvert[3];
+ mul_v3_m4v3(gvert, ob->obmat, &pt->x);
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+ }
+ }
+
if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) {
/* Normalize value to evaluate curve. */
float value = (float)i / (gps->totpoints - 1);
@@ -144,6 +170,9 @@ static void deformStroke(GpencilModifierData *md,
weight *= curvef;
}
+ float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac;
+ target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth);
+
pt->pressure = interpf(target, pt->pressure, weight);
CLAMP_MIN(pt->pressure, 0.0f);
@@ -171,6 +200,32 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+}
+
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -202,6 +257,8 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Thick, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(region_type,
@@ -229,7 +286,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 56cd7fa1456..44ff0616fe9 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -322,9 +322,11 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
} eLineartTriangleFlags;
-/** Controls how many edges a worker thread is processing at one request.
+/**
+ * Controls how many edges a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
- * Don't make it too small so that the worker thread won't request too many times. */
+ * Don't make it too small so that the worker thread won't request too many times.
+ */
#define LRT_THREAD_EDGE_COUNT 1000
typedef struct LineartRenderTaskInfo {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 464316b6a10..4ad8eed6ddd 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -74,7 +74,7 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
static LineartLineChain *lineart_chain_create(LineartRenderBuffer *rb)
{
LineartLineChain *rlc;
- rlc = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChain));
+ rlc = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChain));
BLI_addtail(&rb->chains, rlc);
@@ -119,7 +119,7 @@ static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
return old_rlci;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
copy_v2_v2(rlci->pos, fbcoord);
copy_v3_v3(rlci->gpos, gpos);
@@ -149,7 +149,7 @@ static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb
return rlc->chain.first;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
copy_v2_v2(rlci->pos, fbcoord);
copy_v3_v3(rlci->gpos, gpos);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index e9cb8453f43..52f7d3652a7 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -134,8 +134,8 @@ static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb)
BLI_spin_unlock(&rb->lock_cuts);
/* Otherwise allocate some new memory. */
- return (LineartLineSegment *)lineart_mem_aquire_thread(&rb->render_data_pool,
- sizeof(LineartLineSegment));
+ return (LineartLineSegment *)lineart_mem_acquire_thread(&rb->render_data_pool,
+ sizeof(LineartLineSegment));
}
/**
@@ -631,8 +631,8 @@ static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBu
/* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles
* are relatively small. */
- LineartTriangle *render_triangles = lineart_mem_aquire(&rb->render_data_pool,
- 64 * rb->triangle_size);
+ LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool,
+ 64 * rb->triangle_size);
reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
&rb->render_data_pool,
@@ -648,8 +648,8 @@ static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer
{
LineartElementLinkNode *reln;
- LineartVert *render_vertices = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartVert) * 64);
+ LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartVert) * 64);
reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
&rb->render_data_pool,
@@ -665,7 +665,7 @@ static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer
{
LineartElementLinkNode *reln;
- LineartEdge *render_edges = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
+ LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
&rb->render_data_pool,
@@ -755,7 +755,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
e = new_e; \
e->v1_obindex = v1_obi; \
e->v2_obindex = v2_obi; \
- rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \
+ rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \
BLI_addtail(&e->segments, rls);
#define SELECT_RL(e_num, v1_link, v2_link, newrt) \
@@ -1576,8 +1576,8 @@ static void lineart_geometry_object_load(Depsgraph *dg,
/* Only allocate memory for verts and tris as we don't know how many lines we will generate
* yet. */
- orv = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
- ort = lineart_mem_aquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
+ orv = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
+ ort = lineart_mem_acquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
@@ -1675,7 +1675,7 @@ static void lineart_geometry_object_load(Depsgraph *dg,
e->head.hflag = eflag;
}
- o_la_e = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
+ o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
reln = lineart_list_append_pointer_pool_sized(
&rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
reln->element_count = allocate_la_e;
@@ -1707,8 +1707,8 @@ static void lineart_geometry_object_load(Depsgraph *dg,
la_e->flags = e->head.hflag;
la_e->object_ref = orig_ob;
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartLineSegment));
+ LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartLineSegment));
BLI_addtail(&la_e->segments, rls);
if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) {
lineart_add_edge_to_list(rb, la_e);
@@ -2344,7 +2344,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
/* This is an intersection vert, the size is bigger than LineartVert,
* allocated separately. */
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
/* Indicate the data structure difference. */
result->flag = LRT_VERT_HAS_INTERSECTION_DATA;
@@ -2388,7 +2388,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
LineartVert *new_share;
lineart_triangle_get_other_verts(rt, share, &sv1, &sv2);
- v1 = new_share = lineart_mem_aquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
+ v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
new_share->flag = LRT_VERT_HAS_INTERSECTION_DATA;
@@ -2487,13 +2487,13 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
((LineartVertIntersection *)v1)->intersecting_with = rt;
((LineartVertIntersection *)v2)->intersecting_with = testing;
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge));
result->v1 = v1;
result->v2 = v2;
result->t1 = rt;
result->t2 = testing;
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment));
+ LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment));
BLI_addtail(&result->segments, rls);
/* Don't need to OR flags right now, just a type mark. */
result->flags = LRT_EDGE_FLAG_INTERSECTION;
@@ -2729,7 +2729,7 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
rb->height_per_tile = span_h;
rb->bounding_area_count = sp_w * sp_h;
- rb->initial_bounding_areas = lineart_mem_aquire(
+ rb->initial_bounding_areas = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count);
/* Initialize tiles. */
@@ -2923,8 +2923,8 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
LineartBoundingArea *root,
int recursive_level)
{
- LineartBoundingArea *ba = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartBoundingArea) * 4);
+ LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartBoundingArea) * 4);
LineartTriangle *rt;
LineartEdge *e;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 2c3130b46c9..47ca6e45bd5 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -54,8 +54,8 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
size_t size);
-void *lineart_mem_aquire(struct LineartStaticMemPool *smp, size_t size);
-void *lineart_mem_aquire_thread(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size);
void lineart_mem_destroy(struct LineartStaticMemPool *smp);
void lineart_prepend_edge_direct(struct LineartEdge **first, void *node);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
index 4d136fe0d0e..dcb1f9cde5d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
@@ -43,7 +43,7 @@ void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, v
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, sizeof(LinkData));
+ lip = lineart_mem_acquire(smp, sizeof(LinkData));
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -57,7 +57,7 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h,
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, size);
+ lip = lineart_mem_acquire(smp, size);
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -92,7 +92,7 @@ LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp,
BLI_addhead(&smp->pools, smpn);
return smpn;
}
-void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire(LineartStaticMemPool *smp, size_t size)
{
LineartStaticMemPoolNode *smpn = smp->pools.first;
void *ret;
@@ -107,7 +107,7 @@ void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
return ret;
}
-void *lineart_mem_aquire_thread(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire_thread(LineartStaticMemPool *smp, size_t size)
{
void *ret;
@@ -144,7 +144,7 @@ void lineart_prepend_edge_direct(LineartEdge **first, void *node)
void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
{
- LinkNode *ln = lineart_mem_aquire_thread(smp, sizeof(LinkNode));
+ LinkNode *ln = lineart_mem_acquire_thread(smp, sizeof(LinkNode));
ln->next = (*first);
ln->link = link;
(*first) = ln;
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 2ff72266a64..f1ffd7827b8 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -275,6 +275,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_anisotropic.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_attribute.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_background.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bevel.glsl SRC)
+data_to_c_simple(shaders/material/gpu_shader_material_wavelength.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_blackbody.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bright_contrast.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bump.glsl SRC)
diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h
index b95053a3715..f54ecece659 100644
--- a/source/blender/gpu/GPU_capabilities.h
+++ b/source/blender/gpu/GPU_capabilities.h
@@ -37,6 +37,15 @@ int GPU_max_textures(void);
int GPU_max_textures_vert(void);
int GPU_max_textures_geom(void);
int GPU_max_textures_frag(void);
+int GPU_max_uniforms_vert(void);
+int GPU_max_uniforms_frag(void);
+int GPU_max_batch_indices(void);
+int GPU_max_batch_vertices(void);
+int GPU_max_vertex_attribs(void);
+int GPU_max_varying_floats(void);
+
+int GPU_extensions_len(void);
+const char *GPU_extension_get(int i);
int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size);
diff --git a/source/blender/gpu/GPU_common.h b/source/blender/gpu/GPU_common.h
index 1be74701176..c00bf581e8c 100644
--- a/source/blender/gpu/GPU_common.h
+++ b/source/blender/gpu/GPU_common.h
@@ -24,6 +24,7 @@
#pragma once
#define PROGRAM_NO_OPTI 0
+//#define GPU_NO_USE_PY_REFERENCES
#if defined(NDEBUG)
# define TRUST_NO_ONE 0
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index af94c1fb0e6..6482d9a9d3e 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
void (*callback)(void *userData, int level),
void *userData);
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb);
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref);
+#endif
+
void GPU_framebuffer_push(GPUFrameBuffer *fb);
GPUFrameBuffer *GPU_framebuffer_pop(void);
uint GPU_framebuffer_stack_level_get(void);
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index c457b829bf7..fa7d5d7fba8 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -68,6 +68,9 @@ extern "C" {
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
eGPUSupportLevel GPU_platform_support_level(void);
+const char *GPU_platform_vendor(void);
+const char *GPU_platform_renderer(void);
+const char *GPU_platform_version(void);
const char *GPU_platform_support_level_key(void);
const char *GPU_platform_gpu_name(void);
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index d9a01663de0..f980c8fdcd7 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex);
bool GPU_texture_depth(const GPUTexture *tex);
bool GPU_texture_stencil(const GPUTexture *tex);
bool GPU_texture_integer(const GPUTexture *tex);
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex);
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref);
+#endif
+
int GPU_texture_opengl_bindcode(const GPUTexture *tex);
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);
diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index 6d9182dcf17..d8764502800 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -82,6 +82,46 @@ int GPU_max_textures(void)
return GCaps.max_textures;
}
+int GPU_max_uniforms_vert(void)
+{
+ return GCaps.max_uniforms_vert;
+}
+
+int GPU_max_uniforms_frag(void)
+{
+ return GCaps.max_uniforms_frag;
+}
+
+int GPU_max_batch_indices(void)
+{
+ return GCaps.max_batch_indices;
+}
+
+int GPU_max_batch_vertices(void)
+{
+ return GCaps.max_batch_vertices;
+}
+
+int GPU_max_vertex_attribs(void)
+{
+ return GCaps.max_vertex_attribs;
+}
+
+int GPU_max_varying_floats(void)
+{
+ return GCaps.max_varying_floats;
+}
+
+int GPU_extensions_len(void)
+{
+ return GCaps.extensions_len;
+}
+
+const char *GPU_extension_get(int i)
+{
+ return GCaps.extension_get ? GCaps.extension_get(i) : "\0";
+}
+
bool GPU_mip_render_workaround(void)
{
return GCaps.mip_render_workaround;
diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh
index 2b3292749f8..7c1d4590ce8 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -41,6 +41,15 @@ struct GPUCapabilities {
int max_textures_vert = 0;
int max_textures_geom = 0;
int max_textures_frag = 0;
+ int max_uniforms_vert = 0;
+ int max_uniforms_frag = 0;
+ int max_batch_indices = 0;
+ int max_batch_vertices = 0;
+ int max_vertex_attribs = 0;
+ int max_varying_floats = 0;
+ int extensions_len = 0;
+ const char *(*extension_get)(int);
+
bool mem_stats_support = false;
bool shader_image_load_store_support = false;
/* OpenGL related workarounds. */
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index a9d32dcf297..b5a437b46f7 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -24,7 +24,7 @@
* Use these instead of glGenBuffers & its friends
* - alloc must be called from a thread that is bound
* to the context that will be used for drawing with
- * this vao.
+ * this VAO.
* - free can be called from any thread
*/
diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc
index 63e7024b74b..2d9fb7822ae 100644
--- a/source/blender/gpu/intern/gpu_debug.cc
+++ b/source/blender/gpu/intern/gpu_debug.cc
@@ -86,8 +86,8 @@ bool GPU_debug_group_match(const char *ref)
if (ctx == nullptr) {
return false;
}
- DebugStack &stack = ctx->debug_stack;
- for (StringRef &name : stack) {
+ const DebugStack &stack = ctx->debug_stack;
+ for (const StringRef &name : stack) {
if (name == ref) {
return true;
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 1e3cf479462..1293cc0953d 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer()
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
/** \} */
@@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb,
unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData);
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb)
+{
+ return unwrap(gpu_fb)->py_ref;
+}
+
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
+ unwrap(gpu_fb)->py_ref = py_ref;
+}
+#endif
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index d63d72cf4f7..2b00c239af4 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -100,6 +100,20 @@ class FrameBuffer {
bool scissor_test_ = false;
bool dirty_state_ = true;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ public:
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer.fb
+ */
+ void **py_ref = nullptr;
+#endif
+
+ public:
+ /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer::fb */
+ void **ref = nullptr;
+
public:
FrameBuffer(const char *name);
virtual ~FrameBuffer();
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 175facc0a8d..3c216c1a991 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -47,6 +47,7 @@ extern char datatoc_gpu_shader_material_anisotropic_glsl[];
extern char datatoc_gpu_shader_material_attribute_glsl[];
extern char datatoc_gpu_shader_material_background_glsl[];
extern char datatoc_gpu_shader_material_bevel_glsl[];
+extern char datatoc_gpu_shader_material_wavelength_glsl[];
extern char datatoc_gpu_shader_material_blackbody_glsl[];
extern char datatoc_gpu_shader_material_bright_contrast_glsl[];
extern char datatoc_gpu_shader_material_bump_glsl[];
@@ -191,6 +192,11 @@ static GPUMaterialLibrary gpu_shader_material_bevel_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_wavelength_library = {
+ .code = datatoc_gpu_shader_material_wavelength_glsl,
+ .dependencies = {NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_blackbody_library = {
.code = datatoc_gpu_shader_material_blackbody_glsl,
.dependencies = {NULL},
@@ -593,6 +599,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_attribute_library,
&gpu_shader_material_background_library,
&gpu_shader_material_bevel_library,
+ &gpu_shader_material_wavelength_library,
&gpu_shader_material_blackbody_library,
&gpu_shader_material_bright_contrast_library,
&gpu_shader_material_bump_library,
diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 2ae50d913da..569b51a407a 100644
--- a/source/blender/gpu/intern/gpu_matrix.cc
+++ b/source/blender/gpu/intern/gpu_matrix.cc
@@ -543,7 +543,7 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
&precalc->dims.zmax);
if (isinf(precalc->dims.zmax)) {
/* We cannot retrieve the actual value of the clip_end.
- * Use `FLT_MAX` to avoid nans. */
+ * Use `FLT_MAX` to avoid NAN's. */
precalc->dims.zmax = FLT_MAX;
}
for (int i = 0; i < 4; i++) {
diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc
index 6b9878f2ba4..49dde473300 100644
--- a/source/blender/gpu/intern/gpu_platform.cc
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -41,10 +41,10 @@ namespace blender::gpu {
GPUPlatformGlobal GPG;
-void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_key(eGPUSupportLevel support_level,
+ const char *vendor,
+ const char *renderer,
+ const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "{%s/%s/%s}=", vendor, renderer, version);
@@ -58,29 +58,56 @@ void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
BLI_dynstr_append(ds, "UNSUPPORTED");
}
- support_key = BLI_dynstr_get_cstring(ds);
+ char *support_key = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(support_key, '\n', ' ');
BLI_str_replace_char(support_key, '\r', ' ');
+ return support_key;
}
-void GPUPlatformGlobal::create_gpu_name(const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_gpu_name(const char *vendor, const char *renderer, const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "%s %s %s", vendor, renderer, version);
- gpu_name = BLI_dynstr_get_cstring(ds);
+ char *gpu_name = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(gpu_name, '\n', ' ');
BLI_str_replace_char(gpu_name, '\r', ' ');
+ return gpu_name;
+}
+
+void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str)
+{
+ this->clear();
+
+ this->initialized = true;
+
+ this->device = gpu_device;
+ this->os = os_type;
+ this->driver = driver_type;
+ this->support_level = gpu_support_level;
+
+ this->vendor = BLI_strdup(vendor_str);
+ this->renderer = BLI_strdup(renderer_str);
+ this->version = BLI_strdup(version_str);
+ this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str);
+ this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str);
}
void GPUPlatformGlobal::clear()
{
- MEM_SAFE_FREE(GPG.support_key);
- MEM_SAFE_FREE(GPG.gpu_name);
+ MEM_SAFE_FREE(vendor);
+ MEM_SAFE_FREE(renderer);
+ MEM_SAFE_FREE(version);
+ MEM_SAFE_FREE(support_key);
+ MEM_SAFE_FREE(gpu_name);
initialized = false;
}
@@ -96,22 +123,44 @@ using namespace blender::gpu;
eGPUSupportLevel GPU_platform_support_level()
{
+ BLI_assert(GPG.initialized);
return GPG.support_level;
}
-const char *GPU_platform_support_level_key()
+const char *GPU_platform_vendor(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.vendor;
+}
+
+const char *GPU_platform_renderer(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.renderer;
+}
+
+const char *GPU_platform_version(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.version;
+}
+
+const char *GPU_platform_support_level_key(void)
{
+ BLI_assert(GPG.initialized);
return GPG.support_key;
}
const char *GPU_platform_gpu_name(void)
{
+ BLI_assert(GPG.initialized);
return GPG.gpu_name;
}
/* GPU Types */
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
{
+ BLI_assert(GPG.initialized);
return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver);
}
diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh
index 02d99efa4a9..f823269ab54 100644
--- a/source/blender/gpu/intern/gpu_platform_private.hh
+++ b/source/blender/gpu/intern/gpu_platform_private.hh
@@ -34,16 +34,20 @@ class GPUPlatformGlobal {
eGPUOSType os;
eGPUDriverType driver;
eGPUSupportLevel support_level;
+ char *vendor = nullptr;
+ char *renderer = nullptr;
+ char *version = nullptr;
char *support_key = nullptr;
char *gpu_name = nullptr;
public:
- void create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version);
-
- void create_gpu_name(const char *vendor, const char *renderer, const char *version);
+ void init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str);
void clear(void);
};
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index 75fe7652715..aea27756708 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -723,7 +723,7 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons
static int g_shader_builtin_srgb_transform = 0;
static bool g_shader_builtin_srgb_is_dirty = false;
-static bool gpu_shader_srgb_uniform_dirty_get(void)
+static bool gpu_shader_srgb_uniform_dirty_get()
{
return g_shader_builtin_srgb_is_dirty;
}
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index 782d25747bb..5c33066c720 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -248,7 +248,7 @@ eGPUWriteMask GPU_write_mask_get()
uint GPU_stencil_mask_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.stencil_write_mask;
}
@@ -267,7 +267,7 @@ eGPUStencilTest GPU_stencil_test_get()
/* NOTE: Already premultiplied by U.pixelsize. */
float GPU_line_width_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.line_width;
}
@@ -292,7 +292,7 @@ void GPU_viewport_size_get_i(int coords[4])
bool GPU_depth_mask_get()
{
- GPUState &state = Context::get()->state_manager->state;
+ const GPUState &state = Context::get()->state_manager->state;
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
}
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index c3e9058c6c7..997064e82a2 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -60,6 +60,12 @@ Texture::~Texture()
fb_[i]->attachment_remove(fb_attachment_[i]);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
@@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex)
return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0;
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex)
+{
+ return unwrap(tex)->py_ref;
+}
+
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr);
+ unwrap(tex)->py_ref = py_ref;
+}
+#endif
+
/* TODO remove */
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
{
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 3d808bce152..a8f2e482bdd 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -80,6 +80,13 @@ class Texture {
int refcount = 1;
/** Width & Height (of source data), optional. */
int src_w = 0, src_h = 0;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the texture.
+ * Points to #BPyGPUTexture.tex
+ */
+ void **py_ref = nullptr;
+#endif
protected:
/* ---- Texture format (immutable after init). ---- */
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index f31e0e05a44..31b6549fc3b 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -41,39 +41,42 @@ namespace blender::gpu {
void GLBackend::platform_init()
{
BLI_assert(!GPG.initialized);
- GPG.initialized = true;
+
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+ const char *version = (const char *)glGetString(GL_VERSION);
+ eGPUDeviceType device = GPU_DEVICE_ANY;
+ eGPUOSType os = GPU_OS_ANY;
+ eGPUDriverType driver = GPU_DRIVER_ANY;
+ eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
#ifdef _WIN32
- GPG.os = GPU_OS_WIN;
+ os = GPU_OS_WIN;
#elif defined(__APPLE__)
- GPG.os = GPU_OS_MAC;
+ os = GPU_OS_MAC;
#else
- GPG.os = GPU_OS_UNIX;
+ os = GPU_OS_UNIX;
#endif
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- const char *renderer = (const char *)glGetString(GL_RENDERER);
- const char *version = (const char *)glGetString(GL_VERSION);
-
if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "NVIDIA")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "Intel") ||
/* src/mesa/drivers/dri/intel/intel_context.c */
strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) {
- GPG.device = GPU_DEVICE_INTEL;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_INTEL;
+ driver = GPU_DRIVER_OFFICIAL;
if (strstr(renderer, "UHD Graphics") ||
/* Not UHD but affected by the same bugs. */
strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") ||
strstr(renderer, "Whiskey Lake")) {
- GPG.device |= GPU_DEVICE_INTEL_UHD;
+ device |= GPU_DEVICE_INTEL_UHD;
}
}
else if ((strstr(renderer, "Mesa DRI R")) ||
@@ -81,49 +84,47 @@ void GLBackend::platform_init()
(strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(vendor, "Mesa")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Microsoft")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Apple")) {
/* Apple Silicon. */
- GPG.device = GPU_DEVICE_APPLE;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_APPLE;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(renderer, "Apple Software Renderer")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else {
printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
printf("Detected OpenGL configuration:\n");
printf("Vendor: %s\n", vendor);
printf("Renderer: %s\n", renderer);
- GPG.device = GPU_DEVICE_ANY;
- GPG.driver = GPU_DRIVER_ANY;
}
/* Detect support level */
if (!GLEW_VERSION_3_3) {
- GPG.support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
+ support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
}
else {
- if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_INTEL) && (os & GPU_OS_WIN)) {
/* Old Intel drivers with known bugs that cause material properties to crash.
* Version Build 10.18.14.5067 is the latest available and appears to be working
* ok with our workarounds, so excluded from this list. */
@@ -132,19 +133,19 @@ void GLBackend::platform_init()
strstr(version, "Build 9.18") || strstr(version, "Build 10.18.10.3") ||
strstr(version, "Build 10.18.10.4") || strstr(version, "Build 10.18.10.5") ||
strstr(version, "Build 10.18.14.4")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_ATI) && (os & GPU_OS_UNIX)) {
/* Platform seems to work when SB backend is disabled. This can be done
* by adding the environment variable `R600_DEBUG=nosb`. */
if (strstr(renderer, "AMD CEDAR")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
}
- GPG.create_key(GPG.support_level, vendor, renderer, version);
- GPG.create_gpu_name(vendor, renderer, version);
+
+ GPG.init(device, os, driver, support_level, vendor, renderer, version);
}
void GLBackend::platform_exit()
@@ -204,6 +205,11 @@ static bool detect_mip_render_workaround()
return enable_workaround;
}
+static const char *gl_extension_get(int i)
+{
+ return (char *)glGetStringi(GL_EXTENSIONS, i);
+}
+
static void detect_workarounds()
{
const char *vendor = (const char *)glGetString(GL_VENDOR);
@@ -419,6 +425,16 @@ void GLBackend::capabilities_init()
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_vert);
glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_geom);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &GCaps.max_textures);
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &GCaps.max_uniforms_vert);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &GCaps.max_uniforms_frag);
+ glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GCaps.max_batch_indices);
+ glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GCaps.max_batch_vertices);
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &GCaps.max_vertex_attribs);
+ glGetIntegerv(GL_MAX_VARYING_FLOATS, &GCaps.max_varying_floats);
+
+ glGetIntegerv(GL_NUM_EXTENSIONS, &GCaps.extensions_len);
+ GCaps.extension_get = gl_extension_get;
+
GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo;
GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store;
/* GL specific capabilities. */
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index 976a31ab5cb..fc1d1006d0d 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -44,7 +44,7 @@
using namespace blender::gpu;
/* -------------------------------------------------------------------- */
-/** \name Vao cache
+/** \name VAO Cache
*
* Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
* TODO(fclem): Could be revisited to avoid so much cross references.
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index 444ae1c0ab1..704b6471dd5 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -43,7 +43,7 @@ class GLShaderInterface;
#define GPU_VAO_STATIC_LEN 3
-/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
+/* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
* for each shader interface. Start with a static number of vaos and fallback to dynamic count
* if necessary. Once a batch goes dynamic it does not go back. */
class GLVaoCache {
diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh
index d0bf0fcdf1f..b42f439f9ec 100644
--- a/source/blender/gpu/opengl/gl_immediate.hh
+++ b/source/blender/gpu/opengl/gl_immediate.hh
@@ -38,7 +38,7 @@ namespace blender::gpu {
class GLImmediate : public Immediate {
private:
- /* Use two buffers for strict and unstrict vertex count to
+ /* Use two buffers for strict and non-strict vertex count to
* avoid some huge driver slowdown (see T70922).
* Use accessor functions to get / modify. */
struct {
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index d8d9ecdf287..5745f11ede4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -2,7 +2,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
new file mode 100644
index 00000000000..2c5d38eabbe
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
@@ -0,0 +1,15 @@
+void node_wavelength(float wavelength,
+ sampler1DArray spectrummap,
+ float layer,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ out vec4 color)
+{
+ mat3 xyz_to_rgb = mat3(xyz_to_r, xyz_to_g, xyz_to_b);
+ float t = (wavelength - 380.0) / (780.0 - 380.0);
+ vec3 xyz = texture(spectrummap, vec2(t, layer)).rgb;
+ vec3 rgb = xyz * xyz_to_rgb;
+ rgb *= 1.0 / 2.52; /* Empirical scale from lg to make all comps <= 1. */
+ color = vec4(clamp(rgb, 0.0, 1.0), 1.0);
+}
diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc
index b9fc78dc084..ac42c5875c8 100644
--- a/source/blender/gpu/tests/gpu_testing.cc
+++ b/source/blender/gpu/tests/gpu_testing.cc
@@ -2,6 +2,8 @@
#include "testing/testing.h"
+#include "CLG_log.h"
+
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "gpu_testing.hh"
@@ -13,6 +15,7 @@ namespace blender::gpu {
void GPUTest::SetUp()
{
GHOST_GLSettings glSettings = {0};
+ CLG_init();
ghost_system = GHOST_CreateSystem();
ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings);
context = GPU_context_create(nullptr);
@@ -26,6 +29,7 @@ void GPUTest::TearDown()
GPU_context_discard(context);
GHOST_DisposeOpenGLContext(ghost_system, ghost_context);
GHOST_DisposeSystem(ghost_system);
+ CLG_exit();
}
} // namespace blender::gpu
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index 7ce795280a3..be0e364c85f 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -166,12 +166,6 @@ if(WITH_CODEC_FFMPEG)
${OPENJPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/anim_movie.c
- intern/indexer.c
- intern/util.c
- )
endif()
if(WITH_IMAGE_DDS)
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index f31839751b1..53b0e295385 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -69,9 +69,9 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
-BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]);
-BLI_INLINE void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3]);
-const float *IMB_colormangement_get_xyz_to_rgb(void);
+BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]);
+BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]);
+const float *IMB_colormanagement_get_xyz_to_rgb(void);
/* ** Color space transformation functions ** */
void IMB_colormanagement_transform(float *buffer,
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index 1239d3881de..7d7864306a1 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -135,7 +135,7 @@ struct anim {
struct ImBuf *last_frame;
int64_t last_pts;
int64_t next_pts;
- AVPacket next_packet;
+ AVPacket *next_packet;
#endif
char index_dir[768];
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 96cd1fb61a4..5918a4bf16e 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -79,6 +79,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
+# include <libavutil/imgutils.h>
# include <libavutil/rational.h>
# include <libswscale/swscale.h>
@@ -519,12 +520,10 @@ static int startffmpeg(struct anim *anim)
double frs_den;
int streamcount;
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* The following for color space determination */
int srcRange, dstRange, brightness, contrast, saturation;
int *table;
const int *inv_table;
-# endif
if (anim == NULL) {
return (-1);
@@ -547,7 +546,7 @@ static int startffmpeg(struct anim *anim)
video_stream_index = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -563,16 +562,17 @@ static int startffmpeg(struct anim *anim)
}
video_stream = pFormatCtx->streams[video_stream_index];
- pCodecCtx = video_stream->codec;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return -1;
}
- pCodecCtx->workaround_bugs = 1;
+ pCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
+ pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
pCodecCtx->thread_count = 0;
@@ -593,7 +593,7 @@ static int startffmpeg(struct anim *anim)
return -1;
}
if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&pFormatCtx);
return -1;
}
@@ -639,7 +639,7 @@ static int startffmpeg(struct anim *anim)
anim->params = 0;
anim->x = pCodecCtx->width;
- anim->y = av_get_cropped_height_from_codec(pCodecCtx);
+ anim->y = pCodecCtx->height;
anim->pFormatCtx = pFormatCtx;
anim->pCodecCtx = pCodecCtx;
@@ -654,7 +654,8 @@ static int startffmpeg(struct anim *anim)
anim->last_frame = 0;
anim->last_pts = -1;
anim->next_pts = -1;
- anim->next_packet.stream_index = -1;
+ anim->next_packet = av_packet_alloc();
+ anim->next_packet->stream_index = -1;
anim->pFrame = av_frame_alloc();
anim->pFrameComplete = false;
@@ -668,8 +669,9 @@ static int startffmpeg(struct anim *anim)
if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->next_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -678,10 +680,11 @@ static int startffmpeg(struct anim *anim)
}
}
- if (avpicture_get_size(AV_PIX_FMT_RGBA, anim->x, anim->y) != anim->x * anim->y * 4) {
+ if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) {
fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->next_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -690,14 +693,17 @@ static int startffmpeg(struct anim *anim)
}
if (anim->ib_flags & IB_animdeinterlace) {
- avpicture_fill((AVPicture *)anim->pFrameDeinterlaced,
- MEM_callocN(avpicture_get_size(anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height),
- "ffmpeg deinterlace"),
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height);
+ av_image_fill_arrays(anim->pFrameDeinterlaced->data,
+ anim->pFrameDeinterlaced->linesize,
+ MEM_callocN(av_image_get_buffer_size(anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1),
+ "ffmpeg deinterlace"),
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1);
}
if (pCodecCtx->has_b_frames) {
@@ -720,8 +726,9 @@ static int startffmpeg(struct anim *anim)
if (!anim->img_convert_ctx) {
fprintf(stderr, "Can't transform color space??? Bailing out...\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->next_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -729,7 +736,6 @@ static int startffmpeg(struct anim *anim)
return -1;
}
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* Try do detect if input has 0-255 YCbCR range (JFIF Jpeg MotionJpeg) */
if (!sws_getColorspaceDetails(anim->img_convert_ctx,
(int **)&inv_table,
@@ -756,7 +762,6 @@ static int startffmpeg(struct anim *anim)
else {
fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n");
}
-# endif
return 0;
}
@@ -795,11 +800,11 @@ static void ffmpeg_postprocess(struct anim *anim)
input->data[3]);
if (anim->ib_flags & IB_animdeinterlace) {
- if (avpicture_deinterlace((AVPicture *)anim->pFrameDeinterlaced,
- (const AVPicture *)anim->pFrame,
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height) < 0) {
+ if (av_image_deinterlace(anim->pFrameDeinterlaced,
+ anim->pFrame,
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height) < 0) {
filter_y = true;
}
else {
@@ -808,16 +813,18 @@ static void ffmpeg_postprocess(struct anim *anim)
}
if (!need_aligned_ffmpeg_buffer(anim)) {
- avpicture_fill((AVPicture *)anim->pFrameRGB,
- (unsigned char *)ibuf->rect,
- AV_PIX_FMT_RGBA,
- anim->x,
- anim->y);
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ (unsigned char *)ibuf->rect,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
}
# if defined(__x86_64__) || defined(_M_X64)
/* Scale and flip image over Y axis in one go, using negative strides.
- * This doesn't work with arm/powerpc though and may be misusing the API.
+ * This doesn't work with ARM/PowerPC though and may be misusing the API.
* Limit it x86_64 where it appears to work.
* http://trac.ffmpeg.org/ticket/9060 */
int *dstStride = anim->pFrameRGB->linesize;
@@ -903,82 +910,70 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ if (anim->next_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->next_packet);
+ anim->next_packet->stream_index = -1;
}
- while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
+ while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
- (anim->next_packet.stream_index == anim->videoStream) ? "->" : " ",
- anim->next_packet.stream_index,
+ (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ",
+ anim->next_packet->stream_index,
anim->videoStream,
- (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts,
- (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts,
- (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
- if (anim->next_packet.stream_index == anim->videoStream) {
+ (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts,
+ (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts,
+ (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
+ if (anim->next_packet->stream_index == anim->videoStream) {
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, anim->next_packet);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
(int64_t)anim->next_pts);
break;
}
}
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->next_packet);
+ anim->next_packet->stream_index = -1;
}
if (rval == AVERROR_EOF) {
- /* this sets size and data fields to zero,
- * which is necessary to decode the remaining data
- * in the decoder engine after EOF. It also prevents a memory
- * leak, since av_read_frame spills out a full size packet even
- * on EOF... (and: it's safe to call on NULL packets) */
-
- av_free_packet(&anim->next_packet);
-
- anim->next_packet.size = 0;
- anim->next_packet.data = 0;
-
+ /* Flush any remaining frames out of the decoder. */
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, NULL);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE (after EOF): next_pts=%" PRId64 " pkt_pts=%" PRId64
- ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
(int64_t)anim->next_pts);
rval = 0;
}
}
if (rval < 0) {
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->next_packet);
+ anim->next_packet->stream_index = -1;
av_log(anim->pFormatCtx,
AV_LOG_ERROR,
" DECODE READ FAILED: av_read_frame() "
- "returned error: %d\n",
- rval);
+ "returned error: %s\n",
+ av_err2str(rval));
}
return (rval >= 0);
@@ -1128,7 +1123,7 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
}
}
-/* Wrapper over av_seek_frame(), for formats that doesn't have it's own read_seek() or read_seek2()
+/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or read_seek2()
* functions defined. When seeking in these formats, rule to seek to last necessary I-frame is not
* honored. It is not even guaranteed that I-frame, that must be decoded will be read. See
* https://trac.ffmpeg.org/ticket/1607 and https://developer.blender.org/T86944. */
@@ -1154,7 +1149,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p
/* Read first video stream packet. */
AVPacket read_packet = {0};
while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) {
- if (anim->next_packet.stream_index == anim->videoStream) {
+ if (anim->next_packet->stream_index == anim->videoStream) {
break;
}
}
@@ -1246,9 +1241,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
anim->next_pts = -1;
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ if (anim->next_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->next_packet);
+ anim->next_packet->stream_index = -1;
}
/* memset(anim->pFrame, ...) ?? */
@@ -1351,32 +1346,30 @@ static void free_anim_ffmpeg(struct anim *anim)
}
if (anim->pCodecCtx) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->next_packet);
- /* Special case here: pFrame could share pointers with codec,
- * so in order to avoid double-free we don't use av_frame_free()
- * to free the frame.
- *
- * Could it be a bug in FFmpeg?
- */
- av_free(anim->pFrame);
+ av_frame_free(&anim->pFrame);
if (!need_aligned_ffmpeg_buffer(anim)) {
/* If there's no need for own aligned buffer it means that FFmpeg's
* frame shares the same buffer as temporary ImBuf. In this case we
* should not free the buffer when freeing the FFmpeg buffer.
*/
- avpicture_fill((AVPicture *)anim->pFrameRGB, NULL, AV_PIX_FMT_RGBA, anim->x, anim->y);
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ NULL,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
}
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
sws_freeContext(anim->img_convert_ctx);
IMB_freeImBuf(anim->last_frame);
- if (anim->next_packet.stream_index != -1) {
- av_free_packet(&anim->next_packet);
- }
}
anim->duration_in_frames = 0;
}
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index fc0b99a82fa..68d0b516828 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -1409,7 +1409,7 @@ bool IMB_colormanagement_space_name_is_data(const char *name)
return (colorspace && colorspace->is_data);
}
-const float *IMB_colormangement_get_xyz_to_rgb()
+const float *IMB_colormanagement_get_xyz_to_rgb()
{
return &imbuf_xyz_to_rgb[0][0];
}
diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c
index e93fdd51040..c304ad8d8e5 100644
--- a/source/blender/imbuf/intern/colormanagement_inline.c
+++ b/source/blender/imbuf/intern/colormanagement_inline.c
@@ -55,12 +55,12 @@ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
return unit_float_to_uchar_clamp(val);
}
-void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3])
+void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3])
{
mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, xyz);
}
-void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3])
+void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3])
{
mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb);
}
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index ef9f6d861a3..11ce77e3091 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -48,6 +48,7 @@
#ifdef WITH_FFMPEG
# include "ffmpeg_compat.h"
+# include <libavutil/imgutils.h>
#endif
static const char magic[] = "BlenMIdx";
@@ -488,14 +489,14 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->of = avformat_alloc_context();
rv->of->oformat = av_guess_format("avi", NULL, NULL);
- BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename));
+ rv->of->url = av_strdup(fname);
- fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+ fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url);
rv->st = avformat_new_stream(rv->of, NULL);
rv->st->id = 0;
- rv->c = rv->st->codec;
+ rv->c = avcodec_alloc_context3(NULL);
rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
rv->c->codec_id = AV_CODEC_ID_H264;
rv->c->width = width;
@@ -513,7 +514,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
fprintf(stderr,
"No ffmpeg encoder available? "
"Proxy not built!\n");
- av_free(rv->of);
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
return NULL;
}
@@ -524,7 +527,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
}
- rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
+ rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio;
rv->c->time_base.den = 25;
rv->c->time_base.num = 1;
@@ -557,34 +560,54 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
}
if (rv->of->flags & AVFMT_GLOBALHEADER) {
- rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
- if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+ avcodec_parameters_from_context(rv->st->codecpar, rv->c);
+
+ int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE);
+
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't open outputfile! "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't open IO: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
- avcodec_open2(rv->c, rv->codec, &codec_opts);
+ ret = avcodec_open2(rv->c, rv->codec, &codec_opts);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Couldn't open codec: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
+ }
- rv->orig_height = av_get_cropped_height_from_codec(st->codec);
+ rv->orig_height = st->codecpar->height;
- if (st->codec->width != width || st->codec->height != height ||
- st->codec->pix_fmt != rv->c->pix_fmt) {
+ if (st->codecpar->width != width || st->codecpar->height != height ||
+ st->codecpar->format != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
- avpicture_fill((AVPicture *)rv->frame,
- MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height),
- "alloc proxy output frame"),
- rv->c->pix_fmt,
- round_up(width, 16),
- height);
-
- rv->sws_ctx = sws_getContext(st->codec->width,
+ av_image_fill_arrays(
+ rv->frame->data,
+ rv->frame->linesize,
+ MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt,
+ round_up(width, 16),
+ height,
+ 1);
+
+ rv->sws_ctx = sws_getContext(st->codecpar->width,
rv->orig_height,
- st->codec->pix_fmt,
+ st->codecpar->format,
width,
height,
rv->c->pix_fmt,
@@ -594,26 +617,30 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
NULL);
}
- if (avformat_write_header(rv->of, NULL) < 0) {
+ ret = avformat_write_header(rv->of, NULL);
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't set output parameters? "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't write header: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+
+ if (rv->frame) {
+ av_frame_free(&rv->frame);
+ }
+
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
return rv;
}
-static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
+static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
{
- AVPacket packet = {0};
- int ret, got_output;
-
- av_init_packet(&packet);
-
if (!ctx) {
- return 0;
+ return;
}
if (ctx->sws_ctx && frame &&
@@ -633,35 +660,46 @@ static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fra
frame->pts = ctx->cfra++;
}
- ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output);
+ int ret = avcodec_send_frame(ctx->c, frame);
if (ret < 0) {
- fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename);
- return 0;
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ return;
}
+ AVPacket *packet = av_packet_alloc();
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx->c, packet);
- if (got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
+ break;
}
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error encoding proxy frame %d for '%s': %s\n",
+ ctx->cfra - 1,
+ ctx->of->url,
+ av_err2str(ret));
+ break;
}
- packet.stream_index = ctx->st->index;
+ packet->stream_index = ctx->st->index;
+ av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base);
- if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+ int write_ret = av_interleaved_write_frame(ctx->of, packet);
+ if (write_ret != 0) {
fprintf(stderr,
"Error writing proxy frame %d "
- "into '%s'\n",
+ "into '%s': %s\n",
ctx->cfra - 1,
- ctx->of->filename);
- return 0;
+ ctx->of->url,
+ av_err2str(write_ret));
+ break;
}
-
- return 1;
}
- return 0;
+ av_packet_free(&packet);
}
static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
@@ -674,15 +712,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
}
if (!rollback) {
- while (add_to_proxy_output_ffmpeg(ctx, NULL)) {
- }
+ /* Flush the remaining packets. */
+ add_to_proxy_output_ffmpeg(ctx, NULL);
}
avcodec_flush_buffers(ctx->c);
av_write_trailer(ctx->of);
- avcodec_close(ctx->c);
+ avcodec_free_context(&ctx->c);
if (ctx->of->oformat) {
if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
@@ -777,7 +815,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
/* Find the video stream */
context->videoStream = -1;
for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
- if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -794,9 +832,8 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
}
context->iStream = context->iFormatCtx->streams[context->videoStream];
- context->iCodecCtx = context->iStream->codec;
- context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
+ context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id);
if (context->iCodec == NULL) {
avformat_close_input(&context->iFormatCtx);
@@ -804,7 +841,9 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
return NULL;
}
- context->iCodecCtx->workaround_bugs = 1;
+ context->iCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar);
+ context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
context->iCodecCtx->thread_count = 0;
@@ -822,19 +861,19 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) {
avformat_close_input(&context->iFormatCtx);
+ avcodec_free_context(&context->iCodecCtx);
MEM_freeN(context);
return NULL;
}
for (i = 0; i < num_proxy_sizes; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) {
- context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
- anim,
- context->iStream,
- proxy_sizes[i],
- context->iCodecCtx->width * proxy_fac[i],
- av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i],
- quality);
+ context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim,
+ context->iStream,
+ proxy_sizes[i],
+ context->iCodecCtx->width * proxy_fac[i],
+ context->iCodecCtx->height * proxy_fac[i],
+ quality);
if (!context->proxy_ctx[i]) {
proxy_sizes_in_use &= ~proxy_sizes[i];
}
@@ -873,7 +912,7 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int
}
}
- avcodec_close(context->iCodecCtx);
+ avcodec_free_context(&context->iCodecCtx);
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
@@ -938,23 +977,18 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
short *do_update,
float *progress)
{
- AVFrame *in_frame = 0;
- AVPacket next_packet;
+ AVFrame *in_frame = av_frame_alloc();
+ AVPacket *next_packet = av_packet_alloc();
uint64_t stream_size;
- memset(&next_packet, 0, sizeof(AVPacket));
-
- in_frame = av_frame_alloc();
-
stream_size = avio_size(context->iFormatCtx->pb);
context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
context->pts_time_base = av_q2d(context->iStream->time_base);
- while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) {
- int frame_finished = 0;
+ while (av_read_frame(context->iFormatCtx, next_packet) >= 0) {
float next_progress =
- (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100;
+ (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100;
if (*progress != next_progress) {
*progress = next_progress;
@@ -962,50 +996,59 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
}
if (*stop) {
- av_free_packet(&next_packet);
break;
}
- if (next_packet.stream_index == context->videoStream) {
- if (next_packet.flags & AV_PKT_FLAG_KEY) {
+ if (next_packet->stream_index == context->videoStream) {
+ if (next_packet->flags & AV_PKT_FLAG_KEY) {
context->last_seek_pos = context->seek_pos;
context->last_seek_pos_dts = context->seek_pos_dts;
- context->seek_pos = next_packet.pos;
- context->seek_pos_dts = next_packet.dts;
- context->seek_pos_pts = next_packet.pts;
+ context->seek_pos = next_packet->pos;
+ context->seek_pos_dts = next_packet->dts;
+ context->seek_pos_pts = next_packet->pts;
}
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
- }
+ int ret = avcodec_send_packet(context->iCodecCtx, next_packet);
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret));
+ break;
+ }
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
- av_free_packet(&next_packet);
}
/* process pictures still stuck in decoder engine after EOF
- * according to ffmpeg docs using 0-size packets.
+ * according to ffmpeg docs using NULL packets.
*
* At least, if we haven't already stopped... */
- /* this creates the 0-size packet and prevents a memory leak. */
- av_free_packet(&next_packet);
-
if (!*stop) {
- int frame_finished;
-
- do {
- frame_finished = 0;
+ int ret = avcodec_send_packet(context->iCodecCtx, NULL);
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
}
- } while (frame_finished);
+ if (ret < 0) {
+ fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret));
+ break;
+ }
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
+ av_packet_free(&next_packet);
av_free(in_frame);
return 1;
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 001cd4e1575..12f7553d5e4 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -374,7 +374,7 @@ static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanlin
static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image)
{
- uint16 unit;
+ uint16_t unit;
float xres;
float yres;
@@ -569,7 +569,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
TIFF *image = NULL;
ImBuf *ibuf = NULL, *hbuf;
ImbTIFFMemFile memFile;
- uint32 width, height;
+ uint32_t width, height;
char *format = NULL;
int level;
short spp;
@@ -690,7 +690,7 @@ void imb_loadtiletiff(
ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect)
{
TIFF *image = NULL;
- uint32 width, height;
+ uint32_t width, height;
ImbTIFFMemFile memFile;
image = imb_tiff_client_open(&memFile, mem, size);
@@ -761,7 +761,7 @@ void imb_loadtiletiff(
bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
{
TIFF *image = NULL;
- uint16 samplesperpixel, bitspersample;
+ uint16_t samplesperpixel, bitspersample;
size_t npixels;
unsigned char *pixels = NULL;
unsigned char *from = NULL, *to = NULL;
@@ -774,7 +774,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
/* check for a valid number of bytes per pixel. Like the PNG writer,
* the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
* to gray, RGB, RGBA respectively. */
- samplesperpixel = (uint16)((ibuf->planes + 7) >> 3);
+ samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3);
if ((samplesperpixel > 4) || (samplesperpixel == 2)) {
fprintf(stderr,
"imb_savetiff: unsupported number of bytes per "
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 64dad5de902..1bb047f1317 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -182,7 +182,7 @@ bool IMB_ispic_type_matches(const char *filepath, int filetype)
const ImFileType *type = IMB_file_type_from_ftype(filetype);
if (type != NULL) {
- /* Requesting to load a type that can't check it's own header doesn't make sense.
+ /* Requesting to load a type that can't check its own header doesn't make sense.
* Keep the check for developers. */
BLI_assert(type->is_a != NULL);
if (type->is_a != NULL) {
@@ -245,7 +245,6 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *format, va_lis
void IMB_ffmpeg_init(void)
{
- av_register_all();
avdevice_register_all();
ffmpeg_last_error[0] = '\0';
@@ -269,7 +268,6 @@ static int isffmpeg(const char *filepath)
unsigned int i;
int videoStream;
AVCodec *pCodec;
- AVCodecContext *pCodecCtx;
if (BLI_path_extension_check_n(filepath,
".swf",
@@ -310,8 +308,8 @@ static int isffmpeg(const char *filepath)
/* Find the first video stream */
videoStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codec &&
- (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) {
+ if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codecpar &&
+ (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
videoStream = i;
break;
}
@@ -322,21 +320,15 @@ static int isffmpeg(const char *filepath)
return 0;
}
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
+ AVCodecParameters *codec_par = pFormatCtx->streams[videoStream]->codecpar;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(codec_par->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return 0;
}
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
- avformat_close_input(&pFormatCtx);
- return 0;
- }
-
- avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 1;
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 9785f6d68ab..5664a43233a 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -49,6 +49,7 @@ struct AlembicExportParams {
bool uvs;
bool normals;
bool vcolors;
+ bool orcos;
bool apply_subdiv;
bool curves_as_mesh;
bool flatten_hierarchy;
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index c00e57c8edc..fd7db005dd2 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -200,6 +200,7 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context)
}
m_custom_data_config.pack_uvs = args_.export_params->packuv;
+ m_custom_data_config.mesh = mesh;
m_custom_data_config.mpoly = mesh->mpoly;
m_custom_data_config.mloop = mesh->mloop;
m_custom_data_config.totpoly = mesh->totpoly;
@@ -250,7 +251,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
UVSample uvs_and_indices;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata);
if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) {
@@ -279,6 +280,10 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
mesh_sample.setNormals(normals_sample);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (liquid_sim_modifier_ != nullptr) {
get_velocities(mesh, velocities);
mesh_sample.setVelocities(V3fArraySample(velocities));
@@ -312,7 +317,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
UVSample sample;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
if (!sample.indices.empty() && !sample.uvs.empty()) {
@@ -329,6 +334,10 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (!crease_indices.empty()) {
subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 66e05504303..ccf353595c9 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -22,12 +22,14 @@
*/
#include "abc_customdata.h"
+#include "abc_axis_conversion.h"
#include <Alembic/AbcGeom/All.h>
#include <algorithm>
#include <unordered_map>
#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_math_base.h"
@@ -50,8 +52,13 @@ using Alembic::Abc::V2fArraySample;
using Alembic::AbcGeom::OC4fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
namespace blender::io::alembic {
+/* ORCO, Generated Coordinates, and Reference Points ("Pref") are all terms for the same thing.
+ * Other applications (Maya, Houdini) write these to a property called "Pref". */
+static const std::string propNameOriginalCoordinates("Pref");
+
static void get_uvs(const CDStreamConfig &config,
std::vector<Imath::V2f> &uvs,
std::vector<uint32_t> &uvidx,
@@ -222,6 +229,32 @@ static void write_mcol(const OCompoundProperty &prop,
param.set(sample);
}
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config)
+{
+ const void *customdata = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ if (customdata == nullptr) {
+ /* Data not available, so don't even bother creating an Alembic property for it. */
+ return;
+ }
+ const float(*orcodata)[3] = static_cast<const float(*)[3]>(customdata);
+
+ /* Convert 3D vertices from float[3] z=up to V3f y=up. */
+ std::vector<Imath::V3f> coords(config.totvert);
+ float orco_yup[3];
+ for (int vertex_idx = 0; vertex_idx < config.totvert; vertex_idx++) {
+ copy_yup_from_zup(orco_yup, orcodata[vertex_idx]);
+ coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]);
+ }
+
+ if (!config.abc_ocro.valid()) {
+ /* Create the Alembic property and keep a reference so future frames can reuse it. */
+ config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
+ }
+
+ OV3fGeomParam::Sample sample(coords, kVertexScope);
+ config.abc_ocro.set(sample);
+}
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
@@ -263,6 +296,7 @@ using Alembic::Abc::PropertyHeader;
using Alembic::AbcGeom::IC3fGeomParam;
using Alembic::AbcGeom::IC4fGeomParam;
using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IV3fGeomParam;
static void read_uvs(const CDStreamConfig &config,
void *data,
@@ -448,6 +482,44 @@ static void read_custom_data_uvs(const ICompoundProperty &prop,
read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
}
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss)
+{
+ if (prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) {
+ /* The ORCO property isn't there, so don't bother trying to process it. */
+ return;
+ }
+
+ IV3fGeomParam param(prop, propNameOriginalCoordinates);
+ if (!param.valid() || param.isIndexed()) {
+ /* Invalid or indexed coordinates aren't supported. */
+ return;
+ }
+ if (param.getScope() != kVertexScope) {
+ /* These are original vertex coordinates, so must be vertex-scoped. */
+ return;
+ }
+
+ IV3fGeomParam::Sample sample = param.getExpandedValue(iss);
+ Alembic::AbcGeom::V3fArraySamplePtr abc_ocro = sample.getVals();
+ const size_t totvert = abc_ocro.get()->size();
+
+ void *cd_data;
+ if (CustomData_has_layer(&config.mesh->vdata, CD_ORCO)) {
+ cd_data = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ }
+ else {
+ cd_data = CustomData_add_layer(&config.mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+ }
+
+ float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
+ for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) {
+ const Imath::V3f &abc_coords = (*abc_ocro)[vertex_idx];
+ copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue());
+ }
+}
+
void read_custom_data(const std::string &iobject_full_name,
const ICompoundProperty &prop,
const CDStreamConfig &config,
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 4eb515f132c..9ee964c0545 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -72,12 +72,16 @@ struct CDStreamConfig {
const char **modifier_error_message;
- /* Alembic needs Blender to keep references to C++ objects (the destructors
- * finalize the writing to ABC). This map stores OV2fGeomParam objects for the
- * 2nd and subsequent UV maps; the primary UV map is kept alive by the Alembic
- * mesh sample itself. */
+ /* Alembic needs Blender to keep references to C++ objects (the destructors finalize the writing
+ * to ABC). The following fields are all used to keep these references. */
+
+ /* Mapping from UV map name to its ABC property, for the 2nd and subsequent UV maps; the primary
+ * UV map is kept alive by the Alembic mesh sample itself. */
std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps;
+ /* OCRO coordinates, aka Generated Coordinates. */
+ Alembic::AbcGeom::OV3fGeomParam abc_ocro;
+
CDStreamConfig()
: mloop(NULL),
totloop(0),
@@ -102,6 +106,12 @@ struct CDStreamConfig {
* For now the active layer is used, maybe needs a better way to choose this. */
const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config);
+
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss);
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 8133f615080..11b6c1c18ca 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -439,6 +439,7 @@ static void read_mesh_sample(const std::string &iobject_full_name,
if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
read_mverts(config, abc_mesh_data);
+ read_generated_coordinates(schema.getArbGeomParams(), config, selector);
}
if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
@@ -558,7 +559,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
/* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */
/* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
mesh->flag |= autosmooth;
}
@@ -868,7 +869,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
}
ISubDSchema::Sample sample;
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index 194a9ea397e..ac4c65464c8 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -133,6 +133,19 @@ void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, in
nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
}
+void MaterialNode::add_link(bNode *from_node,
+ const char *from_label,
+ bNode *to_node,
+ const char *to_label)
+{
+ bNodeSocket *from_socket = nodeFindSocket(from_node, SOCK_OUT, from_label);
+ bNodeSocket *to_socket = nodeFindSocket(to_node, SOCK_IN, to_label);
+
+ if (from_socket && to_socket) {
+ nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
+ }
+}
+
void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
{
float reflectivity = val.getFloatValue();
@@ -218,22 +231,32 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
{
int locy = -300 * (node_map.size() - 2);
- if (cot.isColor()) {
- COLLADAFW::Color col = cot.getColor();
- bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
- float *fcol = (float *)socket->default_value;
- fcol[0] = material->r = col.getRed();
- fcol[1] = material->g = col.getGreen();
- fcol[2] = material->b = col.getBlue();
- fcol[3] = material->a = col.getAlpha();
- }
- else if (cot.isTexture()) {
+ if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
if (texture_node != nullptr) {
add_link(texture_node, 0, shader_node, 0);
}
}
+ else {
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
+ float *fcol = (float *)socket->default_value;
+
+ if (cot.isColor()) {
+ COLLADAFW::Color col = cot.getColor();
+ fcol[0] = material->r = col.getRed();
+ fcol[1] = material->g = col.getGreen();
+ fcol[2] = material->b = col.getBlue();
+ fcol[3] = material->a = col.getAlpha();
+ }
+ else {
+ /* no diffuse term = same as black */
+ fcol[0] = material->r = 0.0f;
+ fcol[1] = material->g = 0.0f;
+ fcol[2] = material->b = 0.0f;
+ fcol[3] = material->a = 1.0f;
+ }
+ }
}
Image *MaterialNode::get_diffuse_image()
@@ -326,7 +349,7 @@ void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
else if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
if (texture_node != nullptr) {
- add_link(texture_node, 0, shader_node, 0);
+ add_link(texture_node, "Color", shader_node, "Emission");
}
}
@@ -363,18 +386,38 @@ void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
{
+ bool has_specularity = true;
int locy = -300 * (node_map.size() - 2);
if (cot.isColor()) {
COLLADAFW::Color col = cot.getColor();
- bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
- set_color(node, col);
- /* TODO: Connect node */
+
+ if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) {
+ has_specularity = false;
+ }
+ else {
+ bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
+ set_color(node, col);
+ /* TODO: Connect node */
+ }
}
- /* texture */
else if (cot.isTexture()) {
add_texture_node(cot, -300, locy, "Specular");
/* TODO: Connect node */
}
+ else {
+ /* no specular term) */
+ has_specularity = false;
+ }
+
+ if (!has_specularity) {
+ /* If specularity is black or not defined reset the Specular value to 0
+ TODO: This is a solution only for a corner case. We must find a better
+ way to handle specularity in general. Also note that currently we
+ do not export specularity values, see EffectExporter::operator()
+ */
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular");
+ ((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
+ }
}
bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h
index 2d8c823a4c2..1886acb7a6a 100644
--- a/source/blender/io/collada/Materials.h
+++ b/source/blender/io/collada/Materials.h
@@ -48,6 +48,7 @@ class MaterialNode {
bNodeTree *prepare_material_nodetree();
bNode *add_node(int node_type, int locx, int locy, std::string label);
void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index);
+ void add_link(bNode *from_node, const char *from_label, bNode *to_node, const char *to_label);
bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label);
void setShaderType();
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index dc76b6ed661..73b3e46959b 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -102,7 +102,7 @@ bool GpencilImporterSVG::read()
bGPDlayer *gpl = (bGPDlayer *)BLI_findstring(
&gpd_->layers, layer_id, offsetof(bGPDlayer, info));
if (gpl == nullptr) {
- gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true);
+ gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true, false);
/* Disable lights. */
gpl->flag &= ~GP_LAYER_USE_LIGHTS;
}
diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h
index 6db32e9f40a..94dad37861a 100644
--- a/source/blender/io/gpencil/nanosvg/nanosvg.h
+++ b/source/blender/io/gpencil/nanosvg/nanosvg.h
@@ -1289,7 +1289,7 @@ static const char *nsvg__parseNumber(const char *s, char *it, const int size)
return s;
}
-static const char *nsvg__getNextPathItem(const char *s, char *it)
+static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs)
{
it[0] = '\0';
// Skip white spaces and commas
@@ -1297,6 +1297,15 @@ static const char *nsvg__getNextPathItem(const char *s, char *it)
s++;
if (!*s)
return s;
+
+ /* Blender: Special case for arc command's 4th and 5th arguments. */
+ if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) {
+ it[0] = s[0];
+ it[1] = '\0';
+ s++;
+ return s;
+ }
+
if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
s = nsvg__parseNumber(s, it, 64);
}
@@ -1576,8 +1585,8 @@ static int nsvg__isCoordinate(const char *s)
// optional sign
if (*s == '-' || *s == '+')
s++;
- // must have at least one digit
- return nsvg__isdigit(*s);
+ // must have at least one digit, or start by a dot
+ return (nsvg__isdigit(*s) || *s == '.');
}
static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str)
@@ -2353,7 +2362,12 @@ static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args,
// The loop assumes an iteration per end point (including start and end), this +1.
ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f);
hda = (da / (float)ndivs) / 2.0f;
- kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
+ // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
+ if ((hda < 1e-3f) && (hda > -1e-3f))
+ hda *= 0.5f;
+ else
+ hda = (1.0f - cosf(hda)) / sinf(hda);
+ kappa = fabsf(4.0f / 3.0f * hda);
if (da < 0.0f)
kappa = -kappa;
@@ -2413,7 +2427,7 @@ static void nsvg__parsePath(NSVGparser *p, const char **attr)
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, cmd, nargs);
if (!*item)
break;
if (cmd != '\0' && nsvg__isCoordinate(item)) {
@@ -2740,7 +2754,7 @@ static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag)
s = attr[i + 1];
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, '\0', nargs);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) {
if (npts == 0)
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 5cc525a6cff..583e56de9c2 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -265,9 +265,10 @@ typedef struct bPoseChannel {
* since the alternative is highly complicated - campbell
*/
struct bPoseChannel *custom_tx;
- float custom_scale;
-
- char _pad1[4];
+ float custom_scale; /* Deprecated */
+ float custom_scale_xyz[3];
+ float custom_translation[3];
+ float custom_rotation_euler[3];
/** Transforms - written in by actions or transform. */
float loc[3];
@@ -417,9 +418,9 @@ typedef enum ePchan_DrawFlag {
PCHAN_DRAW_NO_CUSTOM_BONE_SIZE = (1 << 0),
} ePchan_DrawFlag;
-#define PCHAN_CUSTOM_DRAW_SIZE(pchan) \
- (pchan)->custom_scale *( \
- ((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
+/* Note: It doesn't take custom_scale_xyz into account */
+#define PCHAN_CUSTOM_BONE_LENGTH(pchan) \
+ (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
#ifdef DNA_DEPRECATED_ALLOW
/* PoseChannel->bboneflag */
diff --git a/source/blender/makesdna/DNA_boid_types.h b/source/blender/makesdna/DNA_boid_types.h
index 882d4eb1b3b..540446ccd9d 100644
--- a/source/blender/makesdna/DNA_boid_types.h
+++ b/source/blender/makesdna/DNA_boid_types.h
@@ -97,7 +97,8 @@ typedef struct BoidRuleFollowLeader {
} BoidRuleFollowLeader;
typedef struct BoidRuleAverageSpeed {
BoidRule rule;
- float wander, level, speed, rt;
+ float wander, level, speed;
+ char _pad0[4];
} BoidRuleAverageSpeed;
typedef struct BoidRuleFight {
BoidRule rule;
@@ -178,7 +179,7 @@ typedef struct BoidState {
//} BoidSignal;
// typedef struct BoidSignalDefine {
// struct BoidSignalDefine *next, *prev;
-// int id, rt;
+// int id, _pad[4];
// char name[32];
//} BoidSignalDefine;
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index a11e7d77c67..986c009ac26 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings {
/** Factor to extend stroke extremes using fill tool. */
float fill_extend_fac;
- char _pad3[4];
+ /** Number of pixels to dilate fill area. */
+ int dilate_pixels;
struct CurveMapping *curve_sensitivity;
struct CurveMapping *curve_strength;
diff --git a/source/blender/makesdna/DNA_effect_types.h b/source/blender/makesdna/DNA_effect_types.h
index 33f2e1b47c0..307a212a139 100644
--- a/source/blender/makesdna/DNA_effect_types.h
+++ b/source/blender/makesdna/DNA_effect_types.h
@@ -71,13 +71,15 @@ extern "C" {
typedef struct Effect {
struct Effect *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
} Effect;
typedef struct BuildEff {
struct BuildEff *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
float len, sfra;
@@ -88,7 +90,8 @@ typedef struct BuildEff {
typedef struct Particle {
float co[3], no[3];
float time, lifetime;
- short mat_nr, rt;
+ short mat_nr;
+ char _pad0[2];
} Particle;
struct Collection;
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index ea03789ddab..a4ab38f6022 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -184,6 +184,8 @@
.layer_pass = 0, \
.hardeness = 1.0f, \
.curve_intensity = NULL, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_SimplifyGpencilModifierData \
@@ -251,6 +253,8 @@
.thickness_fac = 1.0f, \
.thickness = 30, \
.layer_pass = 0, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_TimeGpencilModifierData \
@@ -295,4 +299,14 @@
.chaining_image_threshold = 0.001f, \
}
+#define _DNA_DEFAULT_LengthGpencilModifierData \
+ { \
+ .start_fac = 0.1f,\
+ .end_fac = 0.1f,\
+ .overshoot_fac = 0.01f,\
+ .pass_index = 0,\
+ .material = NULL,\
+ }
+
+
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 8d7b3896ef9..410212ce100 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -54,6 +54,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
eGpencilModifierType_Lineart = 19,
+ eGpencilModifierType_Length = 20,
/* Keep last. */
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@@ -187,7 +188,12 @@ typedef struct ThickGpencilModifierData {
int thickness;
/** Custom index for passes. */
int layer_pass;
- char _pad[4];
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
+ /** Fading reference object */
+ struct Object *object;
struct CurveMapping *curve_thickness;
} ThickGpencilModifierData;
@@ -199,6 +205,7 @@ typedef enum eThickGpencil_Flag {
GP_THICK_NORMALIZE = (1 << 4),
GP_THICK_INVERT_LAYERPASS = (1 << 5),
GP_THICK_INVERT_MATERIAL = (1 << 6),
+ GP_THICK_FADING = (1 << 7),
} eThickGpencil_Flag;
typedef struct TimeGpencilModifierData {
@@ -291,9 +298,16 @@ typedef struct OpacityGpencilModifierData {
int flag;
/** Main Opacity factor. */
float factor;
+ /** Fading controlling object */
+ int _pad0;
+ struct Object *object;
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
/** Modify stroke, fill or both. */
char modify_color;
- char _pad[3];
+ char _pad1[3];
/** Custom index for passes. */
int layer_pass;
@@ -309,6 +323,7 @@ typedef enum eOpacityGpencil_Flag {
GP_OPACITY_INVERT_MATERIAL = (1 << 5),
GP_OPACITY_CUSTOM_CURVE = (1 << 6),
GP_OPACITY_NORMALIZE = (1 << 7),
+ GP_OPACITY_FADING = (1 << 8),
} eOpacityGpencil_Flag;
typedef struct ArrayGpencilModifierData {
@@ -470,6 +485,39 @@ typedef enum eLatticeGpencil_Flag {
GP_LATTICE_INVERT_MATERIAL = (1 << 4),
} eLatticeGpencil_Flag;
+typedef struct LengthGpencilModifierData {
+ GpencilModifierData modifier;
+ /** Material for filtering. */
+ struct Material *material;
+ /** Layer name. */
+ char layername[64];
+ /** Custom index for passes. */
+ int pass_index;
+ /** Flags. */
+ int flag;
+ /** Custom index for passes. */
+ int layer_pass;
+ /** Length. */
+ float start_fac, end_fac;
+ /** Overshoot trajectory factor. */
+ float overshoot_fac;
+ /** Modifier mode. */
+ int mode;
+ char _pad[4];
+} LengthGpencilModifierData;
+
+typedef enum eLengthGpencil_Flag {
+ GP_LENGTH_INVERT_LAYER = (1 << 0),
+ GP_LENGTH_INVERT_PASS = (1 << 1),
+ GP_LENGTH_INVERT_LAYERPASS = (1 << 2),
+ GP_LENGTH_INVERT_MATERIAL = (1 << 3),
+} eLengthGpencil_Flag;
+
+typedef enum eLengthGpencil_Type {
+ GP_LENGTH_RELATIVE = 0,
+ GP_LENGTH_ABSOLUTE = 1,
+} eLengthGpencil_Type;
+
typedef struct MirrorGpencilModifierData {
GpencilModifierData modifier;
struct Object *object;
@@ -616,6 +664,14 @@ typedef struct OffsetGpencilModifierData {
float loc[3];
float rot[3];
float scale[3];
+ /** Random Offset. */
+ float rnd_offset[3];
+ /** Random Rotation. */
+ float rnd_rot[3];
+ /** Random Scales. */
+ float rnd_scale[3];
+ /** (first element is the index) random values. */
+ int seed;
/** Custom index for passes. */
int layer_pass;
} OffsetGpencilModifierData;
@@ -626,6 +682,7 @@ typedef enum eOffsetGpencil_Flag {
GP_OFFSET_INVERT_VGROUP = (1 << 2),
GP_OFFSET_INVERT_LAYERPASS = (1 << 3),
GP_OFFSET_INVERT_MATERIAL = (1 << 4),
+ GP_OFFSET_UNIFORM_RANDOM_SCALE = (1 << 5),
} eOffsetGpencil_Flag;
typedef struct SmoothGpencilModifierData {
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 8facdca2f9c..0acf979516e 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -47,7 +47,7 @@ struct Curve;
#define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2
#define GPENCIL_MIN_FILL_FAC 0.05f
-#define GPENCIL_MAX_FILL_FAC 5.0f
+#define GPENCIL_MAX_FILL_FAC 8.0f
/* ***************************************** */
/* GP Stroke Points */
@@ -112,6 +112,8 @@ typedef enum eGPDspoint_Flag {
GP_SPOINT_TAG = (1 << 1),
/* stroke point is temp tagged (for some editing operation) */
GP_SPOINT_TEMP_TAG = (1 << 2),
+ /* stroke point is temp tagged (for some editing operation) */
+ GP_SPOINT_TEMP_TAG2 = (1 << 3),
} eGPSPoint_Flag;
/* ***************************************** */
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index 8bb94976414..c5e207c4e20 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -77,8 +77,9 @@ typedef struct IpoCurve {
short totvert;
/** Interpolation and extrapolation modes . */
short ipo, extrap;
- /** Flag= settings; rt= ???. */
- short flag, rt;
+ /** Flag= settings. */
+ short flag;
+ char _pad0[2];
/** Minimum/maximum y-extents for curve. */
float ymin, ymax;
/** ???. */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 282d71f6a87..58c94b6f369 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -45,6 +45,8 @@ struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
struct uiBlock;
+struct Tex;
+struct Material;
#define NODE_MAXSTR 64
@@ -165,6 +167,8 @@ typedef enum eNodeSocketDatatype {
SOCK_IMAGE = 9,
SOCK_GEOMETRY = 10,
SOCK_COLLECTION = 11,
+ SOCK_TEXTURE = 12,
+ SOCK_MATERIAL = 13,
} eNodeSocketDatatype;
/* socket shape */
@@ -593,6 +597,14 @@ typedef struct bNodeSocketValueCollection {
struct Collection *value;
} bNodeSocketValueCollection;
+typedef struct bNodeSocketValueTexture {
+ struct Tex *value;
+} bNodeSocketValueTexture;
+
+typedef struct bNodeSocketValueMaterial {
+ struct Material *value;
+} bNodeSocketValueMaterial;
+
/* data structs, for node->storage */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
@@ -1184,10 +1196,31 @@ typedef struct NodeAttributeVectorMath {
uint8_t input_type_c;
} NodeAttributeVectorMath;
+typedef struct NodeAttributeVectorRotate {
+ /* GeometryNodeAttributeVectorRotateMode */
+ uint8_t mode;
+
+ /* GeometryNodeAttributeInputMode */
+ uint8_t input_type_vector;
+ uint8_t input_type_center;
+ uint8_t input_type_axis;
+ uint8_t input_type_angle;
+ uint8_t input_type_rotation;
+ char _pad[2];
+} NodeAttributeVectorRotate;
+
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
+typedef struct NodeAttributeCurveMap {
+ /* CustomDataType. */
+ uint8_t data_type;
+ char _pad[7];
+ CurveMapping *curve_vec;
+ CurveMapping *curve_rgb;
+} NodeAttributeCurveMap;
+
typedef struct NodeInputVector {
float vector[3];
} NodeInputVector;
@@ -1280,10 +1313,9 @@ typedef struct NodeAttributeSeparateXYZ {
typedef struct NodeAttributeConvert {
/* CustomDataType. */
- uint8_t data_type;
- char _pad[1];
+ int8_t data_type;
/* AttributeDomain. */
- int16_t domain;
+ int8_t domain;
} NodeAttributeConvert;
typedef struct NodeGeometryMeshCircle {
@@ -1308,6 +1340,23 @@ typedef struct NodeGeometryMeshLine {
uint8_t count_mode;
} NodeGeometryMeshLine;
+typedef struct NodeSwitch {
+ /* NodeSwitch. */
+ uint8_t input_type;
+} NodeSwitch;
+
+typedef struct NodeGeometryCurveResample {
+ /* GeometryNodeCurveSampleMode. */
+ uint8_t mode;
+} NodeGeometryCurveResample;
+
+typedef struct NodeGeometryAttributeTransfer {
+ /* AttributeDomain. */
+ int8_t domain;
+ /* GeometryNodeAttributeTransferMapMode. */
+ uint8_t mapping;
+} NodeGeometryAttributeTransfer;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1751,6 +1800,14 @@ typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
} GeometryNodeRotatePointsType;
+typedef enum GeometryNodeAttributeVectorRotateMode {
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3,
+ GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4,
+} GeometryNodeAttributeVectorRotateMode;
+
typedef enum GeometryNodeAttributeRandomizeMode {
GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0,
GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1,
@@ -1802,6 +1859,16 @@ typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
+typedef enum GeometryNodeCurveSampleMode {
+ GEO_NODE_CURVE_SAMPLE_COUNT = 0,
+ GEO_NODE_CURVE_SAMPLE_LENGTH = 1,
+} GeometryNodeCurveSampleMode;
+
+typedef enum GeometryNodeAttributeTransferMapMode {
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0,
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
+} GeometryNodeAttributeTransferMapMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 37013f5b4d6..3d8418fb734 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -182,8 +182,8 @@ typedef struct EffectorWeights {
/** Effector type specific weights. */
float weight[14];
float global_gravity;
- short flag, rt[3];
- char _pad[4];
+ short flag;
+ char _pad[2];
} EffectorWeights;
/* EffectorWeights->flag */
@@ -267,10 +267,9 @@ typedef struct SoftBody {
char namedVG_Spring_K[64];
/* baking */
- int sfra, efra;
- int interval;
+ char _pad1[6];
/** Local==1: use local coords for baking. */
- short local, solverflags;
+ char local, solverflags;
/* -- these must be kept for backwards compatibility -- */
/** Array of size totpointkey. */
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index cc40e26b92b..30b1fbe09d3 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -64,7 +64,7 @@ typedef struct BoidParticle {
struct BoidData data;
float gravity[3];
float wander[3];
- float rt;
+ char _pad0[4];
} BoidParticle;
typedef struct ParticleSpring {
@@ -82,7 +82,7 @@ typedef struct ChildParticle {
float w[4];
/** Face vertex weights and offset. */
float fuv[4], foffset;
- float rt;
+ char _pad0[4];
} ChildParticle;
typedef struct ParticleTarget {
@@ -99,7 +99,8 @@ typedef struct ParticleDupliWeight {
short count;
short flag;
/** Only updated on file save and used on file load. */
- short index, rt;
+ short index;
+ char _pad0[2];
} ParticleDupliWeight;
typedef struct ParticleData {
@@ -191,7 +192,8 @@ typedef struct ParticleSettings {
struct EffectorWeights *effector_weights;
struct Collection *collision_group;
- int flag, rt;
+ int flag;
+ char _pad1[4];
short type, from, distr, texact;
/* physics modes */
short phystype, rotmode, avemode, reactevent;
diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h
index de2fa3f10fe..ad5f386bf2b 100644
--- a/source/blender/makesdna/DNA_pointcache_types.h
+++ b/source/blender/makesdna/DNA_pointcache_types.h
@@ -107,7 +107,8 @@ typedef struct PointCache {
int totpoint;
/** Modifier stack index. */
int index;
- short compression, rt;
+ short compression;
+ char _pad0[2];
char name[64];
char prev_name[64];
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7f7e610a1a..0b07b8271a5 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -952,7 +952,8 @@ typedef struct ParticleEditSettings {
/** Runtime. */
void *paintcursor;
- float emitterdist, rt;
+ float emitterdist;
+ char _pad0[4];
int selectmode;
int edittype;
@@ -1550,7 +1551,8 @@ typedef struct UnitSettings {
typedef struct PhysicsSettings {
float gravity[3];
- int flag, quick_cache_step, rt;
+ int flag, quick_cache_step;
+ char _pad0[4];
} PhysicsSettings;
/* ------------------------------------------- */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index fa11a7dfd13..4b95dd41b30 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -517,11 +517,11 @@ enum {
SEQ_AUDIO_PAN_ANIMATED = (1 << 26),
SEQ_AUDIO_DRAW_WAVEFORM = (1 << 27),
- /* don't include Grease Pencil in OpenGL previews of Scene strips */
- SEQ_SCENE_NO_GPENCIL = (1 << 28),
+ /* don't include Annotations in OpenGL previews of Scene strips */
+ SEQ_SCENE_NO_ANNOTATION = (1 << 28),
SEQ_USE_VIEWS = (1 << 29),
- /* access scene strips directly (like a metastrip) */
+ /* Access scene strips directly (like a meta-strip). */
SEQ_SCENE_STRIPS = (1 << 30),
SEQ_INVALID_EFFECT = (1u << 31),
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index ce3875dfbc7..9f3576a2cbe 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1909,7 +1909,7 @@ typedef struct SpaceSpreadsheet {
/**
* List of #SpreadsheetContext.
* This is a path to the data that is displayed in the spreadsheet.
- * It can be set explicitely by an action of the user (e.g. clicking the preview icon in a
+ * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a
* geometry node) or it can be derived from context automatically based on some heuristic.
*/
ListBase context_path;
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index 5381c524e4d..60c255e8637 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -57,7 +57,8 @@ typedef struct MTex {
short colormodel, pmapto, pmaptoneg;
short normapspace, which_output;
float r, g, b, k;
- float def_var, rt;
+ float def_var;
+ char _pad1[4];
/* common */
float colfac, varfac;
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index 2ce32a723a7..8e63760fef7 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -58,6 +58,21 @@ typedef enum eXRSessionBasePoseType {
XR_BASE_POSE_CUSTOM = 2,
} eXRSessionBasePoseType;
+/** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */
+typedef enum eXrActionType {
+ XR_BOOLEAN_INPUT = 1,
+ XR_FLOAT_INPUT = 2,
+ XR_VECTOR2F_INPUT = 3,
+ XR_POSE_INPUT = 4,
+ XR_VIBRATION_OUTPUT = 100,
+} eXrActionType;
+
+typedef enum eXrOpFlag {
+ XR_OP_PRESS = 0,
+ XR_OP_RELEASE = 1,
+ XR_OP_MODAL = 2,
+} eXrOpFlag;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 95272fb7804..2d55ea05867 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -316,6 +316,7 @@ SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData);
+SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@@ -541,6 +542,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(TimeGpencilModifierData),
SDNA_DEFAULT_DECL(TintGpencilModifierData),
SDNA_DEFAULT_DECL(LineartGpencilModifierData),
+ SDNA_DEFAULT_DECL(LengthGpencilModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 54e077c624c..379e3e77b6e 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -336,6 +336,7 @@ extern StructRNA RNA_LatticeModifier;
extern StructRNA RNA_LatticePoint;
extern StructRNA RNA_LayerCollection;
extern StructRNA RNA_LayerObjects;
+extern StructRNA RNA_LengthGpencilModifier;
extern StructRNA RNA_Library;
extern StructRNA RNA_Light;
extern StructRNA RNA_LightProbe;
@@ -845,6 +846,7 @@ const char *RNA_property_description(PropertyRNA *prop);
PropertyType RNA_property_type(PropertyRNA *prop);
PropertySubType RNA_property_subtype(PropertyRNA *prop);
PropertyUnit RNA_property_unit(PropertyRNA *prop);
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop);
int RNA_property_flag(PropertyRNA *prop);
int RNA_property_override_flag(PropertyRNA *prop);
int RNA_property_tags(PropertyRNA *prop);
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index 17309d847bd..a31182b2f5a 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -388,6 +388,7 @@ void RNA_def_property_string_default(PropertyRNA *prop, const char *value);
void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description);
void RNA_def_property_ui_range(
PropertyRNA *prop, double min, double max, double step, int precision);
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType scale_type);
void RNA_def_property_ui_icon(PropertyRNA *prop, int icon, int consecutive);
void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *updatefunc);
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index b0895609e9a..71af69f77a1 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -237,6 +237,7 @@ extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
extern const EnumPropertyItem rna_enum_preference_section_items[];
extern const EnumPropertyItem rna_enum_attribute_type_items[];
+extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[];
extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 6cd3678017e..4a6d6dddec7 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -95,6 +95,32 @@ typedef enum PropertyUnit {
PROP_UNIT_TEMPERATURE = (11 << 16), /* C */
} PropertyUnit;
+/**
+ * Use values besides #PROP_SCALE_LINEAR
+ * so the movement of the mouse doesn't map linearly to the value of the slider.
+ *
+ * For some settings it's useful to space motion in a non-linear way, see T77868.
+ *
+ * NOTE: The scale types are available for all float sliders.
+ * For integer sliders they are only available if they use the visible value bar.
+ * Sliders with logarithmic scale and value bar must have a range > 0
+ * while logarithmic sliders without the value bar can have a range of >= 0.
+ */
+typedef enum PropertyScaleType {
+ /** Linear scale (default). */
+ PROP_SCALE_LINEAR = 0,
+ /**
+ * Logarithmic scale
+ * - Maximum range: `0 <= x < inf`
+ */
+ PROP_SCALE_LOG = 1,
+ /**
+ * Cubic scale.
+ * - Maximum range: `-inf < x < inf`
+ */
+ PROP_SCALE_CUBIC = 2,
+} PropertyScaleType;
+
#define RNA_SUBTYPE_UNIT(subtype) ((subtype)&0x00FF0000)
#define RNA_SUBTYPE_VALUE(subtype) ((subtype) & ~0x00FF0000)
#define RNA_SUBTYPE_UNIT_VALUE(subtype) ((subtype) >> 16)
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 4fafa356879..95b7b7e5406 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -99,7 +99,7 @@ set(DEFSRC
)
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
list(APPEND DEFSRC
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b20d9218316..efe12114d55 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -684,6 +684,29 @@ static char *rna_def_property_get_func(
}
}
}
+
+ /* Check log scale sliders for negative range. */
+ if (prop->type == PROP_FLOAT) {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ /* NOTE: UI_BTYPE_NUM_SLIDER can't have a softmin of zero. */
+ if ((fprop->ui_scale_type == PROP_SCALE_LOG) && (fprop->hardmin < 0 || fprop->softmin < 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale < 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
+ if (prop->type == PROP_INT) {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ /* Only UI_BTYPE_NUM_SLIDER is implemented and that one can't have a softmin of zero. */
+ if ((iprop->ui_scale_type == PROP_SCALE_LOG) &&
+ (iprop->hardmin <= 0 || iprop->softmin <= 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale <= 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
}
func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get");
@@ -3935,6 +3958,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(iprop->getarray_ex),
rna_function_string(iprop->setarray_ex),
rna_function_string(iprop->range_ex));
+ rna_int_print(f, iprop->ui_scale_type);
+ fprintf(f, ", ");
rna_int_print(f, iprop->softmin);
fprintf(f, ", ");
rna_int_print(f, iprop->softmax);
@@ -3969,6 +3994,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(fprop->getarray_ex),
rna_function_string(fprop->setarray_ex),
rna_function_string(fprop->range_ex));
+ rna_float_print(f, fprop->ui_scale_type);
+ fprintf(f, ", ");
rna_float_print(f, fprop->softmin);
fprintf(f, ", ");
rna_float_print(f, fprop->softmax);
@@ -4341,7 +4368,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_screen.c", NULL, RNA_def_screen},
{"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint},
{"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer},
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
{"rna_simulation.c", NULL, RNA_def_simulation},
#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 657af98f5fe..5c0691f0320 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -148,6 +148,8 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items
# include "DEG_depsgraph_build.h"
# include "DEG_depsgraph_query.h"
+# include "ED_asset.h"
+
# include "WM_api.h"
void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value)
@@ -344,7 +346,7 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_Screen) {
return ID_SCR;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
if (base_type == &RNA_Simulation) {
return ID_SIM;
}
@@ -452,7 +454,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_SCR:
return &RNA_Screen;
case ID_SIM:
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
return &RNA_Simulation;
# else
return &RNA_ID;
@@ -575,6 +577,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain)
return newid;
}
+static void rna_ID_asset_mark(ID *id, bContext *C)
+{
+ if (ED_asset_mark_id(C, id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
+ }
+}
+
+static void rna_ID_asset_clear(ID *id)
+{
+ if (ED_asset_clear_id(id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
+ }
+}
+
static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
{
if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
@@ -1716,6 +1734,18 @@ static void rna_def_ID(BlenderRNA *brna)
parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID");
RNA_def_function_return(func, parm);
+ func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark");
+ RNA_def_function_ui_description(
+ func,
+ "Enable easier reuse of the data-block through the Asset Browser, with the help of "
+ "customizable metadata (like previews, descriptions and tags)");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+
+ func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear");
+ RNA_def_function_ui_description(
+ func,
+ "Delete all asset metadata and turn the asset data-block back into a normal data-block");
+
func = RNA_def_function(srna, "override_create", "rna_ID_override_create");
RNA_def_function_ui_description(func,
"Create an overridden local copy of this linked data-block (not "
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 9b57096ec19..150a455f1c7 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1189,6 +1189,24 @@ PropertyUnit RNA_property_unit(PropertyRNA *prop)
return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop));
}
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop)
+{
+ PropertyRNA *rna_prop = rna_ensure_property(prop);
+
+ switch (rna_prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)rna_prop;
+ return iprop->ui_scale_type;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_prop;
+ return fprop->ui_scale_type;
+ }
+ default:
+ return PROP_SCALE_LINEAR;
+ }
+}
+
int RNA_property_flag(PropertyRNA *prop)
{
return rna_ensure_property(prop)->flag;
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index c54621372ba..c8fccfc27f8 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -674,7 +674,7 @@ static bool rna_Armature_is_editmode_get(PointerRNA *ptr)
return (arm->edbo != NULL);
}
-static void rna_Armature_transform(bArmature *arm, float *mat)
+static void rna_Armature_transform(bArmature *arm, float mat[16])
{
ED_armature_transform(arm, (const float(*)[4])mat, true);
}
diff --git a/source/blender/makesrna/intern/rna_armature_api.c b/source/blender/makesrna/intern/rna_armature_api.c
index 36aa0401c7d..a02f55667e3 100644
--- a/source/blender/makesrna/intern/rna_armature_api.c
+++ b/source/blender/makesrna/intern/rna_armature_api.c
@@ -44,7 +44,7 @@ static void rna_EditBone_align_roll(EditBone *ebo, float no[3])
ebo->roll = ED_armature_ebone_roll_to_vector(ebo, no, false);
}
-static float rna_Bone_do_envelope(Bone *bone, float *vec)
+static float rna_Bone_do_envelope(Bone *bone, float vec[3])
{
float scale = (bone->flag & BONE_MULT_VG_ENV) == BONE_MULT_VG_ENV ? bone->weight : 1.0f;
return distfactor_to_bone(vec,
@@ -56,11 +56,11 @@ static float rna_Bone_do_envelope(Bone *bone, float *vec)
}
static void rna_Bone_convert_local_to_pose(Bone *bone,
- float *r_matrix,
- float *matrix,
- float *matrix_local,
- float *parent_matrix,
- float *parent_matrix_local,
+ float r_matrix[16],
+ float matrix[16],
+ float matrix_local[16],
+ float parent_matrix[16],
+ float parent_matrix_local[16],
bool invert)
{
BoneParentTransform bpt;
@@ -89,14 +89,14 @@ static void rna_Bone_convert_local_to_pose(Bone *bone,
BKE_bone_parent_transform_apply(&bpt, (float(*)[4])matrix, (float(*)[4])r_matrix);
}
-static void rna_Bone_MatrixFromAxisRoll(float *axis, float roll, float *r_matrix)
+static void rna_Bone_MatrixFromAxisRoll(float axis[3], float roll, float r_matrix[9])
{
vec_roll_to_mat3(axis, roll, (float(*)[3])r_matrix);
}
-static void rna_Bone_AxisRollFromMatrix(float *matrix,
- float *axis_override,
- float *r_axis,
+static void rna_Bone_AxisRollFromMatrix(float matrix[9],
+ float axis_override[3],
+ float r_axis[3],
float *r_roll)
{
float mat[3][3];
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 7976df3e4e4..a256002ffc1 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -49,6 +49,19 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
+ {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""},
+ {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"},
+ {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"},
+ {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"},
+ {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"},
+ {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"},
+ {CD_PROP_STRING, "STRING", 0, "String", "Text string"},
+ {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
+ {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
/* Not implement yet */
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
@@ -58,7 +71,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
- {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
@@ -68,6 +81,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index e7daa55af6c..7e1d513502c 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1617,6 +1617,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ /* Number of pixels to dilate fill area. */
+ prop = RNA_def_property(srna, "dilate", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "dilate_pixels");
+ RNA_def_property_range(prop, 0, 20);
+ RNA_def_property_int_default(prop, 1);
+ RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
/* Flags */
prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE);
diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c
index 94fdb130026..beea607e8c0 100644
--- a/source/blender/makesrna/intern/rna_curve_api.c
+++ b/source/blender/makesrna/intern/rna_curve_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Curve_transform(Curve *cu, float *mat, bool shape_keys)
+static void rna_Curve_transform(Curve *cu, float mat[16], bool shape_keys)
{
BKE_curve_transform(cu, (const float(*)[4])mat, shape_keys, true);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index bb54d55f8bd..b3ab8cc15a2 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -137,7 +137,7 @@ static void rna_CurveProfile_remove_point(CurveProfile *profile,
static void rna_CurveProfile_evaluate(struct CurveProfile *profile,
ReportList *reports,
float length_portion,
- float *location)
+ float location[2])
{
if (!profile->table) {
BKE_report(reports, RPT_ERROR, "CurveProfile table not initialized, call initialize()");
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 2fdf7e5eaa7..9b9d561603b 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -1754,6 +1754,28 @@ void RNA_def_property_ui_range(
}
}
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType ui_scale_type)
+{
+ StructRNA *srna = DefRNA.laststruct;
+
+ switch (prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ iprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ fprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ default:
+ CLOG_ERROR(&LOG, "\"%s.%s\", invalid type for scale.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ break;
+ }
+}
+
void RNA_def_property_range(PropertyRNA *prop, double min, double max)
{
StructRNA *srna = DefRNA.laststruct;
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index d4697721bc2..1b89d866aba 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -1160,6 +1160,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* define common props */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Additive",
"Values generated by this modifier are applied on top of "
@@ -1168,12 +1169,14 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, generator_mode_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Mode", "Type of generator to use");
RNA_def_property_update(
prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_verify_data_update");
/* order of the polynomial */
prop = RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Polynomial Order",
@@ -1184,6 +1187,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* coefficients array */
prop = RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_array(prop, 32);
RNA_def_property_flag(prop, PROP_DYNAMIC);
RNA_def_property_dynamic_array_funcs(prop, "rna_FModifierGenerator_coefficients_get_length");
@@ -1221,25 +1225,30 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
/* coefficients */
prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Amplitude", "Scale factor determining the maximum/minimum values");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Phase Multiple", "Scale factor determining the 'speed' of the function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* flags */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
RNA_def_property_ui_text(prop,
"Additive",
@@ -1248,6 +1257,7 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use");
@@ -1271,17 +1281,20 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna)
*/
prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* Frame */
prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "time");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1340,6 +1353,7 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Collections */
prop = RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totvert");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint");
RNA_def_property_ui_text(
prop, "Control Points", "Control points defining the shape of the envelope");
@@ -1348,18 +1362,21 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Range Settings */
prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "midval");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Reference Value", "Value that envelope's influence is centered around / based on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1395,12 +1412,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* before */
prop = RNA_def_property(srna, "mode_before", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "before_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "before_cycles");
RNA_def_property_ui_text(
prop,
@@ -1410,12 +1429,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* after */
prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "after_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "after_cycles");
RNA_def_property_ui_text(prop,
"After Cycles",
@@ -1450,26 +1471,31 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range");
RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow");
@@ -1477,6 +1503,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range");
RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow");
@@ -1484,6 +1511,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range");
RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow");
@@ -1491,6 +1519,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range");
RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow");
@@ -1519,16 +1548,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "modification");
RNA_def_property_enum_items(prop, prop_modification_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "strength");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Strength",
@@ -1537,16 +1569,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "phase");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "depth");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
}
@@ -1569,11 +1604,13 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
/* properties */
prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "step_size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Offset",
"Reference number of frames before frames get held "
@@ -1582,18 +1619,21 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "start_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop,
NULL,
"rna_FModifierStepped_frame_start_set",
@@ -1604,6 +1644,7 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "end_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierStepped_frame_end_set", "rna_FModifierStepped_end_frame_range");
RNA_def_property_ui_text(
@@ -1641,12 +1682,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_show_expanded_set");
RNA_def_property_ui_text(prop, "Expanded", "F-Curve Modifier's panel is expanded in UI");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Enabled", "Enable F-Curve modifier evaluation");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1);
@@ -1654,6 +1697,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
@@ -1661,6 +1705,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* TODO: setting this to true must ensure that all others in stack are turned off too... */
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Active", "F-Curve modifier will show settings in the editor");
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update");
@@ -1669,6 +1714,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* restricted range */
prop = RNA_def_property(srna, "use_restricted_range", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_RANGERESTRICT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Restrict Frame Range",
@@ -1678,6 +1724,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "sfra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range");
RNA_def_property_ui_text(
@@ -1688,6 +1735,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "efra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range");
RNA_def_property_ui_text(
@@ -1698,6 +1746,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend In", "Number of frames from start frame for influence to take effect");
@@ -1705,6 +1754,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendout");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend Out", "Number of frames from end frame for influence to fade out");
@@ -1713,12 +1763,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* influence */
prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "influence");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(
@@ -2162,6 +2214,7 @@ static void rna_def_fcurve_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
/* Collection active property */
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "FModifier");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(
prop, "rna_FCurve_active_modifier_get", "rna_FCurve_active_modifier_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 1fbbffa99d5..91e13a4bee3 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -1037,7 +1037,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src)
static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, bool setactive)
{
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0, false);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -2571,7 +2571,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Auto-Lock Layers",
- "Lock automatically all layers except active one to avoid accidental changes");
+ "Automatically lock all layers except the active one to avoid accidental changes");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_autolock");
prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA);
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 03a55c5a7da..4aad0741151 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -109,6 +109,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_LATTICE,
"Lattice",
"Deform strokes using lattice"},
+ {eGpencilModifierType_Length,
+ "GP_LENGTH",
+ ICON_MOD_EDGESPLIT,
+ "Length",
+ "Extend or shrink strokes"},
{eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"},
{eGpencilModifierType_Offset,
"GP_OFFSET",
@@ -188,6 +193,11 @@ static const EnumPropertyItem gpencil_tint_type_items[] = {
{GP_TINT_GRADIENT, "GRADIENT", 0, "Gradient", ""},
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem gpencil_length_mode_items[] = {
+ {GP_LENGTH_RELATIVE, "RELATIVE", 0, "Relative", "Length in ratio to the stroke's length"},
+ {GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"},
+ {0, NULL, 0, NULL, NULL},
+};
#endif
#ifdef RNA_RUNTIME
@@ -233,6 +243,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_OpacityGpencilModifier;
case eGpencilModifierType_Lattice:
return &RNA_LatticeGpencilModifier;
+ case eGpencilModifierType_Length:
+ return &RNA_LengthGpencilModifier;
case eGpencilModifierType_Mirror:
return &RNA_MirrorGpencilModifier;
case eGpencilModifierType_Smooth:
@@ -351,6 +363,8 @@ static void greasepencil_modifier_object_set(Object *self,
RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE);
RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE);
RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Opacity, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Thick, object, OB_EMPTY);
# undef RNA_GP_MOD_OBJECT_SET
@@ -1116,6 +1130,38 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_ThickGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -1256,6 +1302,34 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_offset");
+ RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_rot");
+ RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_scale");
+ RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Seed", "Random seed");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OFFSET_UNIFORM_RANDOM_SCALE);
+ RNA_def_property_ui_text(
+ prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
}
static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
@@ -1625,6 +1699,38 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hardness", "Factor of stroke hardness");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_OpacityGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -2807,6 +2913,87 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
}
+static void rna_def_modifier_gpencillength(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes");
+ RNA_def_struct_sdna(srna, "LengthGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+
+ prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "start_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "end_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "overshoot_fac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(
+ prop,
+ "Overshoot Factor",
+ "Defines how precise must follow the stroke trajectory for the overshoot extremes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, gpencil_length_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode to define length");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "layername");
+ RNA_def_property_ui_text(prop, "Layer", "Layer name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "pass_index");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER);
+ RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL);
+ RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "layer_pass");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+}
+
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2882,6 +3069,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
rna_def_modifier_gpencillineart(brna);
+ rna_def_modifier_gpencillength(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 95972dd444f..bfe9d4bb77c 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -476,7 +476,7 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
#endif
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 0c0260c889c..245730919b0 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -400,6 +400,7 @@ typedef struct IntPropertyRNA {
PropIntArraySetFuncEx setarray_ex;
PropIntRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
int softmin, softmax;
int hardmin, hardmax;
int step;
@@ -423,6 +424,7 @@ typedef struct FloatPropertyRNA {
PropFloatArraySetFuncEx setarray_ex;
PropFloatRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
float softmin, softmax;
float hardmin, hardmax;
float step;
diff --git a/source/blender/makesrna/intern/rna_lattice_api.c b/source/blender/makesrna/intern/rna_lattice_api.c
index 0b61603dd09..5b69a743d47 100644
--- a/source/blender/makesrna/intern/rna_lattice_api.c
+++ b/source/blender/makesrna/intern/rna_lattice_api.c
@@ -33,7 +33,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Lattice_transform(Lattice *lt, float *mat, bool shape_keys)
+static void rna_Lattice_transform(Lattice *lt, float mat[16], bool shape_keys)
{
BKE_lattice_transform(lt, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index c80f856dd6b..464abc6b543 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -134,7 +134,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_LISTBASE_FUNCS_DEF(simulations)
# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
@@ -407,7 +407,7 @@ void RNA_def_main(BlenderRNA *brna)
"Volumes",
"Volume data-blocks",
RNA_def_main_volumes},
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
{"simulations",
"Simulation",
"rna_Main_simulations_begin",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 6c2fb649986..8e6ff961721 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -806,7 +806,7 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
return volume;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -870,7 +870,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
# endif
@@ -2412,7 +2412,7 @@ void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c
index f92a2932f06..2b0582cae9a 100644
--- a/source/blender/makesrna/intern/rna_mesh_api.c
+++ b/source/blender/makesrna/intern/rna_mesh_api.c
@@ -172,7 +172,7 @@ static void rna_Mesh_normals_split_custom_set_from_vertices(Mesh *mesh,
DEG_id_tag_update(&mesh->id, 0);
}
-static void rna_Mesh_transform(Mesh *mesh, float *mat, bool shape_keys)
+static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys)
{
BKE_mesh_transform(mesh, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_meta_api.c b/source/blender/makesrna/intern/rna_meta_api.c
index 19d0b35959b..93bd9fe3b9c 100644
--- a/source/blender/makesrna/intern/rna_meta_api.c
+++ b/source/blender/makesrna/intern/rna_meta_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Meta_transform(struct MetaBall *mb, float *mat)
+static void rna_Meta_transform(struct MetaBall *mb, float mat[16])
{
BKE_mball_transform(mb, (float(*)[4])mat, true);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index e3fb443951f..674e5845ccb 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -2781,7 +2781,8 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "double_threshold");
RNA_def_property_range(prop, 0, 1.0f);
- RNA_def_property_ui_range(prop, 0, 1, 0.0001, 6);
+ RNA_def_property_ui_range(prop, 0, 1, 1.0, 6);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(
prop, "Overlap Threshold", "Threshold for checking overlapping geometry");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -5530,6 +5531,7 @@ static void rna_def_modifier_remesh(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "voxel_size");
RNA_def_property_range(prop, 0.0001f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0001, 2, 0.1, 3);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(prop,
"Voxel Size",
"Size of the voxel in object space used for volume evaluation. Lower "
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index c9520c939f4..f8fa2aab5e7 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -402,7 +402,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Start Frame",
"Global scene frame number at which this movie starts playing "
"(affects all data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* frame_offset */
prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE);
@@ -412,7 +412,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Frame Offset",
"Offset of footage first frame relative to its file name "
"(affects only how footage is loading, does not change data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* length */
prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index b1b2e9738c1..11f5ff0441a 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -65,6 +65,22 @@
const EnumPropertyItem rna_enum_node_socket_in_out_items[] = {
{SOCK_IN, "IN", 0, "Input", ""}, {SOCK_OUT, "OUT", 0, "Output", ""}, {0, NULL, 0, NULL, NULL}};
+static const EnumPropertyItem node_socket_data_type_items[] = {
+ {SOCK_FLOAT, "FLOAT", 0, "Float", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
+ {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
+ {SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
+ {SOCK_STRING, "STRING", 0, "String", ""},
+ {SOCK_RGBA, "RGBA", 0, "Color", ""},
+ {SOCK_OBJECT, "OBJECT", 0, "Object", ""},
+ {SOCK_IMAGE, "IMAGE", 0, "Image", ""},
+ {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
+ {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
@@ -78,7 +94,7 @@ static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""},
{SOCK_FLOAT, "VALUE", 0, "Value", ""},
- {SOCK_INT, "INT", 0, "Int", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_STRING, "STRING", 0, "String", ""},
@@ -88,6 +104,8 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -957,6 +975,32 @@ static void rna_NodeTree_get_from_context(
RNA_parameter_list_free(&list);
}
+static bool rna_NodeTree_valid_socket_type(eNodeSocketDatatype socket_type,
+ bNodeTreeType *ntreetype)
+{
+ extern FunctionRNA rna_NodeTree_valid_socket_type_func;
+
+ PointerRNA ptr;
+ ParameterList list;
+ FunctionRNA *func;
+ void *ret;
+ bool valid;
+
+ RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */
+ func = &rna_NodeTree_valid_socket_type_func;
+
+ RNA_parameter_list_create(&list, &ptr, func);
+ RNA_parameter_set_lookup(&list, "type", &socket_type);
+ ntreetype->rna_ext.call(NULL, &ptr, func, &list);
+
+ RNA_parameter_get_lookup(&list, "valid", &ret);
+ valid = *(bool *)ret;
+
+ RNA_parameter_list_free(&list);
+
+ return valid;
+}
+
static void rna_NodeTree_unregister(Main *UNUSED(bmain), StructRNA *type)
{
bNodeTreeType *nt = RNA_struct_blender_type_get(type);
@@ -985,7 +1029,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
bNodeTreeType *nt, dummynt;
bNodeTree dummyntree;
PointerRNA dummyptr;
- int have_function[3];
+ int have_function[4];
/* setup dummy tree & tree type to store static properties in */
memset(&dummynt, 0, sizeof(bNodeTreeType));
@@ -1031,6 +1075,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL;
nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL;
nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL;
+ nt->valid_socket_type = (have_function[3]) ? rna_NodeTree_valid_socket_type : NULL;
ntreeTypeAdd(nt);
@@ -1914,6 +1959,29 @@ static const EnumPropertyItem *itemf_function_check(
return item_array;
}
+static bool switch_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value,
+ SOCK_FLOAT,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_VECTOR,
+ SOCK_STRING,
+ SOCK_RGBA,
+ SOCK_GEOMETRY,
+ SOCK_OBJECT,
+ SOCK_COLLECTION);
+}
+
+static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(node_socket_data_type_items, switch_type_supported);
+}
+
static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
@@ -1984,6 +2052,7 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain,
static bool attribute_convert_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
+ CD_AUTO_FROM_NAME,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
@@ -1995,7 +2064,8 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
- return itemf_function_check(rna_enum_attribute_type_items, attribute_convert_type_supported);
+ return itemf_function_check(rna_enum_attribute_type_with_auto_items,
+ attribute_convert_type_supported);
}
static bool attribute_fill_type_supported(const EnumPropertyItem *item)
@@ -2117,6 +2187,17 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_type_itemf(
return itemf_function_check(rna_enum_attribute_type_items, attribute_map_range_type_supported);
}
+static bool attribute_curve_map_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3053,7 +3134,7 @@ static void rna_NodeSocketStandard_draw(ID *id,
}
static void rna_NodeSocketStandard_draw_color(
- ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float *r_color)
+ ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -3073,7 +3154,7 @@ static void rna_NodeSocketInterfaceStandard_draw(ID *id,
static void rna_NodeSocketInterfaceStandard_draw_color(ID *id,
bNodeSocket *sock,
struct bContext *C,
- float *r_color)
+ float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -4375,6 +4456,13 @@ void rna_ShaderNodePointDensity_density_minmax(bNode *self,
RE_point_density_minmax(depsgraph, pd, r_min, r_max);
}
+bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ /* Do not show grease pencil materials for now. */
+ Material *ma = (Material *)value.data;
+ return ma->gp_style == NULL;
+}
+
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@@ -8954,9 +9042,9 @@ static void def_geo_attribute_convert(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf");
- RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME);
RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@@ -9197,6 +9285,85 @@ static void def_geo_attribute_color_ramp(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_attribute_curve_map(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeCurveMap", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeCurveMap_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "curve_vec", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "curve_rgb", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_attribute_vector_rotate(StructRNA *srna)
+{
+ static const EnumPropertyItem rotate_mode_items[] = {
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS,
+ "AXIS_ANGLE",
+ 0,
+ "Axis Angle",
+ "Rotate a point using axis angle"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ,
+ "EULER_XYZ",
+ 0,
+ "Euler",
+ "Rotate a point using XYZ order"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage");
+
+ prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, rotate_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Type of rotation");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Vector", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Center", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Axis", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
+ RNA_def_property_ui_text(prop, "Input Type Angle", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Rotation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
@@ -9351,19 +9518,6 @@ static void def_geo_point_translate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_attribute_sample_texture(StructRNA *srna)
-{
- PropertyRNA *prop;
-
- prop = RNA_def_property(srna, "texture", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "id");
- RNA_def_property_struct_type(prop, "Texture");
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(prop, "Texture", "Texture to sample values from");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations");
-}
-
static void def_geo_object_info(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9631,6 +9785,93 @@ static void def_geo_mesh_line(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_switch(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeSwitch", "storage");
+ prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "input_type");
+ RNA_def_property_enum_items(prop, node_socket_data_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeSwitch_type_itemf");
+ RNA_def_property_ui_text(prop, "Input Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_curve_resample(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_CURVE_SAMPLE_COUNT,
+ "COUNT",
+ 0,
+ "Count",
+ "Sample the specified number of points along each spline"},
+ {GEO_NODE_CURVE_SAMPLE_LENGTH,
+ "LENGTH",
+ 0,
+ "Length",
+ "Calculate the number of samples by splitting each spline into segments with the specified "
+ "length"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "How to specify the amount of samples");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_attribute_transfer(StructRNA *srna)
+{
+ static EnumPropertyItem mapping_items[] = {
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED,
+ "NEAREST_FACE_INTERPOLATED",
+ 0,
+ "Nearest Face Interpolated",
+ "Transfer the attribute from the nearest face on a surface (loose points and edges are "
+ "ignored)"},
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST,
+ "NEAREST",
+ 0,
+ "Nearest",
+ "Transfer the element from the nearest element (using face and edge centers for the "
+ "distance computation)"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO);
+ RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mapping_items);
+ RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_input_material(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(prop, "Material", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -10403,6 +10644,80 @@ static void rna_def_node_socket_collection(BlenderRNA *brna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
}
+static void rna_def_node_socket_texture(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Texture");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+
+ /* socket interface */
+ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
+ RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Texture");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+}
+
+static void rna_def_node_socket_material(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+
+ /* socket interface */
+ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
+ RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+}
+
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@@ -10547,6 +10862,10 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection");
+
+ rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture");
+
+ rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
}
static void rna_def_internal_node(BlenderRNA *brna)
@@ -11331,6 +11650,14 @@ static void rna_def_nodetree(BlenderRNA *brna)
parm = RNA_def_pointer(
func, "result_3", "ID", "From ID", "Original ID data-block selected from the context");
RNA_def_function_output(func, parm);
+
+ /* Check for support of a socket type. */
+ func = RNA_def_function(srna, "valid_socket_type", NULL);
+ RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
+ parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", ""));
}
static void rna_def_composite_nodetree(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 6b93a1c223c..b339682222c 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -62,6 +62,8 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "DEG_depsgraph_query.h"
+
const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_OBJECT, "OBJECT", ICON_OBJECT_DATAMODE, "Object Mode", ""},
{OB_MODE_EDIT, "EDIT", ICON_EDITMODE_HLT, "Edit Mode", ""},
@@ -84,7 +86,7 @@ const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_PAINT_GPENCIL,
"PAINT_GPENCIL",
ICON_GREASEPENCIL,
- "Draw",
+ "Draw Mode",
"Paint Grease Pencil Strokes"},
{OB_MODE_WEIGHT_GPENCIL,
"WEIGHT_GPENCIL",
@@ -1253,10 +1255,16 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index)
return PROP_EDITABLE;
}
+static int rna_MaterialSlot_index(PointerRNA *ptr)
+{
+ /* There is an offset of one, so that `ptr->data` is not null. */
+ return POINTER_AS_INT(ptr->data) - 1;
+}
+
static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info))
{
Object *ob = (Object *)ptr->owner_id;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
bool is_editable;
if ((ob->matbits == NULL) || ob->matbits[index]) {
@@ -1273,9 +1281,14 @@ static PointerRNA rna_MaterialSlot_material_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
- ma = BKE_object_material_get(ob, index + 1);
+ if (DEG_is_evaluated_object(ob)) {
+ ma = BKE_object_material_get_eval(ob, index + 1);
+ }
+ else {
+ ma = BKE_object_material_get(ob, index + 1);
+ }
return rna_pointer_inherit_refine(ptr, &RNA_Material, ma);
}
@@ -1284,7 +1297,7 @@ static void rna_MaterialSlot_material_set(PointerRNA *ptr,
struct ReportList *UNUSED(reports))
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
BLI_assert(BKE_id_is_in_global_main(&ob->id));
BLI_assert(BKE_id_is_in_global_main(value.data));
@@ -1309,15 +1322,17 @@ static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value)
static int rna_MaterialSlot_link_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
-
- return ob->matbits[index] != 0;
+ int index = rna_MaterialSlot_index(ptr);
+ if (index < ob->totcol) {
+ return ob->matbits[index] != 0;
+ }
+ return false;
}
static void rna_MaterialSlot_link_set(PointerRNA *ptr, int value)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
if (value) {
ob->matbits[index] = 1;
@@ -1335,7 +1350,7 @@ static int rna_MaterialSlot_name_length(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1350,7 +1365,7 @@ static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1373,10 +1388,49 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr)
static char *rna_MaterialSlot_path(PointerRNA *ptr)
{
+ int index = rna_MaterialSlot_index(ptr);
+ return BLI_sprintfN("material_slots[%d]", index);
+}
+
+static int rna_Object_material_slots_length(PointerRNA *ptr)
+{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ if (DEG_is_evaluated_object(ob)) {
+ return BKE_object_material_count_eval(ob);
+ }
+ else {
+ return ob->totcol;
+ }
+}
- return BLI_sprintfN("material_slots[%d]", index);
+static void rna_Object_material_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ const int length = rna_Object_material_slots_length(ptr);
+ iter->internal.count.item = 0;
+ iter->internal.count.ptr = ptr->owner_id;
+ iter->valid = length > 0;
+}
+
+static void rna_Object_material_slots_next(CollectionPropertyIterator *iter)
+{
+ const int length = rna_Object_material_slots_length(&iter->ptr);
+ iter->internal.count.item++;
+ iter->valid = iter->internal.count.item < length;
+}
+
+static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter)
+{
+ PointerRNA ptr;
+ RNA_pointer_create((ID *)iter->internal.count.ptr,
+ &RNA_MaterialSlot,
+ /* Add one, so that `ptr->data` is not null. */
+ POINTER_FROM_INT(iter->internal.count.item + 1),
+ &ptr);
+ return ptr;
+}
+
+static void rna_Object_material_slots_end(CollectionPropertyIterator *UNUSED(iter))
+{
}
static PointerRNA rna_Object_display_get(PointerRNA *ptr)
@@ -1448,11 +1502,6 @@ static PointerRNA rna_Object_field_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, ob->pd);
}
@@ -1464,11 +1513,6 @@ static PointerRNA rna_Object_collision_get(PointerRNA *ptr)
return PointerRNA_NULL;
}
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_CollisionSettings, ob->pd);
}
@@ -2958,12 +3002,18 @@ static void rna_def_object(BlenderRNA *brna)
/* materials */
prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
RNA_def_property_struct_type(prop, "MaterialSlot");
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_PROP_NAME);
- /* don't dereference pointer! */
- RNA_def_property_collection_funcs(
- prop, NULL, NULL, NULL, "rna_iterator_array_get", NULL, NULL, NULL, NULL);
+ /* Don't dereference the material slot pointer, it is the slot index encoded in a pointer. */
+ RNA_def_property_collection_funcs(prop,
+ "rna_Object_material_slots_begin",
+ "rna_Object_material_slots_next",
+ "rna_Object_material_slots_end",
+ "rna_Object_material_slots_get",
+ "rna_Object_material_slots_length",
+ NULL,
+ NULL,
+ NULL);
RNA_def_property_ui_text(prop, "Material Slots", "Material slots in the object");
prop = RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index df628caa000..e463323c6dc 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -296,8 +296,8 @@ static bool rna_Object_visible_in_viewport_get(Object *ob, View3D *v3d)
static void rna_Object_mat_convert_space(Object *ob,
ReportList *reports,
bPoseChannel *pchan,
- float *mat,
- float *mat_ret,
+ float mat[16],
+ float mat_ret[16],
int from,
int to)
{
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index d94e68a6808..7ff2a82a465 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -1413,24 +1413,12 @@ static const EnumPropertyItem *rna_Particle_ren_as_itemf(bContext *UNUSED(C),
static PointerRNA rna_Particle_field1_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd) {
- part->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd);
}
static PointerRNA rna_Particle_field2_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd2) {
- part->pd2 = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd2);
}
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index ba65e42895c..b8bb4f58dcd 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -1359,12 +1359,27 @@ static void rna_def_pose_channel(BlenderRNA *brna)
RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_dependency_update");
- prop = RNA_def_property(srna, "custom_shape_scale", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "custom_scale");
- RNA_def_property_range(prop, 0.0f, 1000.0f);
+ prop = RNA_def_property(srna, "custom_shape_scale_xyz", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_scale_xyz");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_float_array_default(prop, rna_default_scale_3d);
RNA_def_property_ui_text(prop, "Custom Shape Scale", "Adjust the size of the custom shape");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+ prop = RNA_def_property(srna, "custom_shape_translation", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_translation");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Translation", "Adjust the location of the custom shape");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
+ prop = RNA_def_property(srna, "custom_shape_rotation_euler", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "custom_rotation_euler");
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Rotation", "Adjust the rotation of the custom shape");
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
prop = RNA_def_property(srna, "use_custom_shape_bone_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "drawflag", PCHAN_DRAW_NO_CUSTOM_BONE_SIZE);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c
index 29516830058..0d35365c2d8 100644
--- a/source/blender/makesrna/intern/rna_pose_api.c
+++ b/source/blender/makesrna/intern/rna_pose_api.c
@@ -47,7 +47,7 @@
# include "BLI_ghash.h"
-static float rna_PoseBone_do_envelope(bPoseChannel *chan, float *vec)
+static float rna_PoseBone_do_envelope(bPoseChannel *chan, float vec[3])
{
Bone *bone = chan->bone;
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index c2089004da2..c49b41867a8 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -98,7 +98,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
}
}
-static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float *aspect)
+static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float aspect[2])
{
if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) {
BMEditMesh *em;
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 4481555b931..81acedb76f2 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -786,7 +786,8 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowMask_update");
prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL);
- RNA_def_property_ui_range(prop, 0.5, 40.0, 10, 2);
+ RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC);
RNA_def_property_ui_text(
prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 24d051fecc8..9ba92431723 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -293,7 +293,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
if (SEQ_transform_test_overlap(seqbase, seq)) {
SEQ_transform_seqbase_shuffle(seqbase, seq, scene); /* XXX - BROKEN!, uses context seqbasep */
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
}
/* A simple wrapper around above func, directly usable as prop update func.
@@ -476,7 +476,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
/* XXX - BROKEN!, uses context seqbasep */
SEQ_transform_seqbase_shuffle_ex(seqbase, seq, scene, channel_delta);
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -505,7 +505,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor
data.data = transform;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
return data.seq;
}
@@ -557,7 +557,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop)
data.data = crop;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
return data.seq;
}
@@ -951,7 +951,7 @@ static Sequence *sequence_get_by_proxy(Editing *ed, StripProxy *proxy)
data.seq = NULL;
data.data = proxy;
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
return data.seq;
}
@@ -990,18 +990,14 @@ static int colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt)
{
SequenceSearchData *data = arg_pt;
- if (seq->modifiers.first) {
- SequenceModifierData *smd = seq->modifiers.first;
+ for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) {
+ if (smd->type == seqModifierType_ColorBalance) {
+ ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
- for (smd = seq->modifiers.first; smd; smd = smd->next) {
- if (smd->type == seqModifierType_ColorBalance) {
- ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
-
- if (&cbmd->color_balance == data->data) {
- data->seq = seq;
- data->smd = smd;
- return -1; /* done so bail out */
- }
+ if (&cbmd->color_balance == data->data) {
+ data->seq = seq;
+ data->smd = smd;
+ return -1; /* done so bail out */
}
}
}
@@ -1020,7 +1016,7 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed,
data.data = cb;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
*r_smd = data.smd;
@@ -1144,7 +1140,7 @@ static Sequence *sequence_get_by_modifier(Editing *ed, SequenceModifierData *smd
data.data = smd;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
return data.seq;
}
@@ -2394,11 +2390,10 @@ static void rna_def_scene(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Input", "Input type to use for the Scene strip");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_use_sequence");
- prop = RNA_def_property(srna, "use_grease_pencil", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_GPENCIL);
- RNA_def_property_ui_text(
- prop, "Use Grease Pencil", "Show Grease Pencil strokes in OpenGL previews");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
+ prop = RNA_def_property(srna, "use_annotations", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_ANNOTATION);
+ RNA_def_property_ui_text(prop, "Use Annotations", "Show Annotations in OpenGL previews");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
rna_def_filter_video(srna);
rna_def_proxy(srna);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 2a513691762..aa07ff4006a 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -3046,6 +3046,10 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
+ if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE &&
+ !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ }
}
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
@@ -3091,6 +3095,11 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN
continue;
}
}
+ if (component_type == GEO_COMPONENT_TYPE_CURVE) {
+ if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ continue;
+ }
+ }
if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) {
RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item);
}
@@ -7485,6 +7494,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
ICON_POINTCLOUD_DATA,
"Point Cloud",
"Point cloud component containing only point data"},
+ {GEO_COMPONENT_TYPE_CURVE,
+ "CURVE",
+ ICON_CURVE_DATA,
+ "Curve",
+ "Curve component containing spline and control point data"},
{GEO_COMPONENT_TYPE_INSTANCES,
"INSTANCES",
ICON_EMPTY_AXIS,
diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index c136605c727..336359a9dc0 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -697,7 +697,7 @@ static MovieTrackingMarker *rna_trackingMarkers_find_frame(MovieTrackingTrack *t
static MovieTrackingMarker *rna_trackingMarkers_insert_frame(MovieTrackingTrack *track,
int framenr,
- float *co)
+ float co[2])
{
MovieTrackingMarker marker, *new_marker;
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 26887b51f81..bacd3943141 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6160,7 +6160,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
prop,
"Python Scripts Directory",
"Alternate script path, matching the default layout with subdirectories: "
- "startup, add-ons and modules (requires restart)");
+ "startup, addons, modules, and presets (requires restart)");
/* TODO, editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 54caaed9231..91327b97fe4 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_nodes.cc
+ intern/MOD_nodes_evaluator.cc
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
@@ -118,6 +119,7 @@ set(SRC
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
+ intern/MOD_nodes_evaluator.hh
intern/MOD_solidify_util.h
intern/MOD_ui_common.h
intern/MOD_util.h
@@ -191,6 +193,18 @@ if(WITH_GMP)
)
endif()
+if(WITH_TBB)
+ add_definitions(-DWITH_TBB)
+
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${TBB_LIBRARIES}
+ )
+endif()
+
if(WITH_OPENVDB)
list(APPEND INC
../../../intern/openvdb
@@ -206,7 +220,7 @@ if(WITH_OPENVDB)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 6769f14f88f..649d36e3d57 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 0282f0d2934..93a9e76ffe4 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -1021,7 +1021,6 @@ ModifierTypeInfo modifierType_Array = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index a94411d897e..8fdd222402e 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -448,7 +448,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
index 40d5386c2fa..9b8782737c3 100644
--- a/source/blender/modifiers/intern/MOD_boolean.cc
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_Boolean = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 0b1c661baed..c38e5126f6b 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -347,7 +347,6 @@ ModifierTypeInfo modifierType_Build = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index f905a38ae12..715bc26e5d3 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -591,7 +591,6 @@ ModifierTypeInfo modifierType_Cast = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index a25d65347c5..40d027f3044 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -311,7 +311,6 @@ ModifierTypeInfo modifierType_Cloth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index e72e0279263..5dd57469914 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 001c7d8d098..fef235b456b 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -852,7 +852,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index d5d53edfd54..20dbb299767 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -236,7 +236,6 @@ ModifierTypeInfo modifierType_Curve = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index 8b299a82f95..dbdc76f0edc 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -495,7 +495,6 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 03db09a5aec..faad1175f3a 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -300,7 +300,6 @@ ModifierTypeInfo modifierType_Decimate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index abe78943508..a7ac9f618af 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -507,7 +507,6 @@ ModifierTypeInfo modifierType_Displace = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index 3e607e88cdd..8b1d541d45d 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index e02befd7efa..2874bebe13a 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -187,7 +187,6 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index c12019a325e..e1197439c7c 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 8a8d6f2305f..36d2ab2a11a 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Fluid = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 2fa05a319d5..ff581e92cdd 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -574,7 +574,6 @@ ModifierTypeInfo modifierType_Hook = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index bda0f9ba5a4..6efeec1970f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -889,7 +889,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index fc527304e76..78e0bf3fa8f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ init_data,
/* requiredDataMask */ required_data_mask,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index e3c42e39dda..29d1ecf6050 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -193,7 +193,6 @@ ModifierTypeInfo modifierType_Lattice = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 191d39d9fce..812bfe3b375 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -463,7 +463,6 @@ ModifierTypeInfo modifierType_Mask = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index cc007651733..778b5746471 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -20,6 +20,7 @@
#include <vector>
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
@@ -204,7 +205,9 @@ static float compute_voxel_size(const ModifierEvalContext *ctx,
}
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume)
+static Volume *mesh_to_volume(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Volume *input_volume)
{
#ifdef WITH_OPENVDB
using namespace blender;
@@ -278,6 +281,17 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ Volume *result_volume = mesh_to_volume(md, ctx, input_volume);
+ if (result_volume != input_volume) {
+ geometry_set->replace_volume(result_volume);
+ }
+}
+
ModifierTypeInfo modifierType_MeshToVolume = {
/* name */ "Mesh to Volume",
/* structName */ "MeshToVolumeModifierData",
@@ -295,8 +309,7 @@ ModifierTypeInfo modifierType_MeshToVolume = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 6d2e0f242d7..361454120ca 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -389,7 +389,6 @@ ModifierTypeInfo modifierType_MeshCache = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index a94dd6da477..8e1e03570b5 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -644,7 +644,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 2c01857adb1..c2f9cd8c867 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -271,7 +271,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index b800ce7f803..6116cf8146a 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Mirror = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 1182c8db093..c3b34f3cd23 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -520,7 +520,6 @@ ModifierTypeInfo modifierType_Multires = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index d808052e5f5..37a21a3c14b 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -75,16 +75,14 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
+#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
#include "ED_spreadsheet.h"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_exec.hh"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
-#include "NOD_type_conversions.hh"
using blender::float3;
using blender::FunctionRef;
@@ -95,12 +93,8 @@ using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
-using blender::bke::PersistentCollectionHandle;
-using blender::bke::PersistentDataHandleMap;
-using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
using blender::fn::GPointer;
-using blender::fn::GValueMap;
using blender::nodes::GeoNodeExecParams;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -185,7 +179,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
add_collection_relation(ctx, *object.instance_collection);
}
- else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
+ else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
@@ -268,368 +262,6 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
-class GeometryNodesEvaluator {
- public:
- using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
-
- private:
- blender::LinearAllocator<> allocator_;
- Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
- Vector<DInputSocket> group_outputs_;
- blender::nodes::MultiFunctionByNode &mf_by_node_;
- const blender::nodes::DataTypeConversions &conversions_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
- LogSocketValueFn log_socket_value_fn_;
-
- public:
- GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
- Vector<DInputSocket> group_outputs,
- blender::nodes::MultiFunctionByNode &mf_by_node,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph,
- LogSocketValueFn log_socket_value_fn)
- : group_outputs_(std::move(group_outputs)),
- mf_by_node_(mf_by_node),
- conversions_(blender::nodes::get_implicit_type_conversions()),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph),
- log_socket_value_fn_(std::move(log_socket_value_fn))
- {
- for (auto item : group_input_data.items()) {
- this->log_socket_value(item.key, item.value);
- this->forward_to_inputs(item.key, item.value);
- }
- }
-
- Vector<GMutablePointer> execute()
- {
- Vector<GMutablePointer> results;
- for (const DInputSocket &group_output : group_outputs_) {
- Vector<GMutablePointer> result = this->get_input_values(group_output);
- this->log_socket_value(group_output, result);
- results.append(result[0]);
- }
- for (GMutablePointer value : value_by_input_.values()) {
- value.destruct();
- }
- return results;
- }
-
- private:
- Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
- {
- Vector<DSocket> from_sockets;
- socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
-
- if (from_sockets.is_empty()) {
- /* The input is not connected, use the value from the socket itself. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- return {get_unlinked_input_value(socket_to_compute, type)};
- }
-
- /* Multi-input sockets contain a vector of inputs. */
- if (socket_to_compute->is_multi_input_socket()) {
- return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
- }
-
- const DSocket from_socket = from_sockets[0];
- GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
- return {value};
- }
-
- Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
- const Span<DSocket> from_sockets)
- {
- Vector<GMutablePointer> values;
- for (const int i : from_sockets.index_range()) {
- const DSocket from_socket = from_sockets[i];
- const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
- if (first_occurence == -1) {
- values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
- }
- else {
- /* If the same from-socket occurs more than once, we make a copy of the first value. This
- * can happen when a node linked to a multi-input-socket is muted. */
- GMutablePointer value = values[first_occurence];
- const CPPType *type = value.type();
- void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
- type->copy_to_uninitialized(value.get(), copy_buffer);
- values.append({type, copy_buffer});
- }
- }
- return values;
- }
-
- GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
- const DSocket from_socket)
- {
- if (from_socket->is_output()) {
- const DOutputSocket from_output_socket{from_socket};
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
- from_output_socket);
- std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
- if (value.has_value()) {
- /* This input has been computed before, return it directly. */
- return {*value};
- }
-
- /* Compute the socket now. */
- this->compute_output_and_forward(from_output_socket);
- return {value_by_input_.pop(key)};
- }
-
- /* Get value from an unlinked input socket. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- const DInputSocket from_input_socket{from_socket};
- return {get_unlinked_input_value(from_input_socket, type)};
- }
-
- void compute_output_and_forward(const DOutputSocket socket_to_compute)
- {
- const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
-
- if (!socket_to_compute->is_available()) {
- /* If the output is not available, use a default value. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(type.default_value(), buffer);
- this->forward_to_inputs(socket_to_compute, {type, buffer});
- return;
- }
-
- /* Prepare inputs required to execute the node. */
- GValueMap<StringRef> node_inputs_map{allocator_};
- for (const InputSocketRef *input_socket : node->inputs()) {
- if (input_socket->is_available()) {
- DInputSocket dsocket{node.context(), input_socket};
- Vector<GMutablePointer> values = this->get_input_values(dsocket);
- this->log_socket_value(dsocket, values);
- for (int i = 0; i < values.size(); ++i) {
- /* Values from Multi Input Sockets are stored in input map with the format
- * <identifier>[<index>]. */
- blender::StringRefNull key = allocator_.copy_string(
- input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
- node_inputs_map.add_new_direct(key, std::move(values[i]));
- }
- }
- }
-
- /* Execute the node. */
- GValueMap<StringRef> node_outputs_map{allocator_};
- GeoNodeExecParams params{
- node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_};
- this->execute_node(node, params);
-
- /* Forward computed outputs to linked input sockets. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- if (output_socket->is_available()) {
- const DOutputSocket dsocket{node.context(), output_socket};
- GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
- this->log_socket_value(dsocket, value);
- this->forward_to_inputs(dsocket, value);
- }
- }
- }
-
- void log_socket_value(const DSocket socket, Span<GPointer> values)
- {
- if (log_socket_value_fn_) {
- log_socket_value_fn_(socket, values);
- }
- }
-
- void log_socket_value(const DSocket socket, Span<GMutablePointer> values)
- {
- this->log_socket_value(socket, values.cast<GPointer>());
- }
-
- void log_socket_value(const DSocket socket, GPointer value)
- {
- this->log_socket_value(socket, Span<GPointer>(&value, 1));
- }
-
- void execute_node(const DNode node, GeoNodeExecParams params)
- {
- const bNode &bnode = params.node();
-
- /* Use the geometry-node-execute callback if it exists. */
- if (bnode.typeinfo->geometry_node_execute != nullptr) {
- bnode.typeinfo->geometry_node_execute(params);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
- if (multi_function != nullptr) {
- this->execute_multi_function_node(node, params, *multi_function);
- return;
- }
-
- /* Just output default values if no implementation exists. */
- this->execute_unknown_node(node, params);
- }
-
- void execute_multi_function_node(const DNode node,
- GeoNodeExecParams params,
- const MultiFunction &fn)
- {
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
- Vector<GMutablePointer> input_data;
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (socket_ref->is_available()) {
- GMutablePointer data = params.extract_input(socket_ref->identifier());
- fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
- input_data.append(data);
- }
- }
- Vector<GMutablePointer> output_data;
- for (const OutputSocketRef *socket_ref : node->outputs()) {
- if (socket_ref->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
- output_data.append(GMutablePointer(type, buffer));
- }
- }
- fn.call(IndexRange(1), fn_params, fn_context);
- for (GMutablePointer value : input_data) {
- value.destruct();
- }
- int output_index = 0;
- for (const int i : node->outputs().index_range()) {
- if (node->output(i).is_available()) {
- GMutablePointer value = output_data[output_index];
- params.set_output_by_move(node->output(i).identifier(), value);
- value.destruct();
- output_index++;
- }
- }
- }
-
- void execute_unknown_node(const DNode node, GeoNodeExecParams params)
- {
- for (const OutputSocketRef *socket : node->outputs()) {
- if (socket->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
- }
- }
- }
-
- void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
- {
- /* For all sockets that are linked with the from_socket push the value to their node. */
- Vector<DInputSocket> to_sockets_all;
-
- auto handle_target_socket_fn = [&](DInputSocket to_socket) {
- to_sockets_all.append_non_duplicates(to_socket);
- };
- auto handle_skipped_socket_fn = [&, this](DSocket socket) {
- this->log_socket_value(socket, value_to_forward);
- };
-
- from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
-
- const CPPType &from_type = *value_to_forward.type();
- Vector<DInputSocket> to_sockets_same_type;
- for (const DInputSocket &to_socket : to_sockets_all) {
- const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- if (from_type == to_type) {
- to_sockets_same_type.append(to_socket);
- }
- else {
- void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
- if (conversions_.is_convertible(from_type, to_type)) {
- conversions_.convert_to_uninitialized(
- from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- to_type.copy_to_uninitialized(to_type.default_value(), buffer);
- }
- add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
- }
- }
-
- if (to_sockets_same_type.size() == 0) {
- /* This value is not further used, so destruct it. */
- value_to_forward.destruct();
- }
- else if (to_sockets_same_type.size() == 1) {
- /* This value is only used on one input socket, no need to copy it. */
- const DInputSocket to_socket = to_sockets_same_type[0];
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
-
- add_value_to_input_socket(key, value_to_forward);
- }
- else {
- /* Multiple inputs use the value, make a copy for every input except for one. */
- const DInputSocket first_to_socket = to_sockets_same_type[0];
- Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
- const CPPType &type = *value_to_forward.type();
- const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
- from_socket);
- add_value_to_input_socket(first_key, value_to_forward);
- for (const DInputSocket &to_socket : other_to_sockets) {
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(value_to_forward.get(), buffer);
- add_value_to_input_socket(key, GMutablePointer{type, buffer});
- }
- }
- }
-
- void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
- GMutablePointer value)
- {
- value_by_input_.add_new(key, value);
- }
-
- GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
- const CPPType &required_type)
- {
- bNodeSocket *bsocket = socket->bsocket();
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
-
- if (bsocket->type == SOCK_OBJECT) {
- Object *object = socket->default_value<bNodeSocketValueObject>()->value;
- PersistentObjectHandle object_handle = handle_map_.lookup(object);
- new (buffer) PersistentObjectHandle(object_handle);
- }
- else if (bsocket->type == SOCK_COLLECTION) {
- Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
- PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
- new (buffer) PersistentCollectionHandle(collection_handle);
- }
- else {
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
- }
-
- if (type == required_type) {
- return {type, buffer};
- }
- if (conversions_.is_convertible(type, required_type)) {
- void *converted_buffer = allocator_.allocate(required_type.size(),
- required_type.alignment());
- conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
- void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
- required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
- }
-};
-
/**
* This code is responsible for creating the new property and also creating the group of
* properties in the prop_ui_container group for the UI info, the mapping for which is
@@ -649,9 +281,7 @@ struct SocketPropertyType {
IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property,
- const PersistentDataHandleMap &handles,
- void *r_value);
+ void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
@@ -744,9 +374,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) {
+ [](const IDProperty &property, void *r_value) {
if (property.type == IDP_FLOAT) {
*(float *)r_value = IDP_Float(&property);
}
@@ -787,9 +415,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
};
return &int_type;
}
@@ -832,9 +458,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
property.len == 3;
},
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ },
};
return &vector_type;
}
@@ -864,9 +490,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(bool *)r_value = IDP_Int(&property) != 0; },
+ [](const IDProperty &property, void *r_value) {
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ },
};
return &boolean_type;
}
@@ -886,9 +512,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { new (r_value) std::string(IDP_String(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ new (r_value) std::string(IDP_String(&property));
+ },
};
return &string_type;
}
@@ -905,10 +531,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- new (r_value) PersistentObjectHandle(handles.lookup(object));
+ *(Object **)r_value = object;
},
};
return &object_type;
@@ -926,10 +552,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- new (r_value) PersistentCollectionHandle(handles.lookup(collection));
+ *(Collection **)r_value = collection;
},
};
return &collection_type;
@@ -1017,7 +643,6 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const PersistentDataHandleMap &handle_map,
const bNodeSocket &socket,
const CPPType &cpp_type,
void *r_value)
@@ -1041,22 +666,7 @@ static void initialize_group_input(NodesModifierData &nmd,
blender::nodes::socket_cpp_value_get(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, handle_map, r_value);
-}
-
-static void fill_data_handle_map(const NodesModifierSettings &settings,
- const DerivedNodeTree &tree,
- PersistentDataHandleMap &handle_map)
-{
- Set<ID *> used_ids;
- find_used_ids_from_settings(settings, used_ids);
- find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids);
-
- int current_handle = 0;
- for (ID *id : used_ids) {
- handle_map.add(current_handle, *id);
- current_handle++;
- }
+ property_type->init_cpp_value(*property, r_value);
}
static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees,
@@ -1247,9 +857,6 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
blender::LinearAllocator<> &allocator = scope.linear_allocator();
blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
- PersistentDataHandleMap handle_map;
- fill_data_handle_map(nmd->settings, tree, handle_map);
-
Map<DOutputSocket, GMutablePointer> group_inputs;
const DTreeContext *root_context = &tree.root_context();
@@ -1275,7 +882,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
@@ -1300,21 +907,19 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
log_ui_hints(socket, values, ctx->object, nmd);
};
- GeometryNodesEvaluator evaluator{group_inputs,
- group_outputs,
- mf_by_node,
- handle_map,
- ctx->object,
- (ModifierData *)nmd,
- ctx->depsgraph,
- log_socket_value};
-
- Vector<GMutablePointer> results = evaluator.execute();
- BLI_assert(results.size() == 1);
- GMutablePointer result = results[0];
-
- GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
- return output_geometry;
+ blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
+ eval_params.input_values = group_inputs;
+ eval_params.output_sockets = group_outputs;
+ eval_params.mf_by_node = &mf_by_node;
+ eval_params.modifier_ = nmd;
+ eval_params.depsgraph = ctx->depsgraph;
+ eval_params.self_object = ctx->object;
+ eval_params.log_socket_value_fn = log_socket_value;
+ blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+
+ BLI_assert(eval_params.r_output_values.size() == 1);
+ GMutablePointer result = eval_params.r_output_values[0];
+ return result.relocate_out<GeometrySet>();
}
/**
@@ -1453,36 +1058,37 @@ static void draw_property_for_socket(uiLayout *layout,
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
- if (property != nullptr && property_type->is_correct_type(*property)) {
+ if (property == nullptr || !property_type->is_correct_type(*property)) {
+ return;
+ }
- char socket_id_esc[sizeof(socket.identifier) * 2];
- BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+ char socket_id_esc[sizeof(socket.identifier) * 2];
+ BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
- char rna_path[sizeof(socket_id_esc) + 4];
- BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
+ char rna_path[sizeof(socket_id_esc) + 4];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
- /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
- * information about what type of ID to select for editing the values. This is because
- * pointer IDProperties contain no information about their type. */
- switch (socket.type) {
- case SOCK_OBJECT: {
- uiItemPointerR(
- layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
- break;
- }
- case SOCK_COLLECTION: {
- uiItemPointerR(layout,
- md_ptr,
- rna_path,
- bmain_ptr,
- "collections",
- socket.name,
- ICON_OUTLINER_COLLECTION);
- break;
- }
- default:
- uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
+ * information about what type of ID to select for editing the values. This is because
+ * pointer IDProperties contain no information about their type. */
+ switch (socket.type) {
+ case SOCK_OBJECT: {
+ uiItemPointerR(
+ layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
+ break;
}
+ case SOCK_COLLECTION: {
+ uiItemPointerR(layout,
+ md_ptr,
+ rna_path,
+ bmain_ptr,
+ "collections",
+ socket.name,
+ ICON_OUTLINER_COLLECTION);
+ break;
+ }
+ default:
+ uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
}
}
@@ -1594,7 +1200,6 @@ ModifierTypeInfo modifierType_Nodes = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
new file mode 100644
index 00000000000..ac5249b40a2
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -0,0 +1,1567 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "MOD_nodes_evaluator.hh"
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_type_conversions.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "FN_generic_value_map.hh"
+#include "FN_multi_function.hh"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_stack.hh"
+#include "BLI_task.h"
+#include "BLI_task.hh"
+#include "BLI_vector_set.hh"
+
+namespace blender::modifiers::geometry_nodes {
+
+using fn::CPPType;
+using fn::GValueMap;
+using nodes::GeoNodeExecParams;
+using namespace fn::multi_function_types;
+
+enum class ValueUsage : uint8_t {
+ /* The value is definitely used. */
+ Required,
+ /* The value may be used. */
+ Maybe,
+ /* The value will definitely not be used. */
+ Unused,
+};
+
+struct SingleInputValue {
+ /**
+ * Points either to null or to a value of the type of input.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValueItem {
+ /**
+ * The socket where this value is coming from. This is required to sort the inputs correctly
+ * based on the link order later on.
+ */
+ DSocket origin;
+ /**
+ * Should only be null directly after construction. After that it should always point to a value
+ * of the correct type.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValue {
+ /**
+ * Collection of all the inputs that have been provided already. Note, the same origin can occur
+ * multiple times. However, it is guaranteed that if two items have the same origin, they will
+ * also have the same value (the pointer is different, but they point to values that would
+ * compare equal).
+ */
+ Vector<MultiInputValueItem> items;
+ /**
+ * Number of items that need to be added until all inputs have been provided.
+ */
+ int expected_size = 0;
+};
+
+struct InputState {
+
+ /**
+ * Type of the socket. If this is null, the socket should just be ignored.
+ */
+ const CPPType *type = nullptr;
+
+ /**
+ * Value of this input socket. By default, the value is empty. When other nodes are done
+ * computing their outputs, the computed values will be forwarded to linked input sockets.
+ * The value will then live here until it is consumed by the node or it was found that the value
+ * is not needed anymore.
+ * Whether the `single` or `multi` value is used depends on the socket.
+ */
+ union {
+ SingleInputValue *single;
+ MultiInputValue *multi;
+ } value;
+
+ /**
+ * How the node intends to use this input. By default all inputs may be used. Based on which
+ * outputs are used, a node can tell the evaluator that an input will definitely be used or is
+ * never used. This allows the evaluator to free values early, avoid copies and other unnecessary
+ * computations.
+ */
+ ValueUsage usage = ValueUsage::Maybe;
+
+ /**
+ * True when this input is/was used for an execution. While a node is running, only the inputs
+ * that have this set to true are allowed to be used. This makes sure that inputs created while
+ * the node is running correctly trigger the node to run again. Furthermore, it gives the node a
+ * consistent view of which inputs are available that does not change unexpectedly.
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it. If this is true, the value can be read without a lock as well, because the value is not
+ * changed by others anymore.
+ */
+ bool was_ready_for_execution = false;
+};
+
+struct OutputState {
+ /**
+ * If this output has been computed and forwarded already. If this is true, the value is not
+ * computed/forwarded again.
+ */
+ bool has_been_computed = false;
+
+ /**
+ * Keeps track of how the output value is used. If a connected input becomes required, this
+ * output has to become required as well. The output becomes ignored when it has zero potential
+ * users that are counted below.
+ */
+ ValueUsage output_usage = ValueUsage::Maybe;
+
+ /**
+ * This is a copy of `output_usage` that is done right before node execution starts. This is
+ * done so that the node gets a consistent view of what outputs are used, even when this changes
+ * while the node is running (the node might be reevaluated in that case).
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it.
+ */
+ ValueUsage output_usage_for_execution = ValueUsage::Maybe;
+
+ /**
+ * Counts how many times the value from this output might be used. If this number reaches zero,
+ * the output is not needed anymore.
+ */
+ int potential_users = 0;
+};
+
+enum class NodeScheduleState {
+ /**
+ * Default state of every node.
+ */
+ NotScheduled,
+ /**
+ * The node has been added to the task group and will be executed by it in the future.
+ */
+ Scheduled,
+ /**
+ * The node is currently running.
+ */
+ Running,
+ /**
+ * The node is running and has been rescheduled while running. In this case the node will run
+ * again. However, we don't add it to the task group immediately, because then the node might run
+ * twice at the same time, which is not allowed. Instead, once the node is done running, it will
+ * reschedule itself.
+ */
+ RunningAndRescheduled,
+};
+
+struct NodeState {
+ /**
+ * Needs to be locked when any data in this state is accessed that is not explicitely marked as
+ * otherwise.
+ */
+ std::mutex mutex;
+
+ /**
+ * States of the individual input and output sockets. One can index into these arrays without
+ * locking. However, to access the data inside a lock is generally necessary.
+ *
+ * These spans have to be indexed with the socket index. Unavailable sockets have a state as
+ * well. Maybe we can handle unavailable sockets differently in Blender in general, so I did not
+ * want to add complexity around it here.
+ */
+ MutableSpan<InputState> inputs;
+ MutableSpan<OutputState> outputs;
+
+ /**
+ * Nodes that don't support lazyness have some special handling the first time they are executed.
+ */
+ bool non_lazy_node_is_initialized = false;
+
+ /**
+ * Used to check that nodes that don't support lazyness do not run more than once.
+ */
+ bool has_been_executed = false;
+
+ /**
+ * Becomes true when the node will never be executed again and its inputs are destructed.
+ * Generally, a node has finished once all of its outputs with (potential) users have been
+ * computed.
+ */
+ bool node_has_finished = false;
+
+ /**
+ * Counts the number of values that still have to be forwarded to this node until it should run
+ * again. It counts values from a multi input socket separately.
+ * This is used as an optimization so that nodes are not scheduled unnecessarily in many cases.
+ */
+ int missing_required_inputs = 0;
+
+ /**
+ * A node is always in one specific schedule state. This helps to ensure that the same node does
+ * not run twice at the same time accidentally.
+ */
+ NodeScheduleState schedule_state = NodeScheduleState::NotScheduled;
+};
+
+/**
+ * Container for a node and its state. Packing them into a single struct allows the use of
+ * `VectorSet` instead of a `Map` for `node_states_` which simplifies parallel loops over all
+ * states.
+ *
+ * Equality operators and a hash function for `DNode` are provided so that one can lookup this type
+ * in `node_states_` just with a `DNode`.
+ */
+struct NodeWithState {
+ DNode node;
+ /* Store a pointer instead of `NodeState` directly to keep it small and movable. */
+ NodeState *state = nullptr;
+
+ friend bool operator==(const NodeWithState &a, const NodeWithState &b)
+ {
+ return a.node == b.node;
+ }
+
+ friend bool operator==(const NodeWithState &a, const DNode &b)
+ {
+ return a.node == b;
+ }
+
+ friend bool operator==(const DNode &a, const NodeWithState &b)
+ {
+ return a == b.node;
+ }
+
+ uint64_t hash() const
+ {
+ return node.hash();
+ }
+
+ static uint64_t hash_as(const DNode &node)
+ {
+ return node.hash();
+ }
+};
+
+class GeometryNodesEvaluator;
+
+/**
+ * Utility class that locks the state of a node. Having this is a separate class is useful because
+ * it allows methods to communicate that they expect the node to be locked.
+ */
+class LockedNode : NonCopyable, NonMovable {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+
+ public:
+ /**
+ * This is the node that is currently locked.
+ */
+ const DNode node;
+ NodeState &node_state;
+
+ /**
+ * Used to delay notifying (and therefore locking) other nodes until the current node is not
+ * locked anymore. This might not be strictly necessary to avoid deadlocks in the current code,
+ * but it is a good measure to avoid accidentally adding a deadlock later on. By not locking
+ * more than one node per thread at a time, deadlocks are avoided.
+ *
+ * The notifications will be send right after the node is not locked anymore.
+ */
+ Vector<DOutputSocket> delayed_required_outputs;
+ Vector<DOutputSocket> delayed_unused_outputs;
+ Vector<DNode> delayed_scheduled_nodes;
+
+ LockedNode(GeometryNodesEvaluator &evaluator, const DNode node, NodeState &node_state)
+ : evaluator_(evaluator), node(node), node_state(node_state)
+ {
+ node_state.mutex.lock();
+ }
+
+ ~LockedNode();
+};
+
+static const CPPType *get_socket_cpp_type(const DSocket socket)
+{
+ return nodes::socket_cpp_type_get(*socket->typeinfo());
+}
+
+static const CPPType *get_socket_cpp_type(const SocketRef &socket)
+{
+ return nodes::socket_cpp_type_get(*socket.typeinfo());
+}
+
+static bool node_supports_lazyness(const DNode node)
+{
+ return node->typeinfo()->geometry_node_execute_supports_lazyness;
+}
+
+/** Implements the callbacks that might be called when a node is executed. */
+class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+ NodeState &node_state_;
+
+ public:
+ NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, NodeState &node_state);
+
+ bool can_get_input(StringRef identifier) const override;
+ bool can_set_output(StringRef identifier) const override;
+ GMutablePointer extract_input(StringRef identifier) override;
+ Vector<GMutablePointer> extract_multi_input(StringRef identifier) override;
+ GPointer get_input(StringRef identifier) const override;
+ GMutablePointer alloc_output_value(const CPPType &type) override;
+ void set_output(StringRef identifier, GMutablePointer value) override;
+ void set_input_unused(StringRef identifier) override;
+ bool output_is_required(StringRef identifier) const override;
+
+ bool lazy_require_input(StringRef identifier) override;
+ bool lazy_output_is_required(StringRef identifier) const override;
+};
+
+class GeometryNodesEvaluator {
+ private:
+ /**
+ * This allocator lives on after the evaluator has been destructed. Therefore outputs of the
+ * entire evaluator should be allocated here.
+ */
+ LinearAllocator<> &outer_allocator_;
+ /**
+ * A local linear allocator for each thread. Only use this for values that do not need to live
+ * longer than the lifetime of the evaluator itself. Considerations for the future:
+ * - We could use an allocator that can free here, some temporary values don't live long.
+ * - If we ever run into false sharing bottlenecks, we could use local allocators that allocate
+ * on cache line boundaries. Note, just because a value is allocated in one specific thread,
+ * does not mean that it will only be used by that thread.
+ */
+ EnumerableThreadSpecific<LinearAllocator<>> local_allocators_;
+
+ /**
+ * Every node that is reachable from the output gets its own state. Once all states have been
+ * constructed, this map can be used for lookups from multiple threads.
+ */
+ VectorSet<NodeWithState> node_states_;
+
+ /**
+ * Contains all the tasks for the nodes that are currently scheduled.
+ */
+ TaskPool *task_pool_ = nullptr;
+
+ GeometryNodesEvaluationParams &params_;
+ const blender::nodes::DataTypeConversions &conversions_;
+
+ friend NodeParamsProvider;
+
+ public:
+ GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
+ : outer_allocator_(params.allocator),
+ params_(params),
+ conversions_(blender::nodes::get_implicit_type_conversions())
+ {
+ }
+
+ void execute()
+ {
+ task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH);
+
+ this->create_states_for_reachable_nodes();
+ this->forward_group_inputs();
+ this->schedule_initial_nodes();
+
+ /* This runs until all initially requested inputs have been computed. */
+ BLI_task_pool_work_and_wait(task_pool_);
+ BLI_task_pool_free(task_pool_);
+
+ this->extract_group_outputs();
+ this->destruct_node_states();
+ }
+
+ void create_states_for_reachable_nodes()
+ {
+ /* This does a depth first search for all the nodes that are reachable from the group
+ * outputs. This finds all nodes that are relevant. */
+ Stack<DNode> nodes_to_check;
+ /* Start at the output sockets. */
+ for (const DInputSocket &socket : params_.output_sockets) {
+ nodes_to_check.push(socket.node());
+ }
+ /* Use the local allocator because the states do not need to outlive the evaluator. */
+ LinearAllocator<> &allocator = local_allocators_.local();
+ while (!nodes_to_check.is_empty()) {
+ const DNode node = nodes_to_check.pop();
+ if (node_states_.contains_as(node)) {
+ /* This node has been handled already. */
+ continue;
+ }
+ /* Create a new state for the node. */
+ NodeState &node_state = *allocator.construct<NodeState>().release();
+ node_states_.add_new({node, &node_state});
+
+ /* Push all linked origins on the stack. */
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+ input.foreach_origin_socket(
+ [&](const DSocket origin) { nodes_to_check.push(origin.node()); });
+ }
+ }
+
+ /* Initialize the more complex parts of the node states in parallel. At this point no new
+ * node states are added anymore, so it is safe to lookup states from `node_states_` from
+ * multiple threads. */
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ LinearAllocator<> &allocator = this->local_allocators_.local();
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->initialize_node_state(item.node, *item.state, allocator);
+ }
+ });
+ }
+
+ void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator)
+ {
+ /* Construct arrays of the correct size. */
+ node_state.inputs = allocator.construct_array<InputState>(node->inputs().size());
+ node_state.outputs = allocator.construct_array<OutputState>(node->outputs().size());
+
+ /* Initialize input states. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ const DInputSocket socket = node.input(i);
+ if (!socket->is_available()) {
+ /* Unavailable sockets should never be used. */
+ input_state.type = nullptr;
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ input_state.type = type;
+ if (type == nullptr) {
+ /* This is not a known data socket, it shouldn't be used. */
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Construct the correct struct that can hold the input(s). */
+ if (socket->is_multi_input_socket()) {
+ input_state.value.multi = allocator.construct<MultiInputValue>().release();
+ /* Count how many values should be added until the socket is complete. */
+ socket.foreach_origin_socket(
+ [&](DSocket UNUSED(origin)) { input_state.value.multi->expected_size++; });
+ /* If no links are connected, we do read the value from socket itself. */
+ if (input_state.value.multi->expected_size == 0) {
+ input_state.value.multi->expected_size = 1;
+ }
+ }
+ else {
+ input_state.value.single = allocator.construct<SingleInputValue>().release();
+ }
+ }
+ /* Initialize output states. */
+ for (const int i : node->outputs().index_range()) {
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket = node.output(i);
+ if (!socket->is_available()) {
+ /* Unavailable outputs should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ if (type == nullptr) {
+ /* Non data sockets should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Count the number of potential users for this socket. */
+ socket.foreach_target_socket(
+ [&, this](const DInputSocket target_socket) {
+ const DNode target_node = target_socket.node();
+ if (!this->node_states_.contains_as(target_node)) {
+ /* The target node is not computed because it is not computed to the output. */
+ return;
+ }
+ output_state.potential_users += 1;
+ },
+ {});
+ if (output_state.potential_users == 0) {
+ /* If it does not have any potential users, it is unused. */
+ output_state.output_usage = ValueUsage::Unused;
+ }
+ }
+ }
+
+ void destruct_node_states()
+ {
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->destruct_node_state(item.node, *item.state);
+ }
+ });
+ }
+
+ void destruct_node_state(const DNode node, NodeState &node_state)
+ {
+ /* Need to destruct stuff manually, because it's allocated by a custom allocator. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ continue;
+ }
+ const InputSocketRef &socket_ref = node->input(i);
+ if (socket_ref.is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.~MultiInputValue();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ if (value != nullptr) {
+ input_state.type->destruct(value);
+ }
+ single_value.~SingleInputValue();
+ }
+ }
+
+ destruct_n(node_state.inputs.data(), node_state.inputs.size());
+ destruct_n(node_state.outputs.data(), node_state.outputs.size());
+
+ node_state.~NodeState();
+ }
+
+ void forward_group_inputs()
+ {
+ for (auto &&item : params_.input_values.items()) {
+ const DOutputSocket socket = item.key;
+ GMutablePointer value = item.value;
+ this->log_socket_value(socket, value);
+
+ const DNode node = socket.node();
+ if (!node_states_.contains_as(node)) {
+ /* The socket is not connected to any output. */
+ value.destruct();
+ continue;
+ }
+ this->forward_output(socket, value);
+ }
+ }
+
+ void schedule_initial_nodes()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ LockedNode locked_node{*this, node, node_state};
+ /* Setting an input as required will schedule any linked node. */
+ this->set_input_required(locked_node, socket);
+ }
+ }
+
+ void schedule_node(LockedNode &locked_node)
+ {
+ switch (locked_node.node_state.schedule_state) {
+ case NodeScheduleState::NotScheduled: {
+ /* The node will be scheduled once it is not locked anymore. We could schedule the node
+ * right here, but that would result in a deadlock if the task pool decides to run the task
+ * immediately (this only happens when Blender is started with a single thread). */
+ locked_node.node_state.schedule_state = NodeScheduleState::Scheduled;
+ locked_node.delayed_scheduled_nodes.append(locked_node.node);
+ break;
+ }
+ case NodeScheduleState::Scheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ case NodeScheduleState::Running: {
+ /* Reschedule node while it is running.
+ * The node will reschedule itself when it is done. */
+ locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled;
+ break;
+ }
+ case NodeScheduleState::RunningAndRescheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ }
+ }
+
+ static void run_node_from_task_pool(TaskPool *task_pool, void *task_data)
+ {
+ void *user_data = BLI_task_pool_user_data(task_pool);
+ GeometryNodesEvaluator &evaluator = *(GeometryNodesEvaluator *)user_data;
+ const NodeWithState *node_with_state = (const NodeWithState *)task_data;
+
+ evaluator.node_task_run(node_with_state->node, *node_with_state->state);
+ }
+
+ void node_task_run(const DNode node, NodeState &node_state)
+ {
+ /* These nodes are sometimes scheduled. We could also check for them in other places, but
+ * it's the easiest to do it here. */
+ if (node->is_group_input_node() || node->is_group_output_node()) {
+ return;
+ }
+
+ const bool do_execute_node = this->node_task_preprocessing(node, node_state);
+
+ /* Only execute the node if all prerequisites are met. There has to be an output that is
+ * required and all required inputs have to be provided already. */
+ if (do_execute_node) {
+ this->execute_node(node, node_state);
+ }
+
+ this->node_task_postprocessing(node, node_state);
+ }
+
+ bool node_task_preprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+ BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled);
+ node_state.schedule_state = NodeScheduleState::Running;
+
+ /* Early return if the node has finished already. */
+ if (locked_node.node_state.node_has_finished) {
+ return false;
+ }
+ /* Prepare outputs and check if actually any new outputs have to be computed. */
+ if (!this->prepare_node_outputs_for_execution(locked_node)) {
+ return false;
+ }
+ /* Initialize nodes that don't support lazyness. This is done after at least one output is
+ * required and before we check that all required inputs are provided. This reduces the
+ * number of "round-trips" through the task pool by one for most nodes. */
+ if (!node_state.non_lazy_node_is_initialized && !node_supports_lazyness(node)) {
+ this->initialize_non_lazy_node(locked_node);
+ node_state.non_lazy_node_is_initialized = true;
+ }
+ /* Prepare inputs and check if all required inputs are provided. */
+ if (!this->prepare_node_inputs_for_execution(locked_node)) {
+ return false;
+ }
+ return true;
+ }
+
+ /* A node is finished when it has computed all outputs that may be used. */
+ bool finish_node_if_possible(LockedNode &locked_node)
+ {
+ if (locked_node.node_state.node_has_finished) {
+ /* Early return in case this node is known to have finished already. */
+ return true;
+ }
+
+ /* Check if there is any output that might be used but has not been computed yet. */
+ bool has_remaining_output = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ if (output_state.has_been_computed) {
+ continue;
+ }
+ if (output_state.output_usage != ValueUsage::Unused) {
+ has_remaining_output = true;
+ break;
+ }
+ }
+ if (!has_remaining_output) {
+ /* If there are no remaining outputs, all the inputs can be destructed and/or can become
+ * unused. This can also trigger a chain reaction where nodes to the left become finished
+ * too. */
+ for (const int i : locked_node.node->inputs().index_range()) {
+ const DInputSocket socket = locked_node.node.input(i);
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.usage == ValueUsage::Maybe) {
+ this->set_input_unused(locked_node, socket);
+ }
+ else if (input_state.usage == ValueUsage::Required) {
+ /* The value was required, so it cannot become unused. However, we can destruct the
+ * value. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+ }
+ }
+ locked_node.node_state.node_has_finished = true;
+ }
+ return locked_node.node_state.node_has_finished;
+ }
+
+ bool prepare_node_outputs_for_execution(LockedNode &locked_node)
+ {
+ bool execution_is_necessary = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ /* Update the output usage for execution to the latest value. */
+ output_state.output_usage_for_execution = output_state.output_usage;
+ if (!output_state.has_been_computed) {
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Only evaluate when there is an output that is required but has not been computed. */
+ execution_is_necessary = true;
+ }
+ }
+ }
+ return execution_is_necessary;
+ }
+
+ void initialize_non_lazy_node(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node->inputs().index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable/non-data sockets. */
+ continue;
+ }
+ /* Nodes that don't support lazyness require all inputs. */
+ const DInputSocket input_socket = locked_node.node.input(i);
+ this->set_input_required(locked_node, input_socket);
+ }
+ }
+
+ /**
+ * Checks if requested inputs are available and "marks" all the inputs that are available
+ * during the node execution. Inputs that are provided after this function ends but before the
+ * node is executed, cannot be read by the node in the execution (note that this only affects
+ * nodes that support lazy inputs).
+ */
+ bool prepare_node_inputs_for_execution(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node_state.inputs.index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable and non-data sockets. */
+ continue;
+ }
+ const DInputSocket socket = locked_node.node.input(i);
+ const bool is_required = input_state.usage == ValueUsage::Required;
+
+ /* No need to check this socket again. */
+ if (input_state.was_ready_for_execution) {
+ continue;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ /* Checks if all the linked sockets have been provided already. */
+ if (multi_value.items.size() == multi_value.expected_size) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, input_state, multi_value.items);
+ }
+ else if (is_required) {
+ /* The input is required but is not fully provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, GPointer{input_state.type, single_value.value});
+ }
+ else if (is_required) {
+ /* The input is required but has not been provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ }
+ /* All required inputs have been provided. */
+ return true;
+ }
+
+ /**
+ * Actually execute the node. All the required inputs are available and at least one output is
+ * required.
+ */
+ void execute_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ if (node_state.has_been_executed) {
+ if (!node_supports_lazyness(node)) {
+ /* Nodes that don't support lazyness must not be executed more than once. */
+ BLI_assert_unreachable();
+ }
+ }
+ node_state.has_been_executed = true;
+
+ /* Use the geometry node execute callback if it exists. */
+ if (bnode.typeinfo->geometry_node_execute != nullptr) {
+ this->execute_geometry_node(node, node_state);
+ return;
+ }
+
+ /* Use the multi-function implementation if it exists. */
+ const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr);
+ if (multi_function != nullptr) {
+ this->execute_multi_function_node(node, *multi_function, node_state);
+ return;
+ }
+
+ this->execute_unknown_node(node, node_state);
+ }
+
+ void execute_geometry_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ NodeParamsProvider params_provider{*this, node, node_state};
+ GeoNodeExecParams params{params_provider};
+ bnode.typeinfo->geometry_node_execute(params);
+ }
+
+ void execute_multi_function_node(const DNode node,
+ const MultiFunction &fn,
+ NodeState &node_state)
+ {
+ MFContextBuilder fn_context;
+ MFParamsBuilder fn_params{fn, 1};
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ /* Prepare the inputs for the multi function. */
+ for (const int i : node->inputs().index_range()) {
+ const InputSocketRef &socket_ref = node->input(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ BLI_assert(!socket_ref.is_multi_input_socket());
+ InputState &input_state = node_state.inputs[i];
+ BLI_assert(input_state.was_ready_for_execution);
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value != nullptr);
+ fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
+ }
+ /* Prepare the outputs for the multi function. */
+ Vector<GMutablePointer> outputs;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ const CPPType &type = *get_socket_cpp_type(socket_ref);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
+ outputs.append({type, buffer});
+ }
+
+ fn.call(IndexRange(1), fn_params, fn_context);
+
+ /* Forward the computed outputs. */
+ int output_index = 0;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket{node.context(), &socket_ref};
+ GMutablePointer value = outputs[output_index];
+ this->forward_output(socket, value);
+ output_state.has_been_computed = true;
+ output_index++;
+ }
+ }
+
+ void execute_unknown_node(const DNode node, NodeState &node_state)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+ for (const OutputSocketRef *socket : node->outputs()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ /* Just forward the default value of the type as a fallback. That's typically better than
+ * crashing or doing nothing. */
+ OutputState &output_state = node_state.outputs[socket->index()];
+ output_state.has_been_computed = true;
+ void *buffer = allocator.allocate(type->size(), type->alignment());
+ type->copy_to_uninitialized(type->default_value(), buffer);
+ this->forward_output({node.context(), socket}, {*type, buffer});
+ }
+ }
+
+ void node_task_postprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+
+ const bool node_has_finished = this->finish_node_if_possible(locked_node);
+ const bool reschedule_requested = node_state.schedule_state ==
+ NodeScheduleState::RunningAndRescheduled;
+ node_state.schedule_state = NodeScheduleState::NotScheduled;
+ if (reschedule_requested && !node_has_finished) {
+ /* Either the node rescheduled itself or another node tried to schedule it while it ran. */
+ this->schedule_node(locked_node);
+ }
+
+ this->assert_expected_outputs_have_been_computed(locked_node);
+ }
+
+ void assert_expected_outputs_have_been_computed(LockedNode &locked_node)
+ {
+#ifdef DEBUG
+ /* Outputs can only be computed when all required inputs have been provided. */
+ if (locked_node.node_state.missing_required_inputs > 0) {
+ return;
+ }
+ /* If the node is still scheduled, it is not necessary that all its expected outputs are
+ * computed yet. */
+ if (locked_node.node_state.schedule_state == NodeScheduleState::Scheduled) {
+ return;
+ }
+
+ const bool supports_lazyness = node_supports_lazyness(locked_node.node);
+ /* Iterating over sockets instead of the states directly, because that makes it easier to
+ * figure out which socket is missing when one of the asserts is hit. */
+ for (const OutputSocketRef *socket_ref : locked_node.node->outputs()) {
+ OutputState &output_state = locked_node.node_state.outputs[socket_ref->index()];
+ if (supports_lazyness) {
+ /* Expected that at least all required sockets have been computed. If more outputs become
+ * required later, the node will be executed again. */
+ if (output_state.output_usage_for_execution == ValueUsage::Required) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ else {
+ /* Expect that all outputs that may be used have been computed, because the node cannot
+ * be executed again. */
+ if (output_state.output_usage_for_execution != ValueUsage::Unused) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(locked_node);
+#endif
+ }
+
+ void extract_group_outputs()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ BLI_assert(socket->is_available());
+ BLI_assert(!socket->is_multi_input_socket());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+
+ /* The value should have been computed by now. If this assert is hit, it means that there
+ * was some scheduling issue before. */
+ BLI_assert(value != nullptr);
+
+ /* Move value into memory owned by the outer allocator. */
+ const CPPType &type = *input_state.type;
+ void *buffer = outer_allocator_.allocate(type.size(), type.alignment());
+ type.move_to_uninitialized(value, buffer);
+
+ params_.r_output_values.append({type, buffer});
+ }
+ }
+
+ /**
+ * Load the required input from the socket or trigger nodes to the left to compute the value.
+ * When this function is called, the node will always be executed again eventually (either
+ * immediately, or when all required inputs have been computed by other nodes).
+ */
+ void set_input_required(LockedNode &locked_node, const DInputSocket input_socket)
+ {
+ BLI_assert(locked_node.node == input_socket.node());
+ InputState &input_state = locked_node.node_state.inputs[input_socket->index()];
+
+ /* Value set as unused cannot become used again. */
+ BLI_assert(input_state.usage != ValueUsage::Unused);
+
+ if (input_state.usage == ValueUsage::Required) {
+ /* The value is already required, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ /* Returning here also ensure that the code below is executed at most once per input. */
+ return;
+ }
+ input_state.usage = ValueUsage::Required;
+
+ if (input_state.was_ready_for_execution) {
+ /* The value was already ready, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+
+ /* Count how many values still have to be added to this input until it is "complete". */
+ int missing_values = 0;
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ missing_values = multi_value.expected_size - multi_value.items.size();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value == nullptr) {
+ missing_values = 1;
+ }
+ }
+ if (missing_values == 0) {
+ /* The input is fully available already, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* Increase the total number of missing required inputs. This ensures that the node will be
+ * scheduled correctly when all inputs have been provided. */
+ locked_node.node_state.missing_required_inputs += missing_values;
+
+ /* Get all origin sockets, because we have to tag those as required as well. */
+ Vector<DSocket> origin_sockets;
+ input_socket.foreach_origin_socket(
+ [&, this](const DSocket origin_socket) { origin_sockets.append(origin_socket); });
+
+ if (origin_sockets.is_empty()) {
+ /* If there are no origin sockets, just load the value from the socket directly. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, input_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ bool will_be_triggered_by_other_node = false;
+ for (const DSocket origin_socket : origin_sockets) {
+ if (origin_socket->is_input()) {
+ /* Load the value directly from the origin socket. In most cases this is an unlinked
+ * group input. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* The value has not been computed yet, so when it will be forwarded by another node, this
+ * node will be triggered. */
+ will_be_triggered_by_other_node = true;
+
+ locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket));
+ }
+ /* If this node will be triggered by another node, we don't have to schedule it now. */
+ if (!will_be_triggered_by_other_node) {
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void set_input_unused(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+
+ /* A required socket cannot become unused. */
+ BLI_assert(input_state.usage != ValueUsage::Required);
+
+ if (input_state.usage == ValueUsage::Unused) {
+ /* Nothing to do in this case. */
+ return;
+ }
+ input_state.usage = ValueUsage::Unused;
+
+ /* If the input is unused, it's value can be destructed now. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+
+ if (input_state.was_ready_for_execution) {
+ /* If the value was already computed, we don't need to notify origin nodes. */
+ return;
+ }
+
+ /* Notify origin nodes that might want to set its inputs as unused as well. */
+ socket.foreach_origin_socket([&, this](const DSocket origin_socket) {
+ if (origin_socket->is_input()) {
+ /* Values from these sockets are loaded directly from the sockets, so there is no node to
+ * notify. */
+ return;
+ }
+ /* Delay notification of the other node until this node is not locked anymore. */
+ locked_node.delayed_unused_outputs.append(DOutputSocket(origin_socket));
+ });
+ }
+
+ void send_output_required_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Output is marked as required already. So the node is scheduled already. */
+ return;
+ }
+ /* The origin node needs to be scheduled so that it provides the requested input
+ * eventually. */
+ output_state.output_usage = ValueUsage::Required;
+ this->schedule_node(locked_node);
+ }
+
+ void send_output_unused_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ output_state.potential_users -= 1;
+ if (output_state.potential_users == 0) {
+ /* The output socket has no users anymore. */
+ output_state.output_usage = ValueUsage::Unused;
+ /* Schedule the origin node in case it wants to set its inputs as unused as well. */
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void add_node_to_task_pool(const DNode node)
+ {
+ /* Push the task to the pool while it is not locked to avoid a deadlock in case when the task
+ * is executed immediately. */
+ const NodeWithState *node_with_state = node_states_.lookup_key_ptr_as(node);
+ BLI_task_pool_push(
+ task_pool_, run_node_from_task_pool, (void *)node_with_state, false, nullptr);
+ }
+
+ /**
+ * Moves a newly computed value from an output socket to all the inputs that might need it.
+ */
+ void forward_output(const DOutputSocket from_socket, GMutablePointer value_to_forward)
+ {
+ BLI_assert(value_to_forward.get() != nullptr);
+
+ Vector<DInputSocket> to_sockets;
+ auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) {
+ if (this->should_forward_to_socket(to_socket)) {
+ to_sockets.append(to_socket);
+ }
+ };
+ auto handle_skipped_socket_fn = [&, this](const DSocket socket) {
+ /* Log socket value on intermediate sockets to support e.g. attribute search or spreadsheet
+ * breadcrumbs on group nodes. */
+ this->log_socket_value(socket, value_to_forward);
+ };
+ from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
+
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ const CPPType &from_type = *value_to_forward.type();
+ Vector<DInputSocket> to_sockets_same_type;
+ for (const DInputSocket &to_socket : to_sockets) {
+ const CPPType &to_type = *get_socket_cpp_type(to_socket);
+ if (from_type == to_type) {
+ /* All target sockets that do not need a conversion will be handled afterwards. */
+ to_sockets_same_type.append(to_socket);
+ continue;
+ }
+ this->forward_to_socket_with_different_type(
+ allocator, value_to_forward, from_socket, to_socket, to_type);
+ }
+ this->forward_to_sockets_with_same_type(
+ allocator, to_sockets_same_type, value_to_forward, from_socket);
+ }
+
+ bool should_forward_to_socket(const DInputSocket socket)
+ {
+ const DNode to_node = socket.node();
+ const NodeWithState *target_node_with_state = node_states_.lookup_key_ptr_as(to_node);
+ if (target_node_with_state == nullptr) {
+ /* If the socket belongs to a node that has no state, the entire node is not used. */
+ return false;
+ }
+ NodeState &target_node_state = *target_node_with_state->state;
+ InputState &target_input_state = target_node_state.inputs[socket->index()];
+
+ std::lock_guard lock{target_node_state.mutex};
+ /* Do not forward to an input socket whose value won't be used. */
+ return target_input_state.usage != ValueUsage::Unused;
+ }
+
+ void forward_to_socket_with_different_type(LinearAllocator<> &allocator,
+ const GPointer value_to_forward,
+ const DOutputSocket from_socket,
+ const DInputSocket to_socket,
+ const CPPType &to_type)
+ {
+ const CPPType &from_type = *value_to_forward.type();
+
+ /* Allocate a buffer for the converted value. */
+ void *buffer = allocator.allocate(to_type.size(), to_type.alignment());
+
+ if (conversions_.is_convertible(from_type, to_type)) {
+ /* Do the conversion if possible. */
+ conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer);
+ }
+ else {
+ /* Cannot convert, use default value instead. */
+ to_type.copy_to_uninitialized(to_type.default_value(), buffer);
+ }
+ this->add_value_to_input_socket(to_socket, from_socket, {to_type, buffer});
+ }
+
+ void forward_to_sockets_with_same_type(LinearAllocator<> &allocator,
+ Span<DInputSocket> to_sockets,
+ GMutablePointer value_to_forward,
+ const DOutputSocket from_socket)
+ {
+ if (to_sockets.is_empty()) {
+ /* Value is not used anymore, so it can be destructed. */
+ value_to_forward.destruct();
+ }
+ else if (to_sockets.size() == 1) {
+ /* Value is only used by one input socket, no need to copy it. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ else {
+ /* Multiple inputs use the value, make a copy for every input except for one. */
+ /* First make the copies, so that the next node does not start modifying the value while we
+ * are still making copies. */
+ const CPPType &type = *value_to_forward.type();
+ for (const DInputSocket &to_socket : to_sockets.drop_front(1)) {
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value_to_forward.get(), buffer);
+ this->add_value_to_input_socket(to_socket, from_socket, {type, buffer});
+ }
+ /* Forward the original value to one of the targets. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ }
+
+ void add_value_to_input_socket(const DInputSocket socket,
+ const DOutputSocket origin,
+ GMutablePointer value)
+ {
+ BLI_assert(socket->is_available());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ /* Lock the node because we want to change its state. */
+ LockedNode locked_node{*this, node, node_state};
+
+ if (socket->is_multi_input_socket()) {
+ /* Add a new value to the multi-input. */
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({origin, value.get()});
+ }
+ else {
+ /* Assign the value to the input. */
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value == nullptr);
+ single_value.value = value.get();
+ }
+
+ if (input_state.usage == ValueUsage::Required) {
+ node_state.missing_required_inputs--;
+ if (node_state.missing_required_inputs == 0) {
+ /* Schedule node if all the required inputs have been provided. */
+ this->schedule_node(locked_node);
+ }
+ }
+ }
+
+ void load_unlinked_input_value(LockedNode &locked_node,
+ const DInputSocket input_socket,
+ InputState &input_state,
+ const DSocket origin_socket)
+ {
+ /* Only takes locked node as parameter, because the node needs to be locked. */
+ UNUSED_VARS(locked_node);
+
+ GMutablePointer value = this->get_value_from_socket(origin_socket, *input_state.type);
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({input_socket, value.get()});
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ single_value.value = value.get();
+ }
+ }
+
+ void destruct_input_value_if_exists(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.items.clear();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.type->destruct(single_value.value);
+ single_value.value = nullptr;
+ }
+ }
+ }
+
+ GMutablePointer get_value_from_socket(const DSocket socket, const CPPType &required_type)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ bNodeSocket *bsocket = socket->bsocket();
+ const CPPType &type = *get_socket_cpp_type(socket);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+
+ if (type == required_type) {
+ return {type, buffer};
+ }
+ if (conversions_.is_convertible(type, required_type)) {
+ /* Convert the loaded value to the required type if possible. */
+ void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
+ type.destruct(buffer);
+ return {required_type, converted_buffer};
+ }
+ /* Use a default fallback value when the loaded type is not compatible. */
+ void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
+ return {required_type, default_buffer};
+ }
+
+ NodeState &get_node_state(const DNode node)
+ {
+ return *node_states_.lookup_key_as(node).state;
+ }
+
+ void log_socket_value(const DSocket socket, Span<GPointer> values)
+ {
+ if (params_.log_socket_value_fn) {
+ params_.log_socket_value_fn(socket, values);
+ }
+ }
+
+ void log_socket_value(const DSocket socket,
+ InputState &input_state,
+ Span<MultiInputValueItem> values)
+ {
+ Vector<GPointer, 16> value_pointers;
+ value_pointers.reserve(values.size());
+ const CPPType &type = *input_state.type;
+ for (const MultiInputValueItem &item : values) {
+ value_pointers.append({type, item.value});
+ }
+ this->log_socket_value(socket, value_pointers);
+ }
+
+ void log_socket_value(const DSocket socket, GPointer value)
+ {
+ this->log_socket_value(socket, Span<GPointer>(&value, 1));
+ }
+};
+
+LockedNode::~LockedNode()
+{
+ /* First unlock the current node. */
+ node_state.mutex.unlock();
+ /* Then send notifications to the other nodes. */
+ for (const DOutputSocket &socket : delayed_required_outputs) {
+ evaluator_.send_output_required_notification(socket);
+ }
+ for (const DOutputSocket &socket : delayed_unused_outputs) {
+ evaluator_.send_output_unused_notification(socket);
+ }
+ for (const DNode &node : delayed_scheduled_nodes) {
+ evaluator_.add_node_to_task_pool(node);
+ }
+}
+
+/* TODO: Use a map data structure or so to make this faster. */
+static DInputSocket get_input_by_identifier(const DNode node, const StringRef identifier)
+{
+ for (const InputSocketRef *socket : node->inputs()) {
+ if (socket->identifier() == identifier) {
+ return {node.context(), socket};
+ }
+ }
+ return {};
+}
+
+static DOutputSocket get_output_by_identifier(const DNode node, const StringRef identifier)
+{
+ for (const OutputSocketRef *socket : node->outputs()) {
+ if (socket->identifier() == identifier) {
+ return {node.context(), socket};
+ }
+ }
+ return {};
+}
+
+NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator,
+ DNode dnode,
+ NodeState &node_state)
+ : evaluator_(evaluator), node_state_(node_state)
+{
+ this->dnode = dnode;
+ this->self_object = evaluator.params_.self_object;
+ this->modifier = &evaluator.params_.modifier_->modifier;
+ this->depsgraph = evaluator.params_.depsgraph;
+}
+
+bool NodeParamsProvider::can_get_input(StringRef identifier) const
+{
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (!input_state.was_ready_for_execution) {
+ return false;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ return multi_value.items.size() == multi_value.expected_size;
+ }
+ SingleInputValue &single_value = *input_state.value.single;
+ return single_value.value != nullptr;
+}
+
+bool NodeParamsProvider::can_set_output(StringRef identifier) const
+{
+ const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ return !output_state.has_been_computed;
+}
+
+GMutablePointer NodeParamsProvider::extract_input(StringRef identifier)
+{
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ single_value.value = nullptr;
+ return {*input_state.type, value};
+}
+
+Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identifier)
+{
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+ BLI_assert(socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ MultiInputValue &multi_value = *input_state.value.multi;
+
+ Vector<GMutablePointer> ret_values;
+ socket.foreach_origin_socket([&](DSocket origin) {
+ for (const MultiInputValueItem &item : multi_value.items) {
+ if (item.origin == origin) {
+ ret_values.append({*input_state.type, item.value});
+ return;
+ }
+ }
+ BLI_assert_unreachable();
+ });
+ multi_value.items.clear();
+ return ret_values;
+}
+
+GPointer NodeParamsProvider::get_input(StringRef identifier) const
+{
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ return {*input_state.type, single_value.value};
+}
+
+GMutablePointer NodeParamsProvider::alloc_output_value(const CPPType &type)
+{
+ LinearAllocator<> &allocator = evaluator_.local_allocators_.local();
+ return {type, allocator.allocate(type.size(), type.alignment())};
+}
+
+void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value)
+{
+ const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ evaluator_.log_socket_value(socket, value);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ BLI_assert(!output_state.has_been_computed);
+ evaluator_.forward_output(socket, value);
+ output_state.has_been_computed = true;
+}
+
+bool NodeParamsProvider::lazy_require_input(StringRef identifier)
+{
+ BLI_assert(node_supports_lazyness(this->dnode));
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (input_state.was_ready_for_execution) {
+ return false;
+ }
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_required(locked_node, socket);
+ return true;
+}
+
+void NodeParamsProvider::set_input_unused(StringRef identifier)
+{
+ const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_unused(locked_node, socket);
+}
+
+bool NodeParamsProvider::output_is_required(StringRef identifier) const
+{
+ const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution != ValueUsage::Unused;
+}
+
+bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const
+{
+ BLI_assert(node_supports_lazyness(this->dnode));
+ const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution == ValueUsage::Required;
+}
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params)
+{
+ GeometryNodesEvaluator evaluator{params};
+ evaluator.execute();
+}
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
new file mode 100644
index 00000000000..84249e4244e
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_generic_pointer.hh"
+
+#include "DNA_modifier_types.h"
+
+namespace blender::modifiers::geometry_nodes {
+
+using namespace nodes::derived_node_tree_types;
+using fn::GMutablePointer;
+using fn::GPointer;
+
+using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
+
+struct GeometryNodesEvaluationParams {
+ blender::LinearAllocator<> allocator;
+
+ Map<DOutputSocket, GMutablePointer> input_values;
+ Vector<DInputSocket> output_sockets;
+ nodes::MultiFunctionByNode *mf_by_node;
+ const NodesModifierData *modifier_;
+ Depsgraph *depsgraph;
+ Object *self_object;
+ LogSocketValueFn log_socket_value_fn;
+
+ Vector<GMutablePointer> r_output_values;
+};
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params);
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index 4daa527577b..a01f63be791 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -60,7 +60,6 @@ ModifierTypeInfo modifierType_None = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index ec10b18665e..4bfd6aba4b2 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -805,7 +805,6 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 9c940745920..f7ac59f9e4b 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -737,7 +737,6 @@ ModifierTypeInfo modifierType_Ocean = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index e7f1fa9943e..60c5667472e 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -679,7 +679,6 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 4c1179af431..38cce5e6a50 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -337,7 +337,6 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 175435fcd44..88851f91337 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -301,7 +301,6 @@ ModifierTypeInfo modifierType_Remesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 84360caa345..b236e0896b7 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -1258,7 +1258,6 @@ ModifierTypeInfo modifierType_Screw = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index 81a0ee72496..b517bc102f8 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -141,7 +141,6 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 93626309727..a12724ec23c 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -293,7 +293,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index 951eacdecda..db01dec4d19 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -21,10 +21,9 @@
* \ingroup modifiers
*/
-#include "BLI_utildefines.h"
-
#include "BLI_math.h"
-
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_defaults.h"
@@ -57,6 +56,21 @@
#define BEND_EPS 0.000001f
+ALIGN_STRUCT struct DeformUserData {
+ bool invert_vgroup;
+ char mode;
+ char deform_axis;
+ int lock_axis;
+ int vgroup;
+ int limit_axis;
+ float weight;
+ float smd_factor;
+ float smd_limit[2];
+ float (*vertexCos)[3];
+ const SpaceTransform *transf;
+ const MDeformVert *dvert;
+};
+
/* Re-maps the indices for X Y Z by shifting them up and wrapping, such that
* X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This
* exists because the deformations (excluding bend) are based on the Z axis.
@@ -205,6 +219,88 @@ static void simpleDeform_bend(const float factor,
}
}
+static void simple_helper(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const struct DeformUserData *curr_deform_data = userdata;
+ float weight = BKE_defvert_array_find_weight_safe(
+ curr_deform_data->dvert, iter, curr_deform_data->vgroup);
+ const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ?
+ curr_deform_data->deform_axis :
+ 2];
+ const float base_limit[2] = {0.0f, 0.0f};
+
+ if (curr_deform_data->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight != 0.0f) {
+ float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+
+ copy_v3_v3(co, curr_deform_data->vertexCos[iter]);
+
+ /* Apply axis limits, and axis mappings */
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
+ axis_limit(0, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
+ axis_limit(1, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
+ axis_limit(2, base_limit, co, dcut);
+ }
+ axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut);
+
+ /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
+ float co_remap[3];
+ float dcut_remap[3];
+ copy_v3_v3_map(co_remap, co, axis_map);
+ copy_v3_v3_map(dcut_remap, dcut, axis_map);
+ switch (curr_deform_data->mode) {
+ case MOD_SIMPLEDEFORM_MODE_TWIST:
+ simpleDeform_twist(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_BEND:
+ simpleDeform_bend(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_TAPER:
+ simpleDeform_taper(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_STRETCH:
+ simpleDeform_stretch(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ default:
+ return; /* No simple-deform mode? */
+ }
+ copy_v3_v3_unmap(co, co_remap, axis_map);
+
+ /* Use vertex weight has coef of linear interpolation */
+ interp_v3_v3v3(
+ curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight);
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+ }
+}
+
/* simple deform modifier */
static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
const ModifierEvalContext *UNUSED(ctx),
@@ -213,14 +309,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
float (*vertexCos)[3],
int numVerts)
{
- const float base_limit[2] = {0.0f, 0.0f};
int i;
float smd_limit[2], smd_factor;
SpaceTransform *transf = NULL, tmp_transf;
- void (*simpleDeform_callback)(const float factor,
- const int axis,
- const float dcut[3],
- float co[3]) = NULL; /* Mode callback */
int vgroup;
MDeformVert *dvert;
@@ -302,23 +393,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
}
- switch (smd->mode) {
- case MOD_SIMPLEDEFORM_MODE_TWIST:
- simpleDeform_callback = simpleDeform_twist;
- break;
- case MOD_SIMPLEDEFORM_MODE_BEND:
- simpleDeform_callback = simpleDeform_bend;
- break;
- case MOD_SIMPLEDEFORM_MODE_TAPER:
- simpleDeform_callback = simpleDeform_taper;
- break;
- case MOD_SIMPLEDEFORM_MODE_STRETCH:
- simpleDeform_callback = simpleDeform_stretch;
- break;
- default:
- return; /* No simple-deform mode? */
- }
-
if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
if (fabsf(smd_factor) < BEND_EPS) {
return;
@@ -327,53 +401,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup);
const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0;
- const uint *axis_map =
- axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2];
-
- for (i = 0; i < numVerts; i++) {
- float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup);
-
- if (invert_vgroup) {
- weight = 1.0f - weight;
- }
-
- if (weight != 0.0f) {
- float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
- if (transf) {
- BLI_space_transform_apply(transf, vertexCos[i]);
- }
-
- copy_v3_v3(co, vertexCos[i]);
-
- /* Apply axis limits, and axis mappings */
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
- axis_limit(0, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
- axis_limit(1, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
- axis_limit(2, base_limit, co, dcut);
- }
- axis_limit(limit_axis, smd_limit, co, dcut);
-
- /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
- float co_remap[3];
- float dcut_remap[3];
- copy_v3_v3_map(co_remap, co, axis_map);
- copy_v3_v3_map(dcut_remap, dcut, axis_map);
- simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */
- copy_v3_v3_unmap(co, co_remap, axis_map);
-
- /* Use vertex weight has coef of linear interpolation */
- interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight);
-
- if (transf) {
- BLI_space_transform_invert(transf, vertexCos[i]);
- }
- }
- }
+ /* Build our data. */
+ const struct DeformUserData deform_pool_data = {
+ .mode = smd->mode,
+ .smd_factor = smd_factor,
+ .deform_axis = deform_axis,
+ .transf = transf,
+ .vertexCos = vertexCos,
+ .invert_vgroup = invert_vgroup,
+ .lock_axis = lock_axis,
+ .vgroup = vgroup,
+ .smd_limit[0] = smd_limit[0],
+ .smd_limit[1] = smd_limit[1],
+ .dvert = dvert,
+ .limit_axis = limit_axis,
+ };
+ /* Do deformation. */
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings);
}
/* SimpleDeform */
@@ -555,7 +602,6 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 5e412185cea..e2d18cf1790 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -2044,7 +2044,6 @@ ModifierTypeInfo modifierType_Skin = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index c5011ed15c1..97027e2ecff 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -286,7 +286,6 @@ ModifierTypeInfo modifierType_Smooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 9a657c44fca..d7d2f948955 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 8e519a72df1..736dd08a713 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -276,7 +276,6 @@ ModifierTypeInfo modifierType_Solidify = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index c3611488f5f..4dc45ad0324 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -509,7 +509,6 @@ ModifierTypeInfo modifierType_Subsurf = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 7416a4baf38..d2c011a21d3 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -241,7 +241,6 @@ ModifierTypeInfo modifierType_Surface = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 99011c5e351..0cc68c2c4a3 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1646,7 +1646,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 04d24ac0883..ef633494c7b 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -178,7 +178,6 @@ ModifierTypeInfo modifierType_Triangulate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL, // requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 3162a33edc2..724d1370a47 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -385,7 +385,6 @@ ModifierTypeInfo modifierType_UVProject = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 77b79167c2f..5742144b6dd 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -342,7 +342,6 @@ ModifierTypeInfo modifierType_UVWarp = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index e7c4004853e..af4b31d6bfc 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -18,6 +18,7 @@
* \ingroup modifiers
*/
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -284,7 +285,7 @@ struct DisplaceGridOp {
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
+static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
{
#ifdef WITH_OPENVDB
VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
@@ -303,14 +304,22 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
BKE_volume_grid_type_operation(grid_type, displace_grid_op);
}
- return volume;
#else
- UNUSED_VARS(md, ctx);
+ UNUSED_VARS(md, volume, ctx);
BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
- return volume;
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ if (input_volume != nullptr) {
+ displace_volume(md, ctx, input_volume);
+ }
+}
+
ModifierTypeInfo modifierType_VolumeDisplace = {
/* name */ "Volume Displace",
/* structName */ "VolumeDisplaceModifierData",
@@ -328,8 +337,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index 3bf5cf6ab5b..c0bf07b8eec 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -223,7 +223,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 9d3d5b0658c..3bebc52c503 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 863656b85a5..c6bab89247e 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -492,7 +492,6 @@ ModifierTypeInfo modifierType_Wave = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 40265e37db9..2f2da7d6554 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -764,7 +764,6 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 915adccc745..b5f72c88800 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 52cee7ce7e5..a71a2f3b480 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index aac29cabf0f..cd03175f16c 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -768,7 +768,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index fd1254fc948..1590f342666 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -2053,7 +2053,6 @@ ModifierTypeInfo modifierType_Weld = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 3d8e74d2cf5..16bf1f7d763 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -196,7 +196,6 @@ ModifierTypeInfo modifierType_Wireframe = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 8168f0dba73..33b56fd0de0 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -145,6 +145,7 @@ set(SRC
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
@@ -154,14 +155,21 @@ set(SRC
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
+ geometry/nodes/node_geo_attribute_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_curve_to_mesh.cc
+ geometry/nodes/node_geo_curve_resample.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_material_assign.cc
+ geometry/nodes/node_geo_material_replace.cc
geometry/nodes/node_geo_mesh_primitive_circle.cc
geometry/nodes/node_geo_mesh_primitive_cone.cc
geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -180,6 +188,7 @@ set(SRC
geometry/nodes/node_geo_points_to_volume.cc
geometry/nodes/node_geo_subdivide.cc
geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_volume_to_mesh.cc
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index e294bef2ea8..7ff05449c0b 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -92,6 +92,9 @@ class DNode {
operator bool() const;
uint64_t hash() const;
+
+ DInputSocket input(int index) const;
+ DOutputSocket output(int index) const;
};
/* A (nullable) reference to a socket and the context it is in. It is unique within an entire
@@ -274,6 +277,16 @@ inline uint64_t DNode::hash() const
return get_default_hash_2(context_, node_ref_);
}
+inline DInputSocket DNode::input(int index) const
+{
+ return {context_, &node_ref_->input(index)};
+}
+
+inline DOutputSocket DNode::output(int index) const
+{
+ return {context_, &node_ref_->output(index)};
+}
+
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index b84c80e916c..d2a702c30a6 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -35,6 +35,7 @@ void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
+void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
@@ -42,14 +43,21 @@ void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
+void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
+void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
+void register_node_type_geo_curve_to_mesh(void);
+void register_node_type_geo_curve_resample(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_material_assign(void);
+void register_node_type_geo_material_replace(void);
void register_node_type_geo_mesh_primitive_circle(void);
void register_node_type_geo_mesh_primitive_cone(void);
void register_node_type_geo_mesh_primitive_cube(void);
@@ -69,6 +77,7 @@ void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_sample_texture(void);
void register_node_type_geo_subdivide(void);
void register_node_type_geo_subdivision_surface(void);
+void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_volume_to_mesh(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 5d1a217db9b..52d7e097f0d 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -22,7 +22,6 @@
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_node_ui_storage.hh"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@@ -33,55 +32,90 @@ struct ModifierData;
namespace blender::nodes {
-using bke::BooleanReadAttribute;
-using bke::BooleanWriteAttribute;
-using bke::Color4fReadAttribute;
-using bke::Color4fWriteAttribute;
-using bke::Float2ReadAttribute;
-using bke::Float2WriteAttribute;
-using bke::Float3ReadAttribute;
-using bke::Float3WriteAttribute;
-using bke::FloatReadAttribute;
-using bke::FloatWriteAttribute;
using bke::geometry_set_realize_instances;
-using bke::Int32ReadAttribute;
-using bke::Int32WriteAttribute;
-using bke::PersistentDataHandleMap;
-using bke::PersistentObjectHandle;
-using bke::ReadAttribute;
-using bke::ReadAttributePtr;
-using bke::WriteAttribute;
-using bke::WriteAttributePtr;
+using bke::OutputAttribute;
+using bke::OutputAttribute_Typed;
+using bke::ReadAttributeLookup;
+using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::GMutablePointer;
+using fn::GMutableSpan;
using fn::GPointer;
+using fn::GSpan;
using fn::GValueMap;
+using fn::GVArray;
+using fn::GVArray_GSpan;
+using fn::GVArray_Span;
+using fn::GVArray_Typed;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArray_GSpan;
+using fn::GVMutableArray_Typed;
+using fn::GVMutableArrayPtr;
+
+/**
+ * This class exists to separate the memory management details of the geometry nodes evaluator from
+ * the node execution functions and related utilities.
+ */
+class GeoNodeExecParamsProvider {
+ public:
+ DNode dnode;
+ const Object *self_object = nullptr;
+ const ModifierData *modifier = nullptr;
+ Depsgraph *depsgraph = nullptr;
+
+ /**
+ * Returns true when the node is allowed to get/extract the input value. The identifier is
+ * expected to be valid. This may return false if the input value has been consumed already.
+ */
+ virtual bool can_get_input(StringRef identifier) const = 0;
+
+ /**
+ * Returns true when the node is allowed to set the output value. The identifier is expected to
+ * be valid. This may return false if the output value has been set already.
+ */
+ virtual bool can_set_output(StringRef identifier) const = 0;
+
+ /**
+ * Take ownership of an input value. The caller is responsible for destructing the value. It does
+ * not have to be freed, because the memory is managed by the geometry nodes evaluator.
+ */
+ virtual GMutablePointer extract_input(StringRef identifier) = 0;
+
+ /**
+ * Similar to #extract_input, but has to be used for multi-input sockets.
+ */
+ virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0;
+
+ /**
+ * Get the input value for the identifier without taking ownership of it.
+ */
+ virtual GPointer get_input(StringRef identifier) const = 0;
+
+ /**
+ * Prepare a memory buffer for an output value of the node. The returned memory has to be
+ * initialized by the caller. The identifier and type are expected to be correct.
+ */
+ virtual GMutablePointer alloc_output_value(const CPPType &type) = 0;
+
+ /**
+ * The value has been allocated with #alloc_output_value.
+ */
+ virtual void set_output(StringRef identifier, GMutablePointer value) = 0;
+
+ /* A description for these methods is provided in GeoNodeExecParams. */
+ virtual void set_input_unused(StringRef identifier) = 0;
+ virtual bool output_is_required(StringRef identifier) const = 0;
+ virtual bool lazy_require_input(StringRef identifier) = 0;
+ virtual bool lazy_output_is_required(StringRef identifier) const = 0;
+};
class GeoNodeExecParams {
private:
- const DNode node_;
- GValueMap<StringRef> &input_values_;
- GValueMap<StringRef> &output_values_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
+ GeoNodeExecParamsProvider *provider_;
public:
- GeoNodeExecParams(const DNode node,
- GValueMap<StringRef> &input_values,
- GValueMap<StringRef> &output_values,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : node_(node),
- input_values_(input_values),
- output_values_(output_values),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
+ GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
{
}
@@ -94,9 +128,9 @@ class GeoNodeExecParams {
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier);
+ this->check_input_access(identifier);
#endif
- return input_values_.extract(identifier);
+ return provider_->extract_input(identifier);
}
/**
@@ -107,9 +141,10 @@ class GeoNodeExecParams {
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.extract<T>(identifier);
+ GMutablePointer gvalue = this->extract_input(identifier);
+ return gvalue.relocate_out<T>();
}
/**
@@ -119,18 +154,10 @@ class GeoNodeExecParams {
*/
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
+ Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
Vector<T> values;
- int index = 0;
- while (true) {
- std::string sub_identifier = identifier;
- if (index > 0) {
- sub_identifier += "[" + std::to_string(index) + "]";
- }
- if (!input_values_.contains(sub_identifier)) {
- break;
- }
- values.append(input_values_.extract<T, StringRef>(sub_identifier));
- index++;
+ for (GMutablePointer gvalue : gvalues) {
+ values.append(gvalue.relocate_out<T>());
}
return values;
}
@@ -141,67 +168,83 @@ class GeoNodeExecParams {
template<typename T> const T &get_input(StringRef identifier) const
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.lookup<T>(identifier);
+ GPointer gvalue = provider_->get_input(identifier);
+ BLI_assert(gvalue.is_type<T>());
+ return *(const T *)gvalue.get();
}
/**
- * Move-construct a new value based on the given value and store it for the given socket
- * identifier.
+ * Store the output value for the given socket identifier.
*/
- void set_output_by_move(StringRef identifier, GMutablePointer value)
+ template<typename T> void set_output(StringRef identifier, T &&value)
{
+ using StoredT = std::decay_t<T>;
+ const CPPType &type = CPPType::get<std::decay_t<T>>();
#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
+ this->check_output_access(identifier, type);
#endif
- output_values_.add_new_by_move(identifier, value);
+ GMutablePointer gvalue = provider_->alloc_output_value(type);
+ new (gvalue.get()) StoredT(std::forward<T>(value));
+ provider_->set_output(identifier, gvalue);
}
- void set_output_by_copy(StringRef identifier, GPointer value)
+ /**
+ * Tell the evaluator that a specific input won't be used anymore.
+ */
+ void set_input_unused(StringRef identifier)
{
-#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
-#endif
- output_values_.add_new_by_copy(identifier, value);
+ provider_->set_input_unused(identifier);
}
/**
- * Store the output value for the given socket identifier.
+ * Returns true when the output has to be computed.
+ * Nodes that support lazyness could use the #lazy_output_is_required variant to possibly avoid
+ * some computations.
*/
- template<typename T> void set_output(StringRef identifier, T &&value)
+ bool output_is_required(StringRef identifier) const
{
-#ifdef DEBUG
- this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
-#endif
- output_values_.add_new(identifier, std::forward<T>(value));
+ return provider_->output_is_required(identifier);
}
/**
- * Get the node that is currently being executed.
+ * Tell the evaluator that a specific input is required.
+ * This returns true when the input will only be available in the next execution.
+ * False is returned if the input is available already.
+ * This can only be used when the node supports lazyness.
*/
- const bNode &node() const
+ bool lazy_require_input(StringRef identifier)
{
- return *node_->bnode();
+ return provider_->lazy_require_input(identifier);
}
- const PersistentDataHandleMap &handle_map() const
+ /**
+ * Asks the evaluator if a specific output is required right now. If this returns false, the
+ * value might still need to be computed later.
+ * This can only be used when the node supports lazyness.
+ */
+ bool lazy_output_is_required(StringRef identifier)
+ {
+ return provider_->lazy_output_is_required(identifier);
+ }
+
+ /**
+ * Get the node that is currently being executed.
+ */
+ const bNode &node() const
{
- return handle_map_;
+ return *provider_->dnode->bnode();
}
const Object *self_object() const
{
- return self_object_;
+ return provider_->self_object;
}
Depsgraph *depsgraph() const
{
- return depsgraph_;
+ return provider_->depsgraph;
}
/**
@@ -217,20 +260,21 @@ class GeoNodeExecParams {
* \note This will add an error message if the string socket is active and
* the input attribute does not exist.
*/
- ReadAttributePtr get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const;
+ GVArrayPtr get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const;
template<typename T>
- bke::TypedReadAttribute<T> get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const T &default_value) const
+ GVArray_Typed<T> get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
- return this->get_input_attribute(name, component, domain, type, &default_value);
+ GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value);
+ return GVArray_Typed<T>(std::move(varray));
}
/**
@@ -247,8 +291,8 @@ class GeoNodeExecParams {
private:
/* Utilities for detecting common errors at when using this class. */
- void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
- void check_set_output(StringRef identifier, const CPPType &value_type) const;
+ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
+ void check_output_access(StringRef identifier, const CPPType &value_type) const;
/* Find the active socket socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index bf6bf34325a..ce1813fdac3 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -263,53 +263,62 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
+DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
-DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
-DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
-DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
-DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
-DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
-DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh
index 34225208fe6..ec4859f0657 100644
--- a/source/blender/nodes/NOD_type_conversions.hh
+++ b/source/blender/nodes/NOD_type_conversions.hh
@@ -72,6 +72,10 @@ class DataTypeConversions {
const CPPType &to_type,
const void *from_value,
void *to_value) const;
+
+ fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
+
+ fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
};
const DataTypeConversions &get_implicit_type_conversions();
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 085b5c463b9..19815d01278 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode)
}
}
+static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
@@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void)
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
+ tt->valid_socket_type = composite_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_CompositorNodeTree;
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 6d3b1d55005..f4cd00b88ed 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -84,6 +84,34 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
+static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
+{
+ /* Geometry, string, object, material, texture and collection sockets can only be connected to
+ * themselves. The other types can be converted between each other. */
+ if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
+ ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ return true;
+ }
+ return (link->tosock->type == link->fromsock->type);
+}
+
+static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_BOOLEAN,
+ SOCK_INT,
+ SOCK_STRING,
+ SOCK_OBJECT,
+ SOCK_GEOMETRY,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -97,6 +125,8 @@ void register_node_tree_type_geo(void)
tt->update = geometry_node_tree_update;
tt->get_from_context = geometry_node_tree_get_from_context;
tt->foreach_nodeclass = foreach_nodeclass;
+ tt->valid_socket_type = geometry_node_tree_socket_type_valid;
+ tt->validate_link = geometry_node_tree_validate_link;
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
index f10b81a33b4..d1b71d6f2ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -50,91 +51,117 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
namespace blender::nodes {
-static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
+
+ node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
+ node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+}
+
+static void align_rotations_auto_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
}
- }
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
-static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
static void align_rotations_on_component(GeometryComponent &component,
@@ -144,30 +171,30 @@ static void align_rotations_on_component(GeometryComponent &component,
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- if (!rotation_attribute) {
+ OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!rotations) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
- FloatReadAttribute factors = params.get_input_attribute<float>(
+ GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- Float3ReadAttribute vectors = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
+ align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
+ align_rotations_fixed_pivot(
+ vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
- rotation_attribute.apply_span_and_save();
+ rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
@@ -183,32 +210,13 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
-
- node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
- node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
-}
-
} // namespace blender::nodes
void register_node_type_geo_align_rotation_to_vector()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
index 95fa24c8bac..21538db5455 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -112,10 +112,13 @@ template<> inline Color4f clamp_value(const Color4f val, const Color4f min, cons
}
template<typename T>
-static void clamp_attribute(Span<T> read_span, MutableSpan<T> span, const T min, const T max)
+static void clamp_attribute(const VArray<T> &inputs,
+ const MutableSpan<T> outputs,
+ const T min,
+ const T max)
{
- for (const int i : span.index_range()) {
- span[i] = clamp_value<T>(read_span[i], min, max);
+ for (const int i : IndexRange(outputs.size())) {
+ outputs[i] = clamp_value<T>(inputs[i], min, max);
}
}
@@ -123,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef source_name,
StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -154,10 +157,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
const int operation = static_cast<int>(storage.operation);
- ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(
attribute_name, domain, data_type);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, domain, data_type);
if (!attribute_result) {
@@ -169,8 +172,6 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
switch (data_type) {
case CD_PROP_FLOAT3: {
- Span<float3> read_span = attribute_input->get_span<float3>();
- MutableSpan<float3> span = attribute_result->get_span_for_write_only<float3>();
float3 min = params.get_input<float3>("Min");
float3 max = params.get_input<float3>("Max");
if (operation == NODE_CLAMP_RANGE) {
@@ -184,38 +185,35 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
std::swap(min.z, max.z);
}
}
- clamp_attribute<float3>(read_span, span, min, max);
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
break;
}
case CD_PROP_FLOAT: {
- Span<float> read_span = attribute_input->get_span<float>();
- MutableSpan<float> span = attribute_result->get_span_for_write_only<float>();
const float min = params.get_input<float>("Min_001");
const float max = params.get_input<float>("Max_001");
+ MutableSpan<float> results = attribute_result.as_span<float>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<float>(read_span, span, max, min);
+ clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
}
else {
- clamp_attribute<float>(read_span, span, min, max);
+ clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
}
break;
}
case CD_PROP_INT32: {
- Span<int> read_span = attribute_input->get_span<int>();
- MutableSpan<int> span = attribute_result->get_span_for_write_only<int>();
const int min = params.get_input<int>("Min_002");
const int max = params.get_input<int>("Max_002");
+ MutableSpan<int> results = attribute_result.as_span<int>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<int>(read_span, span, max, min);
+ clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
}
else {
- clamp_attribute<int>(read_span, span, min, max);
+ clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
}
break;
}
case CD_PROP_COLOR: {
- Span<Color4f> read_span = attribute_input->get_span<Color4f>();
- MutableSpan<Color4f> span = attribute_result->get_span_for_write_only<Color4f>();
Color4f min = params.get_input<Color4f>("Min_003");
Color4f max = params.get_input<Color4f>("Max_003");
if (operation == NODE_CLAMP_RANGE) {
@@ -232,7 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
std::swap(min.a, max.a);
}
}
- clamp_attribute<Color4f>(read_span, span, min, max);
+ MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
+ clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max);
break;
}
default: {
@@ -241,7 +240,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
@@ -256,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
index 2b913beb670..b13e82e676d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_colorband.h"
#include "UI_interface.h"
@@ -42,20 +44,28 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
+ sizeof(NodeAttributeColorRamp), __func__);
+ BKE_colorband_init(&node_storage->color_ramp, true);
+ node->storage = node_storage;
+}
+
static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef input_name,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input attribute's domain if it exists. */
- ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
- if (input_attribute) {
- return input_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
@@ -71,27 +81,27 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
/* Always output a color attribute for now. We might want to allow users to customize.
* Using the type of an existing attribute could work, but does not have a real benefit
* currently. */
- const CustomDataType result_type = CD_PROP_COLOR;
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<Color4f> attribute_result =
+ component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
- Span<float> data_in = attribute_in.get_span();
- MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>();
+ MutableSpan<Color4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
- for (const int i : data_in.index_range()) {
- BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
- }
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
+ }
+ });
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
@@ -106,18 +116,13 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
- sizeof(NodeAttributeColorRamp), __func__);
- BKE_colorband_init(&node_storage->color_ramp, true);
- node->storage = node_storage;
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_color_ramp()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
index e9e07d34c17..d8c52d16f41 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
}
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> attribute_result =
+ component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_x = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- FloatReadAttribute attribute_y = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- FloatReadAttribute attribute_z = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
- MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
- for (const int i : results.index_range()) {
+ for (const int i : IndexRange(attribute_result->size())) {
const float x = attribute_x[i];
const float y = attribute_y[i];
const float z = attribute_z[i];
- const float3 result = float3(x, y, z);
- results[i] = result;
+ attribute_result->set(i, {x, y, z});
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
@@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index fe4045c39a6..a2ff1668a06 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -81,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_math_operation(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const FloatCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
-
if (try_dispatch_float_math_fl_fl_to_bool(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
- const float a = span_a[i];
- const float b = span_b[i];
+ const float a = input_a[i];
+ const float b = input_b[i];
const bool out = math_function(a, b);
span_result[i] = out;
}
@@ -107,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
BLI_assert(false);
}
-static void do_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -120,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -134,8 +131,8 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_equal_operation_color4f(const VArray<Color4f> &input_a,
+ const VArray<Color4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -148,8 +145,8 @@ static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
}
}
-static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -161,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_not_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -174,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_not_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -188,8 +185,8 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a,
+ const VArray<Color4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -202,8 +199,8 @@ static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
}
}
-static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_not_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -237,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -254,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
- const CustomDataType result_type = CD_PROP_BOOL;
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
+ result_name, result_domain);
if (!attribute_result) {
return;
}
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -275,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
return;
}
- MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
+ MutableSpan<bool> result_span = attribute_result.as_span();
/* Use specific types for correct equality operations, but for other operations we use implicit
* conversions and float comparison. In other words, the comparison is not element-wise. */
@@ -283,38 +279,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const float threshold = params.get_input<float>("Threshold");
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_color4f(
+ attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_color4f(
+ attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
}
else {
- do_math_operation(*attribute_a, *attribute_b, operation, result_span);
+ do_math_operation(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
@@ -329,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
index 19c3aaa9c85..7b40456b180 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
- uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
@@ -44,26 +46,27 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node
NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
__func__);
- data->data_type = CD_PROP_FLOAT;
+ data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef source_name,
- StringRef result_name)
+static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return *result_info;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return *source_info;
}
- return ATTR_DOMAIN_POINT;
+ /* The node won't do anything in this case, but we still have to return a value. */
+ return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL};
}
static bool conversion_can_be_skipped(const GeometryComponent &component,
@@ -75,14 +78,14 @@ static bool conversion_can_be_skipped(const GeometryComponent &component,
if (source_name != result_name) {
return false;
}
- ReadAttributePtr read_attribute = component.attribute_try_get_for_read(source_name);
- if (!read_attribute) {
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
+ if (!info) {
return false;
}
- if (read_attribute->domain() != result_domain) {
+ if (info->domain != result_domain) {
return false;
}
- if (read_attribute->cpp_type() != *bke::custom_data_type_to_cpp_type(result_type)) {
+ if (info->data_type != result_type) {
return false;
}
return true;
@@ -92,19 +95,20 @@ static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
const StringRef result_name,
- const CustomDataType result_type,
+ const CustomDataType data_type,
const AttributeDomain domain)
{
- const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
- get_result_domain(
- component, source_name, result_name) :
- domain;
+ const AttributeMetaData auto_info = get_result_domain_and_type(
+ component, source_name, result_name);
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain;
+ const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type :
+ data_type;
if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
return;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -112,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!result_attribute) {
return;
}
- fn::GSpan source_span = source_attribute->get_span();
- fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
- if (source_span.is_empty() || result_span.is_empty()) {
- return;
- }
+ GVArray_GSpan source_span{*source_attribute};
+ GMutableSpan result_span = result_attribute.as_span();
+
BLI_assert(source_span.size() == result_span.size());
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
BLI_assert(cpp_type != nullptr);
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
-
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
@@ -166,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
new file mode 100644
index 00000000000..2fc86269797
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -0,0 +1,231 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_blenlib.h"
+#include "BLI_task.hh"
+
+#include "BKE_colortools.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_curve_map_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ bNode *node = (bNode *)ptr->data;
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ switch (data->data_type) {
+ case CD_PROP_FLOAT:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false);
+ break;
+ case CD_PROP_FLOAT3:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false);
+ break;
+ case CD_PROP_COLOR:
+ uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false);
+ break;
+ }
+}
+
+static void geo_node_attribute_curve_map_free_storage(bNode *node)
+{
+ if (node->storage) {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_free(data->curve_vec);
+ BKE_curvemapping_free(data->curve_rgb);
+ MEM_freeN(node->storage);
+ }
+}
+
+static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree),
+ bNode *dest_node,
+ const bNode *src_node)
+{
+ dest_node->storage = MEM_dupallocN(src_node->storage);
+ NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage;
+ NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage;
+ dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec);
+ dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb);
+}
+
+static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap),
+ __func__);
+
+ data->data_type = CD_PROP_FLOAT;
+ data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f);
+ data->curve_vec->cur = 3;
+ data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
+ node->storage = data;
+}
+
+static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ /* Set the active curve when data type is changed. */
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ if (data->data_type == CD_PROP_FLOAT) {
+ data->curve_vec->cur = 3;
+ }
+ else if (data->data_type == CD_PROP_FLOAT3) {
+ data->curve_vec->cur = 0;
+ }
+}
+
+namespace blender::nodes {
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef input_name,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
+ if (result_attribute) {
+ return result_attribute.domain;
+ }
+
+ /* Otherwise use the input attribute's domain if it exists. */
+ ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
+ if (input_attribute) {
+ return input_attribute.domain;
+ }
+
+ return ATTR_DOMAIN_POINT;
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const std::string input_name = params.get_input<std::string>("Attribute");
+
+ const CustomDataType result_type = (CustomDataType)node_storage.data_type;
+ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ switch (result_type) {
+ case CD_PROP_FLOAT: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ input_name, result_domain, float(0.0f));
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
+ input_name, result_domain, float3(0.0f));
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_COLOR: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
+ GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>(
+ input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f));
+ MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage;
+ BKE_curvemapping_init(data->curve_vec);
+ BKE_curvemapping_init(data->curve_rgb);
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_curve_map()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out);
+ node_type_update(&ntype, geo_node_attribute_curve_map_update);
+ node_type_init(&ntype, geo_node_attribute_curve_map_init);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_storage(&ntype,
+ "NodeAttributeCurveMap",
+ geo_node_attribute_curve_map_free_storage,
+ geo_node_attribute_curve_map_copy_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec;
+ ntype.draw_buttons = geo_node_attribute_curve_map_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
index 7b4483a31a1..60522fd0f72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -68,13 +68,12 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef attribute_name)
+static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -93,7 +92,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
get_result_domain(component, attribute_name) :
domain;
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
@@ -102,38 +101,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
- MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
- MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_COLOR: {
const Color4f value = params.get_input<Color4f>("Value_002");
- MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
- MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
- MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
- attribute_span.fill(value);
+ attribute->fill(&value);
+ break;
}
default:
break;
}
- attribute.apply_span_and_save();
+ attribute.save();
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
@@ -148,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
index fdbdadf90b6..40fe675bd6c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -192,8 +193,8 @@ static float map_smootherstep(const float value,
return min_to + factor_mapped * (max_to - min_to);
}
-static void map_range_float(FloatReadAttribute attribute_input,
- FloatWriteAttribute attribute_result,
+static void map_range_float(const VArray<float> &attribute_input,
+ MutableSpan<float> results,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
@@ -204,33 +205,40 @@ static void map_range_float(FloatReadAttribute attribute_input,
const float min_to = params.get_input<float>("To Min");
const float max_to = params.get_input<float>("To Max");
- Span<float> span = attribute_input.get_span();
- MutableSpan<float> result_span = attribute_result.get_span();
+ VArray_Span<float> span{attribute_input};
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- for (int i : span.index_range()) {
- result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
case NODE_MAP_RANGE_STEPPED: {
const float steps = params.get_input<float>("Steps");
- for (int i : span.index_range()) {
- result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- for (int i : span.index_range()) {
- result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- for (int i : span.index_range()) {
- result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
}
@@ -241,14 +249,16 @@ static void map_range_float(FloatReadAttribute attribute_input,
const float clamp_min = min_to < max_to ? min_to : max_to;
const float clamp_max = min_to < max_to ? max_to : min_to;
- for (int i : result_span.index_range()) {
- result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max);
- }
+ parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = std::clamp(results[i], clamp_min, clamp_max);
+ }
+ });
}
}
-static void map_range_float3(Float3ReadAttribute attribute_input,
- Float3WriteAttribute attribute_result,
+static void map_range_float3(const VArray<float3> &attribute_input,
+ const MutableSpan<float3> results,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
@@ -259,44 +269,51 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
const float3 min_to = params.get_input<float3>("To Min_001");
const float3 max_to = params.get_input<float3>("To Max_001");
- Span<float3> span = attribute_input.get_span();
- MutableSpan<float3> result_span = attribute_result.get_span();
+ VArray_Span<float3> span{attribute_input};
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- for (int i : span.index_range()) {
- result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_STEPPED: {
const float3 steps = params.get_input<float3>("Steps_001");
- for (int i : span.index_range()) {
- result_span[i].x = map_stepped(
- span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
- result_span[i].y = map_stepped(
- span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
- result_span[i].z = map_stepped(
- span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_stepped(
+ span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
+ results[i].y = map_stepped(
+ span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
+ results[i].z = map_stepped(
+ span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- for (int i : span.index_range()) {
- result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- for (int i : span.index_range()) {
- result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
}
@@ -313,8 +330,8 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
- for (int i : result_span.index_range()) {
- clamp_v3_v3v3(result_span[i], clamp_min, clamp_max);
+ for (int i : results.index_range()) {
+ clamp_v3_v3v3(results[i], clamp_min, clamp_max);
}
}
}
@@ -323,13 +340,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef source_name,
StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -349,8 +366,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
- ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
- input_name, domain, data_type);
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
if (!attribute_input) {
params.error_message_add(NodeWarningType::Error,
@@ -358,7 +374,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
return;
}
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, domain, data_type);
if (!attribute_result) {
params.error_message_add(NodeWarningType::Error,
@@ -369,18 +385,19 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
switch (data_type) {
case CD_PROP_FLOAT: {
- map_range_float(*attribute_input, *attribute_result, params);
+ map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
break;
}
case CD_PROP_FLOAT3: {
- map_range_float3(*attribute_input, *attribute_result, params);
+ map_range_float3(
+ attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
break;
}
default:
BLI_assert_unreachable();
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
@@ -393,6 +410,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 5ee31e78be2..ce0ca31cc2b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -149,46 +151,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
operation_use_input_c(operation));
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
- Span<float> span_c,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
+ const VArray<float> &span_c,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_input,
+static void do_math_operation(const VArray<float> &span_input,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_input[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
@@ -200,9 +208,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -224,56 +232,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result =
+ component.attribute_try_get_for_output_only<float>(result_name, result_domain);
if (!attribute_result) {
return;
}
- ReadAttributePtr attribute_a = params.get_input_attribute(
- "A", component, result_domain, result_type, nullptr);
- if (!attribute_a) {
- return;
- }
+ GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ "A", component, result_domain, 0.0f);
- /* Note that passing the data with `get_span<float>()` works
+ MutableSpan<float> result_span = attribute_result.as_span();
+
+ /* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_b) {
- return;
- }
+ GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ "B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- ReadAttributePtr attribute_c = params.get_input_attribute(
- "C", component, result_domain, result_type, nullptr);
- if (!attribute_c) {
- return;
- }
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_c->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ "C", component, result_domain, 0.0f);
+ do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, attribute_b, result_span, operation);
}
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, result_span, operation);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
@@ -288,6 +279,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
index 9d8cd3dfa82..e502a183ef5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_material.h"
#include "DNA_material_types.h"
@@ -57,73 +59,110 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
+ "attribute mix node");
+ data->blend_type = MA_RAMP_BLEND;
+ data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node->storage = data;
+}
+
+static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ update_attribute_input_socket_availabilities(
+ *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+}
+
static void do_mix_operation_float(const int blend_mode,
- const FloatReadAttribute &factors,
- const FloatReadAttribute &inputs_a,
- const FloatReadAttribute &inputs_b,
- FloatWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float> &inputs_a,
+ const VArray<float> &inputs_b,
+ VMutableArray<float> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a{inputs_a[i]};
- const float3 b{inputs_b[i]};
- ramp_blend(blend_mode, a, factor, b);
- const float result = a.x;
- results.set(i, result);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a{inputs_a[i]};
+ const float3 b{inputs_b[i]};
+ ramp_blend(blend_mode, a, factor, b);
+ const float result = a.x;
+ results.set(i, result);
+ }
+ });
}
static void do_mix_operation_float3(const int blend_mode,
- const FloatReadAttribute &factors,
- const Float3ReadAttribute &inputs_a,
- const Float3ReadAttribute &inputs_b,
- Float3WriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float3> &inputs_a,
+ const VArray<float3> &inputs_b,
+ VMutableArray<float3> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a = inputs_a[i];
- const float3 b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a = inputs_a[i];
+ const float3 b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation_color4f(const int blend_mode,
- const FloatReadAttribute &factors,
- const Color4fReadAttribute &inputs_a,
- const Color4fReadAttribute &inputs_b,
- Color4fWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<Color4f> &inputs_a,
+ const VArray<Color4f> &inputs_b,
+ VMutableArray<Color4f> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- Color4f a = inputs_a[i];
- const Color4f b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ Color4f a = inputs_a[i];
+ const Color4f b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation(const CustomDataType result_type,
int blend_mode,
- const FloatReadAttribute &attribute_factor,
- const ReadAttribute &attribute_a,
- const ReadAttribute &attribute_b,
- WriteAttribute &attribute_result)
+ const VArray<float> &attribute_factor,
+ const GVArray &attribute_a,
+ const GVArray &attribute_b,
+ GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
- do_mix_operation_float(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float>(),
+ attribute_b.typed<float>(),
+ attribute_result.typed<float>());
}
else if (result_type == CD_PROP_FLOAT3) {
- do_mix_operation_float3(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.typed<float3>());
}
else if (result_type == CD_PROP_COLOR) {
- do_mix_operation_color4f(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a.typed<Color4f>(),
+ attribute_b.typed<Color4f>(),
+ attribute_result.typed<Color4f>());
}
}
@@ -132,9 +171,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -158,17 +197,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
@@ -192,32 +231,13 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
- "attribute mix node");
- data->blend_type = MA_RAMP_BLEND;
- data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
- update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_mix()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index f09a9bf056e..9c22b7fa87f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -62,7 +62,7 @@ namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
- Span<float3> positions,
+ const VArray<float3> &positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
- distance_attribute_name, result_domain, CD_PROP_FLOAT);
+ OutputAttribute_Typed<float> distance_attribute =
+ component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
- location_attribute_name, result_domain, CD_PROP_FLOAT3);
-
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> location_attribute =
+ component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
+ ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
+ BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- Span<float3> position_span = position_attribute->get_span<float3>();
-
- MutableSpan<float> distance_span = distance_attribute ?
- distance_attribute->get_span_for_write_only<float>() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ?
- location_attribute->get_span_for_write_only<float3>() :
- MutableSpan<float3>();
+ GVArray_Typed<float3> positions{*position_attribute.varray};
+ MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
+ MutableSpan<float>();
+ MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
+ MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
- position_span,
+ positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
if (distance_attribute) {
- distance_attribute.apply_span_and_save();
+ distance_attribute.save();
}
if (location_attribute) {
- location_attribute.apply_span_and_save();
+ location_attribute.save();
}
}
@@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_calc_proximity(
+ geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 28263287a10..286411b7d28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -16,6 +16,7 @@
#include "BLI_hash.h"
#include "BLI_rand.hh"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -125,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] + random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] + random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] - random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] - random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] * random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] * random_value;
+ }
+ });
break;
default:
BLI_assert(false);
@@ -161,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- for (const int i : span.index_range()) {
- const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
+ span[i] = random_value;
+ }
+ });
}
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
@@ -173,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
- ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
+ GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_size);
if (hash_attribute) {
BLI_assert(hashes.size() == hash_attribute->size());
- const CPPType &cpp_type = hash_attribute->cpp_type();
- fn::GSpan items = hash_attribute->get_span();
- for (const int i : hashes.index_range()) {
- hashes[i] = cpp_type.hash(items[i]);
- }
+ const CPPType &cpp_type = hash_attribute->type();
+ GVArray_GSpan items{*hash_attribute};
+ parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
@@ -196,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef attribute_name)
+ const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input domain chosen in the interface. */
@@ -228,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
- fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
- attribute->get_span_for_write_only() :
- attribute->get_span();
+ GMutableSpan span = attribute.as_span();
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
@@ -269,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
}
}
- attribute.apply_span_and_save();
-} // namespace blender::nodes
+ attribute.save();
+}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
@@ -304,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
+ if (geometry_set.has<CurveComponent>()) {
+ randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ attribute_name,
+ data_type,
+ operation,
+ seed);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 837f0c3629a..e4f3230ebb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
+ if (geometry_set.has<CurveComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index d0b2595b5b9..aa558314b9e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_task.hh"
#include "DNA_texture_types.h"
@@ -29,6 +30,7 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_TEXTURE, N_("Texture")},
{SOCK_STRING, N_("Mapping")},
{SOCK_STRING, N_("Result")},
{-1, ""},
@@ -39,29 +41,22 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
{-1, ""},
};
-static void geo_node_attribute_sample_texture_layout(uiLayout *layout,
- bContext *C,
- PointerRNA *ptr)
-{
- uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
-}
-
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef result_attribute_name,
- StringRef map_attribute_name)
+ const StringRef result_name,
+ const StringRef map_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the name of the map attribute. */
- ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
- if (map_attribute) {
- return map_attribute->domain();
+ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
+ if (map_info) {
+ return map_info->domain;
}
/* The node won't execute in this case, but we still have to return a value. */
@@ -70,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
{
- const bNode &node = params.node();
- Tex *texture = reinterpret_cast<Tex *>(node.id);
+ Tex *texture = params.get_input<Tex *>("Texture");
if (texture == nullptr) {
return;
}
@@ -85,25 +79,28 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
- result_attribute_name, result_domain, CD_PROP_COLOR);
+ OutputAttribute_Typed<Color4f> attribute_out =
+ component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain);
if (!attribute_out) {
return;
}
- Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
- for (const int i : IndexRange(mapping_attribute.size())) {
- TexResult texture_result = {0};
- const float3 position = mapping_attribute[i];
- /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
- const float3 remapped_position = position * 2.0f - float3(1.0f);
- BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
- }
- attribute_out.apply_span_and_save();
+ MutableSpan<Color4f> colors = attribute_out.as_span();
+ parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ });
+
+ attribute_out.save();
}
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
@@ -118,6 +115,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
@@ -137,6 +137,5 @@ void register_node_type_geo_sample_texture()
node_type_socket_templates(
&ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
- ntype.draw_buttons = geo_node_attribute_sample_texture_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
index 656dc51149e..137a72bb707 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -71,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef result_name_x,
- StringRef result_name_y,
- StringRef result_name_z)
+ const StringRef name_x,
+ const StringRef name_y,
+ const StringRef name_z)
{
/* Use the highest priority domain from any existing attribute outputs. */
Vector<AttributeDomain, 3> output_domains;
- ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
- ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
- ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
- if (attribute_x) {
- output_domains.append(attribute_x->domain());
+ std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
+ std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
+ std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
+ if (info_x) {
+ output_domains.append(info_x->domain);
}
- if (attribute_y) {
- output_domains.append(attribute_y->domain());
+ if (info_y) {
+ output_domains.append(info_y->domain);
}
- if (attribute_z) {
- output_domains.append(attribute_z->domain());
+ if (info_z) {
+ output_domains.append(info_z->domain);
}
if (output_domains.size() > 0) {
return bke::attribute_domain_highest_priority(output_domains);
@@ -107,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
/* The node is only for float3 to float conversions. */
- const CustomDataType input_type = CD_PROP_FLOAT3;
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- ReadAttributePtr attribute_input = params.get_input_attribute(
- "Vector", component, result_domain, input_type, nullptr);
- if (!attribute_input) {
- return;
- }
- const Span<float3> input_span = attribute_input->get_span<float3>();
+ GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ "Vector", component, result_domain, {0, 0, 0});
+ VArray_Span<float3> input_span{*attribute_input};
- OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
- result_name_x, result_domain, result_type);
- OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
- result_name_y, result_domain, result_type);
- OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
- result_name_z, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result_x =
+ component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
+ OutputAttribute_Typed<float> attribute_result_y =
+ component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
+ OutputAttribute_Typed<float> attribute_result_z =
+ component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
/* Only extract the components for the outputs with a given attribute. */
if (attribute_result_x) {
- extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
- attribute_result_x.apply_span_and_save();
+ extract_input(0, input_span, attribute_result_x.as_span());
+ attribute_result_x.save();
}
if (attribute_result_y) {
- extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
- attribute_result_y.apply_span_and_save();
+ extract_input(1, input_span, attribute_result_y.as_span());
+ attribute_result_y.save();
}
if (attribute_result_z) {
- extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
- attribute_result_z.apply_span_and_save();
+ extract_input(2, input_span, attribute_result_z.as_span());
+ attribute_result_z.save();
}
}
@@ -153,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
new file mode 100644
index 00000000000..4b677dc5c82
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -0,0 +1,597 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Source Geometry")},
+ {SOCK_STRING, N_("Source")},
+ {SOCK_STRING, N_("Destination")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_transfer_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
+ sizeof(NodeGeometryAttributeTransfer), __func__);
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
+ const GeometryComponent &dst_component,
+ const StringRef attribute_name,
+ CustomDataType *r_data_type,
+ AttributeDomain *r_domain)
+{
+ Vector<CustomDataType> data_types;
+ Vector<AttributeDomain> domains;
+
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ if (pointcloud_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (mesh_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ *r_data_type = bke::attribute_data_type_highest_complexity(data_types);
+
+ if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ *r_domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ *r_domain = bke::attribute_domain_highest_priority(domains);
+ }
+}
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty());
+ BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty());
+ BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty());
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ if (!r_indices.is_empty()) {
+ r_indices[i] = nearest.index;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = nearest.co;
+ }
+ }
+}
+
+static void get_closest_pointcloud_points(const PointCloud &pointcloud,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq)
+{
+ BLI_assert(positions.size() == r_indices.size());
+ BLI_assert(pointcloud.totpoint > 0);
+
+ BVHTreeFromPointCloud tree_data;
+ BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ r_indices[i] = nearest.index;
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+
+ free_bvhtree_from_pointcloud(&tree_data);
+}
+
+static void get_closest_mesh_points(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_point_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totvert > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_edges(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_edge_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totedge > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_looptris(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_looptri_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_polygons(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ Array<int> looptri_indices(positions.size());
+ get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ for (const int i : positions.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ r_poly_indices[i] = looptri.poly;
+ }
+}
+
+/* The closest corner is defined to be the closest corner on the closest face. */
+static void get_closest_mesh_corners(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_corner_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {});
+
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = mesh.mpoly[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = mesh.mvert[vertex_index];
+ const float distance_sq = float3::distance_squared(position, mvert.co);
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ closest_loop_index = loop_index;
+ closest_mvert = &mvert;
+ }
+ }
+ if (!r_corner_indices.is_empty()) {
+ r_corner_indices[i] = closest_loop_index;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = closest_mvert->co;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = min_distance_sq;
+ }
+ }
+}
+
+static void get_barycentric_coords(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> positions,
+ const MutableSpan<float3> r_bary_coords)
+{
+ BLI_assert(r_bary_coords.size() == positions.size());
+ BLI_assert(r_bary_coords.size() == looptri_indices.size());
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : r_bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ interp_weights_tri_v3(r_bary_coords[i],
+ mesh.mvert[v0_index].co,
+ mesh.mvert[v1_index].co,
+ mesh.mvert[v2_index].co,
+ positions[i]);
+ }
+}
+
+static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const int tot_samples = dst_positions.size();
+ const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>();
+ if (component == nullptr) {
+ return;
+ }
+ const Mesh *mesh = component->get_for_read();
+ if (mesh == nullptr) {
+ return;
+ }
+ if (mesh->totpoly == 0) {
+ return;
+ }
+ ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
+ if (!src_attribute) {
+ return;
+ }
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(tot_samples);
+ Array<float3> positions(tot_samples);
+ get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+ GMutableSpan dst_span = dst_attribute.as_span();
+ Array<float3> bary_coords;
+
+ /* Compute barycentric coordinates only when they are needed. */
+ if (src_attribute.domain != ATTR_DOMAIN_FACE) {
+ bary_coords.reinitialize(tot_samples);
+ get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
+ }
+ /* Interpolate the source attribute on the surface. */
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ *mesh, looptri_indices, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Not yet supported. */
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ dst_attribute.save();
+}
+
+static void transfer_attribute_nearest(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* Get pointcloud data from geometry. */
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() :
+ nullptr;
+
+ /* Get mesh data from geometry. */
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr;
+
+ const int tot_samples = dst_positions.size();
+
+ Array<int> pointcloud_indices;
+ Array<float> pointcloud_distances_sq;
+ bool use_pointcloud = false;
+
+ /* Depending on where what domain the source attribute lives, these indices are either vertex,
+ * corner, edge or polygon indices. */
+ Array<int> mesh_indices;
+ Array<float> mesh_distances_sq;
+ bool use_mesh = false;
+
+ /* If there is a pointcloud, find the closest points. */
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ if (pointcloud_component->attribute_exists(src_name)) {
+ use_pointcloud = true;
+ pointcloud_indices.reinitialize(tot_samples);
+ pointcloud_distances_sq.reinitialize(tot_samples);
+ get_closest_pointcloud_points(
+ *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq);
+ }
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (mesh != nullptr) {
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name);
+ if (src_attribute) {
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ if (mesh->totvert > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (mesh->totedge > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ if (mesh->totpoly > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!use_pointcloud && !use_mesh) {
+ return;
+ }
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+
+ /* Create a buffer for intermediate values. */
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+
+ if (use_mesh && use_pointcloud) {
+ /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or
+ * a mesh element is closer to every point. */
+ ReadAttributeLookup pointcloud_src_attribute =
+ pointcloud_component->attribute_try_get_for_read(src_name, data_type);
+ ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
+ /* Pointcloud point is closer. */
+ const int index = pointcloud_indices[i];
+ pointcloud_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ else {
+ /* Mesh element is closer. */
+ const int index = mesh_indices[i];
+ mesh_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ }
+ else if (use_pointcloud) {
+ /* The source geometry only has a pointcloud. */
+ ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read(
+ src_name, data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = pointcloud_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ else if (use_mesh) {
+ /* The source geometry only has a mesh. */
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = mesh_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+
+ dst_attribute.save();
+}
+
+static void transfer_attribute(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const NodeGeometryAttributeTransfer &storage =
+ *(const NodeGeometryAttributeTransfer *)params.node().storage;
+ const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode)
+ storage.mapping;
+ const AttributeDomain input_domain = (AttributeDomain)storage.domain;
+
+ CustomDataType data_type;
+ AttributeDomain auto_domain;
+ get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain);
+ const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
+ input_domain;
+
+ GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ "position", dst_domain, {0, 0, 0});
+
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ transfer_attribute_nearest_face_interpolated(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ transfer_attribute_nearest(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ }
+}
+
+static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+{
+ GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
+ const std::string src_attribute_name = params.extract_input<std::string>("Source");
+ const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
+
+ if (src_attribute_name.empty() || dst_attribute_name.empty()) {
+ params.set_output("Geometry", dst_geometry_set);
+ return;
+ }
+
+ dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
+ src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+
+ if (dst_geometry_set.has<MeshComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<MeshComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+ if (dst_geometry_set.has<PointCloudComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<PointCloudComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+
+ params.set_output("Geometry", dst_geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_transfer()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeTransfer",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
+ ntype.draw_buttons = geo_node_attribute_transfer_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index 1ae095a27d2..8877af445f9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -168,196 +169,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
operation_use_input_c(operation));
}
-static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const Float3ReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float3> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float3> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float3> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result};
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const FloatReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float3 out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
@@ -370,9 +385,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
if (result_attribute) {
- return result_attribute->domain();
+ return result_attribute.domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -406,13 +421,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- ReadAttributePtr attribute_b;
- ReadAttributePtr attribute_c;
+ GVArrayPtr attribute_b;
+ GVArrayPtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -427,7 +442,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
@@ -445,17 +460,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float>(),
+ operation);
break;
case NODE_VECTOR_MATH_LENGTH:
- do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl(
+ attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
@@ -465,16 +490,23 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
- do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl3(
+ attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
- do_math_operation_fl3_fl3_fl3_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
}
attribute_result.save();
@@ -493,6 +525,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
new file mode 100644
index 00000000000..4d568ab5c3a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -0,0 +1,352 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Vector")},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {SOCK_STRING, N_("Center")},
+ {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_STRING, N_("Axis")},
+ {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE},
+ {SOCK_STRING, N_("Angle")},
+ {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE},
+ {SOCK_STRING, N_("Rotation")},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage.mode;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiLayout *column = uiLayoutColumn(layout, false);
+
+ uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE);
+
+ uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
+ uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE);
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) {
+ uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE);
+ }
+ if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE);
+ }
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ }
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+ update_attribute_input_socket_availabilities(
+ *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+}
+
+static float3 vector_rotate_around_axis(const float3 vector,
+ const float3 center,
+ const float3 axis,
+ const float angle)
+{
+ float3 result = vector - center;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
+ sizeof(NodeAttributeVectorRotate), __func__);
+
+ node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static float3 vector_rotate_euler(const float3 vector,
+ const float3 center,
+ const float3 rotation,
+ const bool invert)
+{
+ float mat[3][3];
+ float3 result = vector - center;
+ eul_to_mat3(mat, rotation);
+ if (invert) {
+ invert_m3(mat);
+ }
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void do_vector_rotate_around_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_axis{axis};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
+ }
+ });
+}
+
+static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const float3 axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
+ }
+ });
+}
+
+static void do_vector_rotate_euler(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &rotation,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_rotation{rotation};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
+ }
+ });
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name);
+ if (meta_data) {
+ return meta_data->domain;
+ }
+
+ /* Otherwise use the highest priority domain from existing input attributes, or the default. */
+ const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
+ return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain);
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &node = params.node();
+ const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const AttributeDomain result_domain = get_result_domain(component, params, result_name);
+ const bool invert = params.get_input<bool>("Invert");
+
+ GVArrayPtr attribute_vector = params.get_input_attribute(
+ "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_vector) {
+ return;
+ }
+ GVArrayPtr attribute_center = params.get_input_attribute(
+ "Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_center) {
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, CD_PROP_FLOAT3);
+ if (!attribute_result) {
+ return;
+ }
+
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ GVArrayPtr attribute_rotation = params.get_input_attribute(
+ "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_rotation) {
+ return;
+ }
+ do_vector_rotate_euler(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_rotation->typed<float3>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ attribute_result.save();
+ return;
+ }
+
+ GVArrayPtr attribute_angle = params.get_input_attribute(
+ "Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
+ if (!attribute_angle) {
+ return;
+ }
+
+ switch (mode) {
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
+ GVArrayPtr attribute_axis = params.get_input_attribute(
+ "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_axis) {
+ return;
+ }
+ do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_axis->typed<float3>(),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ } break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(1.0f, 0.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 1.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 0.0f, 1.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ:
+ /* Euler is handled before other modes to avoid processing the unavailable angle socket. */
+ BLI_assert_unreachable();
+ break;
+ }
+ attribute_result.save();
+}
+
+static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_vector_rotate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ "Attribute Vector Rotate",
+ NODE_CLASS_ATTRIBUTE,
+ 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init);
+ node_type_size(&ntype, 165, 100, 600);
+ node_type_storage(
+ &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec;
+ ntype.draw_buttons = geo_node_attribute_vector_rotate_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index 96455f080e7..b6fa4c0d48f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -39,15 +39,12 @@ static void compute_min_max_from_position_and_transform(const GeometryComponent
float3 &r_min,
float3 &r_max)
{
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- if (!position_attribute) {
- BLI_assert(component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0);
- return;
- }
- Span<float3> positions = position_attribute->get_span<float3>();
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
for (const float4x4 &transform : transforms) {
- for (const float3 &position : positions) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
const float3 transformed_position = transform * position;
minmax_v3v3_v3(r_min, r_max, transformed_position);
}
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 0ad495aa4db..9bc8a4bb4be 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -40,11 +40,17 @@ static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C
namespace blender::nodes {
+static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
+ sizeof(NodeGeometryCollectionInfo), __func__);
+ data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
+ node->storage = data;
+}
+
static void geo_node_collection_info_exec(GeoNodeExecParams params)
{
- bke::PersistentCollectionHandle collection_handle =
- params.extract_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
GeometrySet geometry_set_out;
@@ -58,10 +64,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
-
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
@@ -73,17 +75,11 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
mul_m4_m4_pre(transform_mat, self_object->imat);
}
- instances.add_instance(instance, transform_mat, -1);
- params.set_output("Geometry", geometry_set_out);
-}
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform_mat, -1);
-static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
- sizeof(NodeGeometryCollectionInfo), __func__);
- data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
- node->storage = data;
+ params.set_output("Geometry", geometry_set_out);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
new file mode 100644
index 00000000000..1c42b9341a0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,238 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+static bNodeSocketTemplate geo_node_curve_resample_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_resample_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
+ sizeof(NodeGeometryCurveResample), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct SampleModeParam {
+ GeometryNodeCurveSampleMode mode;
+ std::optional<float> length;
+ std::optional<int> count;
+};
+
+template<typename T>
+static void sample_span_to_output_spline(const Spline &input_spline,
+ Span<float> index_factors,
+ const VArray<T> &input_data,
+ MutableSpan<T> output_data)
+{
+ BLI_assert(input_data.size() == input_spline.evaluated_points_size());
+
+ parallel_for(output_data.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline::LookupResult interp = input_spline.lookup_data_from_index_factor(
+ index_factors[i]);
+ output_data[i] = blender::attribute_math::mix2(interp.factor,
+ input_data[interp.evaluated_index],
+ input_data[interp.next_evaluated_index]);
+ }
+ });
+}
+
+static SplinePtr resample_spline(const Spline &input_spline, const int count)
+{
+ std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
+ output_spline->set_cyclic(input_spline.is_cyclic());
+ output_spline->normal_mode = input_spline.normal_mode;
+
+ if (input_spline.evaluated_edges_size() < 1) {
+ output_spline->resize(1);
+ output_spline->positions().first() = input_spline.positions().first();
+ return output_spline;
+ }
+
+ output_spline->resize(count);
+
+ Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
+
+ {
+ GVArray_For_Span positions(input_spline.evaluated_positions());
+ GVArray_Typed<float3> positions_typed(positions);
+ sample_span_to_output_spline<float3>(
+ input_spline, uniform_samples, positions_typed, output_spline->positions());
+ }
+ {
+ GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points(
+ GVArray_For_Span(input_spline.radii()));
+ GVArray_Typed<float> interpolated_data_typed{*interpolated_data};
+ sample_span_to_output_spline<float>(
+ input_spline, uniform_samples, interpolated_data_typed, output_spline->radii());
+ }
+ {
+ GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points(
+ GVArray_For_Span(input_spline.tilts()));
+ GVArray_Typed<float> interpolated_data_typed{*interpolated_data};
+ sample_span_to_output_spline<float>(
+ input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts());
+ }
+
+ output_spline->attributes.reallocate(count);
+ input_spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name);
+ BLI_assert(input_attribute);
+ if (!output_spline->attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write(
+ name);
+ if (!output_attribute) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ GVArrayPtr interpolated_attribute = input_spline.interpolate_to_evaluated_points(
+ GVArray_For_GSpan(*input_attribute));
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> interpolated_attribute_typed{*interpolated_attribute};
+ sample_span_to_output_spline<T>(input_spline,
+ uniform_samples,
+ interpolated_attribute_typed,
+ (*output_attribute).typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ return output_spline;
+}
+
+static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+ const SampleModeParam &mode_param)
+{
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ for (const SplinePtr &spline : input_curve.splines()) {
+ if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ BLI_assert(mode_param.count);
+ output_curve->add_spline(resample_spline(*spline, *mode_param.count));
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ BLI_assert(mode_param.length);
+ const float length = spline->length();
+ const int count = length / *mode_param.length;
+ output_curve->add_spline(resample_spline(*spline, count));
+ }
+ }
+
+ return output_curve;
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ SampleModeParam mode_param;
+ mode_param.mode = mode;
+ if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+ mode_param.count.emplace(count);
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ mode_param.length.emplace(resolution);
+ }
+
+ std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_resample()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out);
+ ntype.draw_buttons = geo_node_curve_resample_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_resample_init);
+ node_type_update(&ntype, geo_node_curve_resample_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
new file mode 100644
index 00000000000..f0effdc71f6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -0,0 +1,312 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Profile Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void vert_extrude_to_mesh_data(const Spline &spline,
+ const float3 profile_vert,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ int &vert_offset,
+ int &edge_offset)
+{
+ Span<float3> positions = spline.evaluated_positions();
+
+ for (const int i : IndexRange(positions.size() - 1)) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset;
+ edge.v2 = vert_offset + positions.size() - 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ for (const int i : positions.index_range()) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, positions[i] + profile_vert);
+ }
+}
+
+static void mark_edges_sharp(MutableSpan<MEdge> edges)
+{
+ for (MEdge &edge : edges) {
+ edge.flag |= ME_SHARP;
+ }
+}
+
+static void spline_extrude_to_mesh_data(const Spline &spline,
+ const Spline &profile_spline,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ MutableSpan<MLoop> r_loops,
+ MutableSpan<MPoly> r_polys,
+ int &vert_offset,
+ int &edge_offset,
+ int &loop_offset,
+ int &poly_offset)
+{
+ const int spline_vert_len = spline.evaluated_points_size();
+ const int spline_edge_len = spline.evaluated_edges_size();
+ const int profile_vert_len = profile_spline.evaluated_points_size();
+ const int profile_edge_len = profile_spline.evaluated_edges_size();
+ if (spline_vert_len == 0) {
+ return;
+ }
+
+ if (profile_vert_len == 1) {
+ vert_extrude_to_mesh_data(spline,
+ profile_spline.evaluated_positions()[0],
+ r_verts,
+ r_edges,
+ vert_offset,
+ edge_offset);
+ return;
+ }
+
+ /* Add the edges running along the length of the curve, starting at each profile vertex. */
+ const int spline_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = next_ring_vert_offset + i_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Add the edges running along each profile ring. */
+ const int profile_edges_start = edge_offset;
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = ring_vert_offset + i_next_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Calculate poly and corner indices. */
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+
+ MPoly &poly = r_polys[poly_offset++];
+ poly.loopstart = loop_offset;
+ poly.totloop = 4;
+ poly.flag = ME_SMOOTH;
+
+ MLoop &loop_a = r_loops[loop_offset++];
+ loop_a.v = ring_vert_offset + i_profile;
+ loop_a.e = ring_edge_start + i_profile;
+ MLoop &loop_b = r_loops[loop_offset++];
+ loop_b.v = ring_vert_offset + i_next_profile;
+ loop_b.e = next_spline_edge_start + i_ring;
+ MLoop &loop_c = r_loops[loop_offset++];
+ loop_c.v = next_ring_vert_offset + i_next_profile;
+ loop_c.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_d = r_loops[loop_offset++];
+ loop_d.v = next_ring_vert_offset + i_profile;
+ loop_d.e = spline_edge_start + i_ring;
+ }
+ }
+
+ /* Calculate the positions of each profile ring profile along the spline. */
+ Span<float3> positions = spline.evaluated_positions();
+ Span<float3> tangents = spline.evaluated_tangents();
+ Span<float3> normals = spline.evaluated_normals();
+ Span<float3> profile_positions = profile_spline.evaluated_positions();
+
+ GVArray_Typed<float> radii{
+ spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))};
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ positions[i_ring], normals[i_ring], tangents[i_ring]);
+
+ point_matrix.apply_scale(radii[i_ring]);
+
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+
+ /* Mark edge loops from sharp vector control points sharp. */
+ if (profile_spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
+ Span<int> control_point_offsets = bezier_spline.control_point_offsets();
+ for (const int i : IndexRange(bezier_spline.size())) {
+ if (bezier_spline.point_is_sharp(i)) {
+ mark_edges_sharp(r_edges.slice(
+ spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
+ }
+ }
+ }
+}
+
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+{
+ int profile_vert_total = 0;
+ int profile_edge_total = 0;
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ profile_vert_total += profile_spline->evaluated_points_size();
+ profile_edge_total += profile_spline->evaluated_edges_size();
+ }
+
+ int vert_total = 0;
+ int edge_total = 0;
+ int poly_total = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ const int spline_vert_len = spline->evaluated_points_size();
+ const int spline_edge_len = spline->evaluated_edges_size();
+ vert_total += spline_vert_len * profile_vert_total;
+ poly_total += spline_edge_len * profile_edge_total;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
+ }
+ const int corner_total = poly_total * 4;
+
+ if (vert_total == 0) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+
+ int vert_offset = 0;
+ int edge_offset = 0;
+ int loop_offset = 0;
+ int poly_offset = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ spline_extrude_to_mesh_data(*spline,
+ *profile_spline,
+ verts,
+ edges,
+ loops,
+ polys,
+ vert_offset,
+ edge_offset,
+ loop_offset,
+ poly_offset);
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ return mesh;
+}
+
+static CurveEval get_curve_single_vert()
+{
+ CurveEval curve;
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->add_point(float3(0), 0, 0.0f);
+ curve.add_spline(std::move(spline));
+
+ return curve;
+}
+
+static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ profile_set = bke::geometry_set_realize_instances(profile_set);
+
+ if (!curve_set.has_curve()) {
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
+
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ static const CurveEval vert_curve = get_curve_single_vert();
+
+ Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
+ (profile_curve == nullptr) ? vert_curve : *profile_curve);
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 534b7d754ec..740b828d503 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -44,6 +44,7 @@ static bNodeSocketTemplate geo_node_edge_split_out[] = {
};
namespace blender::nodes {
+
static void geo_node_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -82,6 +83,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_edge_split()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
new file mode 100644
index 00000000000..6bad71a62a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_input_material_out[] = {
+ {SOCK_MATERIAL, N_("Material")},
+ {-1, ""},
+};
+
+static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "material", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_input_material_exec(GeoNodeExecParams params)
+{
+ Material *material = (Material *)params.node().id;
+ params.set_output("Material", material);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out);
+ ntype.draw_buttons = geo_node_input_material_layout;
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 77d2b0df2a9..adfd924f185 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -14,9 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,6 +58,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
totverts += mesh->totvert;
@@ -66,12 +70,22 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
cd_dirty_poly |= mesh->runtime.cd_dirty_poly;
cd_dirty_edge |= mesh->runtime.cd_dirty_edge;
cd_dirty_loop |= mesh->runtime.cd_dirty_loop;
+
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ materials.add(material);
+ }
}
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
+
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -87,6 +101,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
continue;
}
+ Array<int> material_index_map(mesh->totcol);
+ for (const int i : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const int i : IndexRange(mesh->totvert)) {
const MVert &old_vert = mesh->mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
@@ -112,6 +133,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh->totvert;
@@ -149,10 +177,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- data_types.append(attribute->custom_data_type());
- domains.append(attribute->domain());
+ data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
+ domains.append(attribute.domain);
}
}
@@ -164,7 +192,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
- fn::GMutableSpan dst_span)
+ GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -175,10 +203,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
- fn::GSpan src_span = read_attribute->get_span();
+ GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
@@ -201,16 +229,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
- OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
+ OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
attribute_name, domain, data_type);
- if (!write_attribute ||
- &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
- write_attribute->domain() != domain) {
+ if (!write_attribute) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+ GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
- write_attribute.apply_span_and_save();
+ write_attribute.save();
}
}
@@ -244,13 +270,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
- for (const InstancesComponent *component : src_components) {
- const int size = component->instances_amount();
- Span<InstancedData> instanced_data = component->instanced_data();
- Span<float4x4> transforms = component->transforms();
- Span<int> ids = component->ids();
- for (const int i : IndexRange(size)) {
- dst_component.add_instance(instanced_data[i], transforms[i], ids[i]);
+
+ int tot_instances = 0;
+ for (const InstancesComponent *src_component : src_components) {
+ tot_instances += src_component->instances_amount();
+ }
+ dst_component.reserve(tot_instances);
+
+ for (const InstancesComponent *src_component : src_components) {
+ Span<InstanceReference> src_references = src_component->references();
+ Array<int> handle_map(src_references.size());
+ for (const int src_handle : src_references.index_range()) {
+ handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]);
+ }
+
+ Span<float4x4> src_transforms = src_component->instance_transforms();
+ Span<int> src_ids = src_component->instance_ids();
+ Span<int> src_reference_handles = src_component->instance_reference_handles();
+
+ for (const int i : src_transforms.index_range()) {
+ const int src_handle = src_reference_handles[i];
+ const int dst_handle = handle_map[src_handle];
+ const float4x4 &transform = src_transforms[i];
+ const int id = src_ids[i];
+ dst_component.add_instance(dst_handle, transform, id);
}
}
}
@@ -263,6 +306,48 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
+{
+ Vector<CurveComponent *> src_components;
+ for (GeometrySet &geometry_set : src_geometry_sets) {
+ if (geometry_set.has_curve()) {
+ /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
+ * in the case where the input spline has no other users, because the splines can be
+ * moved from the source curve rather than copied from a read-only source. Retrieving
+ * the curve for write will make a copy only when it has a user elsewhere. */
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ src_components.append(&component);
+ }
+ }
+
+ if (src_components.size() == 0) {
+ return;
+ }
+ if (src_components.size() == 1) {
+ result.add(*src_components[0]);
+ return;
+ }
+
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ CurveEval *dst_curve = new CurveEval();
+ for (CurveComponent *component : src_components) {
+ CurveEval *src_curve = component->get_for_write();
+ for (SplinePtr &spline : src_curve->splines()) {
+ dst_curve->add_spline(std::move(spline));
+ }
+ }
+
+ /* For now, remove all custom attributes, since they might have different types,
+ * or an attribute might not exist on all splines. */
+ dst_curve->attributes.reallocate(dst_curve->splines().size());
+ CustomData_reset(&dst_curve->attributes.data);
+ for (SplinePtr &spline : dst_curve->splines()) {
+ CustomData_reset(&spline->attributes.data);
+ }
+
+ dst_component.replace(dst_curve);
+}
+
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -293,6 +378,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
+ join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
new file mode 100644
index 00000000000..8c245afd3d2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -0,0 +1,98 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_assign_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL, N_("Material")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_assign_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
+{
+ int new_material_index = -1;
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *other_material = mesh.mat[i];
+ if (other_material == material) {
+ new_material_index = i;
+ break;
+ }
+ }
+ if (new_material_index == -1) {
+ /* Append a new material index. */
+ new_material_index = mesh.totcol;
+ BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+ }
+
+ mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+ for (const int i : IndexRange(mesh.totpoly)) {
+ if (face_mask[i]) {
+ MPoly &poly = mesh.mpoly[i];
+ poly.mat_nr = new_material_index;
+ }
+ }
+}
+
+static void geo_node_material_assign_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string mask_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+ mask_name, ATTR_DOMAIN_FACE, true);
+ assign_material_to_faces(*mesh, face_mask, material);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_assign()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
new file mode 100644
index 00000000000..40ecab98dea
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -0,0 +1,75 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_replace_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL, N_("Old")},
+ {SOCK_MATERIAL, N_("New")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_replace_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_material_replace_exec(GeoNodeExecParams params)
+{
+ Material *old_material = params.extract_input<Material *>("Old");
+ Material *new_material = params.extract_input<Material *>("New");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ for (const int i : IndexRange(mesh->totcol)) {
+ if (mesh->mat[i] == old_material) {
+ mesh->mat[i] = new_material;
+ }
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_replace()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_replace_in, geo_node_material_replace_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 761d5d6c388..2806472286e 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
@@ -194,9 +194,9 @@ static void calculate_uvs(Mesh *mesh,
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
Array<float2> circle(verts_num);
float angle = 0.0f;
@@ -271,7 +271,7 @@ static void calculate_uvs(Mesh *mesh,
}
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 14c57bc7135..5a4bab86421 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -44,9 +44,9 @@ static void calculate_uvs(
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
@@ -56,7 +56,7 @@ static void calculate_uvs(
uvs[i].y = (co.y + size_y * 0.5f) * dy;
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
static Mesh *create_grid_mesh(const int verts_x,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index fd95cdc81f7..cc93e71a5dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -224,9 +224,9 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
int loop_index = 0;
const float dy = 1.0f / rings;
@@ -256,7 +256,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index bd42b4c11d6..16c943b310c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -47,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.get_input<Object *>("Object");
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
@@ -73,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const int handle = instances.add_reference(*object);
if (transform_space_relative) {
- instances.add_instance(object, transform);
+ instances.add_instance(handle, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
- instances.add_instance(object, unit_transform);
+ instances.add_instance(handle, unit_transform);
}
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 74cca8a2f3c..772bd8a1080 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -28,6 +28,7 @@
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -91,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const FloatReadAttribute *density_factors,
+ const VArray<float> *density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -113,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
- const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
- const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
+ const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
+ const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
+ const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -203,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const Mesh &mesh,
- const FloatReadAttribute &density_factors,
+ const VArray<float> &density_factors,
Span<float3> bary_coords,
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
@@ -249,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totvert);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
-
- const T &v0 = data_in[v0_index];
- const T &v1 = data_in[v1_index];
- const T &v2 = data_in[v2_index];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totloop);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int loop_index_0 = looptri.tri[0];
- const int loop_index_1 = looptri.tri[1];
- const int loop_index_2 = looptri.tri[2];
-
- const T &v0 = data_in[loop_index_0];
- const T &v1 = data_in[loop_index_1];
- const T &v2 = data_in[loop_index_2];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totpoly);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : data_out.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const int poly_index = looptri.poly;
- data_out[i] = data_in[poly_index];
- }
-}
-
-template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
- Span<T> source_span,
- MutableSpan<T> output_span)
+ const GVArray &source_data,
+ GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- interpolate_attribute_corner<T>(
- mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
- interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -363,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
StringRef attribute_name = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
+ GMutableSpan out_span = attribute_out.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -377,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *source_component.get_for_read();
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
- if (!dummy_attribute) {
+ if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
}
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- fn::GSpan source_span = source_attribute->get_span();
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- const int offset = instance_start_offsets[i_instance];
- Span<float3> bary_coords = bary_coords_array[i_instance];
- Span<int> looptri_indices = looptri_indices_array[i_instance];
-
- MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
- interpolate_attribute<T>(mesh,
- bary_coords,
- looptri_indices,
- source_domain,
- source_span.typed<T>(),
- instance_span);
-
- i_instance++;
- }
+ GVArray_Span<T> source_span{*source_attribute};
});
}
- attribute_out.apply_span_and_save();
+ attribute_out.save();
}
}
@@ -427,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> normal_attribute =
+ component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
- MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute.as_span();
+ MutableSpan<float3> result_normals = normal_attribute.as_span();
+ MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -480,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
}
- id_attribute.apply_span_and_save();
- normal_attribute.apply_span_and_save();
- rotation_attribute.apply_span_and_save();
+ id_attribute.save();
+ normal_attribute.save();
+ rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -520,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -530,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &density_factors,
+ &*density_factors,
seed,
positions,
bary_coords,
@@ -589,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index 20022e8d29d..44b8b14f4e7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -14,11 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_persistent_data_handle.hh"
-
#include "DNA_collection_types.h"
#include "BLI_hash.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -48,6 +47,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C)
namespace blender::nodes {
+static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
+ sizeof(NodeGeometryPointInstance), __func__);
+ data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
+ data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
+ node->storage = data;
+}
+
static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
@@ -65,128 +73,133 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
}
-static void get_instanced_data__object(const GeoNodeExecParams &params,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
{
- bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.extract_input<Object *>("Object");
if (object == params.self_object()) {
- object = nullptr;
+ return {};
}
if (object != nullptr) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- r_instances_data.fill(instance);
+ return {*object};
}
+ return {};
}
-static void get_instanced_data__collection(
- const GeoNodeExecParams &params,
- const GeometryComponent &component,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
- bke::PersistentCollectionHandle collection_handle =
- params.get_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
if (collection == nullptr) {
- return;
+ return {};
}
if (BLI_listbase_is_empty(&collection->children) &&
BLI_listbase_is_empty(&collection->gobject)) {
params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty"));
- return;
+ return {};
}
- const bool use_whole_collection = (node_storage->flag &
- GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
- if (use_whole_collection) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
- r_instances_data.fill(instance);
+ if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) {
+ return {*collection};
}
- else {
- Vector<InstancedData> possible_instances;
- /* Direct child objects are instanced as objects. */
- LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- Object *object = cob->ob;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- possible_instances.append(instance);
- }
- /* Direct child collections are instanced as collections. */
- LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- Collection *child_collection = child->collection;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = child_collection;
- possible_instances.append(instance);
- }
- if (!possible_instances.is_empty()) {
- const int seed = params.get_input<int>("Seed");
- Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
- for (const int i : r_instances_data.index_range()) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
- r_instances_data[i] = possible_instances[index];
- }
- }
+ Vector<InstanceReference> references;
+ /* Direct child objects are instanced as objects. */
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
+ references.append(*cob->ob);
+ }
+ /* Direct child collections are instanced as collections. */
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
+ references.append(*child->collection);
}
+
+ return references;
}
-static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams &params,
- const GeometryComponent &component,
- const int amount)
+static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)
node_storage->instance_type;
- Array<std::optional<InstancedData>> instances_data(amount);
switch (type) {
case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: {
- get_instanced_data__object(params, instances_data);
- break;
+ return get_instance_references__object(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
- get_instanced_data__collection(params, component, instances_data);
- break;
+ return get_instance_references__collection(params);
}
}
- return instances_data;
+ return {};
+}
+
+/**
+ * Add the instance references to the component as a separate step from actually creating the
+ * instances in order to avoid a map lookup for every transform. While this might add some
+ * unnecessary references if they are not chosen while adding transforms, in the common cases
+ * there are many more transforms than there are references, so that isn't likely.
+ */
+static Array<int> add_instance_references(InstancesComponent &instance_component,
+ Span<InstanceReference> possible_references)
+{
+ Array<int> possible_handles(possible_references.size());
+ for (const int i : possible_references.index_range()) {
+ possible_handles[i] = instance_component.add_reference(possible_references[i]);
+ }
+ return possible_handles;
}
-static void add_instances_from_geometry_component(InstancesComponent &instances,
- const GeometryComponent &src_geometry,
- const GeoNodeExecParams &params)
+static void add_instances_from_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Span<int> possible_handles,
+ const GeoNodeExecParams &params)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_geometry.attribute_domain_size(domain);
- Array<std::optional<InstancedData>> instances_data = get_instanced_data(
- params, src_geometry, domain_size);
- Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
- Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
-
- for (const int i : IndexRange(domain_size)) {
- if (instances_data[i].has_value()) {
- const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
- instances.add_instance(*instances_data[i], matrix, ids[i]);
- }
+ GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+
+ /* The initial size of the component might be non-zero if there are two component types. */
+ const int start_len = instances.instances_amount();
+ instances.resize(start_len + domain_size);
+ MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
+ MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
+ MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+
+ /* Skip all of the randomness handling if there is only a single possible instance
+ * (anything except for collection mode with "Whole Collection" turned off). */
+ if (possible_handles.size() == 1) {
+ const int handle = possible_handles.first();
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
+ }
+ else {
+ const int seed = params.get_input<int>("Seed");
+ Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
+ const int handle = possible_handles[index];
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
}
}
@@ -199,28 +212,37 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
* rather than making the entire input geometry set real. */
geometry_set = geometry_set_realize_instances(geometry_set);
+ const Vector<InstanceReference> possible_references = get_instance_references(params);
+ if (possible_references.is_empty()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Array<int> possible_handles = add_instance_references(instances, possible_references);
+
if (geometry_set.has<MeshComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<MeshComponent>(),
+ possible_handles,
+ params);
}
if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ possible_handles,
+ params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ possible_handles,
+ params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
- sizeof(NodeGeometryPointInstance), __func__);
- data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
- data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
- node->storage = data;
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_instance()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
index 2e7fce6ea30..828d3f50551 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
@@ -59,9 +59,43 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
+ sizeof(NodeGeometryRotatePoints), __func__);
+
+ node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
+ node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
+}
+
static void point_rotate__axis_angle__object_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -76,8 +110,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
}
static void point_rotate__axis_angle__point_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -92,7 +126,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
}
static void point_rotate__euler__object_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -107,7 +141,7 @@ static void point_rotate__euler__object_space(const int domain_size,
}
static void point_rotate__euler__point_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -127,19 +161,19 @@ static void point_rotate_on_component(GeometryComponent &component,
const bNode &node = params.node();
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotation_attribute) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
+ MutableSpan<float3> rotations = rotation_attribute.as_span();
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- Float3ReadAttribute axis = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- FloatReadAttribute angles = params.get_input_attribute<float>(
+ GVArray_Typed<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -150,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- Float3ReadAttribute eulers = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -161,7 +195,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
- rotation_attribute.apply_span_and_save();
+ rotation_attribute.save();
}
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
@@ -176,44 +210,13 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
- sizeof(NodeGeometryRotatePoints), __func__);
-
- node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
- node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
- node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
- update_attribute_input_socket_availabilities(
- *node,
- "Axis",
- (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Angle",
- (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Rotation",
- (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_rotate()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
index 113e2c620f6..655f5475856 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
@@ -43,6 +43,23 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P
namespace blender::nodes {
+static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
+ sizeof(NodeGeometryPointScale), __func__);
+
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node->storage = data;
+}
+
+static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
/* Note that scale doesn't necessarily need to be created with a vector type-- it could also use
@@ -50,7 +67,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
* for the factor. But for it's simpler to simply always use float3, since that is usually
* expected anyway. */
static const float3 scale_default = float3(1.0f);
- OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
+ OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
@@ -63,27 +80,27 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
CD_PROP_FLOAT3;
- ReadAttributePtr attribute = params.get_input_attribute(
+ GVArrayPtr attribute = params.get_input_attribute(
"Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
}
- MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
+ MutableSpan<float3> scale_span = scale_attribute.as_span();
if (data_type == CD_PROP_FLOAT) {
- Span<float> factors = attribute->get_span<float>();
+ GVArray_Typed<float> factors{*attribute};
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
}
else if (data_type == CD_PROP_FLOAT3) {
- Span<float3> factors = attribute->get_span<float3>();
+ GVArray_Typed<float3> factors{*attribute};
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
}
- scale_attribute.apply_span_and_save();
+ scale_attribute.save();
}
static void geo_node_point_scale_exec(GeoNodeExecParams params)
@@ -102,23 +119,6 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
- sizeof(NodeGeometryPointScale), __func__);
-
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- node->storage = data;
-}
-
-static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_scale()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index 522dea4aa0e..d2b024b208c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -58,27 +58,27 @@ static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
- ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
- const CustomDataType data_type = attribute->custom_data_type();
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
* on other domains, which causes creating the attributes to fail. */
- if (attribute->domain() != ATTR_DOMAIN_POINT) {
+ if (attribute.domain != ATTR_DOMAIN_POINT) {
continue;
}
- OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- Span<T> span = attribute->get_span<T>();
- MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
+ GVArray_Span<T> span{*attribute.varray};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
}
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
+ const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
- Span<bool> masks = mask_attribute.get_span();
+ VArray_Span<bool> masks{mask_attribute};
const int total = masks.count(!invert);
if (total == 0) {
@@ -135,15 +135,27 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
static void geo_node_point_separate_exec(GeoNodeExecParams params)
{
- const std::string mask_attribute_name = params.extract_input<std::string>("Mask");
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ bool wait_for_inputs = false;
+ wait_for_inputs |= params.lazy_require_input("Geometry");
+ wait_for_inputs |= params.lazy_require_input("Mask");
+ if (wait_for_inputs) {
+ return;
+ }
+ const std::string mask_attribute_name = params.get_input<std::string>("Mask");
+ GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
geometry_set = geometry_set_realize_instances(geometry_set);
- params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_attribute_name, true));
- params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_attribute_name, false));
+ if (params.lazy_output_is_required("Geometry 1")) {
+ params.set_output("Geometry 1",
+ separate_geometry_set(geometry_set, mask_attribute_name, true));
+ }
+ if (params.lazy_output_is_required("Geometry 2")) {
+ params.set_output("Geometry 2",
+ separate_geometry_set(geometry_set, mask_attribute_name, false));
+ }
}
} // namespace blender::nodes
@@ -155,5 +167,6 @@ void register_node_type_geo_point_separate()
geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec;
+ ntype.geometry_node_execute_supports_lazyness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 8c7387f7d9b..44203228899 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -42,24 +42,19 @@ namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
- OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
- "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
- if (!attribute) {
- return;
- }
+ GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] + data[i];
+ for (const int i : IndexRange(attribute.size())) {
+ position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
- position_attribute.apply_span_and_save();
+ position_attribute.save();
}
static void geo_node_point_translate_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index a9eb136597e..65306b1c452 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -55,6 +55,35 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
+ sizeof(NodeGeometryPointsToVolume), __func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+
+ bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
+ bNodeSocketValueString *radius_attribute_socket_value =
+ (bNodeSocketValueString *)radius_attribute_socket->default_value;
+ STRNCPY(radius_attribute_socket_value->value, "radius");
+}
+
+static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(
+ voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+
+ update_attribute_input_socket_availabilities(
+ *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+}
+
#ifdef WITH_OPENVDB
namespace {
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
@@ -147,13 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- FloatReadAttribute radii = params.get_input_attribute<float>(
+ GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- r_positions.extend(positions.get_span());
- r_radii.extend(radii.get_span());
+ for (const int i : IndexRange(positions.size())) {
+ r_positions.append(positions[i]);
+ r_radii.append(radii[i]);
+ }
}
static void convert_to_grid_index_space(const float voxel_size,
@@ -184,6 +215,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
+ if (geometry_set_in.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
+ }
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);
@@ -225,35 +260,6 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
- sizeof(NodeGeometryPointsToVolume), __func__);
- data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
- data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node->storage = data;
-
- bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
- bNodeSocketValueString *radius_attribute_socket_value =
- (bNodeSocketValueString *)radius_attribute_socket->default_value;
- STRNCPY(radius_attribute_socket_value->value, "radius");
-}
-
-static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
- bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
- bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
- data->resolution_mode ==
- GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
- nodeSetSocketAvailability(
- voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
-
- update_attribute_input_socket_availabilities(
- *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
-}
-
} // namespace blender::nodes
void register_node_type_geo_points_to_volume()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
new file mode 100644
index 00000000000..bb0a20f4561
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -0,0 +1,177 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_switch_in[] = {
+ {SOCK_BOOLEAN, N_("Switch")},
+
+ {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_BOOLEAN, N_("A")},
+ {SOCK_BOOLEAN, N_("B")},
+ {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_STRING, N_("A")},
+ {SOCK_STRING, N_("B")},
+ {SOCK_GEOMETRY, N_("A")},
+ {SOCK_GEOMETRY, N_("B")},
+ {SOCK_OBJECT, N_("A")},
+ {SOCK_OBJECT, N_("B")},
+ {SOCK_COLLECTION, N_("A")},
+ {SOCK_COLLECTION, N_("B")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_switch_out[] = {
+ {SOCK_FLOAT, N_("Output")},
+ {SOCK_INT, N_("Output")},
+ {SOCK_BOOLEAN, N_("Output")},
+ {SOCK_VECTOR, N_("Output")},
+ {SOCK_RGBA, N_("Output")},
+ {SOCK_STRING, N_("Output")},
+ {SOCK_GEOMETRY, N_("Output")},
+ {SOCK_OBJECT, N_("Output")},
+ {SOCK_COLLECTION, N_("Output")},
+ {-1, ""},
+};
+
+static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ data->input_type = SOCK_FLOAT;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeSwitch *node_storage = (NodeSwitch *)node->storage;
+ int index = 0;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(
+ socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ index++;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ }
+}
+
+template<typename T>
+static void output_input(GeoNodeExecParams &params,
+ const bool input,
+ const StringRef input_suffix,
+ const StringRef output_identifier)
+{
+ const std::string name_a = "A" + input_suffix;
+ const std::string name_b = "B" + input_suffix;
+ if (input) {
+ params.set_input_unused(name_a);
+ if (params.lazy_require_input(name_b)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_b));
+ }
+ else {
+ params.set_input_unused(name_b);
+ if (params.lazy_require_input(name_a)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_a));
+ }
+}
+
+static void geo_node_switch_exec(GeoNodeExecParams params)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+ const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
+ const bool input = params.get_input<bool>("Switch");
+ switch ((eNodeSocketDatatype)storage.input_type) {
+ case SOCK_FLOAT: {
+ output_input<float>(params, input, "", "Output");
+ break;
+ }
+ case SOCK_INT: {
+ output_input<int>(params, input, "_001", "Output_001");
+ break;
+ }
+ case SOCK_BOOLEAN: {
+ output_input<bool>(params, input, "_002", "Output_002");
+ break;
+ }
+ case SOCK_VECTOR: {
+ output_input<float3>(params, input, "_003", "Output_003");
+ break;
+ }
+ case SOCK_RGBA: {
+ output_input<Color4f>(params, input, "_004", "Output_004");
+ break;
+ }
+ case SOCK_STRING: {
+ output_input<std::string>(params, input, "_005", "Output_005");
+ break;
+ }
+ case SOCK_GEOMETRY: {
+ output_input<GeometrySet>(params, input, "_006", "Output_006");
+ break;
+ }
+ case SOCK_OBJECT: {
+ output_input<Object *>(params, input, "_007", "Output_007");
+ break;
+ }
+ case SOCK_COLLECTION: {
+ output_input<Collection *>(params, input, "_008", "Output_008");
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_switch()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
+ node_type_init(&ntype, geo_node_switch_init);
+ node_type_update(&ntype, blender::nodes::geo_node_switch_update);
+ node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec;
+ ntype.geometry_node_execute_supports_lazyness = true;
+ ntype.draw_buttons = geo_node_switch_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index d54982d16c2..9714a4f8a80 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -24,6 +24,7 @@
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -100,7 +101,7 @@ static void transform_instances(InstancesComponent &instances,
const float3 rotation,
const float3 scale)
{
- MutableSpan<float4x4> transforms = instances.transforms();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
@@ -152,6 +153,21 @@ static void transform_volume(Volume *volume,
#endif
}
+static void transform_curve(CurveEval &curve,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+
+ if (use_translate(rotation, scale)) {
+ curve.translate(translation);
+ }
+ else {
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ curve.transform(matrix);
+ }
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -163,21 +179,22 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
-
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
-
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
-
if (geometry_set.has_volume()) {
Volume *volume = geometry_set.get_volume_for_write();
transform_volume(volume, translation, rotation, scale, params);
}
+ if (geometry_set.has_curve()) {
+ CurveEval *curve = geometry_set.get_curve_for_write();
+ transform_curve(*curve, translation, rotation, scale);
+ }
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a4fb99a988e..73a702c753a 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -22,6 +22,7 @@
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
+#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
@@ -29,22 +30,22 @@ namespace blender::nodes {
void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
{
- bNodeTree *btree_cow = node_->btree();
+ bNodeTree *btree_cow = provider_->dnode->btree();
BLI_assert(btree_cow != nullptr);
if (btree_cow == nullptr) {
return;
}
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
+ const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier);
BKE_nodetree_error_message_add(
- *btree_original, context, *node_->bnode(), type, std::move(message));
+ *btree_original, context, *provider_->dnode->bnode(), type, std::move(message));
}
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available() && socket->name() == name) {
return socket->bsocket();
}
@@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
return nullptr;
}
-ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const
+GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const
{
const bNodeSocket *found_socket = this->find_available_socket(name);
BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
+ const int64_t domain_size = component.attribute_domain_size(domain);
+
+ if (default_value == nullptr) {
+ default_value = cpp_type->default_value();
+ }
+
if (found_socket == nullptr) {
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
/* Try getting the attribute without the default value. */
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
+ GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type);
if (attribute) {
return attribute;
}
@@ -80,25 +88,29 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
this->error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + name + "\"");
}
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
+ const DataTypeConversions &conversions = get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_VECTOR) {
const float3 value = this->get_input<float3>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT3, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_RGBA) {
const Color4f value = this->get_input<Color4f>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_COLOR, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
BLI_assert(false);
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
@@ -114,11 +126,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (!attribute) {
- return default_type;
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ return info->data_type;
}
- return attribute->custom_data_type();
+ return default_type;
}
if (found_socket->type == SOCK_FLOAT) {
return CD_PROP_FLOAT;
@@ -157,9 +169,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (attribute) {
- input_domains.append(attribute->domain());
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ input_domains.append(info->domain);
}
}
}
@@ -171,11 +183,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
-void GeoNodeExecParams::check_extract_input(StringRef identifier,
- const CPPType *requested_type) const
+void GeoNodeExecParams::check_input_access(StringRef identifier,
+ const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -185,39 +197,39 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (!input_values_.contains(identifier)) {
+ else if (!provider_->can_get_input(identifier)) {
std::cout << "The identifier '" << identifier
<< "' is valid, but there is no value for it anymore.\n";
std::cout << "Most likely it has been extracted before.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
-void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
+void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -227,29 +239,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (output_values_.contains(identifier)) {
+ else if (!provider_->can_set_output(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index b583523da98..ce2848b52a0 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -35,7 +35,6 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_collection_types.h"
@@ -294,6 +293,21 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN(
+ sizeof(bNodeSocketValueTexture), "node socket value texture");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN(
+ sizeof(bNodeSocketValueMaterial), "node socket value material");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
break;
}
}
@@ -375,6 +389,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value;
+ bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -633,63 +654,17 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
-class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
- private:
- Object *object_;
-
- public:
- ObjectSocketMultiFunction(Object *object) : object_(object)
- {
- static blender::fn::MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static blender::fn::MFSignature create_signature()
- {
- blender::fn::MFSignatureBuilder signature{"Object Socket"};
- signature.depends_on_context();
- signature.single_output<blender::bke::PersistentObjectHandle>("Object");
- return signature.build();
- }
-
- void call(blender::IndexMask mask,
- blender::fn::MFParams params,
- blender::fn::MFContext context) const override
- {
- blender::MutableSpan output =
- params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object");
-
- /* Try to get a handle map, so that the object can be converted to a handle. */
- const blender::bke::PersistentDataHandleMap *handle_map =
- context.get_global_context<blender::bke::PersistentDataHandleMap>(
- "PersistentDataHandleMap");
-
- if (handle_map == nullptr) {
- /* Return empty handles when there is no handle map. */
- output.fill_indices(mask, blender::bke::PersistentObjectHandle());
- return;
- }
-
- blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_);
- for (int64_t i : mask) {
- output[i] = handle;
- }
- }
-};
-
-MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
-MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle);
+MAKE_CPP_TYPE(Object, Object *)
+MAKE_CPP_TYPE(Collection, Collection *)
+MAKE_CPP_TYPE(Texture, Tex *)
+MAKE_CPP_TYPE(Material, Material *)
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
- builder.construct_generator_fn<ObjectSocketMultiFunction>(object);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value;
};
return socktype;
}
@@ -707,9 +682,29 @@ static bNodeSocketType *make_socket_type_geometry()
static bNodeSocketType *make_socket_type_collection()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>();
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_texture()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_material()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
};
return socktype;
}
@@ -755,5 +750,9 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_collection());
+ nodeRegisterSocketType(make_socket_type_texture());
+
+ nodeRegisterSocketType(make_socket_type_material());
+
nodeRegisterSocketType(make_socket_type_virtual());
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index e42572b9cb7..5c0bc0b5ebc 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -240,7 +240,10 @@ void InputSocketRef::foreach_logical_origin(FunctionRef<void(const OutputSocketR
}
const OutputSocketRef &origin = link->from();
const NodeRef &origin_node = origin.node();
- if (origin_node.is_reroute_node()) {
+ if (!origin.is_available()) {
+ /* Non available sockets are ignored. */
+ }
+ else if (origin_node.is_reroute_node()) {
const InputSocketRef &reroute_input = origin_node.input(0);
const OutputSocketRef &reroute_output = origin_node.output(0);
skipped_fn.call_safe(reroute_input);
@@ -281,7 +284,10 @@ void OutputSocketRef::foreach_logical_target(
}
const InputSocketRef &target = link->to();
const NodeRef &target_node = target.node();
- if (target_node.is_reroute_node()) {
+ if (!target.is_available()) {
+ /* Non available sockets are ignored. */
+ }
+ else if (target_node.is_reroute_node()) {
const OutputSocketRef &reroute_output = target_node.output(0);
skipped_fn.call_safe(target);
skipped_fn.call_safe(reroute_output);
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index f87e63d195d..1aec280fd2b 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -459,6 +459,22 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_TEXTURE: {
+ switch (from) {
+ case SOCK_TEXTURE:
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ case SOCK_MATERIAL: {
+ switch (from) {
+ case SOCK_MATERIAL:
+ return 1;
+ default:
+ return -1;
+ }
+ }
default:
return -1;
}
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
index 1c1b7c7feb5..63f7b8a9ee8 100644
--- a/source/blender/nodes/intern/type_conversions.cc
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -24,6 +24,9 @@
namespace blender::nodes {
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
using fn::MFDataType;
template<typename From, typename To, To (*ConversionF)(const From &)>
@@ -227,6 +230,11 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
const void *from_value,
void *to_value) const
{
+ if (from_type == to_type) {
+ from_type.copy_to_uninitialized(from_value, to_value);
+ return;
+ }
+
const ConversionFunctions *functions = this->get_conversion_functions(
MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
BLI_assert(functions != nullptr);
@@ -234,4 +242,108 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
functions->convert_single_to_uninitialized(from_value, to_value);
}
+class GVArray_For_ConvertedGVArray : public GVArray {
+ private:
+ GVArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+
+ public:
+ GVArray_For_ConvertedGVArray(GVArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+};
+
+class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray {
+ private:
+ GVMutableArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+ ConversionFunctions new_to_old_conversions_;
+
+ public:
+ GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVMutableArray(to_type, varray->size()),
+ varray_(std::move(varray)),
+ from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
+ varray_->set_by_relocate(index, buffer);
+ }
+};
+
+fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this);
+}
+
+fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>(
+ std::move(varray), to_type, *this);
+}
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 83f476884e6..5ec982c4e7f 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
return true;
}
+static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
+}
+
bNodeTreeType *ntreeType_Shader;
void register_node_tree_type_sh(void)
@@ -205,6 +211,7 @@ void register_node_tree_type_sh(void)
tt->poll = shader_tree_poll;
tt->get_from_context = shader_get_from_context;
tt->validate_link = shader_validate_link;
+ tt->valid_socket_type = shader_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_ShaderNodeTree;
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 04c32574a65..abc2c7008c7 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -332,3 +332,17 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat,
}
}
}
+
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
+{
+ const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_rgb();
+ data->r[0] = xyz_to_rgb[0];
+ data->r[1] = xyz_to_rgb[3];
+ data->r[2] = xyz_to_rgb[6];
+ data->g[0] = xyz_to_rgb[1];
+ data->g[1] = xyz_to_rgb[4];
+ data->g[2] = xyz_to_rgb[7];
+ data->b[0] = xyz_to_rgb[2];
+ data->b[1] = xyz_to_rgb[5];
+ data->b[2] = xyz_to_rgb[8];
+}
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 857a9914354..dc44f0fa98f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -95,6 +95,11 @@ typedef struct ShaderCallData {
int dummy;
} ShaderCallData;
+typedef struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */
+{
+ float r[3], g[3], b[3];
+} XYZ_to_RGB;
+
void nodestack_get_vec(float *in, short type_in, bNodeStack *ns);
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns);
@@ -113,6 +118,7 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
index 9ef05d781bd..5dc11c4df00 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -60,11 +60,6 @@ typedef struct SkyModelPreetham {
float radiance[3];
} SkyModelPreetham;
-typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */
-{
- float r[3], g[3], b[3];
-} XYZ_to_RGB;
-
static float sky_perez_function(const float *lam, float theta, float gamma)
{
float ctheta = cosf(theta);
@@ -119,20 +114,6 @@ static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[
sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
}
-static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
-{
- const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb();
- data->r[0] = xyz_to_rgb[0];
- data->r[1] = xyz_to_rgb[3];
- data->r[2] = xyz_to_rgb[6];
- data->g[0] = xyz_to_rgb[1];
- data->g[1] = xyz_to_rgb[4];
- data->g[2] = xyz_to_rgb[7];
- data->b[0] = xyz_to_rgb[2];
- data->b[1] = xyz_to_rgb[5];
- data->b[2] = xyz_to_rgb[8];
-}
-
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
index 6b7e1399328..30f69557020 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
@@ -30,6 +30,33 @@ static bNodeSocketTemplate sh_node_wavelength_out[] = {
{-1, ""},
};
+static int node_shader_gpu_wavelength(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const int size = CM_TABLE + 1;
+ float *data = MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture");
+
+ wavelength_to_xyz_table(data, size);
+
+ float layer;
+ GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_wavelength",
+ in,
+ out,
+ ramp_texture,
+ GPU_constant(&layer),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+}
+
/* node type definition */
void register_node_type_sh_wavelength(void)
{
@@ -40,6 +67,7 @@ void register_node_type_sh_wavelength(void)
node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
+ node_type_gpu(&ntype, node_shader_gpu_wavelength);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 48027dc847b..2ae722e3cd8 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -152,6 +152,12 @@ static void update(bNodeTree *ntree)
}
}
+static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Texture;
void register_node_tree_type_tex(void)
@@ -171,6 +177,7 @@ void register_node_tree_type_tex(void)
tt->local_sync = local_sync;
tt->local_merge = local_merge;
tt->get_from_context = texture_get_from_context;
+ tt->valid_socket_type = texture_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_TextureNodeTree;
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index ebe6ed79578..f1a8d450ea5 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1087,7 +1087,7 @@ PyDoc_STRVAR(
"3.0.\n");
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw)
{
- static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL};
+ static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL};
PyObject *py_object;
PyObject *py_depsgraph;
Object *ob, *ob_eval;
@@ -1095,7 +1095,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
struct Scene *scene_eval;
Mesh *me_eval;
BMesh *bm;
- bool use_deform = true;
bool use_cage = false;
bool use_fnorm = true;
const CustomData_MeshMasks data_masks = CD_MASK_BMESH;
@@ -1104,13 +1103,11 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "OO|O&O&O&:from_object",
+ "OO|O&O&:from_object",
(char **)kwlist,
&py_object,
&py_depsgraph,
PyC_ParseBool,
- &use_deform,
- PyC_ParseBool,
&use_cage,
PyC_ParseBool,
&use_fnorm) ||
@@ -1125,13 +1122,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- if (use_deform == false) {
- PyErr_WarnEx(PyExc_FutureWarning,
- "from_object(...): the deform parameter is deprecated, assumed to be True, and "
- "will be removed in version 3.0",
- 1);
- }
-
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
scene_eval = DEG_get_evaluated_scene(depsgraph);
ob_eval = DEG_get_evaluated_object(depsgraph, ob);
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index fc7054f675a..9b6ca7fcec5 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
extern PyObject *pyrna_id_CreatePyObject(ID *id);
extern bool pyrna_id_CheckPyObject(PyObject *obj);
+/* Currently there is no need to expose this publicly. */
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group);
+
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
+static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
+
/* -------------------------------------------------------------------- */
/** \name Python from ID-Property (Internal Conversions)
*
@@ -756,13 +768,7 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject
static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
{
- BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- Py_INCREF(self);
- iter->mode = IDPROP_ITER_KEYS;
- iter->cur = self->prop->data.group.first;
- PyObject_GC_Track(iter);
- return (PyObject *)iter;
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
/* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
@@ -875,6 +881,370 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name ID-Property Group Iterator Type
+ * \{ */
+
+static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
+{
+ if (self->len_init == self->group->prop->len) {
+ return true;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
+ return false;
+}
+
+static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return PyUnicode_FromString(cur->name);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEMS(ret,
+ PyUnicode_FromString(cur->name),
+ BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
+ return ret;
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group Iterator. */
+static void IDGroup_Iter_init_type(void)
+{
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupIterKeys";
+ v_ty->tp_name = "IDPropertyGroupIterValues";
+ i_ty->tp_name = "IDPropertyGroupIterItems";
+
+ k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
+ v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
+ i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
+
+ /* Shared members. */
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
+ SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
+
+#undef SHARED_MEMBER_SET
+}
+
+static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group,
+ const bool reversed,
+ PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
+ iter->reversed = reversed;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first);
+ iter->len_init = group->prop->len;
+ }
+ else {
+ iter->cur = NULL;
+ iter->len_init = 0;
+ }
+ return (PyObject *)iter;
+}
+
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
+}
+
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type);
+}
+
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group View Types (Keys/Values/Items)
+ *
+ * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type.
+ * The is returned by `property.keys()` and is separate from the iterator that loops over keys.
+ *
+ * There are some less common features this type could support (matching Python's `dict_view`)
+ *
+ * TODO:
+ * - Efficient contains checks for values and items which currently convert to a list first.
+ * - Missing `dict_views.isdisjoint`.
+ * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`).
+ * \{ */
+
+static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+/* View Specific API's (Key/Value/Items). */
+
+static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
+}
+
+static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return self->group->prop->len;
+}
+
+static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return BPy_IDGroup_Contains(self->group, value);
+}
+
+static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */
+};
+
+/* Methods. */
+
+PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc,
+ "Return a reverse iterator over the ID Property keys values or items.");
+
+static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored))
+{
+ BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
+ result->reversed = !self->reversed;
+ return (PyObject *)result;
+}
+
+static PyMethodDef BPy_IDGroup_View_methods[] = {
+ {"__reversed__",
+ (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
+ METH_NOARGS,
+ BPy_IDGroup_View_reversed_doc},
+ {NULL, NULL},
+};
+
+PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group View. */
+static void IDGroup_View_init_type(void)
+{
+ PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupViewKeys";
+ v_ty->tp_name = "IDPropertyGroupViewValues";
+ i_ty->tp_name = "IDPropertyGroupViewItems";
+
+ k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
+ v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
+ i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
+
+ k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
+ v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
+ i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
+
+ /* Shared members. */
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
+ SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods);
+
+#undef SHARED_MEMBER_SET
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ID-Property Group Methods
* \{ */
@@ -923,22 +1293,6 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
return pyform;
}
-PyDoc_STRVAR(
- BPy_IDGroup_iter_items_doc,
- ".. method:: iteritems()\n"
- "\n"
- " Iterate through the items in the dict; behaves like dictionary method iteritems.\n");
-static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
-{
- BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- Py_INCREF(self);
- iter->mode = IDPROP_ITER_ITEMS;
- iter->cur = self->prop->data.group.first;
- PyObject_GC_Track(iter);
- return (PyObject *)iter;
-}
-
/* utility function */
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
{
@@ -1023,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
return seq;
}
+PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
PyDoc_STRVAR(BPy_IDGroup_keys_doc,
".. method:: keys()\n"
"\n"
" Return the keys associated with this group as a list of strings.\n");
static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
{
- return BPy_Wrap_GetKeys(self->prop);
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_values_doc,
@@ -1038,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc,
" Return the values associated with this group.\n");
static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
{
- return BPy_Wrap_GetValues(self->id, self->prop);
+ return BPy_IDGroup_ViewValues_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_items_doc,
".. method:: items()\n"
"\n"
- " Return the items associated with this group.\n");
+ " Iterate through the items in the dict; behaves like dictionary method items.\n");
static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
{
- return BPy_Wrap_GetItems(self->id, self->prop);
+ return BPy_IDGroup_ViewItems_CreatePyObject(self);
}
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
@@ -1148,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
static struct PyMethodDef BPy_IDGroup_methods[] = {
{"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
- {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
{"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
{"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
{"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
@@ -1678,120 +2055,59 @@ PyTypeObject BPy_IDArray_Type = {
/** \} */
/* -------------------------------------------------------------------- */
-/** \name ID-Property Group Iterator Type
+/** \name Initialize Types
* \{ */
-static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+void IDProp_Init_Types(void)
{
- return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
-}
+ IDGroup_Iter_init_type();
+ IDGroup_View_init_type();
-static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
-{
- PyObject_GC_UnTrack(self);
- Py_CLEAR(self->group);
- PyObject_GC_Del(self);
+ PyType_Ready(&BPy_IDGroup_Type);
+ PyType_Ready(&BPy_IDArray_Type);
+
+ PyType_Ready(&BPy_IDGroup_IterKeys_Type);
+ PyType_Ready(&BPy_IDGroup_IterValues_Type);
+ PyType_Ready(&BPy_IDGroup_IterItems_Type);
+
+ PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
+ PyType_Ready(&BPy_IDGroup_ViewValues_Type);
+ PyType_Ready(&BPy_IDGroup_ViewItems_Type);
}
-static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+/**
+ * \note `group` may be NULL, unlike most other uses of this argument.
+ * This is supported so RNA keys/values/items methods returns an iterator with the expected type:
+ * - Without having ID-properties.
+ * - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks.
+ * Python's own dictionary-views also works this way too.
+ */
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
{
- Py_VISIT(self->group);
- return 0;
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
+ iter->reversed = false;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ }
+ return iter;
}
-static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
{
- Py_CLEAR(self->group);
- return 0;
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
}
-static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
{
-
- if (self->cur) {
- PyObject *ret;
- IDProperty *cur;
-
- cur = self->cur;
- self->cur = self->cur->next;
-
- if (self->mode == IDPROP_ITER_ITEMS) {
- ret = PyTuple_New(2);
- PyTuple_SET_ITEMS(ret,
- PyUnicode_FromString(cur->name),
- BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
- return ret;
- }
-
- return PyUnicode_FromString(cur->name);
- }
-
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
}
-PyTypeObject BPy_IDGroup_Iter_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- /* For printing, in format "<module>.<name>" */
- "IDPropertyGroupIter", /* char *tp_name; */
- sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
- 0, /* tp_itemsize; For allocation */
-
- /* Methods to implement standard operations */
-
- (destructor)BPy_IDGroup_Iter_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- NULL, /* getattrfunc tp_getattr; */
- NULL, /* setattrfunc tp_setattr; */
- NULL, /* cmpfunc tp_compare; */
- (reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */
-
- /* Method suites for standard classes */
-
- NULL, /* PyNumberMethods *tp_as_number; */
- NULL, /* PySequenceMethods *tp_as_sequence; */
- NULL, /* PyMappingMethods *tp_as_mapping; */
-
- /* More standard operations (here for binary compatibility) */
-
- NULL, /* hashfunc tp_hash; */
- NULL, /* ternaryfunc tp_call; */
- NULL, /* reprfunc tp_str; */
- NULL, /* getattrofunc tp_getattro; */
- NULL, /* setattrofunc tp_setattro; */
-
- /* Functions to access object as input/output buffer */
- NULL, /* PyBufferProcs *tp_as_buffer; */
-
- /*** Flags to define presence of optional/expanded features ***/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* long tp_flags; */
-
- NULL, /* char *tp_doc; Documentation string */
- /*** Assigned meaning in release 2.0 ***/
- /* call function for all accessible objects */
- (traverseproc)BPy_IDGroup_Iter_traverse, /* traverseproc tp_traverse; */
-
- /* delete references to contained objects */
- (inquiry)BPy_IDGroup_Iter_clear, /* inquiry tp_clear; */
-
- /*** Assigned meaning in release 2.1 ***/
- /*** rich comparisons ***/
- NULL, /* richcmpfunc tp_richcompare; */
-
- /*** weak reference enabler ***/
- 0, /* long tp_weaklistoffset; */
-
- /*** Added in release 2.2 ***/
- /* Iterators */
- PyObject_SelfIter, /* getiterfunc tp_iter; */
- (iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
-};
-
-void IDProp_Init_Types(void)
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
{
- PyType_Ready(&BPy_IDGroup_Type);
- PyType_Ready(&BPy_IDGroup_Iter_Type);
- PyType_Ready(&BPy_IDArray_Type);
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
}
/** \} */
@@ -1822,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void)
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
- PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
+
PyModule_AddType(submodule, &BPy_IDArray_Type);
return submodule;
diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h
index 4cccea3a936..1e8e26a3b6d 100644
--- a/source/blender/python/generic/idprop_py_api.h
+++ b/source/blender/python/generic/idprop_py_api.h
@@ -25,16 +25,35 @@ struct ID;
struct IDProperty;
extern PyTypeObject BPy_IDArray_Type;
-extern PyTypeObject BPy_IDGroup_Iter_Type;
extern PyTypeObject BPy_IDGroup_Type;
+extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
+extern PyTypeObject BPy_IDGroup_ViewValues_Type;
+extern PyTypeObject BPy_IDGroup_ViewItems_Type;
+
+extern PyTypeObject BPy_IDGroup_IterKeys_Type;
+extern PyTypeObject BPy_IDGroup_IterValues_Type;
+extern PyTypeObject BPy_IDGroup_IterItems_Type;
+
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
-#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type))
-#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type)
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
+#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
+#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
+#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
+#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
+#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
+#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
+
+#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
+#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
+#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
+#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
+#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
+#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
+
typedef struct BPy_IDProperty {
PyObject_VAR_HEAD
struct ID *id; /* can be NULL */
@@ -52,12 +71,28 @@ typedef struct BPy_IDGroup_Iter {
PyObject_VAR_HEAD
BPy_IDProperty *group;
struct IDProperty *cur;
- int mode;
+ /** Use for detecting manipulation during iteration (which is not allowed). */
+ int len_init;
+ /** Iterate in the reverse direction. */
+ bool reversed;
} BPy_IDGroup_Iter;
+/** Use to implement `IDPropertyGroup.keys/values/items` */
+typedef struct BPy_IDGroup_View {
+ PyObject_VAR_HEAD
+ /** This will be NULL when accessing keys on data that has no ID properties. */
+ BPy_IDProperty *group;
+ bool reversed;
+} BPy_IDGroup_View;
+
PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop);
PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop);
PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop);
+
+PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop);
+
int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val);
PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop);
@@ -67,6 +102,3 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *grou
void IDProp_Init_Types(void);
PyObject *BPyInit_idprop(void);
-
-#define IDPROP_ITER_KEYS 0
-#define IDPROP_ITER_ITEMS 1
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index fe5c559fcc0..1424b35a004 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -37,10 +37,12 @@ set(SRC
gpu_py_api.c
gpu_py_batch.c
gpu_py_buffer.c
+ gpu_py_capabilities.c
gpu_py_element.c
gpu_py_framebuffer.c
gpu_py_matrix.c
gpu_py_offscreen.c
+ gpu_py_platform.c
gpu_py_select.c
gpu_py_shader.c
gpu_py_state.c
@@ -54,10 +56,12 @@ set(SRC
gpu_py_api.h
gpu_py_batch.h
gpu_py_buffer.h
+ gpu_py_capabilities.h
gpu_py_element.h
gpu_py_framebuffer.h
gpu_py_matrix.h
gpu_py_offscreen.h
+ gpu_py_platform.h
gpu_py_select.h
gpu_py_shader.h
gpu_py_state.h
diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c
index 0bc18e73d0c..5119b3612f8 100644
--- a/source/blender/python/gpu/gpu_py_api.c
+++ b/source/blender/python/gpu/gpu_py_api.c
@@ -30,7 +30,9 @@
#include "../generic/python_utildefines.h"
+#include "gpu_py_capabilities.h"
#include "gpu_py_matrix.h"
+#include "gpu_py_platform.h"
#include "gpu_py_select.h"
#include "gpu_py_state.h"
#include "gpu_py_types.h"
@@ -61,9 +63,15 @@ PyObject *BPyInit_gpu(void)
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index d0965e83e33..e36e3b42617 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -37,17 +37,90 @@
#include "gpu_py_buffer.h"
-// #define PYGPU_BUFFER_PROTOCOL
+//#define PYGPU_BUFFER_PROTOCOL
+#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
-static bool pygpu_buffer_dimensions_compare(int ndim,
- const Py_ssize_t *shape_a,
- const Py_ssize_t *shape_b)
+static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
+{
+ Py_ssize_t tot = shape[0];
+ for (int i = 1; i < shape_len; i++) {
+ tot *= shape[i];
+ }
+
+ return tot;
+}
+
+static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
+ const Py_ssize_t shape_a_len,
+ const Py_ssize_t *shape_b,
+ const Py_ssize_t shape_b_len)
+{
+ if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
+ pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) {
+ PyErr_Format(PyExc_BufferError, "array size does not match");
+ return false;
+ }
+
+ return true;
+}
+
+static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
+ Py_ssize_t r_shape[MAX_DIMENSIONS],
+ Py_ssize_t *r_shape_len)
{
- return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t));
+ Py_ssize_t shape_len = 0;
+ if (PyLong_Check(shape_obj)) {
+ shape_len = 1;
+ if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ else if (PySequence_Check(shape_obj)) {
+ shape_len = PySequence_Size(shape_obj);
+ if (shape_len > MAX_DIMENSIONS) {
+ PyErr_SetString(PyExc_AttributeError,
+ "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
+ return false;
+ }
+ if (shape_len < 1) {
+ PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
+ return false;
+ }
+
+ for (int i = 0; i < shape_len; i++) {
+ PyObject *ob = PySequence_GetItem(shape_obj, i);
+ if (!PyLong_Check(ob)) {
+ PyErr_Format(PyExc_TypeError,
+ "invalid dimension %i, expected an int, not a %.200s",
+ i,
+ Py_TYPE(ob)->tp_name);
+ Py_DECREF(ob);
+ return false;
+ }
+
+ r_shape[i] = PyLong_AsLong(ob);
+ Py_DECREF(ob);
+
+ if (r_shape[i] < 1) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "invalid second argument argument expected a sequence "
+ "or an int, not a %.200s",
+ Py_TYPE(shape_obj)->tp_name);
+ }
+
+ *r_shape_len = shape_len;
+ return true;
}
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
@@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
return list;
}
-static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
+static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg))
{
PyObject *list = PyList_New(self->shape_len);
int i;
@@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
return list;
}
+static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type))
+{
+ Py_ssize_t shape[MAX_DIMENSIONS];
+ Py_ssize_t shape_len = 0;
+
+ if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
+ return -1;
+ }
+
+ if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
+ return -1;
+ }
+
+ size_t size = shape_len * sizeof(*self->shape);
+ if (shape_len != self->shape_len) {
+ MEM_freeN(self->shape);
+ self->shape = MEM_mallocN(size, __func__);
+ }
+
+ self->shape_len = shape_len;
+ memcpy(self->shape, shape, size);
+ return 0;
+}
+
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
@@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
return err;
}
-#define MAX_DIMENSIONS 64
static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
PyObject *length_ob, *init = NULL;
BPyGPUBuffer *buffer = NULL;
Py_ssize_t shape[MAX_DIMENSIONS];
- Py_ssize_t i, shape_len = 0;
+ Py_ssize_t shape_len = 0;
if (kwds && PyDict_Size(kwds)) {
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
@@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (PyLong_Check(length_ob)) {
- shape_len = 1;
- if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- else if (PySequence_Check(length_ob)) {
- shape_len = PySequence_Size(length_ob);
- if (shape_len > MAX_DIMENSIONS) {
- PyErr_SetString(PyExc_AttributeError,
- "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
- return NULL;
- }
- if (shape_len < 1) {
- PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
- return NULL;
- }
-
- for (i = 0; i < shape_len; i++) {
- PyObject *ob = PySequence_GetItem(length_ob, i);
- if (!PyLong_Check(ob)) {
- PyErr_Format(PyExc_TypeError,
- "invalid dimension %i, expected an int, not a %.200s",
- i,
- Py_TYPE(ob)->tp_name);
- Py_DECREF(ob);
- return NULL;
- }
- shape[i] = PyLong_AsLong(ob);
- Py_DECREF(ob);
-
- if (shape[i] < 1) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "invalid second argument argument expected a sequence "
- "or an int, not a %.200s",
- Py_TYPE(length_ob)->tp_name);
+ if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
return NULL;
}
@@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (shape_len != pybuffer.ndim ||
- !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) {
- PyErr_Format(PyExc_TypeError, "array size does not match");
- }
- else {
+ if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
buffer = pygpu_buffer_make_from_data(
init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
}
@@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = {
};
static PyGetSetDef pygpu_buffer_getseters[] = {
- {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL},
+ {"dimensions",
+ (getter)pygpu_buffer_dimensions_get,
+ (setter)pygpu_buffer_dimensions_set,
+ NULL,
+ NULL},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format,
const int shape_len,
const Py_ssize_t *shape)
{
- size_t r_size = GPU_texture_dataformat_size(format);
-
- for (int i = 0; i < shape_len; i++) {
- r_size *= shape[i];
- }
-
- return r_size;
+ return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format);
}
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
new file mode 100644
index 00000000000..cedce485253
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -0,0 +1,148 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_capabilities.h"
+
+#include "gpu_py_capabilities.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_size());
+}
+
+static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_layers());
+}
+
+static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures());
+}
+
+static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_vert());
+}
+
+static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_geom());
+}
+
+static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_frag());
+}
+
+static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_vert());
+}
+
+static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_frag());
+}
+
+static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_indices());
+}
+
+static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_vertices());
+}
+
+static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_vertex_attribs());
+}
+
+static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_varying_floats());
+}
+
+static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
+{
+ int extensions_len = GPU_extensions_len();
+ PyObject *ret = PyTuple_New(extensions_len);
+ PyObject **ob_items = ((PyTupleObject *)ret)->ob_item;
+ for (int i = 0; i < extensions_len; i++) {
+ ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
+ }
+
+ return ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_capabilities__tp_methods[] = {
+ {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL},
+ {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL},
+ {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL},
+ {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL},
+ {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL},
+ {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL},
+ {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL},
+ {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL},
+ {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL},
+ {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL},
+ {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL},
+ {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL},
+ {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_capabilities__tp_doc, "This module provides access to the GPU capabilities.");
+static PyModuleDef pygpu_capabilities_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.capabilities",
+ .m_doc = pygpu_capabilities__tp_doc,
+ .m_methods = pygpu_capabilities__tp_methods,
+};
+
+PyObject *bpygpu_capabilities_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_capabilities_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_capabilities.h b/source/blender/python/gpu/gpu_py_capabilities.h
new file mode 100644
index 00000000000..ac138dda0c9
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_capabilities_init(void);
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index 77eb4a37624..0efc0713538 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -37,6 +37,8 @@
#include "gpu_py.h"
#include "gpu_py_texture.h"
+#include "gpu_py.h"
+#include "gpu_py_buffer.h"
#include "gpu_py_framebuffer.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -46,13 +48,7 @@
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
{
if (UNLIKELY(bpygpu_fb->fb == NULL)) {
- PyErr_SetString(PyExc_ReferenceError,
-#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
- "GPU framebuffer was freed, no further access is valid"
-#else
- "GPU framebuffer: internal error"
-#endif
- );
+ PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
return -1;
}
return 0;
@@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
{
- if (!fb) {
- return;
- }
-
if (GPU_is_init()) {
GPU_framebuffer_free(fb);
}
@@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
}
}
+static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
+{
+ if (self->fb) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_framebuffer_py_reference_set(self->fb, NULL);
+ if (!self->shared_reference)
+#endif
+ {
+ pygpu_framebuffer_free_if_possible(self->fb);
+ }
+
+ self->fb = NULL;
+ }
+}
+
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
@@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
- return BPyGPUFrameBuffer_CreatePyObject(fb_python);
+ return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
}
PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc,
@@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U
return ret;
}
+PyDoc_STRVAR(
+ pygpu_framebuffer_read_color_doc,
+ ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
+ "\n"
+ " Read a block of pixels from the frame buffer.\n"
+ "\n"
+ " :param x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type x, y, xsize, ysize: int\n"
+ " :param channels: Number of components to read.\n"
+ " :type channels: int\n"
+ " :param slot: The framebuffer slot to read data from.\n"
+ " :type slot: int\n"
+ " :param format: The format that describes the content of a single channel.\n"
+ " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
+ " :type type: str\n"
+ " :arg data: Optional Buffer object to fill with the pixels values.\n"
+ " :type data: :class:`gpu.types.Buffer`\n"
+ " :return: The Buffer with the read pixels.\n"
+ " :rtype: :class:`gpu.types.Buffer`\n");
+static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
+ int x, y, w, h, channels;
+ uint slot;
+ struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8};
+ BPyGPUBuffer *py_buffer = NULL;
+
+ static const char *_keywords[] = {
+ "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
+ static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &x,
+ &y,
+ &w,
+ &h,
+ &channels,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_dataformat,
+ &BPyGPU_BufferType,
+ &py_buffer)) {
+ return NULL;
+ }
+
+ if (!IN_RANGE_INCL(channels, 1, 4)) {
+ PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
+ return NULL;
+ }
+
+ if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
+ PyErr_SetString(PyExc_ValueError, "slot overflow");
+ return NULL;
+ }
+
+ if (py_buffer) {
+ if (pygpu_dataformat.value_found != py_buffer->format) {
+ PyErr_SetString(PyExc_AttributeError,
+ "the format of the buffer is different from that specified");
+ return NULL;
+ }
+
+ size_t size_curr = bpygpu_Buffer_size(py_buffer);
+ size_t size_expected = w * h * channels *
+ GPU_texture_dataformat_size(pygpu_dataformat.value_found);
+ if (size_curr < size_expected) {
+ PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
+ return NULL;
+ }
+ }
+ else {
+ py_buffer = BPyGPU_Buffer_CreatePyObject(
+ pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL);
+ BLI_assert(bpygpu_Buffer_size(py_buffer) ==
+ w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found));
+ }
+
+ GPU_framebuffer_read_color(self->fb,
+ x,
+ y,
+ w,
+ h,
+ channels,
+ (int)slot,
+ pygpu_dataformat.value_found,
+ py_buffer->buf.as_void);
+
+ return (PyObject *)py_buffer;
+}
+
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc,
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
{
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
- pygpu_framebuffer_free_if_possible(self->fb);
- self->fb = NULL;
+ pygpu_framebuffer_free_safe(self);
Py_RETURN_NONE;
}
#endif
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
{
- pygpu_framebuffer_free_if_possible(self->fb);
+ pygpu_framebuffer_free_safe(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -494,6 +594,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
(PyCFunction)pygpu_framebuffer_viewport_get,
METH_NOARGS,
pygpu_framebuffer_viewport_get_doc},
+ {"read_color",
+ (PyCFunction)pygpu_framebuffer_read_color,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_framebuffer_read_color_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
@@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = {
/** \name Public API
* \{ */
-PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb)
+PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
{
BPyGPUFrameBuffer *self;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (shared_reference) {
+ void **ref = GPU_framebuffer_py_reference_get(fb);
+ if (ref) {
+ /* Retrieve BPyGPUFrameBuffer reference. */
+ self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb));
+ BLI_assert(self->fb == fb);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ }
+#else
+ UNUSED_VARS(shared_reference);
+#endif
+
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ self->shared_reference = shared_reference;
+
+ BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL);
+ GPU_framebuffer_py_reference_set(fb, (void **)&self->fb);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h
index 7113e7c35aa..6727328518c 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.h
+++ b/source/blender/python/gpu/gpu_py_framebuffer.h
@@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type;
typedef struct BPyGPUFrameBuffer {
PyObject_HEAD struct GPUFrameBuffer *fb;
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ bool shared_reference;
+#endif
} BPyGPUFrameBuffer;
-PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1);
+PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 9e9a0b9066a..28bd24a6877 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -53,6 +53,8 @@
#include "../generic/py_capi_utils.h"
#include "gpu_py.h"
+#include "gpu_py_texture.h"
+
#include "gpu_py_offscreen.h" /* own include */
/* Define the free method to avoid breakage. */
@@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
+PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
+ "The color texture attached.\n"
+ "\n"
+ ":type: :class:`gpu.types.GPUTexture`");
+static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
+ return BPyGPUTexture_CreatePyObject(texture, true);
+}
+
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
@@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
(setter)NULL,
pygpu_offscreen_color_texture_doc,
NULL},
+ {"texture_color",
+ (getter)pygpu_offscreen_texture_color_get,
+ (setter)NULL,
+ pygpu_offscreen_texture_color_doc,
+ NULL},
{"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
{"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
new file mode 100644
index 00000000000..e49ad18dfd8
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_platform.h"
+
+#include "gpu_py_platform.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_vendor());
+}
+
+static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_renderer());
+}
+
+static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_version());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_platform__tp_methods[] = {
+ {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL},
+ {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL},
+ {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_platform__tp_doc, "This module provides access to GPU Platform definitions.");
+static PyModuleDef pygpu_platform_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.platform",
+ .m_doc = pygpu_platform__tp_doc,
+ .m_methods = pygpu_platform__tp_methods,
+};
+
+PyObject *bpygpu_platform_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_platform_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_platform.h b/source/blender/python/gpu/gpu_py_platform.h
new file mode 100644
index 00000000000..19e3e41fb49
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_platform_init(void);
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 6b0fade8d1c..173c5afba56 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -25,11 +25,13 @@
#include <Python.h>
+#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
+#include "gpu_py_framebuffer.h"
#include "gpu_py_state.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -334,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb
Py_RETURN_NONE;
}
+PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc,
+ ".. function:: framebuffer_active_get(enable)\n"
+ "\n"
+ " Return the active framefuffer in context.\n");
+static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self))
+{
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ return BPyGPUFrameBuffer_CreatePyObject(fb, true);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -396,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
(PyCFunction)pygpu_state_program_point_size_set,
METH_O,
pygpu_state_program_point_size_set_doc},
+ {"active_framebuffer_get",
+ (PyCFunction)pygpu_state_framebuffer_active_get,
+ METH_NOARGS,
+ pygpu_state_framebuffer_active_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 257c6d773eb..2181c09b537 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
return NULL;
}
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, false);
}
PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`");
@@ -417,6 +417,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self)
static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self)
{
if (self->tex) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_texture_py_reference_set(self->tex, NULL);
+#endif
GPU_texture_free(self->tex);
}
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -540,10 +543,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg)
BKE_imageuser_default(&iuser);
GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
- /* Increase the texture reference count. */
- GPU_texture_ref(tex);
-
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, true);
}
static struct PyMethodDef pygpu_texture__m_methods[] = {
@@ -600,13 +600,33 @@ PyObject *bpygpu_texture_init(void)
/** \name Public API
* \{ */
-PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex)
+PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
{
BPyGPUTexture *self;
+ if (shared_reference) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ void **ref = GPU_texture_py_reference_get(tex);
+ if (ref) {
+ /* Retrieve BPyGPUTexture reference. */
+ self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex));
+ BLI_assert(self->tex == tex);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+#endif
+
+ GPU_texture_ref(tex);
+ }
+
self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type);
self->tex = tex;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ BLI_assert(GPU_texture_py_reference_get(tex) == NULL);
+ GPU_texture_py_reference_set(tex, (void **)&self->tex);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h
index 5130273f971..3eaaa3411bd 100644
--- a/source/blender/python/gpu/gpu_py_texture.h
+++ b/source/blender/python/gpu/gpu_py_texture.h
@@ -33,4 +33,5 @@ typedef struct BPyGPUTexture {
int bpygpu_ParseTexture(PyObject *o, void *p);
PyObject *bpygpu_texture_init(void);
-PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1);
+PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 9ac8d4d9f47..2be2105d327 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
bpy_rna_driver.c
bpy_rna_gizmo.c
bpy_rna_id_collection.c
+ bpy_rna_operator.c
bpy_rna_types_capi.c
bpy_rna_ui.c
bpy_traceback.c
@@ -118,6 +119,7 @@ set(SRC
bpy_rna_driver.h
bpy_rna_gizmo.h
bpy_rna_id_collection.h
+ bpy_rna_operator.h
bpy_rna_types_capi.h
bpy_rna_ui.h
bpy_traceback.h
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8ecee9b3f2e..a0b543097e6 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -74,6 +74,7 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{"version_update", "on ending the versioning code"},
{"load_factory_preferences_post", "on loading factory preferences (after)"},
{"load_factory_startup_post", "on loading factory startup (after)"},
+ {"xr_session_start_pre", "on starting an xr session (before)"},
/* sets the permanent tag */
#define APP_CB_OTHER_FIELDS 1
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 5f31e0bb74d..f95e655d0c6 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
}
+static void bpy_context_end(bContext *C)
+{
+ if (UNLIKELY(C == NULL)) {
+ return;
+ }
+ CTX_wm_operator_poll_msg_clear(C);
+}
+
/**
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
* In this case we don't want the Python context to override the values as it causes problems
@@ -392,7 +400,7 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
/* Needed for Python's initialization for portable Python installations.
* We could use #Py_SetPath, but this overrides Python's internal logic
- * for calculating it's own module search paths.
+ * for calculating its own module search paths.
*
* `sys.executable` is overwritten after initialization to the Python binary. */
{
@@ -524,6 +532,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
+ /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */
+ bpy_context_end(BPY_context_get());
+
/* Decrement user counts of all callback functions. */
BPY_rna_props_clear_all();
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 94ad6a8ef78..4a5e2552598 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
- const char *msg = CTX_wm_operator_poll_msg_get(C);
+ bool msg_free = false;
+ const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
- CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */
+ CTX_wm_operator_poll_msg_clear(C);
+ if (msg_free) {
+ MEM_freeN((void *)msg);
+ }
error_val = -1;
}
else {
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 1711637458a..fb1cb823964 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property keys.\n"
- " :rtype: list of strings\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
{
@@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetKeys(group);
+ return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_items_doc,
@@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property key, value pairs.\n"
- " :rtype: list of key, value tuples\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
{
@@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetItems(self->ptr.owner_id, group);
+ return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_values_doc,
@@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property values.\n"
- " :rtype: list\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
{
@@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetValues(self->ptr.owner_id, group);
+ return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_is_property_set_doc,
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
new file mode 100644
index 00000000000..6e0db3eca49
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -0,0 +1,150 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ *
+ * This file extends `bpy.types.Operator` with C/Python API methods and attributes.
+ */
+
+#include <Python.h>
+
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+
+#include "../generic/python_utildefines.h"
+
+#include "BPY_extern.h"
+#include "bpy_capi_utils.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Operator `poll_message_set` Method
+ * \{ */
+
+static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *py_args = user_data;
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg));
+ }
+
+ PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX);
+ PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first);
+ Py_DECREF(py_args_after_first);
+
+ char *msg = NULL;
+ bool error = false;
+
+ /* NULL for no string. */
+ if (py_msg == NULL) {
+ error = true;
+ }
+ else {
+ if (py_msg == Py_None) {
+ /* pass */
+ }
+ else if (PyUnicode_Check(py_msg)) {
+ msg = BLI_strdup(PyUnicode_AsUTF8(py_msg));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(function, ...): expected string or None, got %.200s",
+ Py_TYPE(py_msg)->tp_name);
+ error = true;
+ }
+ Py_DECREF(py_msg);
+ }
+
+ if (error) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ PyGILState_Release(gilstate);
+ return msg;
+}
+
+static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
+{
+ /* Handles the GIL. */
+ BPY_DECREF(user_data);
+}
+
+PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
+ ".. method:: poll_message_set(message, ...)\n"
+ "\n"
+ " Set the message to show in the tool-tip when poll fails.\n"
+ "\n"
+ " When message is callable, "
+ "additional user defined positional arguments are passed to the message function.\n"
+ "\n"
+ " :param message: The message or a function that returns the message.\n"
+ " :type message: string or a callable that returns a string or None.\n");
+
+static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
+{
+ const ssize_t args_len = PyTuple_GET_SIZE(args);
+ if (args_len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message, ...): requires a message argument");
+ return NULL;
+ }
+
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ if (args_len > 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message): does not support additional arguments");
+ return NULL;
+ }
+ }
+ else if (PyCallable_Check(py_func_or_msg)) {
+ /* pass */
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(message, ...): "
+ "expected at least 1 string or callable argument, got %.200s",
+ Py_TYPE(py_func_or_msg)->tp_name);
+ return NULL;
+ }
+
+ bContext *C = BPY_context_get();
+ struct bContextPollMsgDyn_Params params = {
+ .get_fn = pyop_poll_message_get_fn,
+ .free_fn = pyop_poll_message_free_fn,
+ .user_data = Py_INCREF_RET(args),
+ };
+
+ CTX_wm_operator_poll_msg_set_dynamic(C, &params);
+
+ Py_RETURN_NONE;
+}
+
+PyMethodDef BPY_rna_operator_poll_message_set_method_def = {
+ "poll_message_set",
+ (PyCFunction)BPY_rna_operator_poll_message_set,
+ METH_VARARGS | METH_STATIC,
+ BPY_rna_operator_poll_message_set_doc,
+};
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h
new file mode 100644
index 00000000000..8040d8a492a
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern PyMethodDef BPY_rna_operator_poll_message_set_method_def;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c
index 9b15e84663d..2f6e197d1e2 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.c
+++ b/source/blender/python/intern/bpy_rna_types_capi.c
@@ -41,6 +41,8 @@
#include "bpy_rna_types_capi.h"
#include "bpy_rna_ui.h"
+#include "bpy_rna_operator.h"
+
#include "../generic/py_capi_utils.h"
#include "RNA_access.h"
@@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Operator
+ * \{ */
+
+static struct PyMethodDef pyrna_operator_methods[] = {
+ {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */
+ {NULL, NULL, 0, NULL},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Window Manager Clipboard Property
*
* Avoid using the RNA API because this value may change between checking its length
@@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void)
/* Space */
pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL);
+ /* wmOperator */
+ ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def);
+ BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2);
+ pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL);
+
/* WindowManager */
pyrna_struct_type_extend_capi(
&RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset);
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 3791a6c2d29..16bf7120606 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -152,7 +152,7 @@ int mathutils_array_parse(
return -1;
}
- memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(array, ((const BaseMathObject *)value)->data, size * sizeof(float));
}
else
#endif
@@ -235,7 +235,7 @@ int mathutils_array_parse_alloc(float **array,
}
*array = PyMem_Malloc(size * sizeof(float));
- memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(*array, ((const BaseMathObject *)value)->data, size * sizeof(float));
return size;
}
@@ -471,7 +471,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
return -1;
}
- eulO_to_mat3(rmat, ((EulerObject *)value)->eul, ((EulerObject *)value)->order);
+ eulO_to_mat3(rmat, ((const EulerObject *)value)->eul, ((const EulerObject *)value)->order);
return 0;
}
if (QuaternionObject_Check(value)) {
@@ -480,7 +480,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
}
float tquat[4];
- normalize_qt_qt(tquat, ((QuaternionObject *)value)->quat);
+ normalize_qt_qt(tquat, ((const QuaternionObject *)value)->quat);
quat_to_mat3(rmat, tquat);
return 0;
}
@@ -530,8 +530,8 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
{
/* solid, fast routine across all platforms
* with constant time behavior */
- const int ai = *(int *)(&af);
- const int bi = *(int *)(&bf);
+ const int ai = *(const int *)(&af);
+ const int bi = *(const int *)(&bf);
const int test = SIGNMASK(ai ^ bi);
int diff, v1, v2;
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index f2a8af18073..b6a0183d04e 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -88,7 +88,7 @@ short euler_order_from_string(const char *str, const char *error_prefix)
# define MAKE_ID3(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8))
#endif
- switch (*((PY_INT32_T *)str)) {
+ switch (*((const PY_INT32_T *)str)) {
case MAKE_ID3('X', 'Y', 'Z'):
return EULER_ORDER_XYZ;
case MAKE_ID3('X', 'Z', 'Y'):
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 161d2f41592..c312012b24c 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -187,7 +187,7 @@ static int mathutils_matrix_col_get(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
bmo->data[row] = MATRIX_ITEM(self, row, col);
@@ -210,7 +210,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
MATRIX_ITEM(self, row, col) = bmo->data[row];
@@ -969,6 +969,104 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args)
return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls);
}
+PyDoc_STRVAR(
+ C_Matrix_LocRotScale_doc,
+ ".. classmethod:: LocRotScale(location, rotation, scale)\n"
+ "\n"
+ " Create a matrix combining translation, rotation and scale,\n"
+ " acting as the inverse of the decompose() method.\n"
+ "\n"
+ " Any of the inputs may be replaced with None if not needed.\n"
+ "\n"
+ " :arg location: The translation component.\n"
+ " :type location: :class:`Vector` or None\n"
+ " :arg rotation: The rotation component.\n"
+ " :type rotation: 3x3 :class:`Matrix`, :class:`Quaternion`, :class:`Euler` or None\n"
+ " :arg scale: The scale component.\n"
+ " :type scale: :class:`Vector` or None\n"
+ " :return: Combined transformation matrix. \n"
+ " :rtype: 4x4 :class:`Matrix`\n");
+static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args)
+{
+ PyObject *loc_obj, *rot_obj, *scale_obj;
+ float mat[4][4], loc[3];
+
+ if (!PyArg_ParseTuple(args, "OOO:Matrix.LocRotScale", &loc_obj, &rot_obj, &scale_obj)) {
+ return NULL;
+ }
+
+ /* Decode location. */
+ if (loc_obj == Py_None) {
+ zero_v3(loc);
+ }
+ else if (mathutils_array_parse(
+ loc, 3, 3, loc_obj, "Matrix.LocRotScale(), invalid location argument") == -1) {
+ return NULL;
+ }
+
+ /* Decode rotation. */
+ if (rot_obj == Py_None) {
+ unit_m4(mat);
+ }
+ else if (QuaternionObject_Check(rot_obj)) {
+ QuaternionObject *quat_obj = (QuaternionObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(quat_obj) == -1) {
+ return NULL;
+ }
+
+ quat_to_mat4(mat, quat_obj->quat);
+ }
+ else if (EulerObject_Check(rot_obj)) {
+ EulerObject *eul_obj = (EulerObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(eul_obj) == -1) {
+ return NULL;
+ }
+
+ eulO_to_mat4(mat, eul_obj->eul, eul_obj->order);
+ }
+ else if (MatrixObject_Check(rot_obj)) {
+ MatrixObject *mat_obj = (MatrixObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(mat_obj) == -1) {
+ return NULL;
+ }
+
+ if (mat_obj->num_col == 3 && mat_obj->num_row == 3) {
+ copy_m4_m3(mat, (const float(*)[3])mat_obj->matrix);
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "inappropriate rotation matrix size - expects 3x3 matrix");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "rotation argument must be Matrix, Quaternion, Euler or None");
+ return NULL;
+ }
+
+ /* Decode scale. */
+ if (scale_obj != Py_None) {
+ float scale[3];
+
+ if (mathutils_array_parse(
+ scale, 3, 3, scale_obj, "Matrix.LocRotScale(), invalid scale argument") == -1) {
+ return NULL;
+ }
+
+ rescale_m4(mat, scale);
+ }
+
+ copy_v3_v3(mat[3], loc);
+
+ return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls);
+}
+
void matrix_as_3x3(float mat[3][3], MatrixObject *self)
{
copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0));
@@ -1029,7 +1127,7 @@ static float matrix_determinant_internal(const MatrixObject *self)
MATRIX_ITEM(self, 2, 2));
}
- return determinant_m4((float(*)[4])self->matrix);
+ return determinant_m4((const float(*)[4])self->matrix);
}
static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim)
@@ -1037,15 +1135,15 @@ static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort
/* calculate the classical adjoint */
switch (dim) {
case 2: {
- adjoint_m2_m2((float(*)[2])mat_dst, (float(*)[2])mat_src);
+ adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src);
break;
}
case 3: {
- adjoint_m3_m3((float(*)[3])mat_dst, (float(*)[3])mat_src);
+ adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src);
break;
}
case 4: {
- adjoint_m4_m4((float(*)[4])mat_dst, (float(*)[4])mat_src);
+ adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src);
break;
}
default:
@@ -1115,7 +1213,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[2] = (float(*)[2])in_mat;
if (in_mat != self->matrix) {
- copy_m2_m2(mat, (float(*)[2])self->matrix);
+ copy_m2_m2(mat, (const float(*)[2])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1130,7 +1228,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[3] = (float(*)[3])in_mat;
if (in_mat != self->matrix) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1146,7 +1244,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[4] = (float(*)[4])in_mat;
if (in_mat != self->matrix) {
- copy_m4_m4(mat, (float(*)[4])self->matrix);
+ copy_m4_m4(mat, (const float(*)[4])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1194,7 +1292,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self)
mat3_to_quat(quat, (float(*)[3])self->matrix);
}
else {
- mat4_to_quat(quat, (float(*)[4])self->matrix);
+ mat4_to_quat(quat, (const float(*)[4])self->matrix);
}
return Quaternion_CreatePyObject(quat, NULL);
@@ -1243,10 +1341,10 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args)
/*must be 3-4 cols, 3-4 rows, square matrix */
if (self->num_row == 3 && self->num_col == 3) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
else if (self->num_row == 4 && self->num_col == 4) {
- copy_m3_m4(mat, (float(*)[4])self->matrix);
+ copy_m3_m4(mat, (const float(*)[4])self->matrix);
}
else {
PyErr_SetString(PyExc_ValueError,
@@ -1321,7 +1419,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self)
memcpy(mat[col], MATRIX_COL_PTR(self, col), self->num_row * sizeof(float));
}
- copy_m4_m4((float(*)[4])self->matrix, (float(*)[4])mat);
+ copy_m4_m4((float(*)[4])self->matrix, (const float(*)[4])mat);
self->num_col = 4;
self->num_row = 4;
@@ -1479,7 +1577,7 @@ static bool matrix_invert_args_check(const MatrixObject *self, PyObject *args, b
return true;
case 1:
if (check_type) {
- const MatrixObject *fallback = (MatrixObject *)PyTuple_GET_ITEM(args, 0);
+ const MatrixObject *fallback = (const MatrixObject *)PyTuple_GET_ITEM(args, 0);
if (!MatrixObject_Check(fallback)) {
PyErr_SetString(PyExc_TypeError,
"Matrix.invert: "
@@ -1797,7 +1895,7 @@ static PyObject *Matrix_decompose(MatrixObject *self)
return NULL;
}
- mat4_to_loc_rot_size(loc, rot, size, (float(*)[4])self->matrix);
+ mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix);
mat3_to_quat(quat, rot);
ret = PyTuple_New(3);
@@ -2059,7 +2157,7 @@ static PyObject *Matrix_identity(MatrixObject *self)
static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix)
{
- return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
+ return Matrix_CreatePyObject((const float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
}
PyDoc_STRVAR(Matrix_copy_doc,
@@ -2960,10 +3058,10 @@ static PyObject *Matrix_is_negative_get(MatrixObject *self, void *UNUSED(closure
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_negative_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_negative_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_negative_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_negative_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -2982,10 +3080,10 @@ static PyObject *Matrix_is_orthogonal_get(MatrixObject *self, void *UNUSED(closu
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthonormal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthonormal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3005,10 +3103,10 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthogonal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthogonal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3111,6 +3209,10 @@ static struct PyMethodDef Matrix_methods[] = {
(PyCFunction)C_Matrix_OrthoProjection,
METH_VARARGS | METH_CLASS,
C_Matrix_OrthoProjection_doc},
+ {"LocRotScale",
+ (PyCFunction)C_Matrix_LocRotScale,
+ METH_VARARGS | METH_CLASS,
+ C_Matrix_LocRotScale_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c
index 6abb66899d5..bb6a7720c44 100644
--- a/source/blender/python/mathutils/mathutils_interpolate.c
+++ b/source/blender/python/mathutils/mathutils_interpolate.c
@@ -54,24 +54,15 @@ static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *ar
PyObject *point, *veclist, *ret;
int i;
- if (!PyArg_ParseTuple(args, "OO!:poly_3d_calc", &veclist, &vector_Type, &point)) {
+ if (!PyArg_ParseTuple(args, "OO:poly_3d_calc", &veclist, &point)) {
return NULL;
}
- if (BaseMath_ReadCallback((VectorObject *)point) == -1) {
+ if (mathutils_array_parse(
+ fp, 2, 3 | MU_ARRAY_ZERO, point, "pt must be a 2-3 dimensional vector") == -1) {
return NULL;
}
- fp[0] = ((VectorObject *)point)->vec[0];
- fp[1] = ((VectorObject *)point)->vec[1];
- if (((VectorObject *)point)->size > 2) {
- fp[2] = ((VectorObject *)point)->vec[2];
- }
- else {
- /* if its a 2d vector then set the z to be zero */
- fp[2] = 0.0f;
- }
-
len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, __func__);
if (len == -1) {
return NULL;
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index fe8f9ec0334..d54dbc9ab05 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -52,7 +52,7 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject
BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
PyTuple_SET_ITEMS(py_retval,
- Vector_CreatePyObject((float *)nearest->co, 3, NULL),
+ Vector_CreatePyObject(nearest->co, 3, NULL),
PyLong_FromLong(nearest->index),
PyFloat_FromDouble(nearest->dist));
}
diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h
index f6ab7fd9d3c..7352ac7b12e 100644
--- a/source/blender/render/RE_engine.h
+++ b/source/blender/render/RE_engine.h
@@ -195,7 +195,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine,
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
struct Object *camera,
bool use_spherical_stereo,
- float *r_modelmat);
+ float r_modelmat[16]);
bool RE_engine_get_spherical_stereo(RenderEngine *engine, struct Object *camera);
bool RE_engine_test_break(RenderEngine *engine);
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index 66d38eb19c7..306d144f79d 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -546,7 +546,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool us
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
Object *camera,
bool use_spherical_stereo,
- float *r_modelmat)
+ float r_modelmat[16])
{
/* When using spherical stereo, get model matrix without multiview,
* leaving stereo to be handled by the engine. */
diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c
index 33af3bbaf29..242c8a199fb 100644
--- a/source/blender/render/intern/zbuf.c
+++ b/source/blender/render/intern/zbuf.c
@@ -175,7 +175,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
/* Functions */
/*-----------------------------------------------------------*/
-/* Scanconvert for strand triangles, calls func for each x, y coordinate
+/* Scan-convert for strand triangles, calls function for each x, y coordinate
* and gives UV barycentrics and z. */
void zspan_scanconvert(ZSpan *zspan,
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index 669c55e1f4c..c7c2dc275ee 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -27,39 +27,70 @@
extern "C" {
#endif
+#include "BLI_ghash.h"
+
struct Editing;
struct Sequence;
+struct GSet;
+struct GSetIterator;
-typedef struct SeqIterator {
- struct Sequence **array;
- int tot, cur;
+#define SEQ_ITERATOR_FOREACH(var, collection) \
+ for (SeqIterator iter = {{{NULL}}}; \
+ SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \
+ var = SEQ_iterator_yield(&iter))
- struct Sequence *seq;
- int valid;
-} SeqIterator;
-
-#define SEQ_ALL_BEGIN(ed, _seq) \
+#define SEQ_ALL_BEGIN(ed, var) \
{ \
- SeqIterator iter_macro; \
- for (SEQ_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \
- SEQ_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
+ if (ed != NULL) { \
+ SeqCollection *all_strips = SEQ_query_all_strips_recursive(&ed->seqbase); \
+ GSetIterator gsi; \
+ GSET_ITER (gsi, all_strips->set) { \
+ var = (Sequence *)(BLI_gsetIterator_getKey(&gsi));
#define SEQ_ALL_END \
} \
- SEQ_iterator_end(&iter_macro); \
+ SEQ_collection_free(all_strips); \
+ } \
} \
((void)0)
-void SEQ_iterator_begin(struct Editing *ed, SeqIterator *iter, const bool use_current_sequences);
-void SEQ_iterator_next(SeqIterator *iter);
-void SEQ_iterator_end(SeqIterator *iter);
-int SEQ_iterator_seqbase_recursive_apply(struct ListBase *seqbase,
- int (*apply_fn)(struct Sequence *seq, void *),
- void *arg);
-int SEQ_iterator_recursive_apply(struct Sequence *seq,
- int (*apply_fn)(struct Sequence *, void *),
- void *arg);
+typedef struct SeqCollection {
+ struct SeqCollection *next, *prev;
+ struct GSet *set;
+} SeqCollection;
+
+typedef struct SeqIterator {
+ GSetIterator gsi;
+ SeqCollection *collection;
+ bool iterator_initialized;
+} SeqIterator;
+
+bool SEQ_iterator_ensure(SeqCollection *collection,
+ SeqIterator *iterator,
+ struct Sequence **r_seq);
+struct Sequence *SEQ_iterator_yield(SeqIterator *iterator);
+
+SeqCollection *SEQ_collection_create(void);
+bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data);
+bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data);
+void SEQ_collection_free(SeqCollection *collection);
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src);
+void SEQ_collection_expand(struct ListBase *seqbase,
+ SeqCollection *collection,
+ void query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ void seq_query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase);
+void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 85513faf3e6..4f49b8cb58f 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -44,8 +44,6 @@ enum {
SEQ_SIDE_NO_CHANGE,
};
-#define SEQ_CACHE_COST_MAX 10.0f
-
/* seq_dupli' flags */
#define SEQ_DUPE_UNIQUE_NAME (1 << 0)
#define SEQ_DUPE_CONTEXT (1 << 1)
diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h
index 45f53a64688..9d529089ffc 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -35,7 +35,7 @@ struct Scene;
struct Sequence;
struct StripElem;
-void SEQ_sort(struct Scene *scene);
+void SEQ_sort(struct ListBase *seqbase);
void SEQ_sequence_base_unique_name_recursive(struct ListBase *seqbasep, struct Sequence *seq);
const char *SEQ_sequence_give_name(struct Sequence *seq);
struct ListBase *SEQ_get_seqbase_from_sequence(struct Sequence *seq, int *r_offset);
@@ -54,7 +54,13 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq,
const int preview_width,
const int preview_height,
const eSeqImageFitMethod fit_method);
-
+int SEQ_seqbase_recursive_apply(struct ListBase *seqbase,
+ int (*apply_fn)(struct Sequence *seq, void *),
+ void *arg);
+int SEQ_recursive_apply(struct Sequence *seq,
+ int (*apply_fn)(struct Sequence *, void *),
+ void *arg);
+void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene);
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 278320d873e..d41a2c19d55 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -3033,10 +3033,9 @@ static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, fl
i = seq_render_give_ibuf_seqbase(context, timeline_frame, seq->machine - 1, seqbasep);
}
- /* found nothing? so let's work the way up the metastrip stack, so
+ /* Found nothing? so let's work the way up the meta-strip stack, so
* that it is possible to group a bunch of adjustment strips into
- * a metastrip and have that work on everything below the metastrip
- */
+ * a meta-strip and have that work on everything below the meta-strip. */
if (!i) {
Sequence *meta;
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index f99667dea04..9bbc5362f18 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -31,138 +31,268 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BKE_scene.h"
#include "SEQ_iterator.h"
-/* ************************* iterator ************************** */
-/* *************** (replaces old WHILE_SEQ) ********************* */
-/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */
+/* -------------------------------------------------------------------- */
+/** \Iterator API
+ * \{ */
-/* sequence strip iterator:
- * - builds a full array, recursively into meta strips
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Ensure, that iterator is initialized. During initialization return pointer to collection element
+ * and step gset iterator. When this function is called after iterator has been initialized, it
+ * will do nothing and return true.
+ *
+ * \param collection: collection to iterate
+ * \param iterator: iterator to be initialized
+ * \param r_seq: pointer to Sequence pointer
+ *
+ * \return false when iterator can not be initialized, true otherwise
*/
-
-static void seq_count(ListBase *seqbase, int *tot)
+bool SEQ_iterator_ensure(SeqCollection *collection, SeqIterator *iterator, Sequence **r_seq)
{
- Sequence *seq;
-
- for (seq = seqbase->first; seq; seq = seq->next) {
- (*tot)++;
-
- if (seq->seqbase.first) {
- seq_count(&seq->seqbase, tot);
- }
+ if (iterator->iterator_initialized) {
+ return true;
}
-}
-static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth)
-{
- Sequence *seq;
+ if (BLI_gset_len(collection->set) == 0) {
+ return false;
+ }
- for (seq = seqbase->first; seq; seq = seq->next) {
- seq->depth = depth;
+ iterator->collection = collection;
+ BLI_gsetIterator_init(&iterator->gsi, iterator->collection->set);
+ iterator->iterator_initialized = true;
- if (seq->seqbase.first) {
- seq_build_array(&seq->seqbase, array, depth + 1);
- }
+ *r_seq = BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
- **array = seq;
- (*array)++;
- }
+ return true;
}
-static void seq_array(Editing *ed,
- const bool use_current_sequences,
- Sequence ***r_seqarray,
- int *r_seqarray_len)
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Yield collection element
+ *
+ * \param iterator: iterator to be initialized
+ *
+ * \return collection element or NULL when iteration has ended
+ */
+Sequence *SEQ_iterator_yield(SeqIterator *iterator)
{
- Sequence **array;
+ Sequence *seq = BLI_gsetIterator_done(&iterator->gsi) ? NULL :
+ BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
+ return seq;
+}
- *r_seqarray = NULL;
- *r_seqarray_len = 0;
+/**
+ * Free strip collection.
+ *
+ * \param collection: collection to be freed
+ */
+void SEQ_collection_free(SeqCollection *collection)
+{
+ BLI_gset_free(collection->set, NULL);
+ MEM_freeN(collection);
+}
- if (ed == NULL) {
- return;
- }
+/**
+ * Create new empty strip collection.
+ *
+ * \return empty strip collection.
+ */
+SeqCollection *SEQ_collection_create(void)
+{
+ SeqCollection *collection = MEM_callocN(sizeof(SeqCollection), "SeqCollection");
+ collection->set = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "SeqCollection GSet");
+ return collection;
+}
- if (use_current_sequences) {
- seq_count(ed->seqbasep, r_seqarray_len);
- }
- else {
- seq_count(&ed->seqbase, r_seqarray_len);
+/**
+ * Query strips from seqbase. seq_reference is used by query function as filter condition.
+ *
+ * \param seq_reference: reference strip for query function
+ * \param seqbase: ListBase in which strips are queried
+ * \param seq_query_func: query function callback
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_by_reference(Sequence *seq_reference,
+ ListBase *seqbase,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
+{
+ SeqCollection *collection = SEQ_collection_create();
+ seq_query_func(seq_reference, seqbase, collection);
+ return collection;
+}
+/**
+ * Add strip to collection.
+ *
+ * \param seq: strip to be added
+ * \param collection: collection to which strip will be added
+ * \return false if strip is already in set, otherwise true
+ */
+bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection)
+{
+ if (BLI_gset_lookup(collection->set, seq) != NULL) {
+ return false;
}
+ BLI_gset_insert(collection->set, seq);
+ return true;
+}
- if (*r_seqarray_len == 0) {
- return;
- }
+/**
+ * Remove strip from collection.
+ *
+ * \param seq: strip to be removed
+ * \param collection: collection from which strip will be removed
+ * \return true if strip exists in set and it was removed from set, otherwise false
+ */
+bool SEQ_collection_remove_strip(Sequence *seq, SeqCollection *collection)
+{
+ return BLI_gset_remove(collection->set, seq, NULL);
+}
- *r_seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*r_seqarray_len), "SeqArray");
- if (use_current_sequences) {
- seq_build_array(ed->seqbasep, &array, 0);
- }
- else {
- seq_build_array(&ed->seqbase, &array, 0);
+/**
+ * Move strips from collection_src to collection_dst. Source collection will be freed.
+ *
+ * \param collection_dst: destination collection
+ * \param collection_src: source collection
+ */
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection_src) {
+ SEQ_collection_append_strip(seq, collection_dst);
}
+ SEQ_collection_free(collection_src);
}
-void SEQ_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences)
+/**
+ * Expand collection by running SEQ_query() for each strip, which will be used as reference.
+ * Results of these queries will be merged into provided collection.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: SeqCollection to be expanded
+ * \param seq_query_func: query function callback
+ */
+void SEQ_collection_expand(ListBase *seqbase,
+ SeqCollection *collection,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
{
- memset(iter, 0, sizeof(*iter));
- seq_array(ed, use_current_sequences, &iter->array, &iter->tot);
+ /* Collect expanded results for each sequence in provided SeqIteratorCollection. */
+ ListBase expand_collections = {0};
- if (iter->tot) {
- iter->cur = 0;
- iter->seq = iter->array[iter->cur];
- iter->valid = 1;
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ SeqCollection *expand_collection = SEQ_query_by_reference(seq, seqbase, seq_query_func);
+ BLI_addtail(&expand_collections, expand_collection);
+ }
+
+ /* Merge all expanded results in provided SeqIteratorCollection. */
+ LISTBASE_FOREACH_MUTABLE (SeqCollection *, expand_collection, &expand_collections) {
+ BLI_remlink(&expand_collections, expand_collection);
+ SEQ_collection_merge(collection, expand_collection);
}
}
-void SEQ_iterator_next(SeqIterator *iter)
+/** \} */
+
+/**
+ * Query all strips in seqbase and nested meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase)
{
- if (++iter->cur < iter->tot) {
- iter->seq = iter->array[iter->cur];
- }
- else {
- iter->valid = 0;
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (seq->type == SEQ_TYPE_META) {
+ SEQ_collection_merge(collection, SEQ_query_all_strips_recursive(&seq->seqbase));
+ }
+ SEQ_collection_append_strip(seq, collection);
}
+ return collection;
}
-void SEQ_iterator_end(SeqIterator *iter)
+/**
+ * Query all strips in seqbase. This does not include strips nested in meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase)
{
- if (iter->array) {
- MEM_freeN(iter->array);
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ SEQ_collection_append_strip(seq, collection);
}
-
- iter->valid = 0;
+ return collection;
}
-int SEQ_iterator_seqbase_recursive_apply(ListBase *seqbase,
- int (*apply_fn)(Sequence *seq, void *),
- void *arg)
+/**
+ * Query all selected strips in seqbase.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_selected_strips(ListBase *seqbase)
{
- Sequence *iseq;
- for (iseq = seqbase->first; iseq; iseq = iseq->next) {
- if (SEQ_iterator_recursive_apply(iseq, apply_fn, arg) == -1) {
- return -1; /* bail out */
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->flag & SELECT) == 0) {
+ continue;
}
+ SEQ_collection_append_strip(seq, collection);
}
- return 1;
+ return collection;
}
-int SEQ_iterator_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+/**
+ * Query all effect strips that are directly or indirectly connected to seq_reference.
+ * This includes all effects of seq_reference, strips used by another inputs and their effects, so
+ * that whole chain is fully independent of other strips.
+ *
+ * \param seq_reference: reference strip
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: collection to be filled
+ */
+void SEQ_query_strip_effect_chain(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- int ret = apply_fn(seq, arg);
-
- if (ret == -1) {
- return -1; /* bail out */
+ if (!SEQ_collection_append_strip(seq_reference, collection)) {
+ return; /* Strip is already in set, so all effects connected to it are as well. */
}
- if (ret && seq->seqbase.first) {
- ret = SEQ_iterator_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ /* Find all strips that seq_reference is connected to. */
+ if (seq_reference->type & SEQ_TYPE_EFFECT) {
+ if (seq_reference->seq1) {
+ SEQ_query_strip_effect_chain(seq_reference->seq1, seqbase, collection);
+ }
+ if (seq_reference->seq2) {
+ SEQ_query_strip_effect_chain(seq_reference->seq2, seqbase, collection);
+ }
+ if (seq_reference->seq3) {
+ SEQ_query_strip_effect_chain(seq_reference->seq3, seqbase, collection);
+ }
}
- return ret;
+ /* Find all strips connected to seq_reference. */
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference ||
+ seq_test->seq3 == seq_reference) {
+ SEQ_query_strip_effect_chain(seq_test, seqbase, collection);
+ }
+ }
}
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index f892f1c1b41..f3cea273fdf 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -65,6 +65,7 @@
#include "RE_pipeline.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_render.h"
@@ -259,120 +260,132 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame)
return se;
}
-static int evaluate_seq_frame_gen(Sequence **seq_arr,
- ListBase *seqbase,
- int timeline_frame,
- int chanshown)
+static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input)
{
- /* Use arbitrary sized linked list, the size could be over MAXSEQ. */
- LinkNodePair effect_inputs = {NULL, NULL};
- int totseq = 0;
+ if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input ||
+ seq_effect->seq3 == possibly_input) {
+ return true;
+ }
+ return false;
+}
- memset(seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself.
+ * Order of applying these conditions is important. */
+static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead)
+{
+ /* Sound strips are not rendered. */
+ if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ return false;
+ }
+ /* Muted strips are not rendered. */
+ if ((seq->flag & SEQ_MUTE) != 0) {
+ return false;
+ }
- LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
- if ((seq->type & SEQ_TYPE_EFFECT) && !(seq->flag & SEQ_MUTE)) {
+ bool seq_have_effect_in_stack = false;
+ Sequence *seq_iter;
+ SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) {
+ /* Strips is below another strip with replace blending are not rendered. */
+ if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) {
+ return false;
+ }
- if (seq->seq1) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq1);
- }
+ if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) {
+ /* Strips in same channel or higher than its effect are rendered. */
+ if (seq->machine >= seq_iter->machine) {
+ return true;
+ }
+ /* Mark that this strip has effect in stack, that is above the strip. */
+ seq_have_effect_in_stack = true;
+ }
+ }
- if (seq->seq2) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq2);
- }
+ /* All effects are rendered (with respect to conditions above). */
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
+ return true;
+ }
- if (seq->seq3) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq3);
- }
- }
+ /* If strip has effects in stack, and all effects are above this strip, it it not rendered. */
+ if (seq_have_effect_in_stack) {
+ return false;
+ }
+
+ return true;
+}
- seq_arr[seq->machine] = seq;
- totseq++;
+static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame)
+{
+ SeqCollection *collection = SEQ_collection_create();
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
+ SEQ_collection_append_strip(seq, collection);
}
}
+ return collection;
+}
- /* Drop strips which are used for effect inputs, we don't want
- * them to blend into render stack in any other way than effect
- * string rendering. */
- for (LinkNode *seq_item = effect_inputs.list; seq_item; seq_item = seq_item->next) {
- Sequence *seq = seq_item->link;
- /* It's possible that effect strip would be placed to the same
- * 'machine' as its inputs. We don't want to clear such strips
- * from the stack. */
- if (seq_arr[seq->machine] && seq_arr[seq->machine]->type & SEQ_TYPE_EFFECT) {
- continue;
- }
- /* If we're shown a specified channel, then we want to see the strips
- * which belongs to this machine. */
- if (chanshown != 0 && chanshown <= seq->machine) {
+static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->machine <= channel) {
continue;
}
- seq_arr[seq->machine] = NULL;
+ SEQ_collection_remove_strip(seq, collection);
}
-
- return totseq;
}
-/**
- * Count number of strips in timeline at timeline_frame
- *
- * \param seqbase: ListBase in which strips are located
- * \param timeline_frame: frame on timeline from where gaps are searched for
- * \return number of strips
- */
-int SEQ_render_evaluate_frame(ListBase *seqbase, int timeline_frame)
+/* Remove strips we don't want to render from collection. */
+static void collection_filter_rendered_strips(SeqCollection *collection)
{
- Sequence *seq_arr[MAXSEQ + 1];
- return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (must_render_strip(seq, collection)) {
+ continue;
+ }
+ SEQ_collection_remove_strip(seq, collection);
+ }
}
-static bool video_seq_is_rendered(Sequence *seq)
+static int seq_channel_cmp_fn(const void *a, const void *b)
{
- return (seq && !(seq->flag & SEQ_MUTE) && seq->type != SEQ_TYPE_SOUND_RAM);
+ return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine;
}
-int seq_get_shown_sequences(ListBase *seqbasep,
- int timeline_frame,
- int chanshown,
- Sequence **seq_arr_out)
+int seq_get_shown_sequences(ListBase *seqbase,
+ const int timeline_frame,
+ const int chanshown,
+ Sequence **r_seq_arr)
{
- Sequence *seq_arr[MAXSEQ + 1];
- int b = chanshown;
- int cnt = 0;
+ SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame);
- if (b > MAXSEQ) {
- return 0;
- }
-
- if (evaluate_seq_frame_gen(seq_arr, seqbasep, timeline_frame, chanshown)) {
- if (b == 0) {
- b = MAXSEQ;
- }
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- break;
- }
- }
+ if (chanshown != 0) {
+ collection_filter_channel_up_to_incl(collection, chanshown);
}
+ collection_filter_rendered_strips(collection);
- chanshown = b;
+ const int strip_count = BLI_gset_len(collection->set);
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- if (seq_arr[b]->blend_mode == SEQ_BLEND_REPLACE) {
- break;
- }
- }
+ if (strip_count > MAXSEQ) {
+ BLI_assert(!"Too many strips, this shouldn't happen");
+ return 0;
}
- for (; b <= chanshown && b >= 0; b++) {
- if (video_seq_is_rendered(seq_arr[b])) {
- seq_arr_out[cnt++] = seq_arr[b];
- }
+ /* Copy collection elements into array. */
+ memset(r_seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+ Sequence *seq;
+ int index = 0;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ r_seq_arr[index] = seq;
+ index++;
}
+ SEQ_collection_free(collection);
+
+ /* Sort array by channel. */
+ qsort(r_seq_arr, strip_count, sizeof(Sequence *), seq_channel_cmp_fn);
- return cnt;
+ return strip_count;
}
/** \} */
@@ -1495,7 +1508,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
goto finally;
}
- if (seq->flag & SEQ_SCENE_NO_GPENCIL) {
+ if (seq->flag & SEQ_SCENE_NO_ANNOTATION) {
use_gpencil = false;
}
@@ -1961,7 +1974,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
/**
* \return The image buffer or NULL.
*
- * \note The returned #ImBuf is has it's reference increased, free after usage!
+ * \note The returned #ImBuf has its reference increased, free after usage!
*/
ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, int chanshown)
{
diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h
index 1147516b8ec..a0cdf24d84b 100644
--- a/source/blender/sequencer/intern/render.h
+++ b/source/blender/sequencer/intern/render.h
@@ -60,10 +60,10 @@ struct ImBuf *seq_render_effect_execute_threaded(struct SeqEffectHandle *sh,
struct ImBuf *ibuf2,
struct ImBuf *ibuf3);
void seq_imbuf_to_sequencer_space(struct Scene *scene, struct ImBuf *ibuf, bool make_float);
-int seq_get_shown_sequences(struct ListBase *seqbasep,
+int seq_get_shown_sequences(struct ListBase *seqbase,
int timeline_frame,
int chanshown,
- struct Sequence **seq_arr_out);
+ struct Sequence **r_seq_arr);
struct ImBuf *seq_render_strip(const struct SeqRenderData *context,
struct SeqRenderState *state,
struct Sequence *seq,
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index cc11796496c..55c14944a23 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -448,8 +448,8 @@ static Sequence *seq_dupli(const Scene *scene_src,
seqn->strip->stripdata = NULL;
BLI_listbase_clear(&seqn->seqbase);
- /* WATCH OUT!!! - This metastrip is not recursively duplicated here - do this after!!! */
- /* - seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);*/
+ /* WARNING: This meta-strip is not recursively duplicated here - do this after! */
+ // seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);
}
else if (seq->type == SEQ_TYPE_SCENE) {
seqn->strip->stripdata = NULL;
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index 1106f47c477..5ec2269b993 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -100,7 +100,7 @@ static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *se
{
SEQ_sequence_base_unique_name_recursive(seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 4a27fb3a087..4de6ec3583c 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -39,10 +39,12 @@
#include "BKE_sound.h"
#include "strip_time.h"
+#include "utils.h"
#include "SEQ_add.h"
#include "SEQ_edit.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@@ -149,7 +151,7 @@ void SEQ_edit_update_muting(Editing *ed)
static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
{
LISTBASE_FOREACH (Sequence *, user_seq, seqbase) {
- /* Look in metas for usage of seq. */
+ /* Look in meta-strips for usage of seq. */
if (user_seq->type == SEQ_TYPE_META) {
sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq);
}
@@ -251,22 +253,26 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene,
return false;
}
- /* Remove users of src_seq. Ideally these could be moved into meta as well, but this would be
- * best to do with generalized iterator as described in D10337. */
- sequencer_flag_users_for_removal(scene, seqbase, src_seq);
- SEQ_edit_remove_flagged_sequences(scene, seqbase);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(src_seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Move to meta. */
- BLI_remlink(seqbase, src_seq);
- BLI_addtail(&dst_seqm->seqbase, src_seq);
- SEQ_relations_invalidate_cache_preprocessed(scene, src_seq);
-
- /* Update meta. */
- SEQ_time_update_sequence(scene, dst_seqm);
- if (SEQ_transform_test_overlap(&dst_seqm->seqbase, src_seq)) {
- SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, src_seq, scene);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ /* Move to meta. */
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&dst_seqm->seqbase, seq);
+ SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+
+ /* Update meta. */
+ SEQ_time_update_sequence(scene, dst_seqm);
+ if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) {
+ SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene);
+ }
}
+ SEQ_collection_free(collection);
+
return true;
}
@@ -343,6 +349,29 @@ static void seq_split_set_left_offset(Sequence *seq, int timeline_frame)
SEQ_transform_set_left_handle_frame(seq, timeline_frame);
}
+static void seq_edit_split_handle_strip_offsets(Main *bmain,
+ Scene *scene,
+ Sequence *left_seq,
+ Sequence *right_seq,
+ const int timeline_frame,
+ const eSeqSplitMethod method)
+{
+ switch (method) {
+ case SEQ_SPLIT_SOFT:
+ seq_split_set_left_offset(right_seq, timeline_frame);
+ seq_split_set_right_offset(left_seq, timeline_frame);
+ break;
+ case SEQ_SPLIT_HARD:
+ seq_split_set_right_hold_offset(left_seq, timeline_frame);
+ seq_split_set_left_hold_offset(right_seq, timeline_frame);
+ SEQ_add_reload_new_file(bmain, scene, left_seq, false);
+ SEQ_add_reload_new_file(bmain, scene, right_seq, false);
+ break;
+ }
+ SEQ_time_update_sequence(scene, left_seq);
+ SEQ_time_update_sequence(scene, right_seq);
+}
+
/**
* Split Sequence at timeline_frame in two.
*
@@ -365,33 +394,44 @@ Sequence *SEQ_edit_strip_split(Main *bmain,
return NULL;
}
- if (method == SEQ_SPLIT_HARD) {
- /* Precaution, needed because the length saved on-disk may not match the length saved in the
- * blend file, or our code may have minor differences reading file length between versions.
- * This causes hard-split to fail, see: T47862. */
- SEQ_add_reload_new_file(bmain, scene, seq, true);
- SEQ_time_update_sequence(scene, seq);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
+
+ /* Move strips in collection from seqbase to new ListBase. */
+ ListBase left_strips = {NULL, NULL};
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&left_strips, seq);
}
- Sequence *left_seq = seq;
- Sequence *right_seq = SEQ_sequence_dupli_recursive(
- scene, scene, seqbase, seq, SEQ_DUPE_UNIQUE_NAME | SEQ_DUPE_ANIM);
+ /* Sort list, so that no strip can depend on next strip in list.
+ * This is important for SEQ_time_update_sequence functionality. */
+ SEQ_sort(&left_strips);
+
+ /* Duplicate ListBase. */
+ ListBase right_strips = {NULL, NULL};
+ SEQ_sequence_base_dupli_recursive(scene, scene, &right_strips, &left_strips, SEQ_DUPE_ALL, 0);
+
+ /* Split strips. */
+ Sequence *left_seq = left_strips.first;
+ Sequence *right_seq = right_strips.first;
+ Sequence *return_seq = right_strips.first;
+ while (left_seq && right_seq) {
+ seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method);
+ left_seq = left_seq->next;
+ right_seq = right_seq->next;
+ }
- switch (method) {
- case SEQ_SPLIT_SOFT:
- seq_split_set_left_offset(right_seq, timeline_frame);
- seq_split_set_right_offset(left_seq, timeline_frame);
- break;
- case SEQ_SPLIT_HARD:
- seq_split_set_right_hold_offset(left_seq, timeline_frame);
- seq_split_set_left_hold_offset(right_seq, timeline_frame);
- SEQ_add_reload_new_file(bmain, scene, left_seq, false);
- SEQ_add_reload_new_file(bmain, scene, right_seq, false);
- break;
+ seq = right_strips.first;
+ BLI_movelisttolist(seqbase, &left_strips);
+ BLI_movelisttolist(seqbase, &right_strips);
+
+ for (; seq; seq = seq->next) {
+ SEQ_ensure_unique_name(seq, scene);
}
- SEQ_time_update_sequence(scene, left_seq);
- SEQ_time_update_sequence(scene, right_seq);
- return right_seq;
+
+ return return_seq;
}
/**
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 4a01b0e1938..40d7fade308 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -36,6 +36,7 @@
#include "IMB_imbuf.h"
+#include "SEQ_iterator.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@@ -187,7 +188,7 @@ static void seq_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta)
{
seq_time_update_meta_strip(scene, seq_meta);
- /* Prevent metastrip to move in timeline. */
+ /* Prevent meta-strip to move in timeline. */
SEQ_transform_set_left_handle_frame(seq_meta, seq_meta->startdisp);
SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp);
}
@@ -196,7 +197,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
{
Sequence *seqm;
- /* check all metas recursively */
+ /* Check all meta-strips recursively. */
seqm = seq->seqbase.first;
while (seqm) {
if (seqm->seqbase.first) {
@@ -404,6 +405,17 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re
}
}
+static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, all_strips) {
+ if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Find first gap between strips after initial_frame and describe it by filling data of r_gap_info
*
@@ -425,10 +437,12 @@ void seq_time_gap_info_get(const Scene *scene,
int timeline_frame = initial_frame;
r_gap_info->gap_exists = false;
- if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) {
+ SeqCollection *collection = SEQ_query_all_strips(seqbase);
+
+ if (!strip_exists_at_frame(collection, initial_frame)) {
/* Search backward for gap_start_frame. */
for (; timeline_frame >= sfra; timeline_frame--) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
break;
}
}
@@ -438,7 +452,7 @@ void seq_time_gap_info_get(const Scene *scene,
else {
/* Search forward for gap_start_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) {
+ if (!strip_exists_at_frame(collection, timeline_frame)) {
r_gap_info->gap_start_frame = timeline_frame;
break;
}
@@ -446,7 +460,7 @@ void seq_time_gap_info_get(const Scene *scene,
}
/* Search forward for gap_end_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
const int gap_end_frame = timeline_frame;
r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame;
r_gap_info->gap_exists = true;
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index a15465eb3c0..cf1d7d66476 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -33,10 +33,7 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
-#include "BLI_listbase.h"
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -55,21 +52,27 @@
#include "proxy.h"
#include "utils.h"
-void SEQ_sort(Scene *scene)
+/**
+ * Sort strips in provided seqbase. Effect strips are trailing the list and they are sorted by
+ * channel position as well.
+ * This is important for SEQ_time_update_sequence to work properly
+ *
+ * \param seqbase: ListBase with strips
+ */
+void SEQ_sort(ListBase *seqbase)
{
- /* all strips together per kind, and in order of y location ("machine") */
- ListBase seqbase, effbase;
- Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq, *seqt;
-
- if (ed == NULL) {
+ if (seqbase == NULL) {
return;
}
- BLI_listbase_clear(&seqbase);
+ /* all strips together per kind, and in order of y location ("machine") */
+ ListBase inputbase, effbase;
+ Sequence *seq, *seqt;
+
+ BLI_listbase_clear(&inputbase);
BLI_listbase_clear(&effbase);
- while ((seq = BLI_pophead(ed->seqbasep))) {
+ while ((seq = BLI_pophead(seqbase))) {
if (seq->type & SEQ_TYPE_EFFECT) {
seqt = effbase.first;
@@ -85,22 +88,22 @@ void SEQ_sort(Scene *scene)
}
}
else {
- seqt = seqbase.first;
+ seqt = inputbase.first;
while (seqt) {
if (seqt->machine >= seq->machine) {
- BLI_insertlinkbefore(&seqbase, seqt, seq);
+ BLI_insertlinkbefore(&inputbase, seqt, seq);
break;
}
seqt = seqt->next;
}
if (seqt == NULL) {
- BLI_addtail(&seqbase, seq);
+ BLI_addtail(&inputbase, seq);
}
}
}
- BLI_movelisttolist(&seqbase, &effbase);
- *(ed->seqbasep) = seqbase;
+ BLI_movelisttolist(seqbase, &inputbase);
+ BLI_movelisttolist(seqbase, &effbase);
}
typedef struct SeqUniqueInfo {
@@ -160,7 +163,7 @@ void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq)
while (sui.match) {
sui.match = 0;
seqbase_unique_name(seqbasep, &sui);
- SEQ_iterator_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
+ SEQ_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
}
BLI_strncpy(seq->name + 2, sui.name_dest, sizeof(seq->name) - 2);
@@ -584,3 +587,53 @@ void SEQ_set_scale_to_fit(const Sequence *seq,
break;
}
}
+
+int SEQ_seqbase_recursive_apply(ListBase *seqbase,
+ int (*apply_fn)(Sequence *seq, void *),
+ void *arg)
+{
+ Sequence *iseq;
+ for (iseq = seqbase->first; iseq; iseq = iseq->next) {
+ if (SEQ_recursive_apply(iseq, apply_fn, arg) == -1) {
+ return -1; /* bail out */
+ }
+ }
+ return 1;
+}
+
+int SEQ_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+{
+ int ret = apply_fn(seq, arg);
+
+ if (ret == -1) {
+ return -1; /* bail out */
+ }
+
+ if (ret && seq->seqbase.first) {
+ ret = SEQ_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ }
+
+ return ret;
+}
+
+/**
+ * Ensure, that provided Sequence has unique name. If animation data exists for this Sequence, it
+ * will be duplicated and mapped onto new name
+ *
+ * \param seq: Sequence which name will be ensured to be unique
+ * \param scene: Scene in which name must be unique
+ */
+void SEQ_ensure_unique_name(Sequence *seq, Scene *scene)
+{
+ char name[SEQ_NAME_MAXSTR];
+
+ BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
+ SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
+ SEQ_dupe_animdata(scene, name, seq->name + 2);
+
+ if (seq->type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, seq_child, &seq->seqbase) {
+ SEQ_ensure_unique_name(seq_child, scene);
+ }
+ }
+}
diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c
index 30eaa35a049..fc75771cd62 100644
--- a/source/blender/shader_fx/intern/FX_shader_glow.c
+++ b/source/blender/shader_fx/intern/FX_shader_glow.c
@@ -71,12 +71,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
- if (mode == eShaderFxGlowMode_Luminance) {
- uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
- }
- else {
+ uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
+ if (mode == eShaderFxGlowMode_Color) {
uiItemR(layout, ptr, "select_color", 0, NULL, ICON_NONE);
}
+
uiItemR(layout, ptr, "glow_color", 0, NULL, ICON_NONE);
uiItemS(layout);
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 0f26ec50816..183b22c9791 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -203,6 +203,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
+ xr/intern/wm_xr_actions.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 280ee75a50f..edd5b555e2f 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -71,6 +71,11 @@ struct wmTabletData;
struct wmNDOFMotionData;
#endif
+#ifdef WITH_XR_OPENXR
+struct wmXrActionState;
+struct wmXrPose;
+#endif
+
typedef struct wmGizmo wmGizmo;
typedef struct wmGizmoMap wmGizmoMap;
typedef struct wmGizmoMapType wmGizmoMapType;
@@ -929,7 +934,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *wm_userdata);
bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region);
#ifdef WITH_XR_OPENXR
-/* wm_xr.c */
+/* wm_xr_session.c */
bool WM_xr_session_exists(const wmXrData *xr);
bool WM_xr_session_is_ready(const wmXrData *xr);
struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr);
@@ -939,7 +944,74 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
-#endif
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3]);
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4]);
+
+/* wm_xr_actions.c */
+/* XR action functions to be called pre-XR session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name);
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name);
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ struct wmOperatorType *ot,
+ struct IDProperty *op_properties,
+ eXrOpFlag op_flag);
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const struct wmXrPose *poses);
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths);
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+
+bool WM_xr_active_action_set_set(
+ wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then
+ * all action sets will be treated as active. */
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name);
+
+/* XR action functions to be called post-XR session start. */
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ struct wmXrActionState *r_state);
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude);
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d54925272de..168b775d16d 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -132,17 +132,21 @@ struct wmWindowManager;
extern "C" {
#endif
+typedef void (*wmGenericUserDataFreeFn)(void *data);
+
typedef struct wmGenericUserData {
void *data;
/** When NULL, use #MEM_freeN. */
- void (*free_fn)(void *data);
+ wmGenericUserDataFreeFn free_fn;
bool use_free;
} wmGenericUserData;
+typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data);
+
typedef struct wmGenericCallback {
- void (*exec)(struct bContext *C, void *user_data);
+ wmGenericCallbackFn exec;
void *user_data;
- void (*free_user_data)(void *user_data);
+ wmGenericUserDataFreeFn free_user_data;
} wmGenericCallback;
/* ************** wmOperatorType ************************ */
@@ -680,6 +684,25 @@ typedef struct wmNDOFMotionData {
} wmNDOFMotionData;
#endif /* WITH_INPUT_NDOF */
+#ifdef WITH_XR_OPENXR
+/* Similar to GHOST_XrPose. */
+typedef struct wmXrPose {
+ float position[3];
+ /* Blender convention (w, x, y, z) */
+ float orientation_quat[4];
+} wmXrPose;
+
+typedef struct wmXrActionState {
+ union {
+ bool state_boolean;
+ float state_float;
+ float state_vector2f[2];
+ wmXrPose state_pose;
+ };
+ int type; /* eXrActionType */
+} wmXrActionState;
+#endif
+
/** Timer flags. */
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index cdb7b591907..11783ae3517 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
if (wrap == WM_CURSOR_WRAP_X) {
mode_axis = GHOST_kAxisX;
}
- if (wrap == WM_CURSOR_WRAP_Y) {
+ else if (wrap == WM_CURSOR_WRAP_Y) {
mode_axis = GHOST_kGrabAxisY;
}
}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 0d1f4cc4830..a6588c40f0f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wmWindowManager *wm = CTX_wm_manager(C);
int retval = OPERATOR_CANCELLED;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
if (op == NULL || op->type == NULL) {
return retval;
@@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C,
{
int retval;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
/* Dummy test. */
if (ot) {
@@ -4478,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prevtype = event.type;
event.prevval = event.val;
- /* Ensure the event state is correct, any deviation from this may cause bugs. */
+ /* Ensure the event state is correct, any deviation from this may cause bugs.
+ *
+ * NOTE: #EVENT_NONE is set when unknown keys are pressed,
+ * while not common, avoid a false alarm. */
#ifndef NDEBUG
if ((event_state->type || event_state->val) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) {
+ !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
event_state->type);
}
if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) {
+ !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'",
event_state->prevtype);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index bbcb0669cce..d0ee7075516 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -2157,21 +2157,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
}
-static void wm_free_operator_properties_callback(void *user_data)
-{
- IDProperty *properties = (IDProperty *)user_data;
- IDP_FreeProperty(properties);
-}
-
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_homefile_read_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_homefile_read_exec(C, op);
@@ -2331,13 +2319,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_open_mainfile_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_open_mainfile_dispatch(C, op);
@@ -2637,12 +2619,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
+}
+
+static int wm_recover_last_session_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
{
/* Keep the current setting instead of using the preferences since a file selector
* doesn't give us the option to change the setting. */
wm_open_init_use_scripts(op, false);
- return WM_operator_confirm(C, op, event);
+
+ if (wm_operator_close_file_dialog_if_needed(
+ C, op, wm_recover_last_session_after_dialog_callback)) {
+ return OPERATOR_INTERFACE;
+ }
+ return wm_recover_last_session_exec(C, op);
}
void WM_OT_recover_last_session(wmOperatorType *ot)
@@ -3270,7 +3265,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat
bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
if (file_has_been_saved_before) {
- WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
+ if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) &
+ OPERATOR_CANCELLED) {
+ execute_callback = false;
+ }
}
else {
WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
@@ -3456,4 +3454,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
}
}
+static void wm_free_operator_properties_callback(void *user_data)
+{
+ IDProperty *properties = (IDProperty *)user_data;
+ IDP_FreeProperty(properties);
+}
+
+/**
+ * \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE
+ * then.
+ */
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn post_action_fn)
+{
+ if (U.uiflag & USER_SAVE_PROMPT &&
+ wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
+ wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = post_action_fn;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index de563cd309d..5300649a0cd 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -97,45 +97,69 @@ static AUD_Device *audio_device = NULL;
struct PlayState;
static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
+/**
+ * The current state of the player.
+ *
+ * \warning Don't store results of parsing command-line arguments
+ * in this struct if they need to persist across playing back different
+ * files as these will be cleared when playing other files (drag & drop).
+ */
typedef struct PlayState {
- /* window and viewport size */
+ /** Window and viewport size. */
int win_x, win_y;
- /* current zoom level */
+ /** Current zoom level. */
float zoom;
- /* playback state */
+ /** Playback direction (-1, 1). */
short direction;
+ /** Set the next frame to implement frame stepping (using shortcuts). */
short next_frame;
+ /** Playback once then wait. */
bool once;
- bool turbo;
+ /** Play forwards/backwards. */
bool pingpong;
+ /** Disable frame skipping. */
bool noskip;
+ /** Display current frame over the window. */
bool indicator;
+ /** Single-frame stepping has been enabled (frame loading and update pending). */
bool sstep;
+ /** Playback has stopped the image has been displayed. */
bool wait2;
+ /** Playback stopped state once stop/start variables have been handled. */
bool stopped;
+ /**
+ * When disabled the current animation will exit,
+ * after this either the application exits or a new animation window is opened.
+ *
+ * This is used so drag & drop can load new files which setup a newly created animation window.
+ */
bool go;
- /* waiting for images to load */
+ /** True when waiting for images to load. */
bool loading;
- /* x/y image flip */
+ /** X/Y image flip (set via key bindings). */
bool draw_flip[2];
+ /** The number of frames to step each update (default to 1, command line argument). */
int fstep;
- /* current picture */
+ /** Current frame (picture). */
struct PlayAnimPict *picture;
- /* set once at the start */
+ /** Image size in pixels, set once at the start. */
int ibufx, ibufy;
+ /** Mono-space font ID. */
int fontid;
- /* restarts player for file drop */
+ /** Restarts player for file drop (drag & drop). */
char dropped_file[FILE_MAX];
+ /** Force update when scrubbing with the cursor. */
bool need_frame_update;
+ /** The current frame calculated by scrubbing the mouse cursor. */
int frame_cursor_x;
ColorManagedViewSettings view_settings;
@@ -144,17 +168,14 @@ typedef struct PlayState {
/* for debugging */
#if 0
-void print_ps(PlayState *ps)
+static void print_ps(PlayState *ps)
{
printf("ps:\n");
printf(" direction=%d,\n", (int)ps->direction);
- printf(" next=%d,\n", ps->next);
printf(" once=%d,\n", ps->once);
- printf(" turbo=%d,\n", ps->turbo);
printf(" pingpong=%d,\n", ps->pingpong);
printf(" noskip=%d,\n", ps->noskip);
printf(" sstep=%d,\n", ps->sstep);
- printf(" pause=%d,\n", ps->pause);
printf(" wait2=%d,\n", ps->wait2);
printf(" stopped=%d,\n", ps->stopped);
printf(" go=%d,\n\n", ps->go);
@@ -869,16 +890,14 @@ static void change_frame(PlayState *ps)
static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
{
PlayState *ps = (PlayState *)ps_void;
- GHOST_TEventType type = GHOST_GetEventType(evt);
- int val;
+ const GHOST_TEventType type = GHOST_GetEventType(evt);
+ /* Convert ghost event into value keyboard or mouse. */
+ const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
// print_ps(ps);
playanim_event_qual_update();
- /* convert ghost event into value keyboard or mouse */
- val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
-
/* first check if we're busy loading files */
if (ps->loading) {
switch (type) {
@@ -902,8 +921,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
return 1;
}
- if (ps->wait2 && ps->stopped) {
- ps->stopped = false;
+ if (ps->wait2 && ps->stopped == false) {
+ ps->stopped = true;
}
if (ps->wait2) {
@@ -1379,7 +1398,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
}
-/* return path for restart */
+/**
+ * \return The a path used to restart the animation player or NULL to exit.
+ */
static char *wm_main_playanim_intern(int argc, const char **argv)
{
struct ImBuf *ibuf = NULL;
@@ -1398,7 +1419,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.direction = true;
ps.next_frame = 1;
ps.once = false;
- ps.turbo = false;
ps.pingpong = false;
ps.noskip = false;
ps.sstep = false;
@@ -1420,6 +1440,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE));
IMB_colormanagement_init_default_view_settings(&ps.view_settings, &ps.display_settings);
+ /* Skip the first argument which is assumed to be '-a' (used to launch this player). */
while (argc > 1) {
if (argv[1][0] == '-') {
switch (argv[1][1]) {
@@ -1720,14 +1741,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.wait2 = ps.sstep;
- if (ps.wait2 == false && ps.stopped == false) {
- ps.stopped = true;
+ if (ps.wait2 == false && ps.stopped) {
+ ps.stopped = false;
}
pupdate_time();
if (ps.picture && ps.next_frame) {
- /* always at least set one step */
+ /* Advance to the next frame, always at least set one step.
+ * Implement frame-skipping when enabled and playback is not fast enough. */
while (ps.picture) {
ps.picture = playanim_step(ps.picture, ps.next_frame);
@@ -1740,7 +1762,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
}
}
- if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) {
+ if (ps.wait2 || ptottime < swaptime || ps.noskip) {
break;
}
ptottime -= swaptime;
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 2e9fd1b1b16..cdd5ea12df8 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -133,7 +133,7 @@ static struct WMInitStruct {
* \{ */
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
-static int wm_window_timer(const bContext *C);
+static bool wm_window_timer(const bContext *C);
/* XXX this one should correctly check for apple top header...
* done for Cocoa : returns window contents (and not frame) max size*/
@@ -1498,12 +1498,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
* Timer handlers should check for delta to decide if they just update, or follow real time.
* Timer handlers can also set duration to match frames passed
*/
-static int wm_window_timer(const bContext *C)
+static bool wm_window_timer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
double time = PIL_check_seconds_timer();
- int retval = 0;
+ bool has_event = false;
/* Mutable in case the timer gets removed. */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
@@ -1540,31 +1540,34 @@ static int wm_window_timer(const bContext *C)
event.customdata = wt;
wm_event_add(win, &event);
- retval = 1;
+ has_event = true;
}
}
}
- return retval;
+ return has_event;
}
void wm_window_process_events(const bContext *C)
{
BLI_assert(BLI_thread_is_main());
- int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
+ bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */
- if (hasevent) {
+ if (has_event) {
GHOST_DispatchEvents(g_system);
}
- hasevent |= wm_window_timer(C);
+ has_event |= wm_window_timer(C);
#ifdef WITH_XR_OPENXR
/* XR events don't use the regular window queues. So here we don't only trigger
* processing/dispatching but also handling. */
- hasevent |= wm_xr_events_handle(CTX_wm_manager(C));
+ has_event |= wm_xr_events_handle(CTX_wm_manager(C));
#endif
- /* no event, we sleep 5 milliseconds */
- if (hasevent == 0) {
+ /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle.
+ *
+ * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
+ * events are typically generated from a timer that runs in the main loop. */
+ if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
PIL_sleep_ms(5);
}
}
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index d54090a6025..c7fe07cad7f 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C,
void wm_file_read_report(bContext *C, struct Main *bmain);
void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action);
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn exec_fn);
bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm);
void WM_OT_save_homefile(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 439d611b085..2a67c2bee9f 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -128,6 +128,11 @@ bool wm_xr_events_handle(wmWindowManager *wm)
if (wm->xr.runtime && wm->xr.runtime->context) {
GHOST_XrEventsHandle(wm->xr.runtime->context);
+ /* Process OpenXR action events. */
+ if (WM_xr_session_is_ready(&wm->xr)) {
+ wm_xr_session_actions_update(&wm->xr);
+ }
+
/* wm_window_process_events() uses the return value to determine if it can put the main thread
* to sleep for some milliseconds. We never want that to happen while the VR session runs on
* the main thread. So always return true. */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
new file mode 100644
index 00000000000..51ed3dcfd3c
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
@@ -0,0 +1,480 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ *
+ * \name Window-Manager XR Actions
+ *
+ * Uses the Ghost-XR API to manage OpenXR actions.
+ * All functions are designed to be usable by RNA / the Python API.
+ */
+
+#include "BLI_math.h"
+
+#include "GHOST_C-api.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Action API
+ *
+ * API functions for managing OpenXR actions.
+ *
+ * \{ */
+
+static wmXrActionSet *action_set_create(const char *action_set_name)
+{
+ wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__);
+ action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name");
+ strcpy(action_set->name, action_set_name);
+
+ return action_set;
+}
+
+static void action_set_destroy(void *val)
+{
+ wmXrActionSet *action_set = val;
+
+ MEM_SAFE_FREE(action_set->name);
+
+ MEM_freeN(action_set);
+}
+
+static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
+{
+ return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name);
+}
+
+static wmXrAction *action_create(const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
+ action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
+ strcpy(action->name, action_name);
+ action->type = type;
+
+ const unsigned int count = count_subaction_paths;
+ action->count_subaction_paths = count;
+
+ action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count,
+ "XrAction_SubactionPaths");
+ for (unsigned int i = 0; i < count; ++i) {
+ action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1,
+ "XrAction_SubactionPath");
+ strcpy(action->subaction_paths[i], subaction_paths[i]);
+ }
+
+ size_t size;
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ size = sizeof(bool);
+ break;
+ case XR_FLOAT_INPUT:
+ size = sizeof(float);
+ break;
+ case XR_VECTOR2F_INPUT:
+ size = sizeof(float) * 2;
+ break;
+ case XR_POSE_INPUT:
+ size = sizeof(GHOST_XrPose);
+ break;
+ case XR_VIBRATION_OUTPUT:
+ return action;
+ }
+ action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
+ action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
+
+ if (float_threshold) {
+ BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
+ action->float_threshold = *float_threshold;
+ CLAMP(action->float_threshold, 0.0f, 1.0f);
+ }
+
+ action->ot = ot;
+ action->op_properties = op_properties;
+ action->op_flag = op_flag;
+
+ return action;
+}
+
+static void action_destroy(void *val)
+{
+ wmXrAction *action = val;
+
+ MEM_SAFE_FREE(action->name);
+
+ const unsigned int count = action->count_subaction_paths;
+ char **subaction_paths = action->subaction_paths;
+ if (subaction_paths) {
+ for (unsigned int i = 0; i < count; ++i) {
+ MEM_SAFE_FREE(subaction_paths[i]);
+ }
+ MEM_freeN(subaction_paths);
+ }
+
+ MEM_SAFE_FREE(action->states);
+ MEM_SAFE_FREE(action->states_prev);
+
+ MEM_freeN(action);
+}
+
+static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name);
+}
+
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
+{
+ if (action_set_find(xr, action_set_name)) {
+ return false;
+ }
+
+ wmXrActionSet *action_set = action_set_create(action_set_name);
+
+ GHOST_XrActionSetInfo info = {
+ .name = action_set_name,
+ .customdata_free_fn = action_set_destroy,
+ .customdata = action_set,
+ };
+
+ if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ wmXrSessionState *session_state = &xr->runtime->session_state;
+
+ if (action_set == session_state->active_action_set) {
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_clear(session_state);
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action) {
+ action_set->active_modal_action = NULL;
+ }
+ session_state->active_action_set = NULL;
+ }
+
+ GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name);
+}
+
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ if (action_find(xr, action_set_name, action_name)) {
+ return false;
+ }
+
+ wmXrAction *action = action_create(action_name,
+ type,
+ count_subaction_paths,
+ subaction_paths,
+ float_threshold,
+ ot,
+ op_properties,
+ op_flag);
+
+ GHOST_XrActionInfo info = {
+ .name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ .states = action->states,
+ .customdata_free_fn = action_destroy,
+ .customdata = action,
+ };
+
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ info.type = GHOST_kXrActionTypeBooleanInput;
+ break;
+ case XR_FLOAT_INPUT:
+ info.type = GHOST_kXrActionTypeFloatInput;
+ break;
+ case XR_VECTOR2F_INPUT:
+ info.type = GHOST_kXrActionTypeVector2fInput;
+ break;
+ case XR_POSE_INPUT:
+ info.type = GHOST_kXrActionTypePoseInput;
+ break;
+ case XR_VIBRATION_OUTPUT:
+ info.type = GHOST_kXrActionTypeVibrationOutput;
+ break;
+ }
+
+ if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ if (action_set->controller_pose_action &&
+ STREQ(action_set->controller_pose_action->name, action_name)) {
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_clear(&xr->runtime->session_state);
+ }
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action &&
+ STREQ(action_set->active_modal_action->name, action_name)) {
+ action_set->active_modal_action = NULL;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return;
+ }
+}
+
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const wmXrPose *poses)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
+ count_subaction_paths, sizeof(*ghost_poses), __func__);
+ for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ const wmXrPose *pose = &poses[i];
+ GHOST_XrPose *ghost_pose = &ghost_poses[i];
+ copy_v3_v3(ghost_pose->position, pose->position);
+ copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
+ }
+ info.poses = ghost_poses;
+
+ bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
+ false;
+ MEM_freeN(ghost_poses);
+ return ret;
+}
+
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
+}
+
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ {
+ /* Unset active modal action (if any). */
+ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
+ if (active_action_set) {
+ wmXrAction *active_modal_action = active_action_set->active_modal_action;
+ if (active_modal_action) {
+ if (active_modal_action->active_modal_path) {
+ active_modal_action->active_modal_path = NULL;
+ }
+ active_action_set->active_modal_action = NULL;
+ }
+ }
+ }
+
+ xr->runtime->session_state.active_action_set = action_set;
+
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ action_set->controller_pose_action = action;
+
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_populate(action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ wmXrActionState *r_state)
+{
+ const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ BLI_assert(action->type == (eXrActionType)r_state->type);
+
+ /* Find the action state corresponding to the subaction path. */
+ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
+ if (STREQ(subaction_path, action->subaction_paths[i])) {
+ switch ((eXrActionType)r_state->type) {
+ case XR_BOOLEAN_INPUT:
+ r_state->state_boolean = ((bool *)action->states)[i];
+ break;
+ case XR_FLOAT_INPUT:
+ r_state->state_float = ((float *)action->states)[i];
+ break;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]);
+ break;
+ case XR_POSE_INPUT: {
+ const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i];
+ copy_v3_v3(r_state->state_pose.position, pose->position);
+ copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat);
+ break;
+ }
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ return GHOST_XrApplyHapticAction(
+ xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ true :
+ false;
+}
+
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+}
+
+/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index cc4a7e41e82..1f722855696 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -45,6 +45,12 @@ void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
+{
+ quat_to_mat4(r_mat, pose->orientation_quat);
+ copy_v3_v3(r_mat[3], pose->position);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 25e3da3ffb4..9bf63be61dd 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -24,6 +24,21 @@
#include "wm_xr.h"
+struct wmXrActionSet;
+
+typedef struct wmXrControllerData {
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /** Last known controller pose (in world space) stored for queries. */
+ GHOST_XrPose pose;
+ /** The last known controller matrix, calculated from above's controller pose. */
+ float mat[4][4];
+} wmXrControllerData;
+
typedef struct wmXrSessionState {
bool is_started;
@@ -39,11 +54,23 @@ typedef struct wmXrSessionState {
Object *prev_base_pose_object;
/** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */
int prev_settings_flag;
+ /** Copy of wmXrDrawData.base_pose. */
+ GHOST_XrPose prev_base_pose;
+ /** Copy of GHOST_XrDrawViewInfo.local_pose. */
+ GHOST_XrPose prev_local_pose;
/** Copy of wmXrDrawData.eye_position_ofs. */
float prev_eye_position_ofs[3];
bool force_reset_to_base_pose;
bool is_view_data_set;
+
+ /** Last known controller data. */
+ wmXrControllerData controllers[2];
+
+ /** The currently active action set that will be updated on calls to
+ * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
+ * updated. */
+ struct wmXrActionSet *active_action_set;
} wmXrSessionState;
typedef struct wmXrRuntimeData {
@@ -79,6 +106,40 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrAction {
+ char *name;
+ eXrActionType type;
+ unsigned int count_subaction_paths;
+ char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+ /** Previous states, stored to determine XR events. */
+ void *states_prev;
+
+ /** Input threshold for float/vector2f actions. */
+ float float_threshold;
+
+ /** The currently active subaction path (if any) for modal actions. */
+ char **active_modal_path;
+
+ /** Operator to be called on XR events. */
+ struct wmOperatorType *ot;
+ IDProperty *op_properties;
+ eXrOpFlag op_flag;
+} wmXrAction;
+
+typedef struct wmXrActionSet {
+ char *name;
+
+ /** The XR pose action that determines the controller
+ * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
+ * although it could differ depending on the specification and hardware. */
+ wmXrAction *controller_pose_action;
+
+ /** The currently active modal action (if any). */
+ wmXrAction *active_modal_action;
+} wmXrActionSet;
+
wmXrRuntimeData *wm_xr_runtime_data_create(void);
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
@@ -95,5 +156,12 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
void *wm_xr_session_gpu_binding_context_create(void);
void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context);
+void wm_xr_session_actions_init(wmXrData *xr);
+void wm_xr_session_actions_update(wmXrData *xr);
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action,
+ wmXrData *xr);
+void wm_xr_session_controller_data_clear(wmXrSessionState *state);
+
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index b9ef40e3398..1ddbe228e05 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -18,7 +18,9 @@
* \ingroup wm
*/
+#include "BKE_callbacks.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -49,11 +51,24 @@ static CLG_LogRef LOG = {"wm.xr"};
/* -------------------------------------------------------------------- */
+static void wm_xr_session_create_cb(void)
+{
+ Main *bmain = G_MAIN;
+ wmWindowManager *wm = bmain->wm.first;
+ wmXrData *xr_data = &wm->xr;
+
+ /* Get action set data from Python. */
+ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
+
+ wm_xr_session_actions_init(xr_data);
+}
+
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
xr_data->runtime->session_state.is_started = false;
+
if (xr_data->runtime->exit_fn) {
xr_data->runtime->exit_fn(xr_data);
}
@@ -65,6 +80,10 @@ static void wm_xr_session_exit_cb(void *customdata)
static void wm_xr_session_begin_info_create(wmXrData *xr_data,
GHOST_XrSessionBeginInfo *r_begin_info)
{
+ /* Callback for when the session is created. This is needed to create and bind OpenXR actions
+ * after the session is created but before it is started. */
+ r_begin_info->create_fn = wm_xr_session_create_cb;
+
/* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(),
* to allow external code to execute its own session-exit logic. */
r_begin_info->exit_fn = wm_xr_session_exit_cb;
@@ -289,6 +308,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
/**
* Update information that is only stored for external state queries. E.g. for Python API to
* request the current (as in, last known) viewer pose.
+ * Controller data and action sets will be updated separately via wm_xr_session_actions_update().
*/
void wm_xr_session_state_update(const XrSessionSettings *settings,
const wmXrDrawData *draw_data,
@@ -322,6 +342,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
DEFAULT_SENSOR_WIDTH);
copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+ memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -373,6 +395,132 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
+ return true;
+}
+
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ copy_v4_v4(r_rotation,
+ xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
+ return true;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Session Actions
+ *
+ * XR action processing and event dispatching.
+ *
+ * \{ */
+
+void wm_xr_session_actions_init(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrAttachActionSets(xr->runtime->context);
+}
+
+static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
+ const wmXrAction *controller_pose_action,
+ wmXrSessionState *state)
+{
+ const unsigned int count = (unsigned int)min_ii(
+ (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
+
+ float view_ofs[3];
+ float base_inv[4][4];
+ float tmp[4][4];
+
+ zero_v3(view_ofs);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ add_v3_v3(view_ofs, state->prev_local_pose.position);
+ }
+
+ wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
+ invert_m4(base_inv);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *controller = &state->controllers[i];
+
+ /* Calculate controller matrix in world space. */
+ wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
+
+ /* Apply eye position and base pose offsets. */
+ sub_v3_v3(tmp[3], view_ofs);
+ mul_m4_m4m4(controller->mat, base_inv, tmp);
+
+ /* Save final pose. */
+ mat4_to_loc_quat(
+ controller->pose.position, controller->pose.orientation_quat, controller->mat);
+ }
+}
+
+void wm_xr_session_actions_update(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrContextHandle xr_context = xr->runtime->context;
+ wmXrSessionState *state = &xr->runtime->session_state;
+ wmXrActionSet *active_action_set = state->active_action_set;
+
+ int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
+ if (!ret) {
+ return;
+ }
+
+ /* Only update controller mats for active action set. */
+ if (active_action_set) {
+ if (active_action_set->controller_pose_action) {
+ wm_xr_session_controller_mats_update(
+ &xr->session_settings, active_action_set->controller_pose_action, state);
+ }
+ }
+}
+
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
+{
+ wmXrSessionState *state = &xr->runtime->session_state;
+
+ const unsigned int count = (unsigned int)min_ii(
+ (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *c = &state->controllers[i];
+ strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
+ memset(&c->pose, 0, sizeof(c->pose));
+ zero_m4(c->mat);
+ }
+}
+
+void wm_xr_session_controller_data_clear(wmXrSessionState *state)
+{
+ memset(state->controllers, 0, sizeof(state->controllers));
+}
+
+/** \} */ /* XR-Session Actions */
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Surface
*
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index cb5fc538e69..c2893317924 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -130,14 +130,11 @@ set(SRC
# MSVC 2010 gives linking errors with the manifest
if(WIN32 AND NOT UNIX)
- string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1)
- string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2)
- string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3)
add_definitions(
-DBLEN_VER_RC_STR="${BLENDER_VERSION}"
- -DBLEN_VER_RC_1=${bver1}
- -DBLEN_VER_RC_2=${bver2}
- -DBLEN_VER_RC_3=${bver3}
+ -DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR}
+ -DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR}
+ -DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH}
-DBLEN_VER_RC_4=0
)
@@ -188,7 +185,7 @@ if(WITH_BUILDINFO)
# --------------------------------------------------------------------------
# write header for values that change each build
- # note, generaed file is in build dir's source/creator
+ # note, generated file is in build dir's source/creator
# except when used as an include path.
add_definitions(-DWITH_BUILDINFO_HEADER)
diff --git a/source/creator/creator.c b/source/creator/creator.c
index b40718d1f7c..51efadf5e56 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -132,7 +132,6 @@ struct ApplicationState app_state = {
/** \name Application Level Callbacks
*
* Initialize callbacks for the modules that need them.
- *
* \{ */
static void callback_mem_error(const char *errorStr)
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 9c7b7dc3f34..36fdaef507b 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -1195,15 +1195,17 @@ static const char arg_handle_playback_mode_doc[] =
"\t\tZero disables (clamping to a fixed number of frames instead).";
static int arg_handle_playback_mode(int argc, const char **argv, void *UNUSED(data))
{
- /* not if -b was given first */
+ /* Ignore the animation player if `-b` was given first. */
if (G.background == 0) {
# ifdef WITH_FFMPEG
/* Setup FFmpeg with current debug flags. */
IMB_ffmpeg_init();
# endif
- WM_main_playanim(argc, argv); /* not the same argc and argv as before */
- exit(0); /* 2.4x didn't do this */
+ /* This function knows to skip this argument ('-a'). */
+ WM_main_playanim(argc, argv);
+
+ exit(0);
}
return -2;
diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h
index bcc8a15355a..2260da8db11 100644
--- a/source/creator/creator_intern.h
+++ b/source/creator/creator_intern.h
@@ -72,7 +72,7 @@ enum {
/* for the callbacks: */
#ifndef WITH_PYTHON_MODULE
-# define BLEND_VERSION_FMT "Blender %d.%02d.%d"
+# define BLEND_VERSION_FMT "Blender %d.%d.%d"
# define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH
#endif
diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py
index e2c6432b380..48625a1ecdb 100644
--- a/tests/python/bl_blendfile_library_overrides.py
+++ b/tests/python/bl_blendfile_library_overrides.py
@@ -75,7 +75,7 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
assert(override_operation.operation == 'REPLACE')
# Setting location.y overridded all elements in the location array. -1 is a wildcard.
assert(override_operation.subitem_local_index == -1)
-
+
def test_link_permissive(self):
"""
Linked assets with a permissive template.
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
index 7b480f5fa16..1e570bf9a7f 100644
--- a/tests/python/bl_pyapi_idprop.py
+++ b/tests/python/bl_pyapi_idprop.py
@@ -2,6 +2,7 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
import bpy
+import idprop
import unittest
import numpy as np
from array import array
@@ -139,6 +140,51 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase):
with self.assertRaises(TypeError):
self.id["a"] = self
+class TestIdPropertyGroupView(TestHelper, unittest.TestCase):
+
+ def test_type(self):
+ self.assertEqual(type(self.id.keys()), idprop.types.IDPropertyGroupViewKeys)
+ self.assertEqual(type(self.id.values()), idprop.types.IDPropertyGroupViewValues)
+ self.assertEqual(type(self.id.items()), idprop.types.IDPropertyGroupViewItems)
+
+ self.assertEqual(type(iter(self.id.keys())), idprop.types.IDPropertyGroupIterKeys)
+ self.assertEqual(type(iter(self.id.values())), idprop.types.IDPropertyGroupIterValues)
+ self.assertEqual(type(iter(self.id.items())), idprop.types.IDPropertyGroupIterItems)
+
+ def test_basic(self):
+ text = ["A", "B", "C"]
+ for i, ch in enumerate(text):
+ self.id[ch] = i
+ self.assertEqual(len(self.id.keys()), len(text))
+ self.assertEqual(list(self.id.keys()), text)
+ self.assertEqual(list(reversed(self.id.keys())), list(reversed(text)))
+
+ self.assertEqual(len(self.id.values()), len(text))
+ self.assertEqual(list(self.id.values()), list(range(len(text))))
+ self.assertEqual(list(reversed(self.id.values())), list(reversed(range(len(text)))))
+
+ self.assertEqual(len(self.id.items()), len(text))
+ self.assertEqual(list(self.id.items()), [(k, v) for v, k in enumerate(text)])
+ self.assertEqual(list(reversed(self.id.items())), list(reversed([(k, v) for v, k in enumerate(text)])))
+
+ def test_contains(self):
+ # Check `idprop.types.IDPropertyGroupView{Keys/Values/Items}.__contains__`
+ text = ["A", "B", "C"]
+ for i, ch in enumerate(text):
+ self.id[ch] = i
+
+ self.assertIn("A", self.id)
+ self.assertNotIn("D", self.id)
+
+ self.assertIn("A", self.id.keys())
+ self.assertNotIn("D", self.id.keys())
+
+ self.assertIn(2, self.id.values())
+ self.assertNotIn(3, self.id.values())
+
+ self.assertIn(("A", 0), self.id.items())
+ self.assertNotIn(("D", 3), self.id.items())
+
class TestBufferProtocol(TestHelper, unittest.TestCase):
diff --git a/tests/python/bl_pyapi_mathutils.py b/tests/python/bl_pyapi_mathutils.py
index 9dfc6c159cc..914b1689a5c 100644
--- a/tests/python/bl_pyapi_mathutils.py
+++ b/tests/python/bl_pyapi_mathutils.py
@@ -2,7 +2,7 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_mathutils.py -- --verbose
import unittest
-from mathutils import Matrix, Vector, Quaternion
+from mathutils import Matrix, Vector, Quaternion, Euler
from mathutils import kdtree, geometry
import math
@@ -233,6 +233,27 @@ class MatrixTesting(unittest.TestCase):
self.assertEqual(mat @ mat, prod_mat)
+ def test_loc_rot_scale(self):
+ euler = Euler((math.radians(90), 0, math.radians(90)), 'ZYX')
+ expected = Matrix(((0, -5, 0, 1),
+ (0, 0, -6, 2),
+ (4, 0, 0, 3),
+ (0, 0, 0, 1)))
+
+ result = Matrix.LocRotScale((1, 2, 3), euler, (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ result = Matrix.LocRotScale((1, 2, 3), euler.to_quaternion(), (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ result = Matrix.LocRotScale((1, 2, 3), euler.to_matrix(), (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ def assertAlmostEqualMatrix(self, first, second, size, *, places=6, msg=None, delta=None):
+ for i in range(size):
+ for j in range(size):
+ self.assertAlmostEqual(first[i][j], second[i][j], places=places, msg=msg, delta=delta)
+
class VectorTesting(unittest.TestCase):
diff --git a/tests/python/compositor_render_tests.py b/tests/python/compositor_render_tests.py
index 6a026ae88d2..fbdae595c51 100644
--- a/tests/python/compositor_render_tests.py
+++ b/tests/python/compositor_render_tests.py
@@ -61,4 +61,3 @@ def main():
if not inside_blender and __name__ == "__main__":
main()
-
diff --git a/tests/python/operators.py b/tests/python/operators.py
index 309a872ac67..c209b01c20c 100644
--- a/tests/python/operators.py
+++ b/tests/python/operators.py
@@ -196,6 +196,14 @@ def main():
MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
[OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]),
+ # face shade smooth (not a real test)
+ MeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth",
+ [OperatorSpecEditMode("faces_shade_smooth", {}, "VERT", {i for i in range(8)})]),
+
+ # faces shade flat (not a real test)
+ MeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat",
+ [OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]),
+
# inset faces
MeshTest("CubeInset",
"testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT",
@@ -226,6 +234,10 @@ def main():
MeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect",
[OperatorSpecEditMode("loop_multi_select", {}, "VERT", {})]),
+ # mark seam
+ MeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam",
+ [OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})]),
+
# select all
MeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll",
[OperatorSpecEditMode("select_all", {}, "VERT", {1})]),
@@ -296,6 +308,26 @@ def main():
MeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked",
[OperatorSpecEditMode("select_linked", {}, "VERT", {})]),
+ # select nth (checkered deselect)
+ MeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd",
+ [OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]),
+
+ # unsubdivide
+ # normal case
+ MeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]),
+
+ # T87259 - test cases
+ MeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]),
+ MeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]),
+
+ # vert connect path
+ # Tip: It works only if there is an already existing face or more than 2 vertices.
+ MeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath",
+ [OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]),
+
]
operators_test = RunTest(tests)