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:
authorJeroen Bakker <j.bakker@atmind.nl>2022-03-02 18:03:01 +0300
committerJeroen Bakker <j.bakker@atmind.nl>2022-03-02 18:03:01 +0300
commita41c2a513761e8884e92526b069ff6eed8168676 (patch)
treee624093127815a09d2807dccddaabea35510e154
parenta23b4429915ca8597510b57353c4df331487c620 (diff)
parentc23ec04b4e30f300a670f1cb1dc882e0608d09ad (diff)
Merge branch 'master' into temp-image-buffer-rasterizertemp-image-buffer-rasterizer
-rw-r--r--CMakeLists.txt7
-rw-r--r--GNUmakefile39
-rwxr-xr-xbuild_files/build_environment/install_deps.sh2
-rw-r--r--build_files/cmake/Modules/FindFFmpeg.cmake4
-rw-r--r--build_files/cmake/Modules/FindOSL.cmake2
-rw-r--r--build_files/cmake/Modules/FindOpenColorIO.cmake8
-rw-r--r--build_files/cmake/Modules/FindOpenEXR.cmake93
-rw-r--r--build_files/cmake/Modules/FindOpenImageDenoise.cmake1
-rw-r--r--build_files/cmake/Modules/FindOpenImageIO.cmake41
-rwxr-xr-xbuild_files/cmake/cmake_consistency_check.py387
-rw-r--r--build_files/cmake/cmake_consistency_check_config.py64
-rw-r--r--build_files/cmake/platform/platform_unix.cmake43
-rw-r--r--doc/python_api/static/css/version_switch.css1
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp2
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp39
-rw-r--r--intern/cycles/app/CMakeLists.txt30
-rw-r--r--intern/cycles/app/cycles_standalone.cpp38
-rw-r--r--intern/cycles/app/opengl/display_driver.cpp385
-rw-r--r--intern/cycles/app/opengl/display_driver.h117
-rw-r--r--intern/cycles/app/opengl/shader.cpp197
-rw-r--r--intern/cycles/app/opengl/shader.h45
-rw-r--r--intern/cycles/app/opengl/window.cpp352
-rw-r--r--intern/cycles/app/opengl/window.h35
-rw-r--r--intern/cycles/blender/addon/properties.py9
-rw-r--r--intern/cycles/blender/session.cpp9
-rw-r--r--intern/cycles/blender/shader.cpp16
-rw-r--r--intern/cycles/blender/sync.cpp9
-rw-r--r--intern/cycles/blender/sync.h2
-rw-r--r--intern/cycles/cmake/external_libs.cmake20
-rw-r--r--intern/cycles/device/cpu/device_impl.cpp2
-rw-r--r--intern/cycles/device/hip/device_impl.h2
-rw-r--r--intern/cycles/device/metal/device_impl.mm2
-rw-r--r--intern/cycles/integrator/path_trace.cpp26
-rw-r--r--intern/cycles/integrator/path_trace.h3
-rw-r--r--intern/cycles/integrator/render_scheduler.cpp2
-rw-r--r--intern/cycles/integrator/render_scheduler.h4
-rw-r--r--intern/cycles/kernel/bvh/util.h21
-rw-r--r--intern/cycles/kernel/geom/point.h5
-rw-r--r--intern/cycles/kernel/integrator/init_from_bake.h54
-rw-r--r--intern/cycles/kernel/integrator/shade_surface.h6
-rw-r--r--intern/cycles/kernel/osl/services.cpp8
-rw-r--r--intern/cycles/kernel/svm/light_path.h4
-rw-r--r--intern/cycles/scene/shader.cpp34
-rw-r--r--intern/cycles/util/CMakeLists.txt10
-rw-r--r--intern/cycles/util/math.h3
-rw-r--r--intern/cycles/util/view.cpp269
-rw-r--r--intern/cycles/util/view.h35
-rw-r--r--intern/ffmpeg/tests/ffmpeg_codecs.cc13
-rw-r--r--intern/ghost/GHOST_C-api.h10
-rw-r--r--intern/ghost/GHOST_ISystem.h8
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp10
-rw-r--r--intern/ghost/intern/GHOST_ContextD3D.cpp95
-rw-r--r--intern/ghost/intern/GHOST_System.cpp19
-rw-r--r--intern/ghost/intern/GHOST_System.h8
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h8
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm15
-rw-r--r--intern/ghost/intern/GHOST_SystemNULL.h5
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp45
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp3
-rw-r--r--intern/opencolorio/CMakeLists.txt36
-rw-r--r--intern/opencolorio/gpu_shader_display_transform_frag.glsl (renamed from intern/opencolorio/gpu_shader_display_transform.glsl)109
-rw-r--r--intern/opencolorio/gpu_shader_display_transform_vert.glsl (renamed from intern/opencolorio/gpu_shader_display_transform_vertex.glsl)6
-rw-r--r--intern/opencolorio/ocio_impl.cc18
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc327
-rw-r--r--intern/opencolorio/ocio_shader_shared.hh41
-rw-r--r--intern/opensubdiv/CMakeLists.txt4
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output_gpu.h4
-rw-r--r--intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc647
-rw-r--r--intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h2465
-rw-r--r--intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl383
-rwxr-xr-xrelease/datafiles/blender_icons_update.py9
-rw-r--r--release/datafiles/colormanagement/config.ocio5
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default.c2
m---------release/scripts/addons0
-rw-r--r--release/scripts/modules/addon_utils.py7
-rw-r--r--release/scripts/modules/bl_keymap_utils/io.py2
-rw-r--r--release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py1
-rw-r--r--release/scripts/modules/bl_keymap_utils/versioning.py18
-rw-r--r--release/scripts/modules/rna_keymap_ui.py4
-rw-r--r--release/scripts/modules/rna_manual_reference.py68
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py298
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py157
-rw-r--r--release/scripts/startup/bl_operators/wm.py40
-rw-r--r--release/scripts/startup/bl_ui/properties_animviz.py68
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curves.py12
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py4
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py3
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py3
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py16
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py10
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py104
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh2
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_brush.h2
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/BKE_curves.h8
-rw-r--r--source/blender/blenkernel/BKE_curves.hh166
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh89
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h6
-rw-r--r--source/blender/blenkernel/BKE_idprop.h2
-rw-r--r--source/blender/blenkernel/BKE_idtype.h2
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h29
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh.h29
-rw-r--r--source/blender/blenkernel/BKE_modifier.h8
-rw-r--r--source/blender/blenkernel/BKE_node.h3
-rw-r--r--source/blender/blenkernel/BKE_paint.h4
-rw-r--r--source/blender/blenkernel/BKE_spline.hh50
-rw-r--r--source/blender/blenkernel/BKE_subdiv_modifier.h12
-rw-r--r--source/blender/blenkernel/BKE_volume.h3
-rw-r--r--source/blender/blenkernel/BKE_writeffmpeg.h4
-rw-r--r--source/blender/blenkernel/CMakeLists.txt6
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc24
-rw-r--r--source/blender/blenkernel/intern/action.c2
-rw-r--r--source/blender/blenkernel/intern/anim_data.c2
-rw-r--r--source/blender/blenkernel/intern/anim_path.c4
-rw-r--r--source/blender/blenkernel/intern/anim_visualization.c5
-rw-r--r--source/blender/blenkernel/intern/armature_update.c2
-rw-r--r--source/blender/blenkernel/intern/asset_library_test.cc2
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh10
-rw-r--r--source/blender/blenkernel/intern/blendfile_link_append.c11
-rw-r--r--source/blender/blenkernel/intern/brush.c4
-rw-r--r--source/blender/blenkernel/intern/camera.c25
-rw-r--r--source/blender/blenkernel/intern/constraint.c8
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/curve.cc18
-rw-r--r--source/blender/blenkernel/intern/curve_bevel.c2
-rw-r--r--source/blender/blenkernel/intern/curve_convert.c4
-rw-r--r--source/blender/blenkernel/intern/curve_deform.c4
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc211
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc11
-rw-r--r--source/blender/blenkernel/intern/curves.cc221
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc394
-rw-r--r--source/blender/blenkernel/intern/customdata.cc12
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c8
-rw-r--r--source/blender/blenkernel/intern/displist.cc12
-rw-r--r--source/blender/blenkernel/intern/effect.c4
-rw-r--r--source/blender/blenkernel/intern/fluid.c2
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc213
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc521
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc12
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc12
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc12
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc34
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc157
-rw-r--r--source/blender/blenkernel/intern/idtype.c10
-rw-r--r--source/blender/blenkernel/intern/image.c4
-rw-r--r--source/blender/blenkernel/intern/image_gpu.cc29
-rw-r--r--source/blender/blenkernel/intern/image_partial_update.cc4
-rw-r--r--source/blender/blenkernel/intern/ipo.c2
-rw-r--r--source/blender/blenkernel/intern/key.c26
-rw-r--r--source/blender/blenkernel/intern/layer_utils.c14
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c4
-rw-r--r--source/blender/blenkernel/intern/lib_override.c136
-rw-r--r--source/blender/blenkernel/intern/lib_override_proxy_conversion.c4
-rw-r--r--source/blender/blenkernel/intern/lib_query.c8
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c4
-rw-r--r--source/blender/blenkernel/intern/main.c4
-rw-r--r--source/blender/blenkernel/intern/material.c18
-rw-r--r--source/blender/blenkernel/intern/mesh.cc42
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc2
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc37
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc98
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.c6
-rw-r--r--source/blender/blenkernel/intern/mesh_tessellate.c32
-rw-r--r--source/blender/blenkernel/intern/mesh_validate.cc (renamed from source/blender/blenkernel/intern/mesh_validate.c)80
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.c10
-rw-r--r--source/blender/blenkernel/intern/modifier.c25
-rw-r--r--source/blender/blenkernel/intern/node.cc31
-rw-r--r--source/blender/blenkernel/intern/object.cc58
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc2
-rw-r--r--source/blender/blenkernel/intern/object_update.c6
-rw-r--r--source/blender/blenkernel/intern/paint.c24
-rw-r--r--source/blender/blenkernel/intern/paint_toolslots.c3
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc67
-rw-r--r--source/blender/blenkernel/intern/scene.c48
-rw-r--r--source/blender/blenkernel/intern/softbody.c6
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc13
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc46
-rw-r--r--source/blender/blenkernel/intern/subdiv_mesh.c78
-rw-r--r--source/blender/blenkernel/intern/subdiv_modifier.c71
-rw-r--r--source/blender/blenkernel/intern/vfont.c109
-rw-r--r--source/blender/blenkernel/intern/volume.cc13
-rw-r--r--source/blender/blenkernel/intern/volume_render.cc1
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c439
-rw-r--r--source/blender/blenlib/BLI_array.h11
-rw-r--r--source/blender/blenlib/BLI_array.hh18
-rw-r--r--source/blender/blenlib/BLI_bounds.hh75
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh20
-rw-r--r--source/blender/blenlib/BLI_hash_tables.hh1
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh36
-rw-r--r--source/blender/blenlib/BLI_index_mask_ops.hh60
-rw-r--r--source/blender/blenlib/BLI_math_base.hh104
-rw-r--r--source/blender/blenlib/BLI_math_geom.h3
-rw-r--r--source/blender/blenlib/BLI_math_vec_types.hh35
-rw-r--r--source/blender/blenlib/BLI_math_vector.hh255
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh28
-rw-r--r--source/blender/blenlib/BLI_path_util.h2
-rw-r--r--source/blender/blenlib/BLI_span.hh22
-rw-r--r--source/blender/blenlib/BLI_vector.hh18
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh2
-rw-r--r--source/blender/blenlib/CMakeLists.txt8
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc10
-rw-r--r--source/blender/blenlib/intern/index_mask.cc163
-rw-r--r--source/blender/blenlib/intern/math_geom.c12
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc2
-rw-r--r--source/blender/blenlib/intern/string.c3
-rw-r--r--source/blender/blenlib/tests/BLI_array_test.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_bounds_test.cc57
-rw-r--r--source/blender/blenlib/tests/BLI_index_mask_test.cc149
-rw-r--r--source/blender/blenlib/tests/BLI_math_base_test.cc21
-rw-r--r--source/blender/blenlib/tests/BLI_math_vec_types_test.cc25
-rw-r--r--source/blender/blenlib/tests/BLI_memory_utils_test.cc25
-rw-r--r--source/blender/blenlib/tests/BLI_span_test.cc3
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc3
-rw-r--r--source/blender/blenloader/intern/blend_validate.c15
-rw-r--r--source/blender/blenloader/intern/readfile.c22
-rw-r--r--source/blender/blenloader/intern/versioning_250.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c15
-rw-r--r--source/blender/blenloader/intern/versioning_common.cc2
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c2
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c54
-rw-r--r--source/blender/blenloader/intern/writefile.c18
-rw-r--r--source/blender/blentranslation/BLT_translation.h8
-rw-r--r--source/blender/bmesh/CMakeLists.txt1
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.cc496
-rw-r--r--source/blender/bmesh/intern/bmesh_query.c8
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c2
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc22
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc18
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc20
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc12
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc12
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc2
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c124
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.c5
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c254
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h48
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c6
-rw-r--r--source/blender/draw/engines/image/image_buffer_cache.hh131
-rw-r--r--source/blender/draw/engines/image/image_drawing_mode.hh74
-rw-r--r--source/blender/draw/engines/image/image_engine.cc1
-rw-r--r--source/blender/draw/engines/image/image_instance_data.hh34
-rw-r--r--source/blender/draw/engines/image/image_usage.hh49
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c12
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_motion_path.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c4
-rw-r--r--source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl2
-rw-r--r--source/blender/draw/engines/select/select_draw_utils.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_opaque.c4
-rw-r--r--source/blender/draw/intern/DRW_gpu_wrapper.hh5
-rw-r--r--source/blender/draw/intern/draw_cache.c26
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc18
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c8
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc157
-rw-r--r--source/blender/draw/intern/draw_common.c2
-rw-r--r--source/blender/draw/intern/draw_manager.c2
-rw-r--r--source/blender/draw/intern/draw_manager_data.c2
-rw-r--r--source/blender/draw/intern/draw_subdivision.h9
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc10
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc166
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc40
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_lib.glsl7
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl61
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl6
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl2
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl21
-rw-r--r--source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl2
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c6
-rw-r--r--source/blender/editors/animation/anim_filter.c6
-rw-r--r--source/blender/editors/animation/anim_markers.c13
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c49
-rw-r--r--source/blender/editors/animation/keyframes_keylist.cc48
-rw-r--r--source/blender/editors/armature/pose_edit.c68
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference.cc6
-rw-r--r--source/blender/editors/curve/editcurve.c16
-rw-r--r--source/blender/editors/curve/editcurve_add.c32
-rw-r--r--source/blender/editors/curve/editcurve_undo.c2
-rw-r--r--source/blender/editors/curve/editfont.c4
-rw-r--r--source/blender/editors/curves/CMakeLists.txt2
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc57
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc59
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c6
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c18
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c31
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c32
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c14
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c23
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c37
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h9
-rw-r--r--source/blender/editors/include/ED_curves.h11
-rw-r--r--source/blender/editors/include/ED_curves_sculpt.h17
-rw-r--r--source/blender/editors/include/ED_keyframes_keylist.h12
-rw-r--r--source/blender/editors/include/ED_render.h12
-rw-r--r--source/blender/editors/include/ED_screen.h6
-rw-r--r--source/blender/editors/include/ED_util.h2
-rw-r--r--source/blender/editors/include/ED_view3d.h57
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/interface/interface.c10
-rw-r--r--source/blender/editors/interface/interface_context_menu.c2
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c3
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c6
-rw-r--r--source/blender/editors/interface/interface_handlers.c202
-rw-r--r--source/blender/editors/interface/interface_icons.c20
-rw-r--r--source/blender/editors/interface/interface_layout.c4
-rw-r--r--source/blender/editors/interface/interface_ops.c21
-rw-r--r--source/blender/editors/interface/interface_panel.c19
-rw-r--r--source/blender/editors/interface/interface_query.c2
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c2
-rw-r--r--source/blender/editors/interface/interface_template_list.cc2
-rw-r--r--source/blender/editors/interface/interface_templates.c10
-rw-r--r--source/blender/editors/interface/interface_widgets.c4
-rw-r--r--source/blender/editors/interface/view2d_ops.c2
-rw-r--r--source/blender/editors/io/io_obj.c6
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c2
-rw-r--r--source/blender/editors/mesh/editmesh_bisect.c4
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c2
-rw-r--r--source/blender/editors/mesh/editmesh_loopcut.c9
-rw-r--r--source/blender/editors/mesh/editmesh_select.c4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c13
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c27
-rw-r--r--source/blender/editors/mesh/mesh_data.c9
-rw-r--r--source/blender/editors/mesh/meshtools.c1
-rw-r--r--source/blender/editors/object/CMakeLists.txt3
-rw-r--r--source/blender/editors/object/object_add.cc (renamed from source/blender/editors/object/object_add.c)508
-rw-r--r--source/blender/editors/object/object_bake_api.c18
-rw-r--r--source/blender/editors/object/object_constraint.c25
-rw-r--r--source/blender/editors/object/object_data_transform.c10
-rw-r--r--source/blender/editors/object/object_edit.c130
-rw-r--r--source/blender/editors/object/object_hook.c4
-rw-r--r--source/blender/editors/object/object_intern.h3
-rw-r--r--source/blender/editors/object/object_modes.c4
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c16
-rw-r--r--source/blender/editors/object/object_remesh.cc2
-rw-r--r--source/blender/editors/object/object_transform.c14
-rw-r--r--source/blender/editors/object/object_utils.c2
-rw-r--r--source/blender/editors/physics/particle_edit.c8
-rw-r--r--source/blender/editors/render/render_internal.cc9
-rw-r--r--source/blender/editors/render/render_opengl.cc48
-rw-r--r--source/blender/editors/render/render_preview.cc367
-rw-r--r--source/blender/editors/render/render_shading.cc4
-rw-r--r--source/blender/editors/screen/area.c31
-rw-r--r--source/blender/editors/screen/glutil.c3
-rw-r--r--source/blender/editors/screen/screen_ops.c29
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt17
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.h16
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc411
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.cc (renamed from source/blender/editors/sculpt_paint/paint_image.c)461
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc3
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_ops_paint.cc531
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h13
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c62
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c36
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c24
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c2
-rw-r--r--source/blender/editors/space_action/action_data.c2
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c4
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c4
-rw-r--r--source/blender/editors/space_console/console_ops.c15
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_graph/graph_slider_ops.c22
-rw-r--r--source/blender/editors/space_graph/graph_utils.c1
-rw-r--r--source/blender/editors/space_graph/space_graph.c2
-rw-r--r--source/blender/editors/space_info/info_stats.cc4
-rw-r--r--source/blender/editors/space_nla/nla_channels.c2
-rw-r--r--source/blender/editors/space_nla/nla_draw.c2
-rw-r--r--source/blender/editors/space_node/drawnode.cc20
-rw-r--r--source/blender/editors/space_node/node_context_path.cc2
-rw-r--r--source/blender/editors/space_node/node_draw.cc47
-rw-r--r--source/blender/editors/space_node/node_edit.cc14
-rw-r--r--source/blender/editors/space_node/node_intern.hh8
-rw-r--r--source/blender/editors/space_node/node_relationships.cc22
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.cc33
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc20
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh2
-rw-r--r--source/blender/editors/space_outliner/outliner_select.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc118
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.cc8
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library.cc5
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.hh4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.cc2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c5
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc2
-rw-r--r--source/blender/editors/space_text/text_autocomplete.c4
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c25
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.c28
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_dolly.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_move.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_ndof.c80
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_roll.c29
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_rotate.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom_border.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c59
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c33
-rw-r--r--source/blender/editors/transform/transform.c32
-rw-r--r--source/blender/editors/transform/transform.h8
-rw-r--r--source/blender/editors/transform/transform_constraints.c3
-rw-r--r--source/blender/editors/transform/transform_convert.c6
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object_texspace.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c20
-rw-r--r--source/blender/editors/transform/transform_gizmo_2d.c1
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c4
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c2
-rw-r--r--source/blender/editors/transform/transform_mode.c2
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c14
-rw-r--r--source/blender/editors/transform/transform_ops.c2
-rw-r--r--source/blender/editors/transform/transform_orientations.c4
-rw-r--r--source/blender/editors/transform/transform_snap.c9
-rw-r--r--source/blender/editors/transform/transform_snap_object.c8
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_transverts.c7
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/editors/util/ed_util_ops.cc2
-rw-r--r--source/blender/editors/util/numinput.c24
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c6
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh2
-rw-r--r--source/blender/functions/FN_multi_function_procedure_optimization.hh6
-rw-r--r--source/blender/functions/intern/field.cc4
-rw-r--r--source/blender/geometry/intern/realize_instances.cc39
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c3
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c86
-rw-r--r--source/blender/gpu/CMakeLists.txt26
-rw-r--r--source/blender/gpu/GPU_batch.h1
-rw-r--r--source/blender/gpu/GPU_texture.h13
-rw-r--r--source/blender/gpu/intern/gpu_batch.cc10
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c2
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh8
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc6
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency_private.h2
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc80
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh15
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c4
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc3
-rw-r--r--source/blender/gpu/opengl/gl_context.hh1
-rw-r--r--source/blender/gpu/opengl/gl_immediate.cc2
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc150
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh4
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh1
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp4
-rw-r--r--source/blender/imbuf/IMB_imbuf.h15
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h2
-rw-r--r--source/blender/imbuf/intern/anim_movie.c145
-rw-r--r--source/blender/imbuf/intern/divers.c83
-rw-r--r--source/blender/imbuf/intern/indexer.c15
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp84
-rw-r--r--source/blender/imbuf/intern/util.c2
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc2
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.cc6
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.cc2
-rw-r--r--source/blender/io/collada/CMakeLists.txt4
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.cc2
-rw-r--r--source/blender/io/usd/intern/usd_capi_import.cc3
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc2
-rw-r--r--source/blender/io/usd/intern/usd_reader_curve.cc4
-rw-r--r--source/blender/io/usd/intern/usd_reader_nurbs.cc4
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_io.hh2
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh2
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc2
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.hh11
-rw-r--r--source/blender/makesdna/DNA_ID.h18
-rw-r--r--source/blender/makesdna/DNA_ID_enums.h80
-rw-r--r--source/blender/makesdna/DNA_action_types.h12
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h6
-rw-r--r--source/blender/makesdna/DNA_brush_types.h4
-rw-r--r--source/blender/makesdna/DNA_curve_types.h8
-rw-r--r--source/blender/makesdna/DNA_curves_types.h49
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h4
-rw-r--r--source/blender/makesdna/DNA_fluid_types.h2
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h3
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h2
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h20
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h6
-rw-r--r--source/blender/makesdna/DNA_node_types.h5
-rw-r--r--source/blender/makesdna/DNA_object_types.h22
-rw-r--r--source/blender/makesdna/DNA_scene_types.h13
-rw-r--r--source/blender/makesdna/DNA_userdef_enums.h9
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h1
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h9
-rw-r--r--source/blender/makesdna/DNA_xr_types.h26
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_enum_items.h8
-rw-r--r--source/blender/makesrna/intern/rna_ID.c14
-rw-r--r--source/blender/makesrna/intern/rna_access.c106
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c2
-rw-r--r--source/blender/makesrna/intern/rna_animviz.c43
-rw-r--r--source/blender/makesrna/intern/rna_brush.c11
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c2
-rw-r--r--source/blender/makesrna/intern/rna_context.c1
-rw-r--r--source/blender/makesrna/intern/rna_curve.c6
-rw-r--r--source/blender/makesrna/intern/rna_curves.c17
-rw-r--r--source/blender/makesrna/intern/rna_define.c17
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c11
-rw-r--r--source/blender/makesrna/intern/rna_image.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_key.c10
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_mask.c3
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c1
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c24
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c27
-rw-r--r--source/blender/makesrna/intern/rna_object.c10
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c24
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c21
-rw-r--r--source/blender/makesrna/intern/rna_space.c10
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c7
-rw-r--r--source/blender/makesrna/intern/rna_volume.c1
-rw-r--r--source/blender/makesrna/intern/rna_wm.c117
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c36
-rw-r--r--source/blender/makesrna/intern/rna_xr.c402
-rw-r--r--source/blender/modifiers/intern/MOD_array.c4
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c2
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c3
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc6
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c4
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c6
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c307
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c14
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c2
-rw-r--r--source/blender/modifiers/intern/MOD_util.c2
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c3
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc6
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c6
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh2
-rw-r--r--source/blender/nodes/NOD_socket_search_link.hh2
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_compare.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_color.cc2
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc126
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc59
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc63
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc50
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc31
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc1119
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc119
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc76
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc35
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc9
-rw-r--r--source/blender/nodes/intern/node_common.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc2
-rw-r--r--source/blender/python/gpu/gpu_py_state.c6
-rw-r--r--source/blender/python/intern/bpy_capi_utils.h4
-rw-r--r--source/blender/python/mathutils/mathutils.c4
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c6
-rw-r--r--source/blender/render/intern/bake.c4
-rw-r--r--source/blender/render/intern/multires_bake.c6
-rw-r--r--source/blender/render/intern/pipeline.c11
-rw-r--r--source/blender/sequencer/intern/effects.c2
-rw-r--r--source/blender/sequencer/intern/proxy.c15
-rw-r--r--source/blender/windowmanager/WM_api.h26
-rw-r--r--source/blender/windowmanager/WM_keymap.h72
-rw-r--r--source/blender/windowmanager/WM_toolsystem.h1
-rw-r--r--source/blender/windowmanager/WM_types.h153
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c48
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c8
-rw-r--r--source/blender/windowmanager/intern/wm.c1
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c16
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c152
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c232
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c79
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c112
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c36
-rw-r--r--source/blender/windowmanager/intern/wm_keymap_utils.c69
-rw-r--r--source/blender/windowmanager/intern/wm_operator_utils.c8
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c3
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c1
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c4
-rw-r--r--source/blender/windowmanager/intern/wm_toolsystem.c46
-rw-r--r--source/blender/windowmanager/intern/wm_window.c84
-rw-r--r--source/blender/windowmanager/wm.h8
-rw-r--r--source/blender/windowmanager/wm_event_types.h41
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_action.c78
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actionmap.c47
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h8
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c5
-rw-r--r--source/creator/CMakeLists.txt15
m---------source/tools0
-rw-r--r--tests/check_deprecated.py131
-rw-r--r--tests/python/bl_keymap_validate.py212
686 files changed, 18349 insertions, 8026 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 75f5795db68..9f8800fe303 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -866,7 +866,7 @@ if(WITH_CYCLES_DEVICE_HIP)
endif()
#-----------------------------------------------------------------------------
-# Check check if submodules are cloned
+# Check if submodules are cloned.
if(WITH_INTERNATIONAL)
file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/datafiles/locale")
@@ -889,8 +889,8 @@ if(WITH_PYTHON)
# Do this before main 'platform_*' checks,
# because UNIX will search for the old Python paths which may not exist.
# giving errors about missing paths before this case is met.
- if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.9")
- message(FATAL_ERROR "At least Python 3.9 is required to build, but found Python ${PYTHON_VERSION}")
+ if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.10")
+ message(FATAL_ERROR "At least Python 3.10 is required to build, but found Python ${PYTHON_VERSION}")
endif()
file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/scripts/addons")
@@ -1886,7 +1886,6 @@ elseif(WITH_CYCLES_STANDALONE)
add_subdirectory(intern/glew-mx)
add_subdirectory(intern/guardedalloc)
add_subdirectory(intern/libc_compat)
- add_subdirectory(intern/numaapi)
add_subdirectory(intern/sky)
add_subdirectory(intern/cycles)
diff --git a/GNUmakefile b/GNUmakefile
index b100bf9290e..575f3e904df 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -51,20 +51,13 @@ Testing Targets
* test:
Run automated tests with ctest.
- * test_cmake:
- Runs our own cmake file checker
- which detects errors in the cmake file list definitions
- * test_pep8:
- Checks all python script are pep8
- which are tagged to use the stricter formatting
- * test_deprecated:
- Checks for deprecation tags in our code which may need to be removed
Static Source Code Checking
Not associated with building Blender.
* check_cppcheck: Run blender source through cppcheck (C & C++).
* check_clang_array: Run blender source through clang array checking script (C & C++).
+ * check_deprecated: Check if there is any deprecated code to remove.
* check_splint: Run blenders source through splint (C only).
* check_sparse: Run blenders source through sparse (C only).
* check_smatch: Run blenders source through smatch (C only).
@@ -73,6 +66,10 @@ Static Source Code Checking
using one of the accepted licenses in 'doc/license/SPDX-license-identifiers.txt'
Append with 'SHOW_HEADERS=1' to show all unique headers
which can be useful for spotting license irregularities.
+ * check_cmake: Runs our own cmake file checker which detects errors in the cmake file list definitions.
+ * check_pep8: Checks all Python script are pep8 which are tagged to use the stricter formatting.
+ * check_mypy: Checks all Python scripts using mypy,
+ see: source/tools/check_source/check_mypy_config.py scripts which are included.
Spell Checkers
This runs the spell checker from the developer tools repositor.
@@ -400,20 +397,6 @@ package_archive: .FORCE
test: .FORCE
@$(PYTHON) ./build_files/utils/make_test.py "$(BUILD_DIR)"
-# run pep8 check check on scripts we distribute.
-test_pep8: .FORCE
- @$(PYTHON) tests/python/pep8.py > test_pep8.log 2>&1
- @echo "written: test_pep8.log"
-
-# run some checks on our CMAKE files.
-test_cmake: .FORCE
- @$(PYTHON) build_files/cmake/cmake_consistency_check.py > test_cmake_consistency.log 2>&1
- @echo "written: test_cmake_consistency.log"
-
-# run deprecation tests, see if we have anything to remove.
-test_deprecated: .FORCE
- @$(PYTHON) tests/check_deprecated.py
-
# -----------------------------------------------------------------------------
# Project Files
@@ -491,11 +474,23 @@ check_descriptions: .FORCE
@$(BLENDER_BIN) --background -noaudio --factory-startup --python \
"$(BLENDER_DIR)/source/tools/check_source/check_descriptions.py"
+check_deprecated: .FORCE
+ @PYTHONIOENCODING=utf_8 $(PYTHON) \
+ source/tools/check_source/check_deprecated.py
+
check_licenses: .FORCE
@PYTHONIOENCODING=utf_8 $(PYTHON) \
"$(BLENDER_DIR)/source/tools/check_source/check_licenses.py" \
"--show-headers=$(SHOW_HEADERS)"
+check_pep8: .FORCE
+ @PYTHONIOENCODING=utf_8 $(PYTHON) \
+ tests/python/pep8.py
+
+check_cmake: .FORCE
+ @PYTHONIOENCODING=utf_8 $(PYTHON) \
+ source/tools/check_source/check_cmake_consistency.py
+
# -----------------------------------------------------------------------------
# Utilities
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 3d0f6cacb56..41c6fa495d8 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -366,7 +366,7 @@ CLANG_FORMAT_VERSION_MEX="14.0"
PYTHON_VERSION="3.10.2"
PYTHON_VERSION_SHORT="3.10"
-PYTHON_VERSION_MIN="3.9"
+PYTHON_VERSION_MIN="3.10"
PYTHON_VERSION_MEX="3.12"
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
PYTHON_FORCE_BUILD=false
diff --git a/build_files/cmake/Modules/FindFFmpeg.cmake b/build_files/cmake/Modules/FindFFmpeg.cmake
index 216dd2a12b6..c7a2e51adaa 100644
--- a/build_files/cmake/Modules/FindFFmpeg.cmake
+++ b/build_files/cmake/Modules/FindFFmpeg.cmake
@@ -79,4 +79,6 @@ mark_as_advanced(
unset(_ffmpeg_SEARCH_DIRS)
unset(_ffmpeg_LIBRARIES)
-unset(_ffmpeg_INCLUDE_DIR)
+# In cmake version 3.21 and up, we can instead use the NO_CACHE option for
+# find_path so we don't need to clear it from the cache here.
+unset(_ffmpeg_INCLUDE_DIR CACHE)
diff --git a/build_files/cmake/Modules/FindOSL.cmake b/build_files/cmake/Modules/FindOSL.cmake
index 612fd9feecc..ab5de53d3c9 100644
--- a/build_files/cmake/Modules/FindOSL.cmake
+++ b/build_files/cmake/Modules/FindOSL.cmake
@@ -72,6 +72,7 @@ FIND_PATH(OSL_SHADER_DIR
/usr/include/OSL/
PATH_SUFFIXES
share/OSL/shaders
+ shaders
)
# handle the QUIETLY and REQUIRED arguments and set OSL_FOUND to TRUE if
@@ -95,6 +96,7 @@ ENDIF()
MARK_AS_ADVANCED(
OSL_INCLUDE_DIR
+ OSL_SHADER_DIR
)
FOREACH(COMPONENT ${_osl_FIND_COMPONENTS})
STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
diff --git a/build_files/cmake/Modules/FindOpenColorIO.cmake b/build_files/cmake/Modules/FindOpenColorIO.cmake
index f290f8d63cd..8e152008ea7 100644
--- a/build_files/cmake/Modules/FindOpenColorIO.cmake
+++ b/build_files/cmake/Modules/FindOpenColorIO.cmake
@@ -83,12 +83,14 @@ ENDIF()
MARK_AS_ADVANCED(
OPENCOLORIO_INCLUDE_DIR
OPENCOLORIO_LIBRARY
- OPENCOLORIO_OPENCOLORIO_LIBRARY
- OPENCOLORIO_TINYXML_LIBRARY
- OPENCOLORIO_YAML-CPP_LIBRARY
OPENCOLORIO_VERSION
)
+FOREACH(COMPONENT ${_opencolorio_FIND_COMPONENTS})
+ STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
+ MARK_AS_ADVANCED(OPENCOLORIO_${UPPERCOMPONENT}_LIBRARY)
+ENDFOREACH()
+
UNSET(COMPONENT)
UNSET(UPPERCOMPONENT)
UNSET(_opencolorio_FIND_COMPONENTS)
diff --git a/build_files/cmake/Modules/FindOpenEXR.cmake b/build_files/cmake/Modules/FindOpenEXR.cmake
index 792bb127f99..f772ef4e1ff 100644
--- a/build_files/cmake/Modules/FindOpenEXR.cmake
+++ b/build_files/cmake/Modules/FindOpenEXR.cmake
@@ -29,14 +29,6 @@ ENDIF()
# Old versions (before 2.0?) do not have any version string, just assuming this should be fine though.
SET(_openexr_libs_ver_init "2.0")
-SET(_openexr_FIND_COMPONENTS
- Half
- Iex
- IlmImf
- IlmThread
- Imath
-)
-
SET(_openexr_SEARCH_DIRS
${OPENEXR_ROOT_DIR}
/opt/lib/openexr
@@ -89,6 +81,24 @@ UNSET(_openexr_libs_ver_init)
STRING(REGEX REPLACE "([0-9]+)[.]([0-9]+).*" "\\1_\\2" _openexr_libs_ver ${OPENEXR_VERSION})
+# Different library names in 3.0, and Imath and Half moved out.
+IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0")
+ SET(_openexr_FIND_COMPONENTS
+ Iex
+ IlmThread
+ OpenEXR
+ OpenEXRCore
+ )
+ELSE()
+ SET(_openexr_FIND_COMPONENTS
+ Half
+ Iex
+ IlmImf
+ IlmThread
+ Imath
+ )
+ENDIF()
+
SET(_openexr_LIBRARIES)
FOREACH(COMPONENT ${_openexr_FIND_COMPONENTS})
STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
@@ -107,6 +117,57 @@ ENDFOREACH()
UNSET(_openexr_libs_ver)
+IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0")
+ # For OpenEXR 3.x, we also need to find the now separate Imath library.
+ # For simplicity we add it to the OpenEXR includes and libraries, as we
+ # have no direct dependency on Imath and it's simpler to support both
+ # 2.x and 3.x this way.
+
+ # Find include directory
+ FIND_PATH(IMATH_INCLUDE_DIR
+ NAMES
+ Imath/ImathMath.h
+ HINTS
+ ${_openexr_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+ )
+
+ # Find version
+ FIND_FILE(_imath_config
+ NAMES
+ ImathConfig.h
+ PATHS
+ ${IMATH_INCLUDE_DIR}/Imath
+ NO_DEFAULT_PATH
+ )
+
+ # Find line with version, extract string, and format for library suffix.
+ FILE(STRINGS "${_imath_config}" _imath_build_specification
+ REGEX "^[ \t]*#define[ \t]+IMATH_VERSION_STRING[ \t]+\"[.0-9]+\".*$")
+ STRING(REGEX REPLACE ".*#define[ \t]+IMATH_VERSION_STRING[ \t]+\"([.0-9]+)\".*"
+ "\\1" _imath_libs_ver ${_imath_build_specification})
+ STRING(REGEX REPLACE "([0-9]+)[.]([0-9]+).*" "\\1_\\2" _imath_libs_ver ${_imath_libs_ver})
+
+ # Find library, with or without version number.
+ FIND_LIBRARY(IMATH_LIBRARY
+ NAMES
+ Imath-${_imath_libs_ver} Imath
+ NAMES_PER_DIR
+ HINTS
+ ${_openexr_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+ )
+ LIST(APPEND _openexr_LIBRARIES "${IMATH_LIBRARY}")
+
+ # In cmake version 3.21 and up, we can instead use the NO_CACHE option for
+ # FIND_FILE so we don't need to clear it from the cache here.
+ UNSET(_imath_config CACHE)
+ UNSET(_imath_libs_ver)
+ UNSET(_imath_build_specification)
+ENDIF()
+
# handle the QUIETLY and REQUIRED arguments and set OPENEXR_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
@@ -115,13 +176,25 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR DEFAULT_MSG
IF(OPENEXR_FOUND)
SET(OPENEXR_LIBRARIES ${_openexr_LIBRARIES})
- # Both include paths are needed because of dummy OSL headers mixing #include <OpenEXR/foo.h> and #include <foo.h> :(
- SET(OPENEXR_INCLUDE_DIRS ${OPENEXR_INCLUDE_DIR} ${OPENEXR_INCLUDE_DIR}/OpenEXR)
+ # Both include paths are needed because of dummy OSL headers mixing
+ # #include <OpenEXR/foo.h> and #include <foo.h>, as well as Alembic
+ # include <half.h> directly.
+ SET(OPENEXR_INCLUDE_DIRS
+ ${OPENEXR_INCLUDE_DIR}
+ ${OPENEXR_INCLUDE_DIR}/OpenEXR)
+
+ IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0")
+ LIST(APPEND OPENEXR_INCLUDE_DIRS
+ ${IMATH_INCLUDE_DIR}
+ ${IMATH_INCLUDE_DIR}/Imath)
+ ENDIF()
ENDIF()
MARK_AS_ADVANCED(
OPENEXR_INCLUDE_DIR
OPENEXR_VERSION
+ IMATH_INCLUDE_DIR
+ IMATH_LIBRARY
)
FOREACH(COMPONENT ${_openexr_FIND_COMPONENTS})
STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
diff --git a/build_files/cmake/Modules/FindOpenImageDenoise.cmake b/build_files/cmake/Modules/FindOpenImageDenoise.cmake
index becd2f0cffe..a7f03fefd24 100644
--- a/build_files/cmake/Modules/FindOpenImageDenoise.cmake
+++ b/build_files/cmake/Modules/FindOpenImageDenoise.cmake
@@ -106,6 +106,7 @@ ENDIF()
MARK_AS_ADVANCED(
OPENIMAGEDENOISE_INCLUDE_DIR
+ OPENIMAGEDENOISE_LIBRARY
)
FOREACH(COMPONENT ${_openimagedenoise_FIND_COMPONENTS})
diff --git a/build_files/cmake/Modules/FindOpenImageIO.cmake b/build_files/cmake/Modules/FindOpenImageIO.cmake
index 27a56647eac..0e8742ef2ed 100644
--- a/build_files/cmake/Modules/FindOpenImageIO.cmake
+++ b/build_files/cmake/Modules/FindOpenImageIO.cmake
@@ -44,6 +44,8 @@ FIND_LIBRARY(OPENIMAGEIO_LIBRARY
lib64 lib
)
+set(_openimageio_LIBRARIES ${OPENIMAGEIO_LIBRARY})
+
FIND_FILE(OPENIMAGEIO_IDIFF
NAMES
idiff
@@ -53,14 +55,47 @@ FIND_FILE(OPENIMAGEIO_IDIFF
bin
)
+# Additionally find util library if needed. In old versions this library was
+# included in libOpenImageIO and linking to both would duplicate symbols. In
+# new versions we need to link to both.
+FIND_FILE(_openimageio_export
+ NAMES
+ export.h
+ PATHS
+ ${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO
+ NO_DEFAULT_PATH
+)
+
+# Use existence of OIIO_UTIL_API to check if it's a separate lib.
+FILE(STRINGS "${_openimageio_export}" _openimageio_util_define
+ REGEX "^[ \t]*#[ \t]*define[ \t]+OIIO_UTIL_API.*$")
+
+IF(_openimageio_util_define)
+ FIND_LIBRARY(OPENIMAGEIO_UTIL_LIBRARY
+ NAMES
+ OpenImageIO_Util
+ HINTS
+ ${_openimageio_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+ )
+
+ LIST(APPEND _openimageio_LIBRARIES ${OPENIMAGEIO_UTIL_LIBRARY})
+ENDIF()
+
+# In cmake version 3.21 and up, we can instead use the NO_CACHE option for
+# FIND_FILE so we don't need to clear it from the cache here.
+UNSET(_openimageio_export CACHE)
+UNSET(_openimageio_util_define)
+
# handle the QUIETLY and REQUIRED arguments and set OPENIMAGEIO_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenImageIO DEFAULT_MSG
- OPENIMAGEIO_LIBRARY OPENIMAGEIO_INCLUDE_DIR)
+ _openimageio_LIBRARIES OPENIMAGEIO_INCLUDE_DIR)
IF(OPENIMAGEIO_FOUND)
- SET(OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARY})
+ SET(OPENIMAGEIO_LIBRARIES ${_openimageio_LIBRARIES})
SET(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO_INCLUDE_DIR})
IF(EXISTS ${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO/pugixml.hpp)
SET(OPENIMAGEIO_PUGIXML_FOUND TRUE)
@@ -74,7 +109,9 @@ ENDIF()
MARK_AS_ADVANCED(
OPENIMAGEIO_INCLUDE_DIR
OPENIMAGEIO_LIBRARY
+ OPENIMAGEIO_UTIL_LIBRARY
OPENIMAGEIO_IDIFF
)
UNSET(_openimageio_SEARCH_DIRS)
+UNSET(_openimageio_LIBRARIES)
diff --git a/build_files/cmake/cmake_consistency_check.py b/build_files/cmake/cmake_consistency_check.py
deleted file mode 100755
index ec31b4cdfe3..00000000000
--- a/build_files/cmake/cmake_consistency_check.py
+++ /dev/null
@@ -1,387 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-
-# Note: this code should be cleaned up / refactored.
-
-import sys
-if sys.version_info.major < 3:
- print("\nPython3.x needed, found %s.\nAborting!\n" %
- sys.version.partition(" ")[0])
- sys.exit(1)
-
-import os
-from os.path import (
- dirname,
- join,
- normpath,
- splitext,
-)
-
-from cmake_consistency_check_config import (
- IGNORE_SOURCE,
- IGNORE_SOURCE_MISSING,
- IGNORE_CMAKE,
- UTF8_CHECK,
- SOURCE_DIR,
- BUILD_DIR,
-)
-
-from typing import (
- Callable,
- Dict,
- Generator,
- Iterator,
- List,
- Optional,
- Tuple,
-)
-
-
-global_h = set()
-global_c = set()
-global_refs: Dict[str, List[Tuple[str, int]]] = {}
-
-# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping.
-IGNORE_SOURCE_MISSING_FLAT = [
- (k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING
- for ignore_path in ig_list
-]
-
-# Ignore cmake file, path pairs.
-global_ignore_source_missing: Dict[str, List[str]] = {}
-for k, v in IGNORE_SOURCE_MISSING_FLAT:
- global_ignore_source_missing.setdefault(k, []).append(v)
-del IGNORE_SOURCE_MISSING_FLAT
-
-
-def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None:
- file_handle = open(f, 'r')
- data = file_handle.readlines()
- file_handle.close()
-
- l = data[i]
- ws = l[:len(l) - len(l.lstrip())]
-
- data[i] = "%s%s\n" % (ws, text)
-
- file_handle = open(f, 'w')
- file_handle.writelines(data)
- file_handle.close()
-
-
-def source_list(
- path: str,
- filename_check: Optional[Callable[[str], bool]] = None,
-) -> Generator[str, None, None]:
- for dirpath, dirnames, filenames in os.walk(path):
- # skip '.git'
- dirnames[:] = [d for d in dirnames if not d.startswith(".")]
-
- for filename in filenames:
- if filename_check is None or filename_check(filename):
- yield os.path.join(dirpath, filename)
-
-
-# extension checking
-def is_cmake(filename: str) -> bool:
- ext = splitext(filename)[1]
- return (ext == ".cmake") or (filename == "CMakeLists.txt")
-
-
-def is_c_header(filename: str) -> bool:
- ext = splitext(filename)[1]
- return (ext in {".h", ".hpp", ".hxx", ".hh"})
-
-
-def is_c(filename: str) -> bool:
- ext = splitext(filename)[1]
- return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".metal"})
-
-
-def is_c_any(filename: str) -> bool:
- return is_c(filename) or is_c_header(filename)
-
-
-def cmake_get_src(f: str) -> None:
-
- sources_h = []
- sources_c = []
-
- filen = open(f, "r", encoding="utf8")
- it: Optional[Iterator[str]] = iter(filen)
- found = False
- i = 0
- # print(f)
-
- def is_definition(l: str, f: str, i: int, name: str) -> bool:
- if l.startswith("unset("):
- return False
-
- if ('set(%s' % name) in l or ('set(' in l and l.endswith(name)):
- if len(l.split()) > 1:
- raise Exception("strict formatting not kept 'set(%s*' %s:%d" % (name, f, i))
- return True
-
- if ("list(APPEND %s" % name) in l or ('list(APPEND ' in l and l.endswith(name)):
- if l.endswith(")"):
- raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i))
- return True
- return False
-
- while it is not None:
- context_name = ""
- while it is not None:
- i += 1
- try:
- l = next(it)
- except StopIteration:
- it = None
- break
- l = l.strip()
- if not l.startswith("#"):
- found = is_definition(l, f, i, "SRC")
- if found:
- context_name = "SRC"
- break
- found = is_definition(l, f, i, "INC")
- if found:
- context_name = "INC"
- break
-
- if found:
- cmake_base = dirname(f)
- cmake_base_bin = os.path.join(BUILD_DIR, os.path.relpath(cmake_base, SOURCE_DIR))
-
- # Find known missing sources list (if we have one).
- f_rel = os.path.relpath(f, SOURCE_DIR)
- f_rel_key = f_rel
- if os.sep != "/":
- f_rel_key = f_rel_key.replace(os.sep, "/")
- local_ignore_source_missing = global_ignore_source_missing.get(f_rel_key, [])
-
- while it is not None:
- i += 1
- try:
- l = next(it)
- except StopIteration:
- it = None
- break
-
- l = l.strip()
-
- if not l.startswith("#"):
-
- # Remove in-line comments.
- l = l.split(" # ")[0].rstrip()
-
- if ")" in l:
- if l.strip() != ")":
- raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
- break
-
- # replace dirs
- l = l.replace("${CMAKE_SOURCE_DIR}", SOURCE_DIR)
- l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
- l = l.replace("${CMAKE_CURRENT_BINARY_DIR}", cmake_base_bin)
- l = l.strip('"')
-
- if not l:
- pass
- elif l in local_ignore_source_missing:
- local_ignore_source_missing.remove(l)
- elif l.startswith("$"):
- if context_name == "SRC":
- # assume if it ends with context_name we know about it
- if not l.split("}")[0].endswith(context_name):
- print("Can't use var '%s' %s:%d" % (l, f, i))
- elif len(l.split()) > 1:
- raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
- else:
- new_file = normpath(join(cmake_base, l))
-
- if context_name == "SRC":
- if is_c_header(new_file):
- sources_h.append(new_file)
- global_refs.setdefault(new_file, []).append((f, i))
- elif is_c(new_file):
- sources_c.append(new_file)
- global_refs.setdefault(new_file, []).append((f, i))
- elif l in {"PARENT_SCOPE", }:
- # cmake var, ignore
- pass
- elif new_file.endswith(".list"):
- pass
- elif new_file.endswith(".def"):
- pass
- elif new_file.endswith(".cl"): # opencl
- pass
- elif new_file.endswith(".cu"): # cuda
- pass
- elif new_file.endswith(".osl"): # open shading language
- pass
- elif new_file.endswith(".glsl"):
- pass
- else:
- raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
-
- elif context_name == "INC":
- if new_file.startswith(BUILD_DIR):
- # assume generated path
- pass
- elif os.path.isdir(new_file):
- new_path_rel = os.path.relpath(new_file, cmake_base)
-
- if new_path_rel != l:
- print("overly relative path:\n %s:%d\n %s\n %s" % (f, i, l, new_path_rel))
-
- # # Save time. just replace the line
- # replace_line(f, i - 1, new_path_rel)
-
- else:
- raise Exception("non existent include %s:%d -> %s" % (f, i, new_file))
-
- # print(new_file)
-
- global_h.update(set(sources_h))
- global_c.update(set(sources_c))
- '''
- if not sources_h and not sources_c:
- raise Exception("No sources %s" % f)
-
- sources_h_fs = list(source_list(cmake_base, is_c_header))
- sources_c_fs = list(source_list(cmake_base, is_c))
- '''
- # find missing C files:
- '''
- for ff in sources_c_fs:
- if ff not in sources_c:
- print(" missing: " + ff)
- '''
-
- # reset
- del sources_h[:]
- del sources_c[:]
-
- filen.close()
-
-
-def is_ignore_source(f: str, ignore_used: List[bool]) -> bool:
- for index, ignore_path in enumerate(IGNORE_SOURCE):
- if ignore_path in f:
- ignore_used[index] = True
- return True
- return False
-
-
-def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool:
- for index, ignore_path in enumerate(IGNORE_CMAKE):
- if ignore_path in f:
- ignore_used[index] = True
- return True
- return False
-
-
-def main() -> None:
-
- print("Scanning:", SOURCE_DIR)
-
- ignore_used_source = [False] * len(IGNORE_SOURCE)
- ignore_used_cmake = [False] * len(IGNORE_CMAKE)
-
- for cmake in source_list(SOURCE_DIR, is_cmake):
- if not is_ignore_cmake(cmake, ignore_used_cmake):
- cmake_get_src(cmake)
-
- # First do stupid check, do these files exist?
- print("\nChecking for missing references:")
- is_err = False
- errs = []
- for f in (global_h | global_c):
- if f.startswith(BUILD_DIR):
- continue
-
- if not os.path.exists(f):
- refs = global_refs[f]
- if refs:
- for cf, i in refs:
- errs.append((cf, i))
- else:
- raise Exception("CMake references missing, internal error, aborting!")
- is_err = True
-
- errs.sort()
- errs.reverse()
- for cf, i in errs:
- print("%s:%d" % (cf, i))
- # Write a 'sed' script, useful if we get a lot of these
- # print("sed '%dd' '%s' > '%s.tmp' ; mv '%s.tmp' '%s'" % (i, cf, cf, cf, cf))
-
- if is_err:
- raise Exception("CMake references missing files, aborting!")
- del is_err
- del errs
-
- # now check on files not accounted for.
- print("\nC/C++ Files CMake does not know about...")
- for cf in sorted(source_list(SOURCE_DIR, is_c)):
- if not is_ignore_source(cf, ignore_used_source):
- if cf not in global_c:
- print("missing_c: ", cf)
-
- # Check if automake builds a corresponding .o file.
- '''
- if cf in global_c:
- out1 = os.path.splitext(cf)[0] + ".o"
- out2 = os.path.splitext(cf)[0] + ".Po"
- out2_dir, out2_file = out2 = os.path.split(out2)
- out2 = os.path.join(out2_dir, ".deps", out2_file)
- if not os.path.exists(out1) and not os.path.exists(out2):
- print("bad_c: ", cf)
- '''
-
- print("\nC/C++ Headers CMake does not know about...")
- for hf in sorted(source_list(SOURCE_DIR, is_c_header)):
- if not is_ignore_source(hf, ignore_used_source):
- if hf not in global_h:
- print("missing_h: ", hf)
-
- if UTF8_CHECK:
- # test encoding
- import traceback
- for files in (global_c, global_h):
- for f in sorted(files):
- if os.path.exists(f):
- # ignore outside of our source tree
- if "extern" not in f:
- i = 1
- try:
- for _ in open(f, "r", encoding="utf8"):
- i += 1
- except UnicodeDecodeError:
- print("Non utf8: %s:%d" % (f, i))
- if i > 1:
- traceback.print_exc()
-
- # Check ignores aren't stale
- print("\nCheck for unused 'IGNORE_SOURCE' paths...")
- for index, ignore_path in enumerate(IGNORE_SOURCE):
- if not ignore_used_source[index]:
- print("unused ignore: %r" % ignore_path)
-
- # Check ignores aren't stale
- print("\nCheck for unused 'IGNORE_SOURCE_MISSING' paths...")
- for k, v in sorted(global_ignore_source_missing.items()):
- for ignore_path in v:
- print("unused ignore: %r -> %r" % (ignore_path, k))
-
- # Check ignores aren't stale
- print("\nCheck for unused 'IGNORE_CMAKE' paths...")
- for index, ignore_path in enumerate(IGNORE_CMAKE):
- if not ignore_used_cmake[index]:
- print("unused ignore: %r" % ignore_path)
-
-
-if __name__ == "__main__":
- main()
diff --git a/build_files/cmake/cmake_consistency_check_config.py b/build_files/cmake/cmake_consistency_check_config.py
deleted file mode 100644
index 94e5ddb4289..00000000000
--- a/build_files/cmake/cmake_consistency_check_config.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-import os
-
-IGNORE_SOURCE = (
- "/test/",
- "/tests/gtests/",
- "/release/",
-
- # specific source files
- "extern/audaspace/",
-
- # Use for `WIN32` only.
- "source/creator/blender_launcher_win32.c",
-
- # specific source files
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp",
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp",
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp",
- "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.cpp",
- "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.cpp",
- "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.cpp",
- "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp",
- "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp",
-
- "doc/doxygen/doxygen.extern.h",
- "doc/doxygen/doxygen.intern.h",
- "doc/doxygen/doxygen.main.h",
- "doc/doxygen/doxygen.source.h",
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h",
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h",
- "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h",
- "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.h",
- "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.h",
- "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.h",
- "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h",
- "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h",
-)
-
-# Ignore cmake file, path pairs.
-IGNORE_SOURCE_MISSING = (
- ( # Use for cycles stand-alone.
- "intern/cycles/util/CMakeLists.txt", (
- "../../third_party/numaapi/include",
- )),
- ( # Use for `WITH_NANOVDB`.
- "intern/cycles/kernel/CMakeLists.txt", (
- "nanovdb/util/CSampleFromVoxels.h",
- "nanovdb/util/SampleFromVoxels.h",
- "nanovdb/NanoVDB.h",
- "nanovdb/CNanoVDB.h",
- ),
- ),
-)
-
-IGNORE_CMAKE = (
- "extern/audaspace/CMakeLists.txt",
-)
-
-UTF8_CHECK = True
-
-SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))))
-
-# doesn't have to exist, just use as reference
-BUILD_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(SOURCE_DIR, "..", "build"))))
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 96c402760b7..77d1db97997 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -345,6 +345,7 @@ if(WITH_BOOST)
find_package(IcuLinux)
endif()
mark_as_advanced(Boost_DIR) # why doesn't boost do this?
+ mark_as_advanced(Boost_INCLUDE_DIR) # why doesn't boost do this?
endif()
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
@@ -848,3 +849,45 @@ if(WITH_COMPILER_CCACHE)
set(WITH_COMPILER_CCACHE OFF)
endif()
endif()
+
+# On some platforms certain atomic operations are not possible with assembly and/or intrinsics and
+# they are emulated in software with locks. For example, on armel there is no intrinsics to grant
+# 64 bit atomic operations and STL library uses libatomic to offload software emulation of atomics
+# to.
+# This function will check whether libatomic is required and if so will configure linker flags.
+# If atomic operations are possible without libatomic then linker flags are left as-is.
+function(CONFIGURE_ATOMIC_LIB_IF_NEEDED)
+ # Source which is used to enforce situation when software emulation of atomics is required.
+ # Assume that using 64bit integer gives a definitive asnwer (as in, if 64bit atomic operations
+ # are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible.
+ set(_source
+ "#include <atomic>
+ #include <cstdint>
+ int main(int argc, char **argv) {
+ std::atomic<uint64_t> uint64; uint64++;
+ return 0;
+ }")
+
+ include(CheckCXXSourceCompiles)
+ check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITHOUT_LIBATOMIC)
+
+ if(NOT ATOMIC_OPS_WITHOUT_LIBATOMIC)
+ # Compilation of the test program has failed.
+ # Try it again with -latomic to see if this is what is needed, or whether something else is
+ # going on.
+
+ set(CMAKE_REQUIRED_LIBRARIES atomic)
+ check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITH_LIBATOMIC)
+
+ if(ATOMIC_OPS_WITH_LIBATOMIC)
+ set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -latomic" PARENT_SCOPE)
+ else()
+ # Atomic operations are required part of Blender and it is not possible to process forward.
+ # We expect that either standard library or libatomic will make atomics to work. If both
+ # cases has failed something fishy o na bigger scope is going on.
+ message(FATAL_ERROR "Failed to detect required configuration for atomic operations")
+ endif()
+ endif()
+endfunction()
+
+CONFIGURE_ATOMIC_LIB_IF_NEEDED()
diff --git a/doc/python_api/static/css/version_switch.css b/doc/python_api/static/css/version_switch.css
index 360ff2eea0e..adb80b01c0a 100644
--- a/doc/python_api/static/css/version_switch.css
+++ b/doc/python_api/static/css/version_switch.css
@@ -1,5 +1,6 @@
/* Override RTD theme */
.rst-versions {
+ display: none;
border-top: 0px;
overflow: visible;
}
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
index de3ca099696..69bb45119a6 100644
--- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
@@ -177,7 +177,7 @@ void FFMPEGReader::init(int stream)
// get a decoder and open it
#ifndef FFMPEG_OLD_CODE
- AVCodec* aCodec = avcodec_find_decoder(m_formatCtx->streams[m_stream]->codecpar->codec_id);
+ const AVCodec* aCodec = avcodec_find_decoder(m_formatCtx->streams[m_stream]->codecpar->codec_id);
if(!aCodec)
AUD_THROW(FileException, "File couldn't be read, no decoder found with ffmpeg.");
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
index 10517d1d596..32eb2330594 100644
--- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
@@ -23,6 +23,7 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avio.h>
+#include <libavutil/channel_layout.h>
}
AUD_NAMESPACE_BEGIN
@@ -171,66 +172,66 @@ FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container fo
if(avformat_alloc_output_context2(&m_formatCtx, nullptr, formats[format], filename.c_str()) < 0)
AUD_THROW(FileException, "File couldn't be written, format couldn't be found with ffmpeg.");
- AVOutputFormat* outputFmt = m_formatCtx->oformat;
+ const AVOutputFormat* outputFmt = m_formatCtx->oformat;
if(!outputFmt) {
avformat_free_context(m_formatCtx);
AUD_THROW(FileException, "File couldn't be written, output format couldn't be found with ffmpeg.");
}
- outputFmt->audio_codec = AV_CODEC_ID_NONE;
+ AVCodecID audio_codec = AV_CODEC_ID_NONE;
switch(codec)
{
case CODEC_AAC:
- outputFmt->audio_codec = AV_CODEC_ID_AAC;
+ audio_codec = AV_CODEC_ID_AAC;
break;
case CODEC_AC3:
- outputFmt->audio_codec = AV_CODEC_ID_AC3;
+ audio_codec = AV_CODEC_ID_AC3;
break;
case CODEC_FLAC:
- outputFmt->audio_codec = AV_CODEC_ID_FLAC;
+ audio_codec = AV_CODEC_ID_FLAC;
break;
case CODEC_MP2:
- outputFmt->audio_codec = AV_CODEC_ID_MP2;
+ audio_codec = AV_CODEC_ID_MP2;
break;
case CODEC_MP3:
- outputFmt->audio_codec = AV_CODEC_ID_MP3;
+ audio_codec = AV_CODEC_ID_MP3;
break;
case CODEC_OPUS:
- outputFmt->audio_codec = AV_CODEC_ID_OPUS;
+ audio_codec = AV_CODEC_ID_OPUS;
break;
case CODEC_PCM:
switch(specs.format)
{
case FORMAT_U8:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_U8;
+ audio_codec = AV_CODEC_ID_PCM_U8;
break;
case FORMAT_S16:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
+ audio_codec = AV_CODEC_ID_PCM_S16LE;
break;
case FORMAT_S24:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_S24LE;
+ audio_codec = AV_CODEC_ID_PCM_S24LE;
break;
case FORMAT_S32:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_S32LE;
+ audio_codec = AV_CODEC_ID_PCM_S32LE;
break;
case FORMAT_FLOAT32:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_F32LE;
+ audio_codec = AV_CODEC_ID_PCM_F32LE;
break;
case FORMAT_FLOAT64:
- outputFmt->audio_codec = AV_CODEC_ID_PCM_F64LE;
+ audio_codec = AV_CODEC_ID_PCM_F64LE;
break;
default:
- outputFmt->audio_codec = AV_CODEC_ID_NONE;
+ audio_codec = AV_CODEC_ID_NONE;
break;
}
break;
case CODEC_VORBIS:
- outputFmt->audio_codec = AV_CODEC_ID_VORBIS;
+ audio_codec = AV_CODEC_ID_VORBIS;
break;
default:
- outputFmt->audio_codec = AV_CODEC_ID_NONE;
+ audio_codec = AV_CODEC_ID_NONE;
break;
}
@@ -268,10 +269,10 @@ FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container fo
try
{
- if(outputFmt->audio_codec == AV_CODEC_ID_NONE)
+ if(audio_codec == AV_CODEC_ID_NONE)
AUD_THROW(FileException, "File couldn't be written, audio codec not found with ffmpeg.");
- AVCodec* codec = avcodec_find_encoder(outputFmt->audio_codec);
+ const AVCodec* codec = avcodec_find_encoder(audio_codec);
if(!codec)
AUD_THROW(FileException, "File couldn't be written, audio encoder couldn't be found with ffmpeg.");
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 75ff7114dd7..3248ef0dcda 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -33,15 +33,19 @@ else()
endif()
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
- list(APPEND LIBRARIES ${GLUT_LIBRARIES})
+ add_definitions(${GL_DEFINITIONS})
+ list(APPEND INC_SYS
+ ${GLEW_INCLUDE_DIR}
+ ${SDL2_INCLUDE_DIRS}
+ )
+ list(APPEND LIBRARIES
+ ${CYCLES_GL_LIBRARIES}
+ ${SDL2_LIBRARIES}
+ )
endif()
-list(APPEND LIBRARIES ${CYCLES_GL_LIBRARIES})
-
# Common configuration.
-add_definitions(${GL_DEFINITIONS})
-
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
@@ -55,6 +59,18 @@ if(WITH_CYCLES_STANDALONE)
oiio_output_driver.cpp
oiio_output_driver.h
)
+
+ if(WITH_CYCLES_STANDALONE_GUI)
+ list(APPEND SRC
+ opengl/display_driver.cpp
+ opengl/display_driver.h
+ opengl/shader.cpp
+ opengl/shader.h
+ opengl/window.cpp
+ opengl/window.h
+ )
+ endif()
+
add_executable(cycles ${SRC} ${INC} ${INC_SYS})
unset(SRC)
@@ -69,6 +85,10 @@ if(WITH_CYCLES_STANDALONE)
# OpenImageDenoise uses BNNS from the Accelerate framework.
set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate")
endif()
+ if(WITH_CYCLES_STANDALONE_GUI)
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS
+ " -framework Cocoa -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework ForceFeedback -framework CoreVideo")
+ endif()
endif()
if(UNIX AND NOT APPLE)
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index 0e425ac3d8f..ef20f64debd 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -27,11 +27,10 @@
#include "app/oiio_output_driver.h"
#ifdef WITH_CYCLES_STANDALONE_GUI
-# include "util/view.h"
+# include "opengl/display_driver.h"
+# include "opengl/window.h"
#endif
-#include "app/cycles_xml.h"
-
CCL_NAMESPACE_BEGIN
struct Options {
@@ -117,7 +116,14 @@ static void session_init()
options.output_pass = "combined";
options.session = new Session(options.session_params, options.scene_params);
- if (!options.output_filepath.empty()) {
+#ifdef WITH_CYCLES_STANDALONE_GUI
+ if (!options.session_params.background) {
+ options.session->set_display_driver(make_unique<OpenGLDisplayDriver>(
+ window_opengl_context_enable, window_opengl_context_disable));
+ }
+ else
+#endif
+ if (!options.output_filepath.empty()) {
options.session->set_output_driver(make_unique<OIIOOutputDriver>(
options.output_filepath, options.output_pass, session_print));
}
@@ -126,7 +132,7 @@ static void session_init()
options.session->progress.set_update_callback(function_bind(&session_print_status));
#ifdef WITH_CYCLES_STANDALONE_GUI
else
- options.session->progress.set_update_callback(function_bind(&view_redraw));
+ options.session->progress.set_update_callback(function_bind(&window_redraw));
#endif
/* load scene */
@@ -191,10 +197,10 @@ static void display_info(Progress &progress)
sample_time,
interactive.c_str());
- view_display_info(str.c_str());
+ window_display_info(str.c_str());
if (options.show_help)
- view_display_help();
+ window_display_help();
}
static void display()
@@ -525,15 +531,15 @@ int main(int argc, const char **argv)
string title = "Cycles: " + path_filename(options.filepath);
/* init/exit are callback so they run while GL is initialized */
- view_main_loop(title.c_str(),
- options.width,
- options.height,
- session_init,
- session_exit,
- resize,
- display,
- keyboard,
- motion);
+ window_main_loop(title.c_str(),
+ options.width,
+ options.height,
+ session_init,
+ session_exit,
+ resize,
+ display,
+ keyboard,
+ motion);
}
#endif
diff --git a/intern/cycles/app/opengl/display_driver.cpp b/intern/cycles/app/opengl/display_driver.cpp
new file mode 100644
index 00000000000..8b99f3b6feb
--- /dev/null
+++ b/intern/cycles/app/opengl/display_driver.cpp
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "app/opengl/display_driver.h"
+#include "app/opengl/shader.h"
+
+#include "util/log.h"
+#include "util/string.h"
+
+#include <GL/glew.h>
+#include <SDL.h>
+
+CCL_NAMESPACE_BEGIN
+
+/* --------------------------------------------------------------------
+ * OpenGLDisplayDriver.
+ */
+
+OpenGLDisplayDriver::OpenGLDisplayDriver(const function<bool()> &gl_context_enable,
+ const function<void()> &gl_context_disable)
+ : gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable)
+{
+}
+
+OpenGLDisplayDriver::~OpenGLDisplayDriver()
+{
+}
+
+/* --------------------------------------------------------------------
+ * Update procedure.
+ */
+
+void OpenGLDisplayDriver::next_tile_begin()
+{
+ /* Assuming no tiles used in interactive display. */
+}
+
+bool OpenGLDisplayDriver::update_begin(const Params &params, int texture_width, int texture_height)
+{
+ /* Note that it's the responsibility of OpenGLDisplayDriver to ensure updating and drawing
+ * the texture does not happen at the same time. This is achieved indirectly.
+ *
+ * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
+ * This same lock is also held when do_draw() is called, which together ensure mutual
+ * exclusion.
+ *
+ * This locking is not performed on the Cycles side, because that would cause lock inversion. */
+ if (!gl_context_enable_()) {
+ return false;
+ }
+
+ if (gl_render_sync_) {
+ glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
+ }
+
+ if (!gl_texture_resources_ensure()) {
+ gl_context_disable_();
+ return false;
+ }
+
+ /* Update texture dimensions if needed. */
+ if (texture_.width != texture_width || texture_.height != texture_height) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0);
+ texture_.width = texture_width;
+ texture_.height = texture_height;
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
+ * avoid undefined content. */
+ texture_.need_clear = true;
+ }
+
+ /* Update PBO dimensions if needed.
+ *
+ * NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
+ * at a resolution divider 1. This was we don't need to recreate graphics interoperability
+ * objects which are costly and which are tied to the specific underlying buffer size.
+ * The downside of this approach is that when graphics interoperability is not used we are
+ * sending too much data to GPU when resolution divider is not 1. */
+ const int buffer_width = params.full_size.x;
+ const int buffer_height = params.full_size.y;
+ if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) {
+ const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ texture_.buffer_width = buffer_width;
+ texture_.buffer_height = buffer_height;
+ }
+
+ /* New content will be provided to the texture in one way or another, so mark this in a
+ * centralized place. */
+ texture_.need_update = true;
+
+ return true;
+}
+
+void OpenGLDisplayDriver::update_end()
+{
+ gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+
+ gl_context_disable_();
+}
+
+/* --------------------------------------------------------------------
+ * Texture buffer mapping.
+ */
+
+half4 *OpenGLDisplayDriver::map_texture_buffer()
+{
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
+
+ half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
+ glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
+ if (!mapped_rgba_pixels) {
+ LOG(ERROR) << "Error mapping OpenGLDisplayDriver pixel buffer object.";
+ }
+
+ if (texture_.need_clear) {
+ const int64_t texture_width = texture_.width;
+ const int64_t texture_height = texture_.height;
+ memset(reinterpret_cast<void *>(mapped_rgba_pixels),
+ 0,
+ texture_width * texture_height * sizeof(half4));
+ texture_.need_clear = false;
+ }
+
+ return mapped_rgba_pixels;
+}
+
+void OpenGLDisplayDriver::unmap_texture_buffer()
+{
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+}
+
+/* --------------------------------------------------------------------
+ * Graphics interoperability.
+ */
+
+OpenGLDisplayDriver::GraphicsInterop OpenGLDisplayDriver::graphics_interop_get()
+{
+ GraphicsInterop interop_dst;
+
+ interop_dst.buffer_width = texture_.buffer_width;
+ interop_dst.buffer_height = texture_.buffer_height;
+ interop_dst.opengl_pbo_id = texture_.gl_pbo_id;
+
+ interop_dst.need_clear = texture_.need_clear;
+ texture_.need_clear = false;
+
+ return interop_dst;
+}
+
+void OpenGLDisplayDriver::graphics_interop_activate()
+{
+ gl_context_enable_();
+}
+
+void OpenGLDisplayDriver::graphics_interop_deactivate()
+{
+ gl_context_disable_();
+}
+
+/* --------------------------------------------------------------------
+ * Drawing.
+ */
+
+void OpenGLDisplayDriver::clear()
+{
+ texture_.need_clear = true;
+}
+
+void OpenGLDisplayDriver::draw(const Params &params)
+{
+ /* See do_update_begin() for why no locking is required here. */
+ if (texture_.need_clear) {
+ /* Texture is requested to be cleared and was not yet cleared.
+ * Do early return which should be equivalent of drawing all-zero texture. */
+ return;
+ }
+
+ if (!gl_draw_resources_ensure()) {
+ return;
+ }
+
+ if (gl_upload_sync_) {
+ glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ display_shader_.bind(params.full_size.x, params.full_size.y);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
+
+ if (texture_.width != params.size.x || texture_.height != params.size.y) {
+ /* Resolution divider is different from 1, force nearest interpolation. */
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
+
+ texture_update_if_needed();
+ vertex_buffer_update(params);
+
+ GLuint vertex_array_object;
+ glGenVertexArrays(1, &vertex_array_object);
+ glBindVertexArray(vertex_array_object);
+
+ const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location();
+ const int position_attribute = display_shader_.get_position_attrib_location();
+
+ glEnableVertexAttribArray(texcoord_attribute);
+ glEnableVertexAttribArray(position_attribute);
+
+ glVertexAttribPointer(
+ texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
+ glVertexAttribPointer(position_attribute,
+ 2,
+ GL_FLOAT,
+ GL_FALSE,
+ 4 * sizeof(float),
+ (const GLvoid *)(sizeof(float) * 2));
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glDeleteVertexArrays(1, &vertex_array_object);
+
+ display_shader_.unbind();
+
+ glDisable(GL_BLEND);
+
+ gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+}
+
+bool OpenGLDisplayDriver::gl_draw_resources_ensure()
+{
+ if (!texture_.gl_id) {
+ /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
+ * can not continue. Note that this is not an unrecoverable error, so once the texture is known
+ * we will come back here and create all the GPU resources needed for draw. */
+ return false;
+ }
+
+ if (gl_draw_resource_creation_attempted_) {
+ return gl_draw_resources_created_;
+ }
+ gl_draw_resource_creation_attempted_ = true;
+
+ if (!vertex_buffer_) {
+ glGenBuffers(1, &vertex_buffer_);
+ if (!vertex_buffer_) {
+ LOG(ERROR) << "Error creating vertex buffer.";
+ return false;
+ }
+ }
+
+ gl_draw_resources_created_ = true;
+
+ return true;
+}
+
+void OpenGLDisplayDriver::gl_resources_destroy()
+{
+ gl_context_enable_();
+
+ if (vertex_buffer_ != 0) {
+ glDeleteBuffers(1, &vertex_buffer_);
+ }
+
+ if (texture_.gl_pbo_id) {
+ glDeleteBuffers(1, &texture_.gl_pbo_id);
+ texture_.gl_pbo_id = 0;
+ }
+
+ if (texture_.gl_id) {
+ glDeleteTextures(1, &texture_.gl_id);
+ texture_.gl_id = 0;
+ }
+
+ gl_context_disable_();
+}
+
+bool OpenGLDisplayDriver::gl_texture_resources_ensure()
+{
+ if (texture_.creation_attempted) {
+ return texture_.is_created;
+ }
+ texture_.creation_attempted = true;
+
+ DCHECK(!texture_.gl_id);
+ DCHECK(!texture_.gl_pbo_id);
+
+ /* Create texture. */
+ glGenTextures(1, &texture_.gl_id);
+ if (!texture_.gl_id) {
+ LOG(ERROR) << "Error creating texture.";
+ return false;
+ }
+
+ /* Configure the texture. */
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ /* Create PBO for the texture. */
+ glGenBuffers(1, &texture_.gl_pbo_id);
+ if (!texture_.gl_pbo_id) {
+ LOG(ERROR) << "Error creating texture pixel buffer object.";
+ return false;
+ }
+
+ /* Creation finished with a success. */
+ texture_.is_created = true;
+
+ return true;
+}
+
+void OpenGLDisplayDriver::texture_update_if_needed()
+{
+ if (!texture_.need_update) {
+ return;
+ }
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
+ glTexSubImage2D(
+ GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ texture_.need_update = false;
+}
+
+void OpenGLDisplayDriver::vertex_buffer_update(const Params &params)
+{
+ /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
+ * rendered. */
+ glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
+
+ float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
+ if (!vpointer) {
+ return;
+ }
+
+ vpointer[0] = 0.0f;
+ vpointer[1] = 0.0f;
+ vpointer[2] = params.full_offset.x;
+ vpointer[3] = params.full_offset.y;
+
+ vpointer[4] = 1.0f;
+ vpointer[5] = 0.0f;
+ vpointer[6] = (float)params.size.x + params.full_offset.x;
+ vpointer[7] = params.full_offset.y;
+
+ vpointer[8] = 1.0f;
+ vpointer[9] = 1.0f;
+ vpointer[10] = (float)params.size.x + params.full_offset.x;
+ vpointer[11] = (float)params.size.y + params.full_offset.y;
+
+ vpointer[12] = 0.0f;
+ vpointer[13] = 1.0f;
+ vpointer[14] = params.full_offset.x;
+ vpointer[15] = (float)params.size.y + params.full_offset.y;
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/opengl/display_driver.h b/intern/cycles/app/opengl/display_driver.h
new file mode 100644
index 00000000000..92578412d68
--- /dev/null
+++ b/intern/cycles/app/opengl/display_driver.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#pragma once
+
+#include <atomic>
+
+#include "app/opengl/shader.h"
+
+#include "session/display_driver.h"
+
+#include "util/function.h"
+#include "util/unique_ptr.h"
+
+CCL_NAMESPACE_BEGIN
+
+class OpenGLDisplayDriver : public DisplayDriver {
+ public:
+ /* Callbacks for enabling and disabling the OpenGL context. Must be provided to support enabling
+ * the context on the Cycles render thread independent of the main thread. */
+ OpenGLDisplayDriver(const function<bool()> &gl_context_enable,
+ const function<void()> &gl_context_disable);
+ ~OpenGLDisplayDriver();
+
+ virtual void graphics_interop_activate() override;
+ virtual void graphics_interop_deactivate() override;
+
+ virtual void clear() override;
+
+ void set_zoom(float zoom_x, float zoom_y);
+
+ protected:
+ virtual void next_tile_begin() override;
+
+ virtual bool update_begin(const Params &params, int texture_width, int texture_height) override;
+ virtual void update_end() override;
+
+ virtual half4 *map_texture_buffer() override;
+ virtual void unmap_texture_buffer() override;
+
+ virtual GraphicsInterop graphics_interop_get() override;
+
+ virtual void draw(const Params &params) override;
+
+ /* Make sure texture is allocated and its initial configuration is performed. */
+ bool gl_texture_resources_ensure();
+
+ /* Ensure all runtime GPU resources needed for drawing are allocated.
+ * Returns true if all resources needed for drawing are available. */
+ bool gl_draw_resources_ensure();
+
+ /* Destroy all GPU resources which are being used by this object. */
+ void gl_resources_destroy();
+
+ /* Update GPU texture dimensions and content if needed (new pixel data was provided).
+ *
+ * NOTE: The texture needs to be bound. */
+ void texture_update_if_needed();
+
+ /* Update vertex buffer with new coordinates of vertex positions and texture coordinates.
+ * This buffer is used to render texture in the viewport.
+ *
+ * NOTE: The buffer needs to be bound. */
+ void vertex_buffer_update(const Params &params);
+
+ /* Texture which contains pixels of the render result. */
+ struct {
+ /* Indicates whether texture creation was attempted and succeeded.
+ * Used to avoid multiple attempts of texture creation on GPU issues or GPU context
+ * misconfiguration. */
+ bool creation_attempted = false;
+ bool is_created = false;
+
+ /* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write
+ * pixels to it.
+ *
+ * NOTE: Allocated on the engine's context. */
+ uint gl_id = 0;
+ uint gl_pbo_id = 0;
+
+ /* Is true when new data was written to the PBO, meaning, the texture might need to be resized
+ * and new data is to be uploaded to the GPU. */
+ bool need_update = false;
+
+ /* Content of the texture is to be filled with zeroes. */
+ std::atomic<bool> need_clear = true;
+
+ /* Dimensions of the texture in pixels. */
+ int width = 0;
+ int height = 0;
+
+ /* Dimensions of the underlying PBO. */
+ int buffer_width = 0;
+ int buffer_height = 0;
+ } texture_;
+
+ OpenGLShader display_shader_;
+
+ /* Special track of whether GPU resources were attempted to be created, to avoid attempts of
+ * their re-creation on failure on every redraw. */
+ bool gl_draw_resource_creation_attempted_ = false;
+ bool gl_draw_resources_created_ = false;
+
+ /* Vertex buffer which hold vertices of a triangle fan which is textures with the texture
+ * holding the render result. */
+ uint vertex_buffer_ = 0;
+
+ void *gl_render_sync_ = nullptr;
+ void *gl_upload_sync_ = nullptr;
+
+ float2 zoom_ = make_float2(1.0f, 1.0f);
+
+ function<bool()> gl_context_enable_ = nullptr;
+ function<void()> gl_context_disable_ = nullptr;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/opengl/shader.cpp b/intern/cycles/app/opengl/shader.cpp
new file mode 100644
index 00000000000..9db9ea7fce9
--- /dev/null
+++ b/intern/cycles/app/opengl/shader.cpp
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include "app/opengl/shader.h"
+
+#include "util/log.h"
+#include "util/string.h"
+
+#include <GL/glew.h>
+
+CCL_NAMESPACE_BEGIN
+
+/* --------------------------------------------------------------------
+ * OpenGLShader.
+ */
+
+static const char *VERTEX_SHADER =
+ "#version 330\n"
+ "uniform vec2 fullscreen;\n"
+ "in vec2 texCoord;\n"
+ "in vec2 pos;\n"
+ "out vec2 texCoord_interp;\n"
+ "\n"
+ "vec2 normalize_coordinates()\n"
+ "{\n"
+ " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
+ "}\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
+ " texCoord_interp = texCoord;\n"
+ "}\n\0";
+
+static const char *FRAGMENT_SHADER =
+ "#version 330\n"
+ "uniform sampler2D image_texture;\n"
+ "in vec2 texCoord_interp;\n"
+ "out vec4 fragColor;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " vec4 rgba = texture(image_texture, texCoord_interp);\n"
+ /* Harcoded Rec.709 gamma, should use OpenColorIO eventually. */
+ " fragColor = pow(rgba, vec4(0.45, 0.45, 0.45, 1.0));\n"
+ "}\n\0";
+
+static void shader_print_errors(const char *task, const char *log, const char *code)
+{
+ LOG(ERROR) << "Shader: " << task << " error:";
+ LOG(ERROR) << "===== shader string ====";
+
+ stringstream stream(code);
+ string partial;
+
+ int line = 1;
+ while (getline(stream, partial, '\n')) {
+ if (line < 10) {
+ LOG(ERROR) << " " << line << " " << partial;
+ }
+ else {
+ LOG(ERROR) << line << " " << partial;
+ }
+ line++;
+ }
+ LOG(ERROR) << log;
+}
+
+static int compile_shader_program(void)
+{
+ const struct Shader {
+ const char *source;
+ const GLenum type;
+ } shaders[2] = {{VERTEX_SHADER, GL_VERTEX_SHADER}, {FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
+
+ const GLuint program = glCreateProgram();
+
+ for (int i = 0; i < 2; i++) {
+ const GLuint shader = glCreateShader(shaders[i].type);
+
+ string source_str = shaders[i].source;
+ const char *c_str = source_str.c_str();
+
+ glShaderSource(shader, 1, &c_str, NULL);
+ glCompileShader(shader);
+
+ GLint compile_status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
+
+ if (!compile_status) {
+ GLchar log[5000];
+ GLsizei length = 0;
+ glGetShaderInfoLog(shader, sizeof(log), &length, log);
+ shader_print_errors("compile", log, c_str);
+ return 0;
+ }
+
+ glAttachShader(program, shader);
+ }
+
+ /* Link output. */
+ glBindFragDataLocation(program, 0, "fragColor");
+
+ /* Link and error check. */
+ glLinkProgram(program);
+
+ GLint link_status;
+ glGetProgramiv(program, GL_LINK_STATUS, &link_status);
+ if (!link_status) {
+ GLchar log[5000];
+ GLsizei length = 0;
+ glGetShaderInfoLog(program, sizeof(log), &length, log);
+ shader_print_errors("linking", log, VERTEX_SHADER);
+ shader_print_errors("linking", log, FRAGMENT_SHADER);
+ return 0;
+ }
+
+ return program;
+}
+
+int OpenGLShader::get_position_attrib_location()
+{
+ if (position_attribute_location_ == -1) {
+ const uint shader_program = get_shader_program();
+ position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name);
+ }
+ return position_attribute_location_;
+}
+
+int OpenGLShader::get_tex_coord_attrib_location()
+{
+ if (tex_coord_attribute_location_ == -1) {
+ const uint shader_program = get_shader_program();
+ tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name);
+ }
+ return tex_coord_attribute_location_;
+}
+
+void OpenGLShader::bind(int width, int height)
+{
+ create_shader_if_needed();
+
+ if (!shader_program_) {
+ return;
+ }
+
+ glUseProgram(shader_program_);
+ glUniform1i(image_texture_location_, 0);
+ glUniform2f(fullscreen_location_, width, height);
+}
+
+void OpenGLShader::unbind()
+{
+}
+
+uint OpenGLShader::get_shader_program()
+{
+ return shader_program_;
+}
+
+void OpenGLShader::create_shader_if_needed()
+{
+ if (shader_program_ || shader_compile_attempted_) {
+ return;
+ }
+
+ shader_compile_attempted_ = true;
+
+ shader_program_ = compile_shader_program();
+ if (!shader_program_) {
+ return;
+ }
+
+ glUseProgram(shader_program_);
+
+ image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture");
+ if (image_texture_location_ < 0) {
+ LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform.";
+ destroy_shader();
+ return;
+ }
+
+ fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen");
+ if (fullscreen_location_ < 0) {
+ LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform.";
+ destroy_shader();
+ return;
+ }
+}
+
+void OpenGLShader::destroy_shader()
+{
+ glDeleteProgram(shader_program_);
+ shader_program_ = 0;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/opengl/shader.h b/intern/cycles/app/opengl/shader.h
new file mode 100644
index 00000000000..6ca121ca6ff
--- /dev/null
+++ b/intern/cycles/app/opengl/shader.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 OpenGL Foundation */
+
+#pragma once
+
+#include "util/types.h"
+
+CCL_NAMESPACE_BEGIN
+
+class OpenGLShader {
+ public:
+ static constexpr const char *position_attribute_name = "pos";
+ static constexpr const char *tex_coord_attribute_name = "texCoord";
+
+ OpenGLShader() = default;
+ virtual ~OpenGLShader() = default;
+
+ /* Get attribute location for position and texture coordinate respectively.
+ * NOTE: The shader needs to be bound to have access to those. */
+ int get_position_attrib_location();
+ int get_tex_coord_attrib_location();
+
+ void bind(int width, int height);
+ void unbind();
+
+ protected:
+ uint get_shader_program();
+
+ void create_shader_if_needed();
+ void destroy_shader();
+
+ /* Cached values of various OpenGL resources. */
+ int position_attribute_location_ = -1;
+ int tex_coord_attribute_location_ = -1;
+
+ uint shader_program_ = 0;
+ int image_texture_location_ = -1;
+ int fullscreen_location_ = -1;
+
+ /* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or
+ * linking has failed. Do not attempt to re-compile the shader. */
+ bool shader_compile_attempted_ = false;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/opengl/window.cpp b/intern/cycles/app/opengl/window.cpp
new file mode 100644
index 00000000000..7351ae3eecd
--- /dev/null
+++ b/intern/cycles/app/opengl/window.cpp
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "app/opengl/window.h"
+
+#include "util/string.h"
+#include "util/thread.h"
+#include "util/time.h"
+#include "util/version.h"
+
+#include <GL/glew.h>
+#include <SDL.h>
+
+CCL_NAMESPACE_BEGIN
+
+/* structs */
+
+struct Window {
+ WindowInitFunc initf = nullptr;
+ WindowExitFunc exitf = nullptr;
+ WindowResizeFunc resize = nullptr;
+ WindowDisplayFunc display = nullptr;
+ WindowKeyboardFunc keyboard = nullptr;
+ WindowMotionFunc motion = nullptr;
+
+ bool first_display = true;
+ bool redraw = false;
+
+ int mouseX = 0, mouseY = 0;
+ int mouseBut0 = 0, mouseBut2 = 0;
+
+ int width = 0, height = 0;
+
+ SDL_Window *window = nullptr;
+ SDL_GLContext gl_context = nullptr;
+ thread_mutex gl_context_mutex;
+} V;
+
+/* public */
+
+static void window_display_text(int x, int y, const char *text)
+{
+/* Not currently supported, need to add text rendering support. */
+#if 0
+ const char *c;
+
+ glRasterPos3f(x, y, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ printf("display %s\n", text);
+
+ for (c = text; *c != '\0'; c++) {
+ const uint8_t *bitmap = helvetica10_character_map[*c];
+ glBitmap(bitmap[0],
+ helvetica10_height,
+ helvetica10_x_offset,
+ helvetica10_y_offset,
+ bitmap[0],
+ 0.0f,
+ bitmap + 1);
+ }
+#else
+ static string last_text = "";
+
+ if (text != last_text) {
+ printf("%s\n", text);
+ last_text = text;
+ }
+#endif
+}
+
+void window_display_info(const char *info)
+{
+ const int height = 20;
+
+#if 0
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
+ glRectf(0.0f, V.height - height, V.width, V.height);
+ glDisable(GL_BLEND);
+
+ glColor3f(0.5f, 0.5f, 0.5f);
+#endif
+
+ window_display_text(10, 7 + V.height - height, info);
+
+#if 0
+ glColor3f(1.0f, 1.0f, 1.0f);
+#endif
+}
+
+void window_display_help()
+{
+ const int w = (int)((float)V.width / 1.15f);
+ const int h = (int)((float)V.height / 1.15f);
+
+ const int x1 = (V.width - w) / 2;
+#if 0
+ const int x2 = x1 + w;
+#endif
+
+ const int y1 = (V.height - h) / 2;
+ const int y2 = y1 + h;
+
+#if 0
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.8f);
+ glRectf(x1, y1, x2, y2);
+ glDisable(GL_BLEND);
+
+ glColor3f(0.8f, 0.8f, 0.8f);
+#endif
+
+ string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING;
+
+ window_display_text(x1 + 20, y2 - 20, info.c_str());
+ window_display_text(x1 + 20, y2 - 40, "(C) 2011-2016 Blender Foundation");
+ window_display_text(x1 + 20, y2 - 80, "Controls:");
+ window_display_text(x1 + 20, y2 - 100, "h: Info/Help");
+ window_display_text(x1 + 20, y2 - 120, "r: Reset");
+ window_display_text(x1 + 20, y2 - 140, "p: Pause");
+ window_display_text(x1 + 20, y2 - 160, "esc: Cancel");
+ window_display_text(x1 + 20, y2 - 180, "q: Quit program");
+
+ window_display_text(x1 + 20, y2 - 210, "i: Interactive mode");
+ window_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera");
+ window_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera");
+ window_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera");
+ window_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces");
+
+#if 0
+ glColor3f(1.0f, 1.0f, 1.0f);
+#endif
+}
+
+static void window_display()
+{
+ if (V.first_display) {
+ if (V.initf) {
+ V.initf();
+ }
+ if (V.exitf) {
+ atexit(V.exitf);
+ }
+
+ V.first_display = false;
+ }
+
+ window_opengl_context_enable();
+
+ glViewport(0, 0, V.width, V.height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClearColor(0.05f, 0.05f, 0.05f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, V.width, 0, V.height, -1, 1);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glRasterPos3f(0, 0, 0);
+
+ if (V.display)
+ V.display();
+
+ SDL_GL_SwapWindow(V.window);
+ window_opengl_context_disable();
+}
+
+static void window_reshape(int width, int height)
+{
+ if (V.width != width || V.height != height) {
+ if (V.resize) {
+ V.resize(width, height);
+ }
+ }
+
+ V.width = width;
+ V.height = height;
+}
+
+static bool window_keyboard(unsigned char key)
+{
+ if (V.keyboard)
+ V.keyboard(key);
+
+ if (key == 'q') {
+ if (V.exitf)
+ V.exitf();
+ return true;
+ }
+
+ return false;
+}
+
+static void window_mouse(int button, int state, int x, int y)
+{
+ if (button == SDL_BUTTON_LEFT) {
+ if (state == SDL_MOUSEBUTTONDOWN) {
+ V.mouseX = x;
+ V.mouseY = y;
+ V.mouseBut0 = 1;
+ }
+ else if (state == SDL_MOUSEBUTTONUP) {
+ V.mouseBut0 = 0;
+ }
+ }
+ else if (button == SDL_BUTTON_RIGHT) {
+ if (state == SDL_MOUSEBUTTONDOWN) {
+ V.mouseX = x;
+ V.mouseY = y;
+ V.mouseBut2 = 1;
+ }
+ else if (state == SDL_MOUSEBUTTONUP) {
+ V.mouseBut2 = 0;
+ }
+ }
+}
+
+static void window_motion(int x, int y)
+{
+ const int but = V.mouseBut0 ? 0 : 2;
+ const int distX = x - V.mouseX;
+ const int distY = y - V.mouseY;
+
+ if (V.motion)
+ V.motion(distX, distY, but);
+
+ V.mouseX = x;
+ V.mouseY = y;
+}
+
+bool window_opengl_context_enable()
+{
+ V.gl_context_mutex.lock();
+ SDL_GL_MakeCurrent(V.window, V.gl_context);
+ return true;
+}
+
+void window_opengl_context_disable()
+{
+ SDL_GL_MakeCurrent(V.window, nullptr);
+ V.gl_context_mutex.unlock();
+}
+
+void window_main_loop(const char *title,
+ int width,
+ int height,
+ WindowInitFunc initf,
+ WindowExitFunc exitf,
+ WindowResizeFunc resize,
+ WindowDisplayFunc display,
+ WindowKeyboardFunc keyboard,
+ WindowMotionFunc motion)
+{
+ V.width = width;
+ V.height = height;
+ V.first_display = true;
+ V.redraw = false;
+ V.initf = initf;
+ V.exitf = exitf;
+ V.resize = resize;
+ V.display = display;
+ V.keyboard = keyboard;
+ V.motion = motion;
+
+ SDL_Init(SDL_INIT_VIDEO);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
+ V.window = SDL_CreateWindow(title,
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width,
+ height,
+ SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
+ if (V.window == nullptr) {
+ fprintf(stderr, "Failed to create window: %s\n", SDL_GetError());
+ return;
+ }
+
+ SDL_RaiseWindow(V.window);
+
+ V.gl_context = SDL_GL_CreateContext(V.window);
+ glewInit();
+ SDL_GL_MakeCurrent(V.window, nullptr);
+
+ window_reshape(width, height);
+ window_display();
+
+ while (true) {
+ bool quit = false;
+ SDL_Event event;
+ while (!quit && SDL_PollEvent(&event)) {
+ if (event.type == SDL_TEXTINPUT) {
+ quit = window_keyboard(event.text.text[0]);
+ }
+ else if (event.type == SDL_MOUSEMOTION) {
+ window_motion(event.motion.x, event.motion.y);
+ }
+ else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
+ window_mouse(event.button.button, event.button.state, event.button.x, event.button.y);
+ }
+ else if (event.type == SDL_WINDOWEVENT) {
+ if (event.window.event == SDL_WINDOWEVENT_RESIZED ||
+ event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
+ window_reshape(event.window.data1, event.window.data2);
+ }
+ }
+ else if (event.type == SDL_QUIT) {
+ if (V.exitf) {
+ V.exitf();
+ }
+ quit = true;
+ }
+ }
+
+ if (quit) {
+ break;
+ }
+
+ if (V.redraw) {
+ V.redraw = false;
+ window_display();
+ }
+
+ SDL_WaitEventTimeout(NULL, 100);
+ }
+
+ SDL_GL_DeleteContext(V.gl_context);
+ SDL_DestroyWindow(V.window);
+ SDL_Quit();
+}
+
+void window_redraw()
+{
+ V.redraw = true;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/app/opengl/window.h b/intern/cycles/app/opengl/window.h
new file mode 100644
index 00000000000..531b5cab3fc
--- /dev/null
+++ b/intern/cycles/app/opengl/window.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#pragma once
+
+/* Functions to display a simple OpenGL window using SDL, simplified to the
+ * bare minimum we need to reduce boilerplate code in tests apps. */
+
+CCL_NAMESPACE_BEGIN
+
+typedef void (*WindowInitFunc)();
+typedef void (*WindowExitFunc)();
+typedef void (*WindowResizeFunc)(int width, int height);
+typedef void (*WindowDisplayFunc)();
+typedef void (*WindowKeyboardFunc)(unsigned char key);
+typedef void (*WindowMotionFunc)(int x, int y, int button);
+
+void window_main_loop(const char *title,
+ int width,
+ int height,
+ WindowInitFunc initf,
+ WindowExitFunc exitf,
+ WindowResizeFunc resize,
+ WindowDisplayFunc display,
+ WindowKeyboardFunc keyboard,
+ WindowMotionFunc motion);
+
+void window_display_info(const char *info);
+void window_display_help();
+void window_redraw();
+
+bool window_opengl_context_enable();
+void window_opengl_context_disable();
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 4e62bae6fe3..e3e5734c6b6 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -1514,9 +1514,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
row.prop(self, "peer_memory")
if compute_device_type == 'METAL':
- row = layout.row()
- row.use_property_split = True
- row.prop(self, "use_metalrt")
+ import platform
+ # MetalRT only works on Apple Silicon at present, pending argument encoding fixes on AMD
+ if platform.machine() == 'arm64':
+ row = layout.row()
+ row.use_property_split = True
+ row.prop(self, "use_metalrt")
def draw(self, context):
diff --git a/intern/cycles/blender/session.cpp b/intern/cycles/blender/session.cpp
index 8917c703700..5e9066da5de 100644
--- a/intern/cycles/blender/session.cpp
+++ b/intern/cycles/blender/session.cpp
@@ -493,8 +493,13 @@ void BlenderSession::render_frame_finish()
session->set_output_driver(nullptr);
session->full_buffer_written_cb = function_null;
- /* The display driver holds OpenGL resources which belong to an OpenGL context held by the render
- * engine on Blender side. Force destruction of those resources. */
+ /* The display driver is the source of drawing context for both drawing and possible graphics
+ * interop objects in the path trace. Once the frame is finished the OpenGL context might be
+ * freed form Blender side. Need to ensure that all GPU resources are freed prior to that
+ * point.
+ * Ideally would only do this when OpenGL context is actually destroyed, but there is no way to
+ * know when this happens (at least in the code at the time when this comment was written).
+ * The penalty of re-creating resources on every frame is unlikely to be noticed. */
display_driver_ = nullptr;
session->set_display_driver(nullptr);
diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp
index 9de507966d8..ec50ad9db9a 100644
--- a/intern/cycles/blender/shader.cpp
+++ b/intern/cycles/blender/shader.cpp
@@ -32,7 +32,8 @@ typedef map<string, ConvertNode *> ProxyMap;
void BlenderSync::find_shader(BL::ID &id, array<Node *> &used_shaders, Shader *default_shader)
{
- Shader *shader = (id) ? shader_map.find(id) : default_shader;
+ Shader *synced_shader = (id) ? shader_map.find(id) : nullptr;
+ Shader *shader = (synced_shader) ? synced_shader : default_shader;
used_shaders.push_back_slow(shader);
shader->tag_used(scene);
@@ -1573,18 +1574,13 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
}
}
-void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d)
+void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all)
{
- /* for auto refresh images */
- ImageManager *image_manager = scene->image_manager;
- const int frame = b_scene.frame_current();
- const bool auto_refresh_update = image_manager->set_animation_frame_update(frame);
-
shader_map.pre_sync();
- sync_world(b_depsgraph, b_v3d, auto_refresh_update);
- sync_lights(b_depsgraph, auto_refresh_update);
- sync_materials(b_depsgraph, auto_refresh_update);
+ sync_world(b_depsgraph, b_v3d, update_all);
+ sync_lights(b_depsgraph, update_all);
+ sync_materials(b_depsgraph, update_all);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp
index 0b11af2dbf9..d4949a5ff30 100644
--- a/intern/cycles/blender/sync.cpp
+++ b/intern/cycles/blender/sync.cpp
@@ -246,7 +246,12 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
int height,
void **python_thread_state)
{
- if (!has_updates_) {
+ /* For auto refresh images. */
+ ImageManager *image_manager = scene->image_manager;
+ const int frame = b_scene.frame_current();
+ const bool auto_refresh_update = image_manager->set_animation_frame_update(frame);
+
+ if (!has_updates_ && !auto_refresh_update) {
return;
}
@@ -261,7 +266,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
sync_view_layer(b_view_layer);
sync_integrator(b_view_layer, background);
sync_film(b_view_layer, b_v3d);
- sync_shaders(b_depsgraph, b_v3d);
+ sync_shaders(b_depsgraph, b_v3d, auto_refresh_update);
sync_images();
geometry_synced.clear(); /* use for objects and motion sync */
diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h
index d92efb80a5d..5cc18452ac1 100644
--- a/intern/cycles/blender/sync.h
+++ b/intern/cycles/blender/sync.h
@@ -114,7 +114,7 @@ class BlenderSync {
/* Shader */
array<Node *> find_used_shaders(BL::Object &b_ob);
void sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all);
- void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d);
+ void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all);
void sync_nodes(Shader *shader, BL::ShaderNodeTree &b_ntree);
/* Object */
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index c964fbe0d72..6ad64d684c0 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -479,26 +479,22 @@ else()
endif()
###########################################################################
-# GLUT
+# SDL
###########################################################################
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
- if(MSVC AND EXISTS ${_cycles_lib_dir})
- add_definitions(-DFREEGLUT_STATIC -DFREEGLUT_LIB_PRAGMAS=0)
- set(GLUT_LIBRARIES "${_cycles_lib_dir}/opengl/lib/freeglut_static.lib")
- set(GLUT_INCLUDE_DIR "${_cycles_lib_dir}/opengl/include")
- else()
- find_package(GLUT)
+ # We can't use the version from the Blender precompiled libraries because
+ # it does not include the video subsystem.
+ find_package(SDL2)
- if(NOT GLUT_FOUND)
- set(WITH_CYCLES_STANDALONE_GUI OFF)
- message(STATUS "GLUT not found, disabling Cycles standalone GUI")
- endif()
+ if(NOT SDL2_FOUND)
+ set(WITH_CYCLES_STANDALONE_GUI OFF)
+ message(STATUS "SDL not found, disabling Cycles standalone GUI")
endif()
include_directories(
SYSTEM
- ${GLUT_INCLUDE_DIR}
+ ${SDL2_INCLUDE_DIRS}
)
endif()
diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp
index 158a789db5a..612c391f7d5 100644
--- a/intern/cycles/device/cpu/device_impl.cpp
+++ b/intern/cycles/device/cpu/device_impl.cpp
@@ -191,7 +191,7 @@ device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_
void CPUDevice::const_copy_to(const char *name, void *host, size_t size)
{
-#if WITH_EMBREE
+#ifdef WITH_EMBREE
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));
diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h
index 00269ac287c..9afef3789af 100644
--- a/intern/cycles/device/hip/device_impl.h
+++ b/intern/cycles/device/hip/device_impl.h
@@ -12,8 +12,6 @@
# ifdef WITH_HIP_DYNLOAD
# include "hipew.h"
-# else
-# include "util/opengl.h"
# endif
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm
index 593c9c3cf06..c01f51fb506 100644
--- a/intern/cycles/device/metal/device_impl.mm
+++ b/intern/cycles/device/metal/device_impl.mm
@@ -77,11 +77,11 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
}
case METAL_GPU_APPLE: {
max_threads_per_threadgroup = 512;
+ use_metalrt = info.use_metalrt;
break;
}
}
- use_metalrt = info.use_metalrt;
if (auto metalrt = getenv("CYCLES_METALRT")) {
use_metalrt = (atoi(metalrt) != 0);
}
diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp
index 220d4c9ffa2..eb12b0a6a11 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -54,14 +54,7 @@ PathTrace::PathTrace(Device *device,
PathTrace::~PathTrace()
{
- /* Destroy any GPU resource which was used for graphics interop.
- * Need to have access to the PathTraceDisplay as it is the only source of drawing context which
- * is used for interop. */
- if (display_) {
- for (auto &&path_trace_work : path_trace_works_) {
- path_trace_work->destroy_gpu_resources(display_.get());
- }
- }
+ destroy_gpu_resources();
}
void PathTrace::load_kernels()
@@ -559,6 +552,11 @@ void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver)
void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver)
{
+ /* The display driver is the source of the drawing context which might be used by
+ * path trace works. Make sure there is no graphics interop using resources from
+ * the old display, as it might no longer be available after this call. */
+ destroy_gpu_resources();
+
if (driver) {
display_ = make_unique<PathTraceDisplay>(move(driver));
}
@@ -1075,6 +1073,18 @@ bool PathTrace::has_denoised_result() const
return render_state_.has_denoised_result;
}
+void PathTrace::destroy_gpu_resources()
+{
+ /* Destroy any GPU resource which was used for graphics interop.
+ * Need to have access to the PathTraceDisplay as it is the only source of drawing context which
+ * is used for interop. */
+ if (display_) {
+ for (auto &&path_trace_work : path_trace_works_) {
+ path_trace_work->destroy_gpu_resources(display_.get());
+ }
+ }
+}
+
/* --------------------------------------------------------------------
* Report generation.
*/
diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h
index 1be5ce847bc..a470a6e1402 100644
--- a/intern/cycles/integrator/path_trace.h
+++ b/intern/cycles/integrator/path_trace.h
@@ -226,6 +226,9 @@ class PathTrace {
void progress_set_status(const string &status, const string &substatus = "");
+ /* Destroy GPU resources (such as graphics interop) used by work. */
+ void destroy_gpu_resources();
+
/* Pointer to a device which is configured to be used for path tracing. If multiple devices
* are configured this is a `MultiDevice`. */
Device *device_ = nullptr;
diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp
index fe4697e082b..90a5a01320b 100644
--- a/intern/cycles/integrator/render_scheduler.cpp
+++ b/intern/cycles/integrator/render_scheduler.cpp
@@ -244,7 +244,7 @@ void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work)
render_work.tile.write = tile_write;
render_work.full.write = full_write;
- /* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which
+ /* Do not write tile if it has zero samples in it, treat it similarly to all other tiles which
* got canceled. */
if (!state_.tile_result_was_written && has_rendered_samples) {
render_work.tile.write = true;
diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h
index 404f65e98a1..dce876d44bd 100644
--- a/intern/cycles/integrator/render_scheduler.h
+++ b/intern/cycles/integrator/render_scheduler.h
@@ -124,7 +124,7 @@ class RenderScheduler {
/* Get sample up to which rendering has been done.
* This is an absolute 0-based value.
*
- * For example, if start sample is 10 and and 5 samples were rendered, then this call will
+ * For example, if start sample is 10 and 5 samples were rendered, then this call will
* return 14.
*
* If there were no samples rendered, then the behavior is undefined. */
@@ -132,7 +132,7 @@ class RenderScheduler {
/* Get number of samples rendered within the current scheduling session.
*
- * For example, if start sample is 10 and and 5 samples were rendered, then this call will
+ * For example, if start sample is 10 and 5 samples were rendered, then this call will
* return 5.
*
* Note that this is based on the scheduling information. In practice this means that if someone
diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h
index 1fd3a3f2850..71045157372 100644
--- a/intern/cycles/kernel/bvh/util.h
+++ b/intern/cycles/kernel/bvh/util.h
@@ -5,27 +5,6 @@
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. This is from "A Fast and Robust Method for Avoiding
- * Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and
- */
-ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
-{
- const float int_scale = 256.0f;
- int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z));
-
- float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)),
- __int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)),
- __int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z)));
- const float origin = 1.0f / 32.0f;
- const float float_scale = 1.0f / 65536.0f;
- return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x,
- fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y,
- fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z);
-}
-
#if defined(__KERNEL_CPU__)
ccl_device int intersections_compare(const void *a, const void *b)
{
diff --git a/intern/cycles/kernel/geom/point.h b/intern/cycles/kernel/geom/point.h
index f7c6cb86c5e..041ecb3c2cf 100644
--- a/intern/cycles/kernel/geom/point.h
+++ b/intern/cycles/kernel/geom/point.h
@@ -128,9 +128,10 @@ ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd
return r;
}
else {
- float3 dir = make_float3(r, r, r);
+ const float normalized_r = r * (1.0f / M_SQRT3_F);
+ float3 dir = make_float3(normalized_r, normalized_r, normalized_r);
object_dir_transform(kg, sd, &dir);
- return average(dir);
+ return len(dir);
}
}
diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h
index e616123e9e7..b84059d6676 100644
--- a/intern/cycles/kernel/integrator/init_from_bake.h
+++ b/intern/cycles/kernel/integrator/init_from_bake.h
@@ -30,6 +30,50 @@ ccl_device_inline float bake_clamp_mirror_repeat(float u, float max)
return ((((int)fu) & 1) ? 1.0f - u : u) * max;
}
+/* Offset towards center of triangle to avoid ray-tracing precision issues. */
+ccl_device const float2 bake_offset_towards_center(KernelGlobals kg,
+ const int prim,
+ const float u,
+ const float v)
+{
+ float3 tri_verts[3];
+ triangle_vertices(kg, prim, tri_verts);
+
+ /* Empirically determined values, by no means perfect. */
+ const float position_offset = 1e-4f;
+ const float uv_offset = 1e-5f;
+
+ /* Offset position towards center, amount relative to absolute size of position coordinates. */
+ const float3 P = u * tri_verts[0] + v * tri_verts[1] + (1.0f - u - v) * tri_verts[2];
+ const float3 center = (tri_verts[0] + tri_verts[1] + tri_verts[2]) / 3.0f;
+ const float3 to_center = center - P;
+
+ const float3 offset_P = P + normalize(to_center) *
+ min(len(to_center), max(max3(fabs(P)), 1.0f) * position_offset);
+
+ /* Compute barycentric coordinates at new position. */
+ const float3 v1 = tri_verts[1] - tri_verts[0];
+ const float3 v2 = tri_verts[2] - tri_verts[0];
+ const float3 vP = offset_P - tri_verts[0];
+
+ const float d11 = dot(v1, v1);
+ const float d12 = dot(v1, v2);
+ const float d22 = dot(v2, v2);
+ const float dP1 = dot(vP, v1);
+ const float dP2 = dot(vP, v2);
+
+ const float denom = d11 * d22 - d12 * d12;
+ if (denom == 0.0f) {
+ return make_float2(0.0f, 0.0f);
+ }
+
+ const float offset_v = clamp((d22 * dP1 - d12 * dP2) / denom, uv_offset, 1.0f - uv_offset);
+ const float offset_w = clamp((d11 * dP2 - d12 * dP1) / denom, uv_offset, 1.0f - uv_offset);
+ const float offset_u = clamp(1.0f - offset_v - offset_w, uv_offset, 1.0f - uv_offset);
+
+ return make_float2(offset_u, offset_v);
+}
+
/* Return false to indicate that this pixel is finished.
* Used by CPU implementation to not attempt to sample pixel for multiple samples once its known
* that the pixel did converge. */
@@ -87,7 +131,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
/* Initialize path state for path integration. */
path_state_init_integrator(kg, state, sample, rng_hash);
- /* Barycentric UV with sub-pixel offset. */
+ /* Barycentric UV. */
float u = primitive[2];
float v = primitive[3];
@@ -96,6 +140,14 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
float dvdx = differential[2];
float dvdy = differential[3];
+ /* Exactly at vertex? Nudge inwards to avoid self-intersection. */
+ if ((u == 0.0f || u == 1.0f) && (v == 0.0f || v == 1.0f)) {
+ const float2 uv = bake_offset_towards_center(kg, prim, u, v);
+ u = uv.x;
+ v = uv.y;
+ }
+
+ /* Sub-pixel offset. */
if (sample > 0) {
u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f);
v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f),
diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h
index f548d91031d..d2442755646 100644
--- a/intern/cycles/kernel/integrator/shade_surface.h
+++ b/intern/cycles/kernel/integrator/shade_surface.h
@@ -352,12 +352,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
float ao_pdf;
sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
- if (!(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f)) {
- return;
- }
-
Ray ray ccl_optional_struct_init;
- ray.P = sd->P;
+ ray.P = shadow_ray_offset(kg, sd, ao_D);
ray.D = ao_D;
ray.t = kernel_data.integrator.ao_bounces_distance;
ray.time = sd->time;
diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp
index 16e76b37b0b..85bdb47600e 100644
--- a/intern/cycles/kernel/osl/services.cpp
+++ b/intern/cycles/kernel/osl/services.cpp
@@ -1638,12 +1638,16 @@ bool OSLRenderServices::trace(TraceOpt &options,
ray.D = TO_FLOAT3(R);
ray.t = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist;
ray.time = sd->time;
+ ray.self.object = OBJECT_NONE;
+ ray.self.prim = PRIM_NONE;
+ ray.self.light_object = OBJECT_NONE;
+ ray.self.light_prim = PRIM_NONE;
if (options.mindist == 0.0f) {
/* avoid self-intersections */
if (ray.P == sd->P) {
- bool transmit = (dot(sd->Ng, ray.D) < 0.0f);
- ray.P = ray_offset(sd->P, (transmit) ? -sd->Ng : sd->Ng);
+ ray.self.object = sd->object;
+ ray.self.prim = sd->prim;
}
}
else {
diff --git a/intern/cycles/kernel/svm/light_path.h b/intern/cycles/kernel/svm/light_path.h
index dce0f83da68..7c2189d3608 100644
--- a/intern/cycles/kernel/svm/light_path.h
+++ b/intern/cycles/kernel/svm/light_path.h
@@ -58,8 +58,8 @@ ccl_device_noinline void svm_node_light_path(KernelGlobals kg,
info = (float)integrator_state_bounce(state, path_flag);
}
- /* For background, light emission and shadow evaluation we from a
- * surface or volume we are effective one bounce further. */
+ /* For background, light emission and shadow evaluation from a
+ * surface or volume we are effectively one bounce further. */
if (path_flag & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) {
info += 1.0f;
}
diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp
index dde250d5d78..8a08f2a5be9 100644
--- a/intern/cycles/scene/shader.cpp
+++ b/intern/cycles/scene/shader.cpp
@@ -817,28 +817,28 @@ void ShaderManager::init_xyz_transforms()
Transform xyz_to_rgb;
if (config->hasRole("aces_interchange")) {
- /* Standard OpenColorIO role, defined as ACES2065-1. */
- const Transform xyz_E_to_aces = make_transform(1.0498110175f,
- 0.0f,
- -0.0000974845f,
- 0.0f,
- -0.4959030231f,
- 1.3733130458f,
- 0.0982400361f,
- 0.0f,
- 0.0f,
- 0.0f,
- 0.9912520182f,
- 0.0f);
- const Transform xyz_D65_to_E = make_transform(
- 1.0521111f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9184170f, 0.0f);
-
+ /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */
Transform aces_to_rgb;
if (!to_scene_linear_transform(config, "aces_interchange", aces_to_rgb)) {
return;
}
- xyz_to_rgb = aces_to_rgb * xyz_E_to_aces * xyz_D65_to_E;
+ /* This is the OpenColorIO builtin transform:
+ * UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */
+ const Transform ACES_AP0_to_xyz_D65 = make_transform(0.938280f,
+ -0.004451f,
+ 0.016628f,
+ 0.000000f,
+ 0.337369f,
+ 0.729522f,
+ -0.066890f,
+ 0.000000f,
+ 0.001174f,
+ -0.003711f,
+ 1.091595f,
+ 0.000000f);
+ const Transform xyz_to_aces = transform_inverse(ACES_AP0_to_xyz_D65);
+ xyz_to_rgb = aces_to_rgb * xyz_to_aces;
}
else if (config->hasRole("XYZ")) {
/* Custom role used before the standard existed. */
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index 0e348b1ac0f..fddac1dbbcf 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -7,7 +7,6 @@ set(INC
)
set(INC_SYS
- ${GLEW_INCLUDE_DIR}
)
set(SRC
@@ -34,14 +33,6 @@ set(LIB
${TBB_LIBRARIES}
)
-if(WITH_CYCLES_STANDALONE)
- if(WITH_CYCLES_STANDALONE_GUI)
- list(APPEND SRC
- view.cpp
- )
- endif()
-endif()
-
set(SRC_HEADERS
algorithm.h
aligned_malloc.h
@@ -142,7 +133,6 @@ set(SRC_HEADERS
unique_ptr.h
vector.h
version.h
- view.h
windows.h
xml.h
)
diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h
index ed9f230398d..555a5304764 100644
--- a/intern/cycles/util/math.h
+++ b/intern/cycles/util/math.h
@@ -67,6 +67,9 @@ CCL_NAMESPACE_BEGIN
#ifndef M_SQRT2_F
# define M_SQRT2_F (1.4142135623730950f) /* sqrt(2) */
#endif
+#ifndef M_SQRT3_F
+# define M_SQRT3_F (1.7320508075688772f) /* sqrt(3) */
+#endif
#ifndef M_LN2_F
# define M_LN2_F (0.6931471805599453f) /* ln(2) */
#endif
diff --git a/intern/cycles/util/view.cpp b/intern/cycles/util/view.cpp
deleted file mode 100644
index 475f8dbcee8..00000000000
--- a/intern/cycles/util/view.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/* SPDX-License-Identifier: Apache-2.0
- * Copyright 2011-2022 Blender Foundation */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "util/opengl.h"
-#include "util/string.h"
-#include "util/time.h"
-#include "util/version.h"
-#include "util/view.h"
-
-#ifdef __APPLE__
-# include <GLUT/glut.h>
-#else
-# include <GL/glut.h>
-#endif
-
-CCL_NAMESPACE_BEGIN
-
-/* structs */
-
-struct View {
- ViewInitFunc initf;
- ViewExitFunc exitf;
- ViewResizeFunc resize;
- ViewDisplayFunc display;
- ViewKeyboardFunc keyboard;
- ViewMotionFunc motion;
-
- bool first_display;
- bool redraw;
-
- int mouseX, mouseY;
- int mouseBut0, mouseBut2;
-
- int width, height;
-} V;
-
-/* public */
-
-static void view_display_text(int x, int y, const char *text)
-{
- const char *c;
-
- glRasterPos3f(x, y, 0);
-
- for (c = text; *c != '\0'; c++)
- glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, *c);
-}
-
-void view_display_info(const char *info)
-{
- const int height = 20;
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
- glRectf(0.0f, V.height - height, V.width, V.height);
- glDisable(GL_BLEND);
-
- glColor3f(0.5f, 0.5f, 0.5f);
-
- view_display_text(10, 7 + V.height - height, info);
-
- glColor3f(1.0f, 1.0f, 1.0f);
-}
-
-void view_display_help()
-{
- const int w = (int)((float)V.width / 1.15f);
- const int h = (int)((float)V.height / 1.15f);
-
- const int x1 = (V.width - w) / 2;
- const int x2 = x1 + w;
-
- const int y1 = (V.height - h) / 2;
- const int y2 = y1 + h;
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glColor4f(0.5f, 0.5f, 0.5f, 0.8f);
- glRectf(x1, y1, x2, y2);
- glDisable(GL_BLEND);
-
- glColor3f(0.8f, 0.8f, 0.8f);
-
- string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING;
-
- view_display_text(x1 + 20, y2 - 20, info.c_str());
- view_display_text(x1 + 20, y2 - 40, "(C) 2011-2022 Blender Foundation");
- view_display_text(x1 + 20, y2 - 80, "Controls:");
- view_display_text(x1 + 20, y2 - 100, "h: Info/Help");
- view_display_text(x1 + 20, y2 - 120, "r: Reset");
- view_display_text(x1 + 20, y2 - 140, "p: Pause");
- view_display_text(x1 + 20, y2 - 160, "esc: Cancel");
- view_display_text(x1 + 20, y2 - 180, "q: Quit program");
-
- view_display_text(x1 + 20, y2 - 210, "i: Interactive mode");
- view_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera");
- view_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera");
- view_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera");
- view_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces");
-
- glColor3f(1.0f, 1.0f, 1.0f);
-}
-
-static void view_display()
-{
- if (V.first_display) {
- if (V.initf)
- V.initf();
- if (V.exitf)
- atexit(V.exitf);
-
- V.first_display = false;
- }
-
- glClearColor(0.05f, 0.05f, 0.05f, 0.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, V.width, 0, V.height, -1, 1);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- glRasterPos3f(0, 0, 0);
-
- if (V.display)
- V.display();
-
- glutSwapBuffers();
-}
-
-static void view_reshape(int width, int height)
-{
- if (width <= 0 || height <= 0)
- return;
-
- V.width = width;
- V.height = height;
-
- glViewport(0, 0, width, height);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- if (V.resize)
- V.resize(width, height);
-}
-
-static void view_keyboard(unsigned char key, int x, int y)
-{
- if (V.keyboard)
- V.keyboard(key);
-
- if (key == 'm')
- printf("mouse %d %d\n", x, y);
- if (key == 'q') {
- if (V.exitf)
- V.exitf();
- exit(0);
- }
-}
-
-static void view_mouse(int button, int state, int x, int y)
-{
- if (button == 0) {
- if (state == GLUT_DOWN) {
- V.mouseX = x;
- V.mouseY = y;
- V.mouseBut0 = 1;
- }
- else if (state == GLUT_UP) {
- V.mouseBut0 = 0;
- }
- }
- else if (button == 2) {
- if (state == GLUT_DOWN) {
- V.mouseX = x;
- V.mouseY = y;
- V.mouseBut2 = 1;
- }
- else if (state == GLUT_UP) {
- V.mouseBut2 = 0;
- }
- }
-}
-
-static void view_motion(int x, int y)
-{
- const int but = V.mouseBut0 ? 0 : 2;
- const int distX = x - V.mouseX;
- const int distY = y - V.mouseY;
-
- if (V.motion)
- V.motion(distX, distY, but);
-
- V.mouseX = x;
- V.mouseY = y;
-}
-
-static void view_idle()
-{
- if (V.redraw) {
- V.redraw = false;
- glutPostRedisplay();
- }
-
- time_sleep(0.1);
-}
-
-void view_main_loop(const char *title,
- int width,
- int height,
- ViewInitFunc initf,
- ViewExitFunc exitf,
- ViewResizeFunc resize,
- ViewDisplayFunc display,
- ViewKeyboardFunc keyboard,
- ViewMotionFunc motion)
-{
- const char *name = "app";
- char *argv = (char *)name;
- int argc = 1;
-
- memset(&V, 0, sizeof(V));
- V.width = width;
- V.height = height;
- V.first_display = true;
- V.redraw = false;
- V.initf = initf;
- V.exitf = exitf;
- V.resize = resize;
- V.display = display;
- V.keyboard = keyboard;
- V.motion = motion;
-
- glutInit(&argc, &argv);
- glutInitWindowSize(width, height);
- glutInitWindowPosition(0, 0);
- glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
- glutCreateWindow(title);
-
- glewInit();
-
- view_reshape(width, height);
-
- glutDisplayFunc(view_display);
- glutIdleFunc(view_idle);
- glutReshapeFunc(view_reshape);
- glutKeyboardFunc(view_keyboard);
- glutMouseFunc(view_mouse);
- glutMotionFunc(view_motion);
-
- glutMainLoop();
-}
-
-void view_redraw()
-{
- V.redraw = true;
-}
-
-CCL_NAMESPACE_END
diff --git a/intern/cycles/util/view.h b/intern/cycles/util/view.h
deleted file mode 100644
index 51c242c21f7..00000000000
--- a/intern/cycles/util/view.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: Apache-2.0
- * Copyright 2011-2022 Blender Foundation */
-
-#ifndef __UTIL_VIEW_H__
-#define __UTIL_VIEW_H__
-
-/* Functions to display a simple OpenGL window using GLUT, simplified to the
- * bare minimum we need to reduce boilerplate code in tests apps. */
-
-CCL_NAMESPACE_BEGIN
-
-typedef void (*ViewInitFunc)();
-typedef void (*ViewExitFunc)();
-typedef void (*ViewResizeFunc)(int width, int height);
-typedef void (*ViewDisplayFunc)();
-typedef void (*ViewKeyboardFunc)(unsigned char key);
-typedef void (*ViewMotionFunc)(int x, int y, int button);
-
-void view_main_loop(const char *title,
- int width,
- int height,
- ViewInitFunc initf,
- ViewExitFunc exitf,
- ViewResizeFunc resize,
- ViewDisplayFunc display,
- ViewKeyboardFunc keyboard,
- ViewMotionFunc motion);
-
-void view_display_info(const char *info);
-void view_display_help();
-void view_redraw();
-
-CCL_NAMESPACE_END
-
-#endif /*__UTIL_VIEW_H__*/
diff --git a/intern/ffmpeg/tests/ffmpeg_codecs.cc b/intern/ffmpeg/tests/ffmpeg_codecs.cc
index bb6e13579e5..d0c40736884 100644
--- a/intern/ffmpeg/tests/ffmpeg_codecs.cc
+++ b/intern/ffmpeg/tests/ffmpeg_codecs.cc
@@ -4,12 +4,13 @@
extern "C" {
#include <libavcodec/avcodec.h>
+#include <libavutil/channel_layout.h>
#include <libavutil/log.h>
}
namespace {
-bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat)
+bool test_vcodec(const AVCodec *codec, AVPixelFormat pixelformat)
{
av_log_set_level(AV_LOG_QUIET);
bool result = false;
@@ -30,7 +31,7 @@ bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat)
}
return result;
}
-bool test_acodec(AVCodec *codec, AVSampleFormat fmt)
+bool test_acodec(const AVCodec *codec, AVSampleFormat fmt)
{
av_log_set_level(AV_LOG_QUIET);
bool result = false;
@@ -54,7 +55,7 @@ bool test_acodec(AVCodec *codec, AVSampleFormat fmt)
bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat)
{
bool result = false;
- AVCodec *codec = avcodec_find_encoder(codec_id);
+ const AVCodec *codec = avcodec_find_encoder(codec_id);
if (codec)
result = test_vcodec(codec, pixelformat);
return result;
@@ -63,7 +64,7 @@ bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat)
bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat)
{
bool result = false;
- AVCodec *codec = avcodec_find_encoder_by_name(codecname);
+ const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
if (codec)
result = test_vcodec(codec, pixelformat);
return result;
@@ -72,7 +73,7 @@ bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat)
bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt)
{
bool result = false;
- AVCodec *codec = avcodec_find_encoder(codec_id);
+ const AVCodec *codec = avcodec_find_encoder(codec_id);
if (codec)
result = test_acodec(codec, fmt);
return result;
@@ -81,7 +82,7 @@ bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt)
bool test_codec_audio_by_name(const char *codecname, AVSampleFormat fmt)
{
bool result = false;
- AVCodec *codec = avcodec_find_encoder_by_name(codecname);
+ const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
if (codec)
result = test_acodec(codec, fmt);
return result;
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 441c7315f1a..4e48a908c00 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -249,6 +249,16 @@ extern GHOST_TSuccess GHOST_EndFullScreen(GHOST_SystemHandle systemhandle);
*/
extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle);
+/**
+ * Get the Window under the cursor.
+ * \param x: The x-coordinate of the cursor.
+ * \param y: The y-coordinate of the cursor.
+ * \return The window under the cursor or nullptr in none.
+ */
+extern GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle,
+ int32_t x,
+ int32_t y);
+
/***************************************************************************************
* Event management functionality
***************************************************************************************/
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 837ec25d0f8..ed193ee7e5d 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -309,6 +309,14 @@ class GHOST_ISystem {
*/
virtual void useWindowFocus(const bool use_focus) = 0;
+ /**
+ * Get the Window under the cursor.
+ * \param x: The x-coordinate of the cursor.
+ * \param y: The y-coordinate of the cursor.
+ * \return The window under the cursor or nullptr if none.
+ */
+ virtual GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) = 0;
+
/***************************************************************************************
* Event management functionality
***************************************************************************************/
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index a47d2468937..e3d01c24283 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -233,6 +233,16 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle)
return (int)system->getFullScreen();
}
+GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle,
+ int32_t x,
+ int32_t y)
+{
+ GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
+ GHOST_IWindow *window = system->getWindowUnderCursor(x, y);
+
+ return (GHOST_WindowHandle)window;
+}
+
bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp
index 11f15fd1ee3..ded76daa145 100644
--- a/intern/ghost/intern/GHOST_ContextD3D.cpp
+++ b/intern/ghost/intern/GHOST_ContextD3D.cpp
@@ -110,9 +110,11 @@ class GHOST_SharedOpenGLResource {
struct SharedData {
HANDLE device;
GLuint fbo;
- HANDLE render_buf{nullptr};
+ HANDLE render_target{nullptr};
} m_shared;
+ enum RenderTarget { TARGET_RENDERBUF, TARGET_TEX2D };
+
public:
GHOST_SharedOpenGLResource(ID3D11Device *device,
ID3D11DeviceContext *device_ctx,
@@ -179,37 +181,64 @@ class GHOST_SharedOpenGLResource {
}
if (m_is_initialized) {
- if (m_shared.render_buf) {
- wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
+ if (m_shared.render_target
+#if 1
+ /* TODO: #wglDXUnregisterObjectNV() causes an access violation on AMD when the shared
+ * resource is a GL texture. Since there is currently no good alternative, just skip
+ * unregistering the shared resource. */
+ && !m_use_gl_texture2d
+#endif
+ ) {
+ wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target);
}
if (m_shared.device) {
wglDXCloseDeviceNV(m_shared.device);
}
glDeleteFramebuffers(1, &m_shared.fbo);
- glDeleteRenderbuffers(1, &m_gl_render_buf);
+ if (m_use_gl_texture2d) {
+ glDeleteTextures(1, &m_gl_render_target);
+ }
+ else {
+ glDeleteRenderbuffers(1, &m_gl_render_target);
+ }
}
}
- void reregisterSharedObject()
+ /* Returns true if the shared object was successfully registered, false otherwise. */
+ bool reregisterSharedObject(RenderTarget target)
{
- if (m_shared.render_buf) {
- wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
+ if (m_shared.render_target) {
+ wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target);
}
if (!m_render_target_tex) {
- return;
+ return false;
}
- m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device,
- m_render_target_tex,
- m_gl_render_buf,
- GL_RENDERBUFFER,
- WGL_ACCESS_READ_WRITE_NV);
+ if (target == TARGET_TEX2D) {
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA8,
+ m_cur_width,
+ m_cur_height,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ nullptr);
+ }
- if (!m_shared.render_buf) {
+ m_shared.render_target = wglDXRegisterObjectNV(m_shared.device,
+ m_render_target_tex,
+ m_gl_render_target,
+ (target == TARGET_TEX2D) ? GL_TEXTURE_2D :
+ GL_RENDERBUFFER,
+ WGL_ACCESS_READ_WRITE_NV);
+ if (!m_shared.render_target) {
fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
- return;
+ return false;
}
+
+ return true;
}
GHOST_TSuccess initialize()
@@ -221,16 +250,33 @@ class GHOST_SharedOpenGLResource {
}
/* Build the renderbuffer. */
- glGenRenderbuffers(1, &m_gl_render_buf);
- glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_buf);
+ glGenRenderbuffers(1, &m_gl_render_target);
+ glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_target);
+
+ if (!reregisterSharedObject(TARGET_RENDERBUF)) {
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ if (m_gl_render_target) {
+ glDeleteRenderbuffers(1, &m_gl_render_target);
+ }
+ /* Fall back to texture 2d. */
+ m_use_gl_texture2d = true;
+ glGenTextures(1, &m_gl_render_target);
+ glBindTexture(GL_TEXTURE_2D, m_gl_render_target);
- reregisterSharedObject();
+ reregisterSharedObject(TARGET_TEX2D);
+ }
/* Build the framebuffer */
glGenFramebuffers(1, &m_shared.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo);
- glFramebufferRenderbuffer(
- GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_buf);
+ if (m_use_gl_texture2d) {
+ glFramebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_gl_render_target, 0);
+ }
+ else {
+ glFramebufferRenderbuffer(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_target);
+ }
m_is_initialized = true;
return GHOST_kSuccess;
@@ -245,7 +291,7 @@ class GHOST_SharedOpenGLResource {
if ((m_cur_width != width) || (m_cur_height != height)) {
m_cur_width = width;
m_cur_height = height;
- reregisterSharedObject();
+ reregisterSharedObject(m_use_gl_texture2d ? TARGET_TEX2D : TARGET_RENDERBUF);
}
}
@@ -293,18 +339,19 @@ class GHOST_SharedOpenGLResource {
private:
void beginGLOnly()
{
- wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
+ wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_target);
}
void endGLOnly()
{
- wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
+ wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_target);
}
ID3D11Device *m_device;
ID3D11DeviceContext *m_device_ctx;
- GLuint m_gl_render_buf;
+ GLuint m_gl_render_target;
unsigned int m_cur_width, m_cur_height;
bool m_is_initialized{false};
+ bool m_use_gl_texture2d{false};
};
GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index e9c63502f66..3df85e18bc7 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -189,6 +189,25 @@ bool GHOST_System::getFullScreen(void)
return fullScreen;
}
+GHOST_IWindow *GHOST_System::getWindowUnderCursor(int32_t x, int32_t y)
+{
+ /* TODO: This solution should follow the order of the activated windows (Z-order).
+ * It is imperfect but usable in most cases. */
+ for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) {
+ if (iwindow->getState() == GHOST_kWindowStateMinimized) {
+ continue;
+ }
+
+ GHOST_Rect bounds;
+ iwindow->getClientBounds(bounds);
+ if (bounds.isInside(x, y)) {
+ return iwindow;
+ }
+ }
+
+ return NULL;
+}
+
void GHOST_System::dispatchEvents()
{
#ifdef WITH_INPUT_NDOF
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 8602dd94e8c..0e1e3f734ae 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -157,6 +157,14 @@ class GHOST_System : public GHOST_ISystem {
void useWindowFocus(const bool use_focus);
bool m_windowFocus;
+ /**
+ * Get the Window under the cursor.
+ * \param x: The x-coordinate of the cursor.
+ * \param y: The y-coordinate of the cursor.
+ * \return The window under the cursor or nullptr if none.
+ */
+ GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y);
+
/***************************************************************************************
* Event management functionality
***************************************************************************************/
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index a601d00561a..8b6dfb4efed 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -109,6 +109,14 @@ class GHOST_SystemCocoa : public GHOST_System {
*/
GHOST_TSuccess disposeContext(GHOST_IContext *context);
+ /**
+ * Get the Window under the cursor.
+ * \param x: The x-coordinate of the cursor.
+ * \param y: The y-coordinate of the cursor.
+ * \return The window under the cursor or nullptr if none.
+ */
+ GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y);
+
/***************************************************************************************
* Event management functionality
***************************************************************************************/
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 51d4083b436..f0db6b6fdfc 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -307,6 +307,7 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
case ']':
return GHOST_kKeyRightBracket;
case '`':
+ case '<': /* The position of '`' is equivalent to this symbol in the French layout. */
return GHOST_kKeyAccentGrave;
default:
return GHOST_kKeyUnknown;
@@ -771,6 +772,20 @@ GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context)
return GHOST_kSuccess;
}
+GHOST_IWindow *GHOST_SystemCocoa::getWindowUnderCursor(int32_t x, int32_t y)
+{
+ NSPoint scr_co = NSMakePoint(x, y);
+
+ int windowNumberAtPoint = [NSWindow windowNumberAtPoint:scr_co belowWindowWithWindowNumber:0];
+ NSWindow *nswindow = [NSApp windowWithWindowNumber:windowNumberAtPoint];
+
+ if (nswindow == nil) {
+ return nil;
+ }
+
+ return m_windowManager->getWindowAssociatedWithOSWindow((void *)nswindow);
+}
+
/**
* \note : returns coordinates in Cocoa screen coordinates
*/
diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h
index 7b92d1d13a1..48973a00573 100644
--- a/intern/ghost/intern/GHOST_SystemNULL.h
+++ b/intern/ghost/intern/GHOST_SystemNULL.h
@@ -114,4 +114,9 @@ class GHOST_SystemNULL : public GHOST_System {
type,
((glSettings.flags & GHOST_glStereoVisual) != 0));
}
+
+ GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y)
+ {
+ return NULL;
+ }
};
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 1c26935ed64..e588c7485b4 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -53,9 +53,6 @@
#ifndef VK_COMMA
# define VK_COMMA 0xBC
#endif // VK_COMMA
-#ifndef VK_QUOTE
-# define VK_QUOTE 0xDE
-#endif // VK_QUOTE
#ifndef VK_BACK_QUOTE
# define VK_BACK_QUOTE 0xC0
#endif // VK_BACK_QUOTE
@@ -635,14 +632,32 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw,
GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
{
GHOST_TKey key = GHOST_kKeyUnknown;
- switch (PRIMARYLANGID(m_langId)) {
- case LANG_FRENCH:
- if (vKey == VK_OEM_8)
- key = GHOST_kKeyF13; // oem key; used purely for shortcuts .
+ char ch = (char)MapVirtualKeyA(vKey, MAPVK_VK_TO_CHAR);
+ switch (ch) {
+ case u'\"':
+ case u'\'':
+ key = GHOST_kKeyQuote;
break;
- case LANG_ENGLISH:
- if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬"
- key = GHOST_kKeyAccentGrave;
+ case u'.':
+ key = GHOST_kKeyNumpadPeriod;
+ break;
+ case u'/':
+ key = GHOST_kKeySlash;
+ break;
+ case u'`':
+ case u'²':
+ key = GHOST_kKeyAccentGrave;
+ break;
+ default:
+ if (vKey == VK_OEM_7) {
+ key = GHOST_kKeyQuote;
+ }
+ else if (vKey == VK_OEM_8) {
+ if (PRIMARYLANGID(m_langId) == LANG_FRENCH) {
+ /* OEM key; used purely for shortcuts. */
+ key = GHOST_kKeyF13;
+ }
+ }
break;
}
@@ -777,9 +792,6 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten
case VK_CLOSE_BRACKET:
key = GHOST_kKeyRightBracket;
break;
- case VK_QUOTE:
- key = GHOST_kKeyQuote;
- break;
case VK_GR_LESS:
key = GHOST_kKeyGrLess;
break;
@@ -821,9 +833,6 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten
case VK_CAPITAL:
key = GHOST_kKeyCapsLock;
break;
- case VK_OEM_8:
- key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode);
- break;
case VK_MEDIA_PLAY_PAUSE:
key = GHOST_kKeyMediaPlay;
break;
@@ -836,8 +845,10 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten
case VK_MEDIA_NEXT_TRACK:
key = GHOST_kKeyMediaLast;
break;
+ case VK_OEM_7:
+ case VK_OEM_8:
default:
- key = GHOST_kKeyUnknown;
+ key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode);
break;
}
}
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 5d8feb8e48a..2ac3d9ec2a5 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -412,6 +412,9 @@ void GHOST_XrContext::getExtensionsToEnable(
/* Interaction profile extensions. */
try_ext.push_back(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME);
try_ext.push_back(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME);
+#ifdef XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME
+ try_ext.push_back(XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME);
+#endif
try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME);
/* Controller model extension. */
diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt
index dfccb9301ac..be6ccc5c2c5 100644
--- a/intern/opencolorio/CMakeLists.txt
+++ b/intern/opencolorio/CMakeLists.txt
@@ -7,6 +7,7 @@ set(INC
../guardedalloc
../../source/blender/blenlib
../../source/blender/gpu
+ ../../source/blender/gpu/intern
../../source/blender/makesdna
)
@@ -20,6 +21,7 @@ set(SRC
ocio_capi.h
ocio_impl.h
+ ocio_shader_shared.hh
)
set(LIB
@@ -56,8 +58,38 @@ if(WITH_OPENCOLORIO)
)
endif()
- data_to_c_simple(gpu_shader_display_transform.glsl SRC)
- data_to_c_simple(gpu_shader_display_transform_vertex.glsl SRC)
+ set(GLSL_SRC
+ gpu_shader_display_transform_vert.glsl
+ gpu_shader_display_transform_frag.glsl
+
+ ocio_shader_shared.hh
+ )
+
+ set(GLSL_C)
+ foreach(GLSL_FILE ${GLSL_SRC})
+ data_to_c_simple(${GLSL_FILE} GLSL_C)
+ endforeach()
+
+ blender_add_lib(bf_ocio_shaders "${GLSL_C}" "" "" "")
+
+ list(APPEND LIB
+ bf_ocio_shaders
+ )
+
+ set(GLSL_SOURCE_CONTENT "")
+ foreach(GLSL_FILE ${GLSL_SRC})
+ get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME)
+ string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME})
+ string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n")
+ endforeach()
+
+ set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_source_list.h")
+ file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}")
+ list(APPEND SRC ${glsl_source_list_file})
+ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
+
+ target_include_directories(bf_ocio_shaders PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+
endif()
diff --git a/intern/opencolorio/gpu_shader_display_transform.glsl b/intern/opencolorio/gpu_shader_display_transform_frag.glsl
index f5a7a7bf45d..3c2352c13ba 100644
--- a/intern/opencolorio/gpu_shader_display_transform.glsl
+++ b/intern/opencolorio/gpu_shader_display_transform_frag.glsl
@@ -1,39 +1,10 @@
/* Blender OpenColorIO implementation */
-uniform sampler2D image_texture;
-uniform sampler2D overlay_texture;
-
-uniform float dither;
-uniform float scale;
-uniform float exponent;
-uniform bool predivide;
-uniform bool overlay;
+/* -------------------------------------------------------------------- */
+/** \name Curve Mapping Implementation
+ * \{ */
#ifdef USE_CURVE_MAPPING
-uniform sampler1D curve_mapping_texture;
-
-layout(std140) uniform OCIO_GPUCurveMappingParameters
-{
- /* Curve mapping parameters
- *
- * See documentation for OCIO_CurveMappingSettings to get fields descriptions.
- * (this ones pretty much copies stuff from C structure.)
- */
- vec4 curve_mapping_mintable;
- vec4 curve_mapping_range;
- vec4 curve_mapping_ext_in_x;
- vec4 curve_mapping_ext_in_y;
- vec4 curve_mapping_ext_out_x;
- vec4 curve_mapping_ext_out_y;
- vec4 curve_mapping_first_x;
- vec4 curve_mapping_first_y;
- vec4 curve_mapping_last_x;
- vec4 curve_mapping_last_y;
- vec4 curve_mapping_black;
- vec4 curve_mapping_bwmul;
- int curve_mapping_lut_size;
- int curve_mapping_use_extend_extrapolate;
-};
float read_curve_mapping(int table, int index)
{
@@ -43,27 +14,27 @@ float read_curve_mapping(int table, int index)
float curvemap_calc_extend(int table, float x, vec2 first, vec2 last)
{
if (x <= first[0]) {
- if (curve_mapping_use_extend_extrapolate == 0) {
+ if (curve_mapping.use_extend_extrapolate == 0) {
/* horizontal extrapolation */
return first[1];
}
else {
- float fac = (curve_mapping_ext_in_x[table] != 0.0) ?
- ((x - first[0]) / curve_mapping_ext_in_x[table]) :
+ float fac = (curve_mapping.ext_in_x[table] != 0.0) ?
+ ((x - first[0]) / curve_mapping.ext_in_x[table]) :
10000.0;
- return first[1] + curve_mapping_ext_in_y[table] * fac;
+ return first[1] + curve_mapping.ext_in_y[table] * fac;
}
}
else if (x >= last[0]) {
- if (curve_mapping_use_extend_extrapolate == 0) {
+ if (curve_mapping.use_extend_extrapolate == 0) {
/* horizontal extrapolation */
return last[1];
}
else {
- float fac = (curve_mapping_ext_out_x[table] != 0.0) ?
- ((x - last[0]) / curve_mapping_ext_out_x[table]) :
+ float fac = (curve_mapping.ext_out_x[table] != 0.0) ?
+ ((x - last[0]) / curve_mapping.ext_out_x[table]) :
-10000.0;
- return last[1] + curve_mapping_ext_out_y[table] * fac;
+ return last[1] + curve_mapping.ext_out_y[table] * fac;
}
}
return 0.0;
@@ -71,10 +42,10 @@ float curvemap_calc_extend(int table, float x, vec2 first, vec2 last)
float curvemap_evaluateF(int table, float value)
{
- float mintable_ = curve_mapping_mintable[table];
- float range = curve_mapping_range[table];
+ float mintable_ = curve_mapping.mintable[table];
+ float range = curve_mapping.range[table];
float mintable = 0.0;
- int CM_TABLE = curve_mapping_lut_size - 1;
+ int CM_TABLE = curve_mapping.lut_size - 1;
float fi;
int i;
@@ -87,8 +58,8 @@ float curvemap_evaluateF(int table, float value)
if (fi < 0.0 || fi > float(CM_TABLE)) {
return curvemap_calc_extend(table,
value,
- vec2(curve_mapping_first_x[table], curve_mapping_first_y[table]),
- vec2(curve_mapping_last_x[table], curve_mapping_last_y[table]));
+ vec2(curve_mapping.first_x[table], curve_mapping.first_y[table]),
+ vec2(curve_mapping.last_x[table], curve_mapping.last_y[table]));
}
else {
if (i < 0) {
@@ -106,7 +77,7 @@ float curvemap_evaluateF(int table, float value)
vec4 curvemapping_evaluate_premulRGBF(vec4 col)
{
- col.rgb = (col.rgb - curve_mapping_black.rgb) * curve_mapping_bwmul.rgb;
+ col.rgb = (col.rgb - curve_mapping.black.rgb) * curve_mapping.bwmul.rgb;
vec4 result;
result.r = curvemap_evaluateF(0, col.r);
@@ -115,8 +86,15 @@ vec4 curvemapping_evaluate_premulRGBF(vec4 col)
result.a = col.a;
return result;
}
+
#endif /* USE_CURVE_MAPPING */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dithering
+ * \{ */
+
/* Using a triangle distribution which gives a more final uniform noise.
* See Banding in Games:A Noisy Rant(revision 5) Mikkel Gjøl, Playdead (slide 27) */
/* GPUs are rounding before writing to framebuffer so we center the distribution around 0.0. */
@@ -135,23 +113,33 @@ float dither_random_value(vec2 co)
vec2 round_to_pixel(sampler2D tex, vec2 uv)
{
- vec2 size = textureSize(tex, 0);
- return vec2(ivec2(uv * size)) / size;
+ vec2 size = vec2(textureSize(tex, 0));
+ return floor(uv * size) / size;
}
vec4 apply_dither(vec4 col, vec2 uv)
{
- col.rgb += dither_random_value(uv) * 0.0033 * dither;
+ col.rgb += dither_random_value(uv) * 0.0033 * parameters.dither;
return col;
}
-vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main Processing
+ * \{ */
+
+/* Prototypes: Implementation is generaterd and defined after. */
+vec4 OCIO_to_scene_linear(vec4 pixel);
+vec4 OCIO_to_display(vec4 pixel);
+
+vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay)
{
#ifdef USE_CURVE_MAPPING
col = curvemapping_evaluate_premulRGBF(col);
#endif
- if (predivide) {
+ if (parameters.use_predivide) {
if (col.a > 0.0 && col.a < 1.0) {
col.rgb *= 1.0 / col.a;
}
@@ -166,7 +154,7 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv)
col = OCIO_to_scene_linear(col);
/* Apply exposure in scene linear. */
- col.rgb *= scale;
+ col.rgb *= parameters.scale;
/* Convert to display space. */
col = OCIO_to_display(col);
@@ -177,34 +165,31 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv)
* i.e: The linear color space w.r.t. display chromaticity and radiometry.
* We separate the colormanagement process into two steps to be able to
* merge UI using alpha blending in the correct color space. */
- if (overlay) {
- col.rgb = pow(col.rgb, vec3(exponent * 2.2));
+ if (parameters.use_overlay) {
+ col.rgb = pow(col.rgb, vec3(parameters.exponent * 2.2));
col = clamp(col, 0.0, 1.0);
col *= 1.0 - col_overlay.a;
col += col_overlay; /* Assumed unassociated alpha. */
col.rgb = pow(col.rgb, vec3(1.0 / 2.2));
}
else {
- col.rgb = pow(col.rgb, vec3(exponent));
+ col.rgb = pow(col.rgb, vec3(parameters.exponent));
}
- if (dither > 0.0) {
+ if (parameters.dither > 0.0) {
+ vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st);
col = apply_dither(col, noise_uv);
}
return col;
}
-/* ------------------------------------------------------------------------ */
-
-in vec2 texCoord_interp;
-out vec4 fragColor;
+/** \} */
void main()
{
vec4 col = texture(image_texture, texCoord_interp.st);
vec4 col_overlay = texture(overlay_texture, texCoord_interp.st);
- vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st);
- fragColor = OCIO_ProcessColor(col, col_overlay, noise_uv);
+ fragColor = OCIO_ProcessColor(col, col_overlay);
}
diff --git a/intern/opencolorio/gpu_shader_display_transform_vertex.glsl b/intern/opencolorio/gpu_shader_display_transform_vert.glsl
index 8cf9628b06b..06788be11de 100644
--- a/intern/opencolorio/gpu_shader_display_transform_vertex.glsl
+++ b/intern/opencolorio/gpu_shader_display_transform_vert.glsl
@@ -1,10 +1,4 @@
-uniform mat4 ModelViewProjectionMatrix;
-
-in vec2 texCoord;
-in vec2 pos;
-out vec2 texCoord_interp;
-
void main()
{
gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc
index 11e2a5f7371..a02a37522b9 100644
--- a/intern/opencolorio/ocio_impl.cc
+++ b/intern/opencolorio/ocio_impl.cc
@@ -320,16 +320,18 @@ void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rg
}
if (config->hasRole("aces_interchange")) {
- /* Standard OpenColorIO role, defined as ACES2065-1. */
- const float xyz_E_to_aces[3][3] = {{1.0498110175f, -0.4959030231f, 0.0f},
- {0.0f, 1.3733130458f, 0.0f},
- {-0.0000974845f, 0.0982400361f, 0.9912520182f}};
- const float xyz_D65_to_E[3][3] = {
- {1.0521111f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.9184170f}};
-
+ /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */
float aces_to_rgb[3][3];
if (to_scene_linear_matrix(config, "aces_interchange", aces_to_rgb)) {
- mul_m3_series(xyz_to_rgb, aces_to_rgb, xyz_E_to_aces, xyz_D65_to_E);
+ /* This is the OpenColorIO builtin transform:
+ * UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */
+ const float ACES_AP0_to_xyz_D65[3][3] = {{0.938280f, 0.337369f, 0.001174f},
+ {-0.004451f, 0.729522f, -0.003711f},
+ {0.016628f, -0.066890f, 1.091595f}};
+ float xyz_to_aces[3][3];
+ invert_m3_m3(xyz_to_aces, ACES_AP0_to_xyz_D65);
+
+ mul_m3_m3m3(xyz_to_rgb, aces_to_rgb, xyz_to_aces);
}
}
else if (config->hasRole("XYZ")) {
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index 09803cd8038..e3d44ae9d55 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -21,14 +21,14 @@
#include "GPU_shader.h"
#include "GPU_uniform_buffer.h"
+#include "gpu_shader_create_info.hh"
+
using namespace OCIO_NAMESPACE;
#include "MEM_guardedalloc.h"
#include "ocio_impl.h"
-
-extern "C" char datatoc_gpu_shader_display_transform_glsl[];
-extern "C" char datatoc_gpu_shader_display_transform_vertex_glsl[];
+#include "ocio_shader_shared.hh"
/* **** OpenGL drawing routines using GLSL for color space transform ***** */
@@ -39,41 +39,19 @@ enum OCIO_GPUTextureSlots {
TEXTURE_SLOT_LUTS_OFFSET = 3,
};
-/* Curve mapping parameters
- *
- * See documentation for OCIO_CurveMappingSettings to get fields descriptions.
- * (this ones pretty much copies stuff from C structure.)
- */
-struct OCIO_GPUCurveMappingParameters {
- float curve_mapping_mintable[4];
- float curve_mapping_range[4];
- float curve_mapping_ext_in_x[4];
- float curve_mapping_ext_in_y[4];
- float curve_mapping_ext_out_x[4];
- float curve_mapping_ext_out_y[4];
- float curve_mapping_first_x[4];
- float curve_mapping_first_y[4];
- float curve_mapping_last_x[4];
- float curve_mapping_last_y[4];
- float curve_mapping_black[4];
- float curve_mapping_bwmul[4];
- int curve_mapping_lut_size;
- int curve_mapping_use_extend_extrapolate;
- int _pad[2];
- /** WARNING: Needs to be 16byte aligned. Used as UBO data. */
+enum OCIO_GPUUniformBufSlots {
+ UNIFORMBUF_SLOT_DISPLAY = 0,
+ UNIFORMBUF_SLOT_CURVEMAP = 1,
+ UNIFORMBUF_SLOT_LUTS = 2,
};
struct OCIO_GPUShader {
/* GPU shader. */
struct GPUShader *shader = nullptr;
- /** Uniform locations. */
- int scale_loc = 0;
- int exponent_loc = 0;
- int dither_loc = 0;
- int overlay_loc = 0;
- int predivide_loc = 0;
- int ubo_bind = 0;
+ /** Uniform parameters. */
+ OCIO_GPUParameters parameters = {};
+ GPUUniformBuf *parameters_buffer = nullptr;
/* Destructor. */
~OCIO_GPUShader()
@@ -81,6 +59,9 @@ struct OCIO_GPUShader {
if (shader) {
GPU_shader_free(shader);
}
+ if (parameters_buffer) {
+ GPU_uniformbuf_free(parameters_buffer);
+ }
}
};
@@ -103,6 +84,7 @@ struct OCIO_GPUTextures {
/* Uniforms */
std::vector<OCIO_GPUUniform> uniforms;
+ GPUUniformBuf *uniforms_buffer = nullptr;
/* Destructor. */
~OCIO_GPUTextures()
@@ -113,6 +95,9 @@ struct OCIO_GPUTextures {
if (dummy) {
GPU_texture_free(dummy);
}
+ if (uniforms_buffer) {
+ GPU_uniformbuf_free(uniforms_buffer);
+ }
}
};
@@ -165,97 +150,134 @@ static bool createGPUShader(OCIO_GPUShader &shader,
const GpuShaderDescRcPtr &shaderdesc_to_display,
const bool use_curve_mapping)
{
- std::ostringstream os;
- {
- /* Fragment shader */
+ using namespace blender::gpu::shader;
- /* Work around OpenColorIO not supporting latest GLSL yet. */
- os << "#define texture2D texture\n";
- os << "#define texture3D texture\n";
+ std::string source;
+ source += shaderdesc_to_scene_linear->getShaderText();
+ source += "\n";
+ source += shaderdesc_to_display->getShaderText();
+ source += "\n";
- if (use_curve_mapping) {
- os << "#define USE_CURVE_MAPPING\n";
+ {
+ /* Replace all uniform declarations by a comment.
+ * This avoids double declarations from the backend. */
+ size_t index = 0;
+ while (true) {
+ index = source.find("uniform ", index);
+ if (index == -1) {
+ break;
+ }
+ source.replace(index, 2, "//");
+ index += 2;
}
-
- os << shaderdesc_to_scene_linear->getShaderText() << "\n";
- os << shaderdesc_to_display->getShaderText() << "\n";
-
- os << datatoc_gpu_shader_display_transform_glsl;
}
- shader.shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl,
- os.str().c_str(),
- nullptr,
- nullptr,
- nullptr,
- "OCIOShader");
-
- if (shader.shader == nullptr) {
- return false;
- }
-
- shader.scale_loc = GPU_shader_get_uniform(shader.shader, "scale");
- shader.exponent_loc = GPU_shader_get_uniform(shader.shader, "exponent");
- shader.dither_loc = GPU_shader_get_uniform(shader.shader, "dither");
- shader.overlay_loc = GPU_shader_get_uniform(shader.shader, "overlay");
- shader.predivide_loc = GPU_shader_get_uniform(shader.shader, "predivide");
- shader.ubo_bind = GPU_shader_get_uniform_block_binding(shader.shader,
- "OCIO_GPUCurveMappingParameters");
-
- GPU_shader_bind(shader.shader);
-
- /* Set texture bind point uniform once. This is saved by the shader. */
- GPUShader *sh = shader.shader;
- GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), TEXTURE_SLOT_IMAGE);
- GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "overlay_texture"), TEXTURE_SLOT_OVERLAY);
+ StageInterfaceInfo iface("OCIO_Interface", "");
+ iface.smooth(Type::VEC2, "texCoord_interp");
+
+ ShaderCreateInfo info("OCIO_Display");
+ /* Work around OpenColorIO not supporting latest GLSL yet. */
+ info.define("texture2D", "texture");
+ info.define("texture3D", "texture");
+ info.typedef_source("ocio_shader_shared.hh");
+ info.sampler(TEXTURE_SLOT_IMAGE, ImageType::FLOAT_2D, "image_texture");
+ info.sampler(TEXTURE_SLOT_OVERLAY, ImageType::FLOAT_2D, "overlay_texture");
+ info.uniform_buf(UNIFORMBUF_SLOT_DISPLAY, "OCIO_GPUParameters", "parameters");
+ info.push_constant(Type::MAT4, "ModelViewProjectionMatrix");
+ info.vertex_in(0, Type::VEC2, "pos");
+ info.vertex_in(1, Type::VEC2, "texCoord");
+ info.vertex_out(iface);
+ info.fragment_out(0, Type::VEC4, "fragColor");
+ info.vertex_source("gpu_shader_display_transform_vert.glsl");
+ info.fragment_source("gpu_shader_display_transform_frag.glsl");
+ info.fragment_source_generated = source;
if (use_curve_mapping) {
- GPU_shader_uniform_int(
- sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), TEXTURE_SLOT_CURVE_MAPPING);
+ info.define("USE_CURVE_MAPPING");
+ info.uniform_buf(UNIFORMBUF_SLOT_CURVEMAP, "OCIO_GPUCurveMappingParameters", "curve_mapping");
+ info.sampler(TEXTURE_SLOT_CURVE_MAPPING, ImageType::FLOAT_1D, "curve_mapping_texture");
}
/* Set LUT textures. */
- for (int i = 0; i < textures.luts.size(); i++) {
- GPU_shader_uniform_int(sh,
- GPU_shader_get_uniform(sh, textures.luts[i].sampler_name.c_str()),
- TEXTURE_SLOT_LUTS_OFFSET + i);
- }
+ int slot = TEXTURE_SLOT_LUTS_OFFSET;
+ for (OCIO_GPULutTexture &texture : textures.luts) {
+ ImageType type = GPU_texture_dimensions(texture.texture) == 2 ? ImageType::FLOAT_2D :
+ ImageType::FLOAT_3D;
+ info.sampler(slot++, type, texture.sampler_name.c_str());
+ }
+
+ /* Set LUT uniforms. */
+ if (!textures.uniforms.empty()) {
+ /* NOTE: For simplicity, we pad everything to size of vec4 avoiding sorting and alignment
+ * issues. It is unlikely that this becomes a real issue. */
+ size_t ubo_size = textures.uniforms.size() * sizeof(float) * 4;
+ void *ubo_data_buf = malloc(ubo_size);
+
+ uint32_t *ubo_data = reinterpret_cast<uint32_t *>(ubo_data_buf);
+
+ std::stringstream ss;
+ ss << "struct OCIO_GPULutParameters {\n";
+
+ int index = 0;
+ for (OCIO_GPUUniform &uniform : textures.uniforms) {
+ index += 1;
+ const GpuShaderDesc::UniformData &data = uniform.data;
+ const char *name = uniform.name.c_str();
+ char prefix = ' ';
+ int vec_len;
+ switch (data.m_type) {
+ case UNIFORM_DOUBLE: {
+ vec_len = 1;
+ float value = float(data.m_getDouble());
+ memcpy(ubo_data, &value, sizeof(float));
+ break;
+ }
+ case UNIFORM_BOOL: {
+ prefix = 'b';
+ vec_len = 1;
+ int value = int(data.m_getBool());
+ memcpy(ubo_data, &value, sizeof(int));
+ break;
+ }
+ case UNIFORM_FLOAT3:
+ vec_len = 3;
+ memcpy(ubo_data, data.m_getFloat3().data(), sizeof(float) * 3);
+ break;
+ case UNIFORM_VECTOR_FLOAT:
+ vec_len = data.m_vectorFloat.m_getSize();
+ memcpy(ubo_data, data.m_vectorFloat.m_getVector(), sizeof(float) * vec_len);
+ break;
+ case UNIFORM_VECTOR_INT:
+ prefix = 'i';
+ vec_len = data.m_vectorInt.m_getSize();
+ memcpy(ubo_data, data.m_vectorInt.m_getVector(), sizeof(int) * vec_len);
+ break;
+ default:
+ continue;
+ }
+ /* Align every member to 16bytes. */
+ ubo_data += 4;
+ /* Use a generic variable name because some GLSL compilers can interpret the preprocessor
+ * define as recursive. */
+ ss << " " << prefix << "vec4 var" << index << ";\n";
+ /* Use a define to keep the generated code working. */
+ blender::StringRef suffix = blender::StringRefNull("xyzw").substr(0, vec_len);
+ ss << "#define " << name << " lut_parameters.var" << index << "." << suffix << "\n";
+ }
+ ss << "};\n";
+ info.typedef_source_generated = ss.str();
- /* Set uniforms. */
- for (OCIO_GPUUniform &uniform : textures.uniforms) {
- const GpuShaderDesc::UniformData &data = uniform.data;
- const char *name = uniform.name.c_str();
+ info.uniform_buf(UNIFORMBUF_SLOT_LUTS, "OCIO_GPULutParameters", "lut_parameters");
- if (data.m_getDouble) {
- GPU_shader_uniform_1f(sh, name, (float)data.m_getDouble());
- }
- else if (data.m_getBool) {
- GPU_shader_uniform_1f(sh, name, (float)(data.m_getBool() ? 1.0f : 0.0f));
- }
- else if (data.m_getFloat3) {
- GPU_shader_uniform_3f(sh,
- name,
- (float)data.m_getFloat3()[0],
- (float)data.m_getFloat3()[1],
- (float)data.m_getFloat3()[2]);
- }
- else if (data.m_vectorFloat.m_getSize && data.m_vectorFloat.m_getVector) {
- GPU_shader_uniform_vector(sh,
- GPU_shader_get_uniform(sh, name),
- (int)data.m_vectorFloat.m_getSize(),
- 1,
- (float *)data.m_vectorFloat.m_getVector());
- }
- else if (data.m_vectorInt.m_getSize && data.m_vectorInt.m_getVector) {
- GPU_shader_uniform_vector_int(sh,
- GPU_shader_get_uniform(sh, name),
- (int)data.m_vectorInt.m_getSize(),
- 1,
- (int *)data.m_vectorInt.m_getVector());
- }
+ textures.uniforms_buffer = GPU_uniformbuf_create_ex(
+ ubo_size, ubo_data_buf, "OCIO_LutParameters");
+
+ free(ubo_data_buf);
}
- return true;
+ shader.shader = GPU_shader_create_from_info(reinterpret_cast<GPUShaderCreateInfo *>(&info));
+
+ return (shader.shader != nullptr);
}
/** \} */
@@ -302,7 +324,7 @@ static bool addGPULut2D(OCIO_GPUTextures &textures,
GPU_R16F;
OCIO_GPULutTexture lut;
- lut.texture = GPU_texture_create_2d(texture_name, width, height, 0, format, values);
+ lut.texture = GPU_texture_create_2d(texture_name, width, height, 1, format, values);
if (lut.texture == nullptr) {
return false;
}
@@ -334,7 +356,7 @@ static bool addGPULut3D(OCIO_GPUTextures &textures,
OCIO_GPULutTexture lut;
lut.texture = GPU_texture_create_3d(
- texture_name, edgelen, edgelen, edgelen, 0, GPU_RGB16F, GPU_DATA_FLOAT, values);
+ texture_name, edgelen, edgelen, edgelen, 1, GPU_RGB16F, GPU_DATA_FLOAT, values);
if (lut.texture == nullptr) {
return false;
}
@@ -438,27 +460,65 @@ static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap,
/* Update uniforms. */
OCIO_GPUCurveMappingParameters data;
for (int i = 0; i < 4; i++) {
- data.curve_mapping_range[i] = curve_mapping_settings->range[i];
- data.curve_mapping_mintable[i] = curve_mapping_settings->mintable[i];
- data.curve_mapping_ext_in_x[i] = curve_mapping_settings->ext_in_x[i];
- data.curve_mapping_ext_in_y[i] = curve_mapping_settings->ext_in_y[i];
- data.curve_mapping_ext_out_x[i] = curve_mapping_settings->ext_out_x[i];
- data.curve_mapping_ext_out_y[i] = curve_mapping_settings->ext_out_y[i];
- data.curve_mapping_first_x[i] = curve_mapping_settings->first_x[i];
- data.curve_mapping_first_y[i] = curve_mapping_settings->first_y[i];
- data.curve_mapping_last_x[i] = curve_mapping_settings->last_x[i];
- data.curve_mapping_last_y[i] = curve_mapping_settings->last_y[i];
+ data.range[i] = curve_mapping_settings->range[i];
+ data.mintable[i] = curve_mapping_settings->mintable[i];
+ data.ext_in_x[i] = curve_mapping_settings->ext_in_x[i];
+ data.ext_in_y[i] = curve_mapping_settings->ext_in_y[i];
+ data.ext_out_x[i] = curve_mapping_settings->ext_out_x[i];
+ data.ext_out_y[i] = curve_mapping_settings->ext_out_y[i];
+ data.first_x[i] = curve_mapping_settings->first_x[i];
+ data.first_y[i] = curve_mapping_settings->first_y[i];
+ data.last_x[i] = curve_mapping_settings->last_x[i];
+ data.last_y[i] = curve_mapping_settings->last_y[i];
}
for (int i = 0; i < 3; i++) {
- data.curve_mapping_black[i] = curve_mapping_settings->black[i];
- data.curve_mapping_bwmul[i] = curve_mapping_settings->bwmul[i];
+ data.black[i] = curve_mapping_settings->black[i];
+ data.bwmul[i] = curve_mapping_settings->bwmul[i];
}
- data.curve_mapping_lut_size = curve_mapping_settings->lut_size;
- data.curve_mapping_use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate;
+ data.lut_size = curve_mapping_settings->lut_size;
+ data.use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate;
GPU_uniformbuf_update(curvemap.buffer, &data);
}
+static void updateGPUDisplayParameters(OCIO_GPUShader &shader,
+ float scale,
+ float exponent,
+ float dither,
+ bool use_predivide,
+ bool use_overlay)
+{
+ bool do_update = false;
+ if (shader.parameters_buffer == nullptr) {
+ shader.parameters_buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUParameters));
+ do_update = true;
+ }
+ OCIO_GPUParameters &data = shader.parameters;
+ if (data.scale != scale) {
+ data.scale = scale;
+ do_update = true;
+ }
+ if (data.exponent != exponent) {
+ data.exponent = exponent;
+ do_update = true;
+ }
+ if (data.dither != dither) {
+ data.dither = dither;
+ do_update = true;
+ }
+ if (bool(data.use_predivide) != use_predivide) {
+ data.use_predivide = use_predivide;
+ do_update = true;
+ }
+ if (bool(data.use_overlay) != use_overlay) {
+ data.use_overlay = use_overlay;
+ do_update = true;
+ }
+ if (do_update) {
+ GPU_uniformbuf_update(shader.parameters_buffer, &data);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -609,7 +669,7 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
/* Update and bind curve mapping data. */
if (curve_mapping_settings) {
updateGPUCurveMapping(curvemap, curve_mapping_settings);
- GPU_uniformbuf_bind(curvemap.buffer, shader.ubo_bind);
+ GPU_uniformbuf_bind(curvemap.buffer, UNIFORMBUF_SLOT_CURVEMAP);
GPU_texture_bind(curvemap.texture, TEXTURE_SLOT_CURVE_MAPPING);
}
@@ -623,17 +683,16 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
GPU_texture_bind(textures.luts[i].texture, TEXTURE_SLOT_LUTS_OFFSET + i);
}
+ if (textures.uniforms_buffer) {
+ GPU_uniformbuf_bind(textures.uniforms_buffer, UNIFORMBUF_SLOT_LUTS);
+ }
+
+ updateGPUDisplayParameters(shader, scale, exponent, dither, use_predivide, use_overlay);
+ GPU_uniformbuf_bind(shader.parameters_buffer, UNIFORMBUF_SLOT_DISPLAY);
+
/* TODO(fclem): remove remains of IMM. */
immBindShader(shader.shader);
- /* Bind Shader and set uniforms. */
- // GPU_shader_bind(shader.shader);
- GPU_shader_uniform_float(shader.shader, shader.scale_loc, scale);
- GPU_shader_uniform_float(shader.shader, shader.exponent_loc, exponent);
- GPU_shader_uniform_float(shader.shader, shader.dither_loc, dither);
- GPU_shader_uniform_int(shader.shader, shader.overlay_loc, use_overlay);
- GPU_shader_uniform_int(shader.shader, shader.predivide_loc, use_predivide);
-
return true;
}
diff --git a/intern/opencolorio/ocio_shader_shared.hh b/intern/opencolorio/ocio_shader_shared.hh
new file mode 100644
index 00000000000..c7045217196
--- /dev/null
+++ b/intern/opencolorio/ocio_shader_shared.hh
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#ifndef GPU_SHADER
+# include "GPU_shader_shared_utils.h"
+#endif
+
+struct OCIO_GPUCurveMappingParameters {
+ /* Curve mapping parameters
+ *
+ * See documentation for OCIO_CurveMappingSettings to get fields descriptions.
+ * (this ones pretty much copies stuff from C structure.)
+ */
+ float4 mintable;
+ float4 range;
+ float4 ext_in_x;
+ float4 ext_in_y;
+ float4 ext_out_x;
+ float4 ext_out_y;
+ float4 first_x;
+ float4 first_y;
+ float4 last_x;
+ float4 last_y;
+ float4 black;
+ float4 bwmul;
+ int lut_size;
+ int use_extend_extrapolate;
+ int _pad0;
+ int _pad1;
+};
+
+struct OCIO_GPUParameters {
+ float dither;
+ float scale;
+ float exponent;
+ bool1 use_predivide;
+ bool1 use_overlay;
+ int _pad0;
+ int _pad1;
+ int _pad2;
+};
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
index 38ce9791b5a..bb3aa16a9fe 100644
--- a/intern/opensubdiv/CMakeLists.txt
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -66,6 +66,8 @@ if(WITH_OPENSUBDIV)
internal/evaluator/evaluator_capi.cc
internal/evaluator/evaluator_impl.cc
internal/evaluator/evaluator_impl.h
+ internal/evaluator/gl_compute_evaluator.cc
+ internal/evaluator/gl_compute_evaluator.h
internal/evaluator/patch_map.cc
internal/evaluator/patch_map.h
@@ -104,6 +106,8 @@ if(WITH_OPENSUBDIV)
add_definitions(-DNOMINMAX)
add_definitions(-D_USE_MATH_DEFINES)
endif()
+
+ data_to_c_simple(internal/evaluator/shaders/glsl_compute_kernel.glsl SRC)
else()
list(APPEND SRC
stub/opensubdiv_stub.cc
diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h
index 783efd484aa..dc137e4322e 100644
--- a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h
+++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h
@@ -20,13 +20,11 @@
#define OPENSUBDIV_EVAL_OUTPUT_GPU_H_
#include "internal/evaluator/eval_output.h"
+#include "internal/evaluator/gl_compute_evaluator.h"
-#include <opensubdiv/osd/glComputeEvaluator.h>
#include <opensubdiv/osd/glPatchTable.h>
#include <opensubdiv/osd/glVertexBuffer.h>
-using OpenSubdiv::Osd::GLComputeEvaluator;
-using OpenSubdiv::Osd::GLStencilTableSSBO;
using OpenSubdiv::Osd::GLVertexBuffer;
namespace blender {
diff --git a/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc
new file mode 100644
index 00000000000..acf628c7035
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc
@@ -0,0 +1,647 @@
+//
+// Copyright 2015 Pixar
+//
+// Licensed under the Apache License, Version 2.0 (the "Apache License")
+// with the following modification; you may not use this file except in
+// compliance with the Apache License and the following modification to it:
+// Section 6. Trademarks. is deleted and replaced with:
+//
+// 6. Trademarks. This License does not grant permission to use the trade
+// names, trademarks, service marks, or product names of the Licensor
+// and its affiliates, except as required to comply with Section 4(c) of
+// the License and to reproduce the content of the NOTICE file.
+//
+// You may obtain a copy of the Apache License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the Apache License with the above modification is
+// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the Apache License for the specific
+// language governing permissions and limitations under the Apache License.
+//
+
+#include "gl_compute_evaluator.h"
+
+#include <GL/glew.h>
+
+#include <opensubdiv/far/error.h>
+#include <opensubdiv/far/patchDescriptor.h>
+#include <opensubdiv/far/stencilTable.h>
+#include <opensubdiv/osd/glslPatchShaderSource.h>
+
+#include <cassert>
+#include <cmath>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using OpenSubdiv::Far::LimitStencilTable;
+using OpenSubdiv::Far::StencilTable;
+using OpenSubdiv::Osd::BufferDescriptor;
+using OpenSubdiv::Osd::PatchArray;
+using OpenSubdiv::Osd::PatchArrayVector;
+
+extern "C" char datatoc_glsl_compute_kernel_glsl[];
+
+namespace blender {
+namespace opensubdiv {
+
+template<class T> GLuint createSSBO(std::vector<T> const &src)
+{
+ if (src.empty()) {
+ return 0;
+ }
+
+ GLuint devicePtr = 0;
+
+#if defined(GL_ARB_direct_state_access)
+ if (GLEW_ARB_direct_state_access) {
+ glCreateBuffers(1, &devicePtr);
+ glNamedBufferData(devicePtr, src.size() * sizeof(T), &src.at(0), GL_STATIC_DRAW);
+ }
+ else
+#endif
+ {
+ GLint prev = 0;
+ glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &prev);
+ glGenBuffers(1, &devicePtr);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, devicePtr);
+ glBufferData(GL_SHADER_STORAGE_BUFFER, src.size() * sizeof(T), &src.at(0), GL_STATIC_DRAW);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, prev);
+ }
+
+ return devicePtr;
+}
+
+GLStencilTableSSBO::GLStencilTableSSBO(StencilTable const *stencilTable)
+{
+ _numStencils = stencilTable->GetNumStencils();
+ if (_numStencils > 0) {
+ _sizes = createSSBO(stencilTable->GetSizes());
+ _offsets = createSSBO(stencilTable->GetOffsets());
+ _indices = createSSBO(stencilTable->GetControlIndices());
+ _weights = createSSBO(stencilTable->GetWeights());
+ _duWeights = _dvWeights = 0;
+ _duuWeights = _duvWeights = _dvvWeights = 0;
+ }
+ else {
+ _sizes = _offsets = _indices = _weights = 0;
+ _duWeights = _dvWeights = 0;
+ _duuWeights = _duvWeights = _dvvWeights = 0;
+ }
+}
+
+GLStencilTableSSBO::GLStencilTableSSBO(LimitStencilTable const *limitStencilTable)
+{
+ _numStencils = limitStencilTable->GetNumStencils();
+ if (_numStencils > 0) {
+ _sizes = createSSBO(limitStencilTable->GetSizes());
+ _offsets = createSSBO(limitStencilTable->GetOffsets());
+ _indices = createSSBO(limitStencilTable->GetControlIndices());
+ _weights = createSSBO(limitStencilTable->GetWeights());
+ _duWeights = createSSBO(limitStencilTable->GetDuWeights());
+ _dvWeights = createSSBO(limitStencilTable->GetDvWeights());
+ _duuWeights = createSSBO(limitStencilTable->GetDuuWeights());
+ _duvWeights = createSSBO(limitStencilTable->GetDuvWeights());
+ _dvvWeights = createSSBO(limitStencilTable->GetDvvWeights());
+ }
+ else {
+ _sizes = _offsets = _indices = _weights = 0;
+ _duWeights = _dvWeights = 0;
+ _duuWeights = _duvWeights = _dvvWeights = 0;
+ }
+}
+
+GLStencilTableSSBO::~GLStencilTableSSBO()
+{
+ if (_sizes)
+ glDeleteBuffers(1, &_sizes);
+ if (_offsets)
+ glDeleteBuffers(1, &_offsets);
+ if (_indices)
+ glDeleteBuffers(1, &_indices);
+ if (_weights)
+ glDeleteBuffers(1, &_weights);
+ if (_duWeights)
+ glDeleteBuffers(1, &_duWeights);
+ if (_dvWeights)
+ glDeleteBuffers(1, &_dvWeights);
+ if (_duuWeights)
+ glDeleteBuffers(1, &_duuWeights);
+ if (_duvWeights)
+ glDeleteBuffers(1, &_duvWeights);
+ if (_dvvWeights)
+ glDeleteBuffers(1, &_dvvWeights);
+}
+
+// ---------------------------------------------------------------------------
+
+GLComputeEvaluator::GLComputeEvaluator() : _workGroupSize(64), _patchArraysSSBO(0)
+{
+ memset((void *)&_stencilKernel, 0, sizeof(_stencilKernel));
+ memset((void *)&_patchKernel, 0, sizeof(_patchKernel));
+}
+
+GLComputeEvaluator::~GLComputeEvaluator()
+{
+ if (_patchArraysSSBO) {
+ glDeleteBuffers(1, &_patchArraysSSBO);
+ }
+}
+
+static GLuint compileKernel(BufferDescriptor const &srcDesc,
+ BufferDescriptor const &dstDesc,
+ BufferDescriptor const &duDesc,
+ BufferDescriptor const &dvDesc,
+ BufferDescriptor const &duuDesc,
+ BufferDescriptor const &duvDesc,
+ BufferDescriptor const &dvvDesc,
+ const char *kernelDefine,
+ int workGroupSize)
+{
+ GLuint program = glCreateProgram();
+
+ GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
+
+ std::string patchBasisShaderSource =
+ OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource();
+ const char *patchBasisShaderSourceDefine = "#define OSD_PATCH_BASIS_GLSL\n";
+
+ std::ostringstream defines;
+ defines << "#define LENGTH " << srcDesc.length << "\n"
+ << "#define SRC_STRIDE " << srcDesc.stride << "\n"
+ << "#define DST_STRIDE " << dstDesc.stride << "\n"
+ << "#define WORK_GROUP_SIZE " << workGroupSize << "\n"
+ << kernelDefine << "\n"
+ << patchBasisShaderSourceDefine << "\n";
+
+ bool deriv1 = (duDesc.length > 0 || dvDesc.length > 0);
+ bool deriv2 = (duuDesc.length > 0 || duvDesc.length > 0 || dvvDesc.length > 0);
+ if (deriv1) {
+ defines << "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n";
+ }
+ if (deriv2) {
+ defines << "#define OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES\n";
+ }
+
+ std::string defineStr = defines.str();
+
+ const char *shaderSources[4] = {"#version 430\n", 0, 0, 0};
+
+ shaderSources[1] = defineStr.c_str();
+ shaderSources[2] = patchBasisShaderSource.c_str();
+ shaderSources[3] = datatoc_glsl_compute_kernel_glsl;
+ glShaderSource(shader, 4, shaderSources, NULL);
+ glCompileShader(shader);
+ glAttachShader(program, shader);
+
+ GLint linked = 0;
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+
+ if (linked == GL_FALSE) {
+ char buffer[1024];
+ glGetShaderInfoLog(shader, 1024, NULL, buffer);
+ OpenSubdiv::Far::Error(OpenSubdiv::Far::FAR_RUNTIME_ERROR, buffer);
+
+ glGetProgramInfoLog(program, 1024, NULL, buffer);
+ OpenSubdiv::Far::Error(OpenSubdiv::Far::FAR_RUNTIME_ERROR, buffer);
+
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ glDeleteShader(shader);
+
+ return program;
+}
+
+bool GLComputeEvaluator::Compile(BufferDescriptor const &srcDesc,
+ BufferDescriptor const &dstDesc,
+ BufferDescriptor const &duDesc,
+ BufferDescriptor const &dvDesc,
+ BufferDescriptor const &duuDesc,
+ BufferDescriptor const &duvDesc,
+ BufferDescriptor const &dvvDesc)
+{
+
+ // create a stencil kernel
+ if (!_stencilKernel.Compile(
+ srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, _workGroupSize)) {
+ return false;
+ }
+
+ // create a patch kernel
+ if (!_patchKernel.Compile(
+ srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, _workGroupSize)) {
+ return false;
+ }
+
+ // create a patch arrays buffer
+ if (!_patchArraysSSBO) {
+ glGenBuffers(1, &_patchArraysSSBO);
+ }
+
+ return true;
+}
+
+/* static */
+void GLComputeEvaluator::Synchronize(void * /*kernel*/)
+{
+ // XXX: this is currently just for the performance measuring purpose.
+ // need to be reimplemented by fence and sync.
+ glFinish();
+}
+
+int GLComputeEvaluator::GetDispatchSize(int count) const
+{
+ return (count + _workGroupSize - 1) / _workGroupSize;
+}
+
+void GLComputeEvaluator::DispatchCompute(int totalDispatchSize) const
+{
+ int maxWorkGroupCount[2] = {0, 0};
+
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &maxWorkGroupCount[0]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &maxWorkGroupCount[1]);
+
+ const GLuint maxResX = static_cast<GLuint>(maxWorkGroupCount[0]);
+
+ const int dispatchSize = GetDispatchSize(totalDispatchSize);
+ GLuint dispatchRX = static_cast<GLuint>(dispatchSize);
+ GLuint dispatchRY = 1u;
+ if (dispatchRX > maxResX) {
+ /* Since there are some limitations with regards to the maximum work group size (could be as
+ * low as 64k elements per call), we split the number elements into a "2d" number, with the
+ * final index being computed as `res_x + res_y * max_work_group_size`. Even with a maximum
+ * work group size of 64k, that still leaves us with roughly `64k * 64k = 4` billion elements
+ * total, which should be enough. If not, we could also use the 3rd dimension. */
+ /* TODO(fclem): We could dispatch fewer groups if we compute the prime factorization and
+ * get the smallest rect fitting the requirements. */
+ dispatchRX = dispatchRY = std::ceil(std::sqrt(dispatchSize));
+ /* Avoid a completely empty dispatch line caused by rounding. */
+ if ((dispatchRX * (dispatchRY - 1)) >= dispatchSize) {
+ dispatchRY -= 1;
+ }
+ }
+
+ /* X and Y dimensions may have different limits so the above computation may not be right, but
+ * even with the standard 64k minimum on all dimensions we still have a lot of room. Therefore,
+ * we presume it all fits. */
+ assert(dispatchRY < static_cast<GLuint>(maxWorkGroupCount[1]));
+
+ glDispatchCompute(dispatchRX, dispatchRY, 1);
+}
+
+bool GLComputeEvaluator::EvalStencils(GLuint srcBuffer,
+ BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ BufferDescriptor const &dvDesc,
+ GLuint sizesBuffer,
+ GLuint offsetsBuffer,
+ GLuint indicesBuffer,
+ GLuint weightsBuffer,
+ GLuint duWeightsBuffer,
+ GLuint dvWeightsBuffer,
+ int start,
+ int end) const
+{
+
+ return EvalStencils(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ 0,
+ BufferDescriptor(),
+ 0,
+ BufferDescriptor(),
+ 0,
+ BufferDescriptor(),
+ sizesBuffer,
+ offsetsBuffer,
+ indicesBuffer,
+ weightsBuffer,
+ duWeightsBuffer,
+ dvWeightsBuffer,
+ 0,
+ 0,
+ 0,
+ start,
+ end);
+}
+
+bool GLComputeEvaluator::EvalStencils(GLuint srcBuffer,
+ BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ BufferDescriptor const &dvDesc,
+ GLuint duuBuffer,
+ BufferDescriptor const &duuDesc,
+ GLuint duvBuffer,
+ BufferDescriptor const &duvDesc,
+ GLuint dvvBuffer,
+ BufferDescriptor const &dvvDesc,
+ GLuint sizesBuffer,
+ GLuint offsetsBuffer,
+ GLuint indicesBuffer,
+ GLuint weightsBuffer,
+ GLuint duWeightsBuffer,
+ GLuint dvWeightsBuffer,
+ GLuint duuWeightsBuffer,
+ GLuint duvWeightsBuffer,
+ GLuint dvvWeightsBuffer,
+ int start,
+ int end) const
+{
+
+ if (!_stencilKernel.program)
+ return false;
+ int count = end - start;
+ if (count <= 0) {
+ return true;
+ }
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, srcBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, dstBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, duBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, dvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, duuBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, duvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, dvvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, sizesBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, offsetsBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, indicesBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, weightsBuffer);
+ if (duWeightsBuffer)
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, duWeightsBuffer);
+ if (dvWeightsBuffer)
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, dvWeightsBuffer);
+ if (duuWeightsBuffer)
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 13, duuWeightsBuffer);
+ if (duvWeightsBuffer)
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 14, duvWeightsBuffer);
+ if (dvvWeightsBuffer)
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 15, dvvWeightsBuffer);
+
+ glUseProgram(_stencilKernel.program);
+
+ glUniform1i(_stencilKernel.uniformStart, start);
+ glUniform1i(_stencilKernel.uniformEnd, end);
+ glUniform1i(_stencilKernel.uniformSrcOffset, srcDesc.offset);
+ glUniform1i(_stencilKernel.uniformDstOffset, dstDesc.offset);
+ if (_stencilKernel.uniformDuDesc > 0) {
+ glUniform3i(_stencilKernel.uniformDuDesc, duDesc.offset, duDesc.length, duDesc.stride);
+ }
+ if (_stencilKernel.uniformDvDesc > 0) {
+ glUniform3i(_stencilKernel.uniformDvDesc, dvDesc.offset, dvDesc.length, dvDesc.stride);
+ }
+ if (_stencilKernel.uniformDuuDesc > 0) {
+ glUniform3i(_stencilKernel.uniformDuuDesc, duuDesc.offset, duuDesc.length, duuDesc.stride);
+ }
+ if (_stencilKernel.uniformDuvDesc > 0) {
+ glUniform3i(_stencilKernel.uniformDuvDesc, duvDesc.offset, duvDesc.length, duvDesc.stride);
+ }
+ if (_stencilKernel.uniformDvvDesc > 0) {
+ glUniform3i(_stencilKernel.uniformDvvDesc, dvvDesc.offset, dvvDesc.length, dvvDesc.stride);
+ }
+
+ DispatchCompute(count);
+
+ glUseProgram(0);
+
+ glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
+ for (int i = 0; i < 16; ++i) {
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, 0);
+ }
+
+ return true;
+}
+
+bool GLComputeEvaluator::EvalPatches(GLuint srcBuffer,
+ BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ GLuint patchCoordsBuffer,
+ const PatchArrayVector &patchArrays,
+ GLuint patchIndexBuffer,
+ GLuint patchParamsBuffer) const
+{
+
+ return EvalPatches(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ 0,
+ BufferDescriptor(),
+ 0,
+ BufferDescriptor(),
+ 0,
+ BufferDescriptor(),
+ numPatchCoords,
+ patchCoordsBuffer,
+ patchArrays,
+ patchIndexBuffer,
+ patchParamsBuffer);
+}
+
+bool GLComputeEvaluator::EvalPatches(GLuint srcBuffer,
+ BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ BufferDescriptor const &dvDesc,
+ GLuint duuBuffer,
+ BufferDescriptor const &duuDesc,
+ GLuint duvBuffer,
+ BufferDescriptor const &duvDesc,
+ GLuint dvvBuffer,
+ BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ GLuint patchCoordsBuffer,
+ const PatchArrayVector &patchArrays,
+ GLuint patchIndexBuffer,
+ GLuint patchParamsBuffer) const
+{
+
+ if (!_patchKernel.program)
+ return false;
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, srcBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, dstBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, duBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, dvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, duuBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, duvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, dvvBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, patchCoordsBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, patchIndexBuffer);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, patchParamsBuffer);
+
+ glUseProgram(_patchKernel.program);
+
+ glUniform1i(_patchKernel.uniformSrcOffset, srcDesc.offset);
+ glUniform1i(_patchKernel.uniformDstOffset, dstDesc.offset);
+
+ int patchArraySize = sizeof(PatchArray);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, _patchArraysSSBO);
+ glBufferData(
+ GL_SHADER_STORAGE_BUFFER, patchArrays.size() * patchArraySize, NULL, GL_STATIC_DRAW);
+ for (int i = 0; i < (int)patchArrays.size(); ++i) {
+ glBufferSubData(
+ GL_SHADER_STORAGE_BUFFER, i * patchArraySize, sizeof(PatchArray), &patchArrays[i]);
+ }
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, _patchArraysSSBO);
+
+ if (_patchKernel.uniformDuDesc > 0) {
+ glUniform3i(_patchKernel.uniformDuDesc, duDesc.offset, duDesc.length, duDesc.stride);
+ }
+ if (_patchKernel.uniformDvDesc > 0) {
+ glUniform3i(_patchKernel.uniformDvDesc, dvDesc.offset, dvDesc.length, dvDesc.stride);
+ }
+ if (_patchKernel.uniformDuuDesc > 0) {
+ glUniform3i(_patchKernel.uniformDuuDesc, duuDesc.offset, duuDesc.length, duuDesc.stride);
+ }
+ if (_patchKernel.uniformDuvDesc > 0) {
+ glUniform3i(_patchKernel.uniformDuvDesc, duvDesc.offset, duvDesc.length, duvDesc.stride);
+ }
+ if (_patchKernel.uniformDvvDesc > 0) {
+ glUniform3i(_patchKernel.uniformDvvDesc, dvvDesc.offset, dvvDesc.length, dvvDesc.stride);
+ }
+
+ DispatchCompute(numPatchCoords);
+
+ glUseProgram(0);
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, 0);
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, 0);
+
+ return true;
+}
+// ---------------------------------------------------------------------------
+
+GLComputeEvaluator::_StencilKernel::_StencilKernel() : program(0)
+{
+}
+GLComputeEvaluator::_StencilKernel::~_StencilKernel()
+{
+ if (program) {
+ glDeleteProgram(program);
+ }
+}
+
+bool GLComputeEvaluator::_StencilKernel::Compile(BufferDescriptor const &srcDesc,
+ BufferDescriptor const &dstDesc,
+ BufferDescriptor const &duDesc,
+ BufferDescriptor const &dvDesc,
+ BufferDescriptor const &duuDesc,
+ BufferDescriptor const &duvDesc,
+ BufferDescriptor const &dvvDesc,
+ int workGroupSize)
+{
+ // create stencil kernel
+ if (program) {
+ glDeleteProgram(program);
+ }
+
+ const char *kernelDefine = "#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS\n";
+
+ program = compileKernel(
+ srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, kernelDefine, workGroupSize);
+ if (program == 0)
+ return false;
+
+ // cache uniform locations (TODO: use uniform block)
+ uniformStart = glGetUniformLocation(program, "batchStart");
+ uniformEnd = glGetUniformLocation(program, "batchEnd");
+ uniformSrcOffset = glGetUniformLocation(program, "srcOffset");
+ uniformDstOffset = glGetUniformLocation(program, "dstOffset");
+ uniformDuDesc = glGetUniformLocation(program, "duDesc");
+ uniformDvDesc = glGetUniformLocation(program, "dvDesc");
+ uniformDuuDesc = glGetUniformLocation(program, "duuDesc");
+ uniformDuvDesc = glGetUniformLocation(program, "duvDesc");
+ uniformDvvDesc = glGetUniformLocation(program, "dvvDesc");
+
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+
+GLComputeEvaluator::_PatchKernel::_PatchKernel() : program(0)
+{
+}
+GLComputeEvaluator::_PatchKernel::~_PatchKernel()
+{
+ if (program) {
+ glDeleteProgram(program);
+ }
+}
+
+bool GLComputeEvaluator::_PatchKernel::Compile(BufferDescriptor const &srcDesc,
+ BufferDescriptor const &dstDesc,
+ BufferDescriptor const &duDesc,
+ BufferDescriptor const &dvDesc,
+ BufferDescriptor const &duuDesc,
+ BufferDescriptor const &duvDesc,
+ BufferDescriptor const &dvvDesc,
+ int workGroupSize)
+{
+ // create stencil kernel
+ if (program) {
+ glDeleteProgram(program);
+ }
+
+ const char *kernelDefine = "#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES\n";
+
+ program = compileKernel(
+ srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, kernelDefine, workGroupSize);
+ if (program == 0)
+ return false;
+
+ // cache uniform locations
+ uniformSrcOffset = glGetUniformLocation(program, "srcOffset");
+ uniformDstOffset = glGetUniformLocation(program, "dstOffset");
+ uniformPatchArray = glGetUniformLocation(program, "patchArray");
+ uniformDuDesc = glGetUniformLocation(program, "duDesc");
+ uniformDvDesc = glGetUniformLocation(program, "dvDesc");
+ uniformDuuDesc = glGetUniformLocation(program, "duuDesc");
+ uniformDuvDesc = glGetUniformLocation(program, "duvDesc");
+ uniformDvvDesc = glGetUniformLocation(program, "dvvDesc");
+
+ return true;
+}
+
+} // namespace opensubdiv
+} // namespace blender
diff --git a/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h
new file mode 100644
index 00000000000..85c12f73b08
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h
@@ -0,0 +1,2465 @@
+//
+// Copyright 2015 Pixar
+//
+// Licensed under the Apache License, Version 2.0 (the "Apache License")
+// with the following modification; you may not use this file except in
+// compliance with the Apache License and the following modification to it:
+// Section 6. Trademarks. is deleted and replaced with:
+//
+// 6. Trademarks. This License does not grant permission to use the trade
+// names, trademarks, service marks, or product names of the Licensor
+// and its affiliates, except as required to comply with Section 4(c) of
+// the License and to reproduce the content of the NOTICE file.
+//
+// You may obtain a copy of the Apache License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the Apache License with the above modification is
+// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the Apache License for the specific
+// language governing permissions and limitations under the Apache License.
+//
+
+#ifndef OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_
+#define OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_
+
+#include <opensubdiv/osd/bufferDescriptor.h>
+#include <opensubdiv/osd/opengl.h>
+#include <opensubdiv/osd/types.h>
+#include <opensubdiv/version.h>
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+namespace Far {
+class LimitStencilTable;
+class StencilTable;
+} // namespace Far
+} // namespace OPENSUBDIV_VERSION
+} // namespace OpenSubdiv
+
+namespace blender {
+namespace opensubdiv {
+
+/// \brief GL stencil table (Shader Storage buffer)
+///
+/// This class is a GLSL SSBO representation of OpenSubdiv::Far::StencilTable.
+///
+/// GLSLComputeKernel consumes this table to apply stencils
+///
+class GLStencilTableSSBO {
+ public:
+ static GLStencilTableSSBO *Create(OpenSubdiv::Far::StencilTable const *stencilTable,
+ void *deviceContext = NULL)
+ {
+ (void)deviceContext; // unused
+ return new GLStencilTableSSBO(stencilTable);
+ }
+ static GLStencilTableSSBO *Create(OpenSubdiv::Far::LimitStencilTable const *limitStencilTable,
+ void *deviceContext = NULL)
+ {
+ (void)deviceContext; // unused
+ return new GLStencilTableSSBO(limitStencilTable);
+ }
+
+ explicit GLStencilTableSSBO(OpenSubdiv::Far::StencilTable const *stencilTable);
+ explicit GLStencilTableSSBO(OpenSubdiv::Far::LimitStencilTable const *limitStencilTable);
+ ~GLStencilTableSSBO();
+
+ // interfaces needed for GLSLComputeKernel
+ GLuint GetSizesBuffer() const
+ {
+ return _sizes;
+ }
+ GLuint GetOffsetsBuffer() const
+ {
+ return _offsets;
+ }
+ GLuint GetIndicesBuffer() const
+ {
+ return _indices;
+ }
+ GLuint GetWeightsBuffer() const
+ {
+ return _weights;
+ }
+ GLuint GetDuWeightsBuffer() const
+ {
+ return _duWeights;
+ }
+ GLuint GetDvWeightsBuffer() const
+ {
+ return _dvWeights;
+ }
+ GLuint GetDuuWeightsBuffer() const
+ {
+ return _duuWeights;
+ }
+ GLuint GetDuvWeightsBuffer() const
+ {
+ return _duvWeights;
+ }
+ GLuint GetDvvWeightsBuffer() const
+ {
+ return _dvvWeights;
+ }
+ int GetNumStencils() const
+ {
+ return _numStencils;
+ }
+
+ private:
+ GLuint _sizes;
+ GLuint _offsets;
+ GLuint _indices;
+ GLuint _weights;
+ GLuint _duWeights;
+ GLuint _dvWeights;
+ GLuint _duuWeights;
+ GLuint _duvWeights;
+ GLuint _dvvWeights;
+ int _numStencils;
+};
+
+// ---------------------------------------------------------------------------
+
+class GLComputeEvaluator {
+ public:
+ typedef bool Instantiatable;
+ static GLComputeEvaluator *Create(OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ void *deviceContext = NULL)
+ {
+ return Create(srcDesc,
+ dstDesc,
+ duDesc,
+ dvDesc,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor(),
+ deviceContext);
+ }
+
+ static GLComputeEvaluator *Create(OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ void *deviceContext = NULL)
+ {
+ (void)deviceContext; // not used
+ GLComputeEvaluator *instance = new GLComputeEvaluator();
+ if (instance->Compile(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc))
+ return instance;
+ delete instance;
+ return NULL;
+ }
+
+ /// Constructor.
+ GLComputeEvaluator();
+
+ /// Destructor. note that the GL context must be made current.
+ ~GLComputeEvaluator();
+
+ /// ----------------------------------------------------------------------
+ ///
+ /// Stencil evaluations with StencilTable
+ ///
+ /// ----------------------------------------------------------------------
+
+ /// \brief Generic static stencil function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// transparently from OsdMesh template interface.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLSL kernel
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ static bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ STENCIL_TABLE const *stencilTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalStencils(srcBuffer, srcDesc, dstBuffer, dstDesc, stencilTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc,
+ dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor());
+ if (instance) {
+ bool r = instance->EvalStencils(srcBuffer, srcDesc, dstBuffer, dstDesc, stencilTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic static stencil function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// transparently from OsdMesh template interface.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLSL kernel
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ static bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ STENCIL_TABLE const *stencilTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalStencils(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ stencilTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc);
+ if (instance) {
+ bool r = instance->EvalStencils(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ stencilTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic static stencil function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// transparently from OsdMesh template interface.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLSL kernel
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ static bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ STENCIL_TABLE const *stencilTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalStencils(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ stencilTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc);
+ if (instance) {
+ bool r = instance->EvalStencils(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ stencilTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic stencil function.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ STENCIL_TABLE const *stencilTable) const
+ {
+ return EvalStencils(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ stencilTable->GetSizesBuffer(),
+ stencilTable->GetOffsetsBuffer(),
+ stencilTable->GetIndicesBuffer(),
+ stencilTable->GetWeightsBuffer(),
+ 0,
+ 0,
+ /* start = */ 0,
+ /* end = */ stencilTable->GetNumStencils());
+ }
+
+ /// \brief Generic stencil function.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ STENCIL_TABLE const *stencilTable) const
+ {
+ return EvalStencils(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ stencilTable->GetSizesBuffer(),
+ stencilTable->GetOffsetsBuffer(),
+ stencilTable->GetIndicesBuffer(),
+ stencilTable->GetWeightsBuffer(),
+ stencilTable->GetDuWeightsBuffer(),
+ stencilTable->GetDvWeightsBuffer(),
+ /* start = */ 0,
+ /* end = */ stencilTable->GetNumStencils());
+ }
+
+ /// \brief Generic stencil function.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param stencilTable stencil table to be applied. The table must have
+ /// SSBO interfaces.
+ ///
+ template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE>
+ bool EvalStencils(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ STENCIL_TABLE const *stencilTable) const
+ {
+ return EvalStencils(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ duuBuffer->BindVBO(),
+ duuDesc,
+ duvBuffer->BindVBO(),
+ duvDesc,
+ dvvBuffer->BindVBO(),
+ dvvDesc,
+ stencilTable->GetSizesBuffer(),
+ stencilTable->GetOffsetsBuffer(),
+ stencilTable->GetIndicesBuffer(),
+ stencilTable->GetWeightsBuffer(),
+ stencilTable->GetDuWeightsBuffer(),
+ stencilTable->GetDvWeightsBuffer(),
+ stencilTable->GetDuuWeightsBuffer(),
+ stencilTable->GetDuvWeightsBuffer(),
+ stencilTable->GetDvvWeightsBuffer(),
+ /* start = */ 0,
+ /* end = */ stencilTable->GetNumStencils());
+ }
+
+ /// \brief Dispatch the GLSL compute kernel on GPU asynchronously
+ /// returns false if the kernel hasn't been compiled yet.
+ ///
+ /// @param srcBuffer GL buffer of input primvar source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the srcBuffer
+ ///
+ /// @param dstBuffer GL buffer of output primvar destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer GL buffer of output derivative wrt u
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer GL buffer of output derivative wrt v
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param sizesBuffer GL buffer of the sizes in the stencil table
+ ///
+ /// @param offsetsBuffer GL buffer of the offsets in the stencil table
+ ///
+ /// @param indicesBuffer GL buffer of the indices in the stencil table
+ ///
+ /// @param weightsBuffer GL buffer of the weights in the stencil table
+ ///
+ /// @param duWeightsBuffer GL buffer of the du weights in the stencil table
+ ///
+ /// @param dvWeightsBuffer GL buffer of the dv weights in the stencil table
+ ///
+ /// @param start start index of stencil table
+ ///
+ /// @param end end index of stencil table
+ ///
+ bool EvalStencils(GLuint srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ GLuint sizesBuffer,
+ GLuint offsetsBuffer,
+ GLuint indicesBuffer,
+ GLuint weightsBuffer,
+ GLuint duWeightsBuffer,
+ GLuint dvWeightsBuffer,
+ int start,
+ int end) const;
+
+ /// \brief Dispatch the GLSL compute kernel on GPU asynchronously
+ /// returns false if the kernel hasn't been compiled yet.
+ ///
+ /// @param srcBuffer GL buffer of input primvar source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the srcBuffer
+ ///
+ /// @param dstBuffer GL buffer of output primvar destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the dstBuffer
+ ///
+ /// @param duBuffer GL buffer of output derivative wrt u
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer GL buffer of output derivative wrt v
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer GL buffer of output 2nd derivative wrt u
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer GL buffer of output 2nd derivative wrt u and v
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer GL buffer of output 2nd derivative wrt v
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param sizesBuffer GL buffer of the sizes in the stencil table
+ ///
+ /// @param offsetsBuffer GL buffer of the offsets in the stencil table
+ ///
+ /// @param indicesBuffer GL buffer of the indices in the stencil table
+ ///
+ /// @param weightsBuffer GL buffer of the weights in the stencil table
+ ///
+ /// @param duWeightsBuffer GL buffer of the du weights in the stencil table
+ ///
+ /// @param dvWeightsBuffer GL buffer of the dv weights in the stencil table
+ ///
+ /// @param duuWeightsBuffer GL buffer of the duu weights in the stencil table
+ ///
+ /// @param duvWeightsBuffer GL buffer of the duv weights in the stencil table
+ ///
+ /// @param dvvWeightsBuffer GL buffer of the dvv weights in the stencil table
+ ///
+ /// @param start start index of stencil table
+ ///
+ /// @param end end index of stencil table
+ ///
+ bool EvalStencils(GLuint srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ GLuint duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ GLuint duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ GLuint dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ GLuint sizesBuffer,
+ GLuint offsetsBuffer,
+ GLuint indicesBuffer,
+ GLuint weightsBuffer,
+ GLuint duWeightsBuffer,
+ GLuint dvWeightsBuffer,
+ GLuint duuWeightsBuffer,
+ GLuint duvWeightsBuffer,
+ GLuint dvvWeightsBuffer,
+ int start,
+ int end) const;
+
+ /// ----------------------------------------------------------------------
+ ///
+ /// Limit evaluations with PatchTable
+ ///
+ /// ----------------------------------------------------------------------
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatches(
+ srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc,
+ dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor());
+ if (instance) {
+ bool r = instance->EvalPatches(
+ srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatches(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc);
+ if (instance) {
+ bool r = instance->EvalPatches(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatches(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc);
+ if (instance) {
+ bool r = instance->EvalPatches(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetPatchArrays(),
+ patchTable->GetPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ /// \brief Generic limit eval function with derivatives. This function has
+ /// a same signature as other device kernels have so that it can be
+ /// called in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetPatchArrays(),
+ patchTable->GetPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ /// \brief Generic limit eval function with derivatives. This function has
+ /// a same signature as other device kernels have so that it can be
+ /// called in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatches(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ duuBuffer->BindVBO(),
+ duuDesc,
+ duvBuffer->BindVBO(),
+ duvDesc,
+ dvvBuffer->BindVBO(),
+ dvvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetPatchArrays(),
+ patchTable->GetPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ bool EvalPatches(GLuint srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ GLuint patchCoordsBuffer,
+ const OpenSubdiv::Osd::PatchArrayVector &patchArrays,
+ GLuint patchIndexBuffer,
+ GLuint patchParamsBuffer) const;
+
+ bool EvalPatches(GLuint srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ GLuint dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ GLuint duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ GLuint dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ GLuint duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ GLuint duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ GLuint dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ GLuint patchCoordsBuffer,
+ const OpenSubdiv::Osd::PatchArrayVector &patchArrays,
+ GLuint patchIndexBuffer,
+ GLuint patchParamsBuffer) const;
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesVarying(
+ srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc,
+ dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor());
+ if (instance) {
+ bool r = instance->EvalPatchesVarying(
+ srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetVaryingPatchArrays(),
+ patchTable->GetVaryingPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc);
+ if (instance) {
+ bool r = instance->EvalPatchesVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetVaryingPatchArrays(),
+ patchTable->GetVaryingPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc);
+ if (instance) {
+ bool r = instance->EvalPatchesVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ duuBuffer->BindVBO(),
+ duuDesc,
+ duvBuffer->BindVBO(),
+ duvDesc,
+ dvvBuffer->BindVBO(),
+ dvvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetVaryingPatchArrays(),
+ patchTable->GetVaryingPatchIndexBuffer(),
+ patchTable->GetPatchParamBuffer());
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc,
+ dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor());
+ if (instance) {
+ bool r = instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel = 0) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ 0,
+ OpenSubdiv::Osd::BufferDescriptor(),
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetFVarPatchArrays(fvarChannel),
+ patchTable->GetFVarPatchIndexBuffer(fvarChannel),
+ patchTable->GetFVarPatchParamBuffer(fvarChannel));
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc);
+ if (instance) {
+ bool r = instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel = 0) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetFVarPatchArrays(fvarChannel),
+ patchTable->GetFVarPatchIndexBuffer(fvarChannel),
+ patchTable->GetFVarPatchParamBuffer(fvarChannel));
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ /// @param instance cached compiled instance. Clients are supposed to
+ /// pre-compile an instance of this class and provide
+ /// to this function. If it's null the kernel still
+ /// compute by instantiating on-demand kernel although
+ /// it may cause a performance problem.
+ ///
+ /// @param deviceContext not used in the GLXFB evaluator
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel,
+ GLComputeEvaluator const *instance,
+ void *deviceContext = NULL)
+ {
+
+ if (instance) {
+ return instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ }
+ else {
+ // Create an instance on demand (slow)
+ (void)deviceContext; // unused
+ instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc);
+ if (instance) {
+ bool r = instance->EvalPatchesFaceVarying(srcBuffer,
+ srcDesc,
+ dstBuffer,
+ dstDesc,
+ duBuffer,
+ duDesc,
+ dvBuffer,
+ dvDesc,
+ duuBuffer,
+ duuDesc,
+ duvBuffer,
+ duvDesc,
+ dvvBuffer,
+ dvvDesc,
+ numPatchCoords,
+ patchCoords,
+ patchTable,
+ fvarChannel);
+ delete instance;
+ return r;
+ }
+ return false;
+ }
+ }
+
+ /// \brief Generic limit eval function. This function has a same
+ /// signature as other device kernels have so that it can be called
+ /// in the same way.
+ ///
+ /// @param srcBuffer Input primvar buffer.
+ /// must have BindVBO() method returning a GL
+ /// buffer object of source data
+ ///
+ /// @param srcDesc vertex buffer descriptor for the input buffer
+ ///
+ /// @param dstBuffer Output primvar buffer
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dstDesc vertex buffer descriptor for the output buffer
+ ///
+ /// @param duBuffer Output buffer derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duDesc vertex buffer descriptor for the duBuffer
+ ///
+ /// @param dvBuffer Output buffer derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvDesc vertex buffer descriptor for the dvBuffer
+ ///
+ /// @param duuBuffer Output buffer 2nd derivative wrt u
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duuDesc vertex buffer descriptor for the duuBuffer
+ ///
+ /// @param duvBuffer Output buffer 2nd derivative wrt u and v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param duvDesc vertex buffer descriptor for the duvBuffer
+ ///
+ /// @param dvvBuffer Output buffer 2nd derivative wrt v
+ /// must have BindVBO() method returning a GL
+ /// buffer object of destination data
+ ///
+ /// @param dvvDesc vertex buffer descriptor for the dvvBuffer
+ ///
+ /// @param numPatchCoords number of patchCoords.
+ ///
+ /// @param patchCoords array of locations to be evaluated.
+ /// must have BindVBO() method returning an
+ /// array of PatchCoord struct in VBO.
+ ///
+ /// @param patchTable GLPatchTable or equivalent
+ ///
+ /// @param fvarChannel face-varying channel
+ ///
+ template<typename SRC_BUFFER,
+ typename DST_BUFFER,
+ typename PATCHCOORD_BUFFER,
+ typename PATCH_TABLE>
+ bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ DST_BUFFER *dstBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ DST_BUFFER *duBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ DST_BUFFER *dvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ DST_BUFFER *duuBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ DST_BUFFER *duvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ DST_BUFFER *dvvBuffer,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int numPatchCoords,
+ PATCHCOORD_BUFFER *patchCoords,
+ PATCH_TABLE *patchTable,
+ int fvarChannel = 0) const
+ {
+
+ return EvalPatches(srcBuffer->BindVBO(),
+ srcDesc,
+ dstBuffer->BindVBO(),
+ dstDesc,
+ duBuffer->BindVBO(),
+ duDesc,
+ dvBuffer->BindVBO(),
+ dvDesc,
+ duuBuffer->BindVBO(),
+ duuDesc,
+ duvBuffer->BindVBO(),
+ duvDesc,
+ dvvBuffer->BindVBO(),
+ dvvDesc,
+ numPatchCoords,
+ patchCoords->BindVBO(),
+ patchTable->GetFVarPatchArrays(fvarChannel),
+ patchTable->GetFVarPatchIndexBuffer(fvarChannel),
+ patchTable->GetFVarPatchParamBuffer(fvarChannel));
+ }
+
+ /// ----------------------------------------------------------------------
+ ///
+ /// Other methods
+ ///
+ /// ----------------------------------------------------------------------
+
+ /// Configure GLSL kernel. A valid GL context must be made current before
+ /// calling this function. Returns false if it fails to compile the kernel.
+ bool Compile(
+ OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc = OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc = OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc = OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc = OpenSubdiv::Osd::BufferDescriptor(),
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc = OpenSubdiv::Osd::BufferDescriptor());
+
+ /// Wait the dispatched kernel finishes.
+ static void Synchronize(void *deviceContext);
+
+ private:
+ struct _StencilKernel {
+ _StencilKernel();
+ ~_StencilKernel();
+ bool Compile(OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int workGroupSize);
+ GLuint program;
+ GLuint uniformStart;
+ GLuint uniformEnd;
+ GLuint uniformSrcOffset;
+ GLuint uniformDstOffset;
+ GLuint uniformDuDesc;
+ GLuint uniformDvDesc;
+ GLuint uniformDuuDesc;
+ GLuint uniformDuvDesc;
+ GLuint uniformDvvDesc;
+ } _stencilKernel;
+
+ struct _PatchKernel {
+ _PatchKernel();
+ ~_PatchKernel();
+ bool Compile(OpenSubdiv::Osd::BufferDescriptor const &srcDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dstDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duuDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &duvDesc,
+ OpenSubdiv::Osd::BufferDescriptor const &dvvDesc,
+ int workGroupSize);
+ GLuint program;
+ GLuint uniformSrcOffset;
+ GLuint uniformDstOffset;
+ GLuint uniformPatchArray;
+ GLuint uniformDuDesc;
+ GLuint uniformDvDesc;
+ GLuint uniformDuuDesc;
+ GLuint uniformDuvDesc;
+ GLuint uniformDvvDesc;
+ } _patchKernel;
+
+ int _workGroupSize;
+ GLuint _patchArraysSSBO;
+
+ int GetDispatchSize(int count) const;
+
+ void DispatchCompute(int totalDispatchSize) const;
+};
+} // namespace opensubdiv
+} // namespace blender
+
+#endif // OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_
diff --git a/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl b/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl
new file mode 100644
index 00000000000..2f60aee0999
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl
@@ -0,0 +1,383 @@
+//
+// Copyright 2013 Pixar
+//
+// Licensed under the Apache License, Version 2.0 (the "Apache License")
+// with the following modification; you may not use this file except in
+// compliance with the Apache License and the following modification to it:
+// Section 6. Trademarks. is deleted and replaced with:
+//
+// 6. Trademarks. This License does not grant permission to use the trade
+// names, trademarks, service marks, or product names of the Licensor
+// and its affiliates, except as required to comply with Section 4(c) of
+// the License and to reproduce the content of the NOTICE file.
+//
+// You may obtain a copy of the Apache License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the Apache License with the above modification is
+// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the Apache License for the specific
+// language governing permissions and limitations under the Apache License.
+//
+
+//------------------------------------------------------------------------------
+
+layout(local_size_x = WORK_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
+layout(std430) buffer;
+
+// source and destination buffers
+
+uniform int srcOffset = 0;
+uniform int dstOffset = 0;
+layout(binding = 0) buffer src_buffer
+{
+ float srcVertexBuffer[];
+};
+layout(binding = 1) buffer dst_buffer
+{
+ float dstVertexBuffer[];
+};
+
+ // derivative buffers (if needed)
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+uniform ivec3 duDesc;
+uniform ivec3 dvDesc;
+layout(binding = 2) buffer du_buffer
+{
+ float duBuffer[];
+};
+layout(binding = 3) buffer dv_buffer
+{
+ float dvBuffer[];
+};
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+uniform ivec3 duuDesc;
+uniform ivec3 duvDesc;
+uniform ivec3 dvvDesc;
+layout(binding = 10) buffer duu_buffer
+{
+ float duuBuffer[];
+};
+layout(binding = 11) buffer duv_buffer
+{
+ float duvBuffer[];
+};
+layout(binding = 12) buffer dvv_buffer
+{
+ float dvvBuffer[];
+};
+#endif
+
+ // stencil buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+uniform int batchStart = 0;
+uniform int batchEnd = 0;
+layout(binding = 4) buffer stencilSizes
+{
+ int _sizes[];
+};
+layout(binding = 5) buffer stencilOffsets
+{
+ int _offsets[];
+};
+layout(binding = 6) buffer stencilIndices
+{
+ int _indices[];
+};
+layout(binding = 7) buffer stencilWeights
+{
+ float _weights[];
+};
+
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+layout(binding = 8) buffer stencilDuWeights
+{
+ float _duWeights[];
+};
+layout(binding = 9) buffer stencilDvWeights
+{
+ float _dvWeights[];
+};
+# endif
+
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+layout(binding = 13) buffer stencilDuuWeights
+{
+ float _duuWeights[];
+};
+layout(binding = 14) buffer stencilDuvWeights
+{
+ float _duvWeights[];
+};
+layout(binding = 15) buffer stencilDvvWeights
+{
+ float _dvvWeights[];
+};
+# endif
+
+uint getGlobalInvocationIndex()
+{
+ uint invocations_per_row = gl_WorkGroupSize.x * gl_NumWorkGroups.x;
+ return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * invocations_per_row;
+}
+
+#endif
+
+// patch buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+layout(binding = 4) buffer patchArray_buffer
+{
+ OsdPatchArray patchArrayBuffer[];
+};
+layout(binding = 5) buffer patchCoord_buffer
+{
+ OsdPatchCoord patchCoords[];
+};
+layout(binding = 6) buffer patchIndex_buffer
+{
+ int patchIndexBuffer[];
+};
+layout(binding = 7) buffer patchParam_buffer
+{
+ OsdPatchParam patchParamBuffer[];
+};
+
+OsdPatchCoord GetPatchCoord(int coordIndex)
+{
+ return patchCoords[coordIndex];
+}
+
+OsdPatchArray GetPatchArray(int arrayIndex)
+{
+ return patchArrayBuffer[arrayIndex];
+}
+
+OsdPatchParam GetPatchParam(int patchIndex)
+{
+ return patchParamBuffer[patchIndex];
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+struct Vertex {
+ float vertexData[LENGTH];
+};
+
+void clear(out Vertex v)
+{
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = 0;
+ }
+}
+
+Vertex readVertex(int index)
+{
+ Vertex v;
+ int vertexIndex = srcOffset + index * SRC_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
+ }
+ return v;
+}
+
+void writeVertex(int index, Vertex v)
+{
+ int vertexIndex = dstOffset + index * DST_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
+ }
+}
+
+void addWithWeight(inout Vertex v, const Vertex src, float weight)
+{
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] += weight * src.vertexData[i];
+ }
+}
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+void writeDu(int index, Vertex du)
+{
+ int duIndex = duDesc.x + index * duDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duBuffer[duIndex + i] = du.vertexData[i];
+ }
+}
+
+void writeDv(int index, Vertex dv)
+{
+ int dvIndex = dvDesc.x + index * dvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvBuffer[dvIndex + i] = dv.vertexData[i];
+ }
+}
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+void writeDuu(int index, Vertex duu)
+{
+ int duuIndex = duuDesc.x + index * duuDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duuBuffer[duuIndex + i] = duu.vertexData[i];
+ }
+}
+
+void writeDuv(int index, Vertex duv)
+{
+ int duvIndex = duvDesc.x + index * duvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duvBuffer[duvIndex + i] = duv.vertexData[i];
+ }
+}
+
+void writeDvv(int index, Vertex dvv)
+{
+ int dvvIndex = dvvDesc.x + index * dvvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvvBuffer[dvvIndex + i] = dvv.vertexData[i];
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+void main()
+{
+ int current = int(getGlobalInvocationIndex()) + batchStart;
+
+ if (current >= batchEnd) {
+ return;
+ }
+
+ Vertex dst;
+ clear(dst);
+
+ int offset = _offsets[current], size = _sizes[current];
+
+ for (int stencil = 0; stencil < size; ++stencil) {
+ int vindex = offset + stencil;
+ addWithWeight(dst, readVertex(_indices[vindex]), _weights[vindex]);
+ }
+
+ writeVertex(current, dst);
+
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ Vertex du, dv;
+ clear(du);
+ clear(dv);
+ for (int i = 0; i < size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset + i]);
+ addWithWeight(du, src, _duWeights[offset + i]);
+ addWithWeight(dv, src, _dvWeights[offset + i]);
+ }
+
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+# endif
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ Vertex duu, duv, dvv;
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+ for (int i = 0; i < size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset + i]);
+ addWithWeight(duu, src, _duuWeights[offset + i]);
+ addWithWeight(duv, src, _duvWeights[offset + i]);
+ addWithWeight(dvv, src, _dvvWeights[offset + i]);
+ }
+
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) {
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+# endif
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+// PERFORMANCE: stride could be constant, but not as significant as length
+
+void main()
+{
+
+ int current = int(gl_GlobalInvocationID.x);
+
+ OsdPatchCoord coord = GetPatchCoord(current);
+ OsdPatchArray array = GetPatchArray(coord.arrayIndex);
+ OsdPatchParam param = GetPatchParam(coord.patchIndex);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int nPoints = OsdEvaluatePatchBasis(
+ patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
+
+ for (int cv = 0; cv < nPoints; ++cv) {
+ int index = patchIndexBuffer[indexBase + cv];
+ addWithWeight(dst, readVertex(index), wP[cv]);
+ addWithWeight(du, readVertex(index), wDu[cv]);
+ addWithWeight(dv, readVertex(index), wDv[cv]);
+ addWithWeight(duu, readVertex(index), wDuu[cv]);
+ addWithWeight(duv, readVertex(index), wDuv[cv]);
+ addWithWeight(dvv, readVertex(index), wDvv[cv]);
+ }
+ writeVertex(current, dst);
+
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+# endif
+# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) { // length
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+# endif
+}
+
+#endif
diff --git a/release/datafiles/blender_icons_update.py b/release/datafiles/blender_icons_update.py
index f4299fc510f..c1cf8d6fcb0 100755
--- a/release/datafiles/blender_icons_update.py
+++ b/release/datafiles/blender_icons_update.py
@@ -26,8 +26,8 @@ if sys.platform[:3] == "win":
env["SystemDrive"] = os.environ.get("SystemDrive", "")
env["SystemRoot"] = os.environ.get("SystemRoot", "")
-inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape")
-blender_bin = os.environ.get("BLENDER_BIN", "blender")
+inkscape_bin = "inkscape"
+blender_bin = "blender"
if sys.platform == 'darwin':
inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape'
@@ -36,6 +36,11 @@ if sys.platform == 'darwin':
blender_app_path = '/Applications/Blender.app/Contents/MacOS/Blender'
if os.path.exists(blender_app_path):
blender_bin = blender_app_path
+ else:
+ blender_bin = "Blender"
+
+inkscape_bin = os.environ.get("INKSCAPE_BIN", inkscape_bin)
+blender_bin = os.environ.get("BLENDER_BIN", blender_bin)
cmd = (
inkscape_bin,
diff --git a/release/datafiles/colormanagement/config.ocio b/release/datafiles/colormanagement/config.ocio
index bd342a0577e..bdb04cbf9ce 100644
--- a/release/datafiles/colormanagement/config.ocio
+++ b/release/datafiles/colormanagement/config.ocio
@@ -9,7 +9,7 @@
#
# See ocio-license.txt for details.
-ocio_profile_version: 1
+ocio_profile_version: 2
search_path: "luts:filmic"
strictparsing: true
@@ -100,8 +100,7 @@ colorspaces:
from_reference: !<GroupTransform>
children:
- !<FileTransform> {src: srgb_to_xyz.spimtx, interpolation: linear}
- - !<FileTransform> {src: xyz_D65_to_E.spimtx, interpolation: linear}
- - !<FileTransform> {src: xyz_to_aces.spimtx, interpolation: linear}
+ - !<BuiltinTransform> {style: "UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD", direction: inverse}
- !<ColorSpace>
name: nuke_rec709
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject af8f04db7686d1851d3ac9091472ca21989c8ef
+Subproject 2a5095eed3028e91624d27ca93e4c65f572b809
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index 34168cc0126..7cf8158c42d 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -144,7 +144,7 @@ const UserDef U_default = {
* so invert this by default, see: T67579. */
NDOF_ROTX_INVERT_AXIS | NDOF_ROTY_INVERT_AXIS | NDOF_ROTZ_INVERT_AXIS |
NDOF_PANX_INVERT_AXIS | NDOF_PANY_INVERT_AXIS | NDOF_PANZ_INVERT_AXIS |
- NDOF_ZOOM_INVERT),
+ NDOF_ZOOM_INVERT | NDOF_CAMERA_PAN_ZOOM),
.image_draw_method = IMAGE_DRAW_METHOD_AUTO,
.glalphaclip = 0.004,
.autokey_mode = (AUTOKEY_MODE_NORMAL & ~AUTOKEY_ON),
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject b0274e50da58bc1f0086794a16029ec6e2e9b92
+Subproject 842c215b746f7e14f9299fa8ae50d2fecd870a9
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 554150de87d..3e823f2b6b7 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -489,12 +489,7 @@ def disable_all():
def _blender_manual_url_prefix():
- if _bpy.app.version_cycle in {"rc", "release"}:
- manual_version = "%d.%d" % _bpy.app.version[:2]
- else:
- manual_version = "dev"
-
- return "https://docs.blender.org/manual/en/" + manual_version
+ return "https://docs.blender.org/manual/en/%d.%d" % _bpy.app.version[:2]
def module_bl_info(mod, *, info_basis=None):
diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py
index f34002741c6..6631461eaba 100644
--- a/release/scripts/modules/bl_keymap_utils/io.py
+++ b/release/scripts/modules/bl_keymap_utils/io.py
@@ -52,6 +52,8 @@ def kmi_args_as_data(kmi):
s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True"))
if (mod := kmi.key_modifier) and (mod != 'NONE'):
s.append(f"\"key_modifier\": '{mod:s}'")
+ if (direction := kmi.direction) and (direction != 'ANY'):
+ s.append(f"\"direction\": '{direction:s}'")
if kmi.repeat:
if (
diff --git a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
index 604d1ffd547..6d41e290512 100644
--- a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
+++ b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
@@ -189,6 +189,7 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
'VERTEX_GPENCIL': "gpencil_vertex_tool",
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
'WEIGHT_GPENCIL': "gpencil_weight_tool",
+ 'SCULPT_CURVES': "curves_sculpt_tool",
}.get(mode, None)
else:
attr = None
diff --git a/release/scripts/modules/bl_keymap_utils/versioning.py b/release/scripts/modules/bl_keymap_utils/versioning.py
index ee7cc5daceb..402c21f8ef1 100644
--- a/release/scripts/modules/bl_keymap_utils/versioning.py
+++ b/release/scripts/modules/bl_keymap_utils/versioning.py
@@ -30,4 +30,22 @@ def keyconfig_update(keyconfig_data, keyconfig_version):
# Setting repeat true on other kinds of events is harmless.
item_event["repeat"] = True
+ if keyconfig_version <= (3, 2, 5):
+ # Only copy once.
+ if not has_copy:
+ keyconfig_data = copy.deepcopy(keyconfig_data)
+ has_copy = True
+
+ for _km_name, _km_parms, km_items_data in keyconfig_data:
+ for (_item_op, item_event, _item_prop) in km_items_data["items"]:
+ if ty_new := {
+ 'EVT_TWEAK_L': 'LEFTMOUSE',
+ 'EVT_TWEAK_M': 'MIDDLEMOUSE',
+ 'EVT_TWEAK_R': 'RIGHTMOUSE',
+ }.get(item_event.get("type")):
+ item_event["type"] = ty_new
+ if (value := item_event["value"]) != 'ANY':
+ item_event["direction"] = value
+ item_event["value"] = 'CLICK_DRAG'
+
return keyconfig_data
diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py
index 2676c00c655..5da98cd783d 100644
--- a/release/scripts/modules/rna_keymap_ui.py
+++ b/release/scripts/modules/rna_keymap_ui.py
@@ -180,6 +180,10 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
subrow.prop(kmi, "type", text="")
subrow.prop(kmi, "value", text="")
+ if map_type in {'KEYBOARD', 'MOUSE'} and kmi.value == 'CLICK_DRAG':
+ subrow = sub.row()
+ subrow.prop(kmi, "direction")
+
subrow = sub.row()
subrow.scale_x = 0.75
subrow.prop(kmi, "any", toggle=True)
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index dc2d0acbbee..4b10c29346e 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -3,10 +3,7 @@
import bpy
-if bpy.app.version_cycle in {'rc', 'release'}:
- manual_version = '%d.%d' % bpy.app.version[:2]
-else:
- manual_version = 'dev'
+manual_version = '%d.%d' % bpy.app.version[:2]
url_manual_prefix = "https://docs.blender.org/manual/en/" + manual_version + "/"
@@ -50,7 +47,6 @@ url_manual_mapping = (
("bpy.types.lineartgpencilmodifier.use_offset_towards_custom_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-offset-towards-custom-camera"),
("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"),
("bpy.types.cyclesobjectsettings.shadow_terminator_geometry_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-geometry-offset"),
- ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"),
("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"),
("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"),
("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"),
@@ -114,6 +110,7 @@ url_manual_mapping = (
("bpy.types.gpencilsculptsettings.intersection_threshold*", "grease_pencil/modes/draw/tools/cutter.html#bpy-types-gpencilsculptsettings-intersection-threshold"),
("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"),
("bpy.types.lineartgpencilmodifier.use_intersection_mask*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection-mask"),
+ ("bpy.types.lineartgpencilmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-collection"),
("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"),
("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"),
("bpy.types.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"),
@@ -123,7 +120,6 @@ url_manual_mapping = (
("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"),
("bpy.types.brushgpencilsettings.eraser_strength_factor*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-eraser-strength-factor"),
("bpy.types.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"),
- ("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"),
("bpy.types.cyclesrendersettings.denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-input-passes"),
("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"),
("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"),
@@ -358,6 +354,7 @@ url_manual_mapping = (
("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"),
("bpy.types.viewlayer.use_pass_cryptomatte_object*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-object"),
("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"),
+ ("bpy.ops.ed.lib_id_generate_preview_from_object*", "editors/asset_browser.html#bpy-ops-ed-lib-id-generate-preview-from-object"),
("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"),
("bpy.types.brushgpencilsettings.random_strength*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random-strength"),
("bpy.types.brushgpencilsettings.simplify_factor*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-simplify-factor"),
@@ -379,6 +376,7 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.use_split_pattern*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-pattern"),
("bpy.types.freestylesettings.use_view_map_cache*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-view-map-cache"),
("bpy.types.geometrynodecurvehandletypeselection*", "modeling/geometry_nodes/curve/handle_type_selection.html#bpy-types-geometrynodecurvehandletypeselection"),
+ ("bpy.types.geometrynodeinputmeshvertexneighbors*", "modeling/geometry_nodes/mesh/vertex_neighbors.html#bpy-types-geometrynodeinputmeshvertexneighbors"),
("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"),
("bpy.types.lineartgpencilmodifier.source_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-source-camera"),
("bpy.types.lineartgpencilmodifier.use_face_mark*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark"),
@@ -405,7 +403,6 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.use_fill_limit*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-use-fill-limit"),
("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"),
("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"),
- ("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"),
("bpy.types.cyclesrendersettings.fast_gi_method*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-fast-gi-method"),
("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"),
("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"),
@@ -458,6 +455,7 @@ url_manual_mapping = (
("bpy.types.cyclescamerasettings.panorama_type*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-panorama-type"),
("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-camera"),
("bpy.types.cyclesrendersettings.film_exposure*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-exposure"),
+ ("bpy.types.cyclesrendersettings.sample_offset*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-offset"),
("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-texture-limit"),
("bpy.types.cyclesrendersettings.use_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-denoising"),
("bpy.types.editbone.bbone_custom_handle_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-start"),
@@ -483,6 +481,8 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.use_dashed_line*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-dashed-line"),
("bpy.types.freestylelinestyle.use_same_object*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-same-object"),
("bpy.types.functionnodeinputspecialcharacters*", "modeling/geometry_nodes/text/special_characters.html#bpy-types-functionnodeinputspecialcharacters"),
+ ("bpy.types.geometrynodeinputmeshedgeneighbors*", "modeling/geometry_nodes/mesh/edge_neighbors.html#bpy-types-geometrynodeinputmeshedgeneighbors"),
+ ("bpy.types.geometrynodeinputmeshfaceneighbors*", "modeling/geometry_nodes/mesh/face_neighbors.html#bpy-types-geometrynodeinputmeshfaceneighbors"),
("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"),
("bpy.types.greasepencil.edit_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-edit-curve-resolution"),
("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"),
@@ -495,6 +495,7 @@ url_manual_mapping = (
("bpy.types.sequencertimelineoverlay.show_grid*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-grid"),
("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"),
("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"),
+ ("bpy.types.spacenodeoverlay.show_context_path*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-context-path"),
("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"),
("bpy.types.spacespreadsheet.object_eval_state*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-object-eval-state"),
("bpy.types.spaceuveditor.display_stretch_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-display-stretch-type"),
@@ -522,6 +523,7 @@ url_manual_mapping = (
("bpy.types.freestylelineset.select_edge_mark*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-edge-mark"),
("bpy.types.freestylelinestyle.use_length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-max"),
("bpy.types.freestylelinestyle.use_length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-min"),
+ ("bpy.types.geometrynodeinputmeshedgevertices*", "modeling/geometry_nodes/mesh/edge_vertices.html#bpy-types-geometrynodeinputmeshedgevertices"),
("bpy.types.geometrynodeinputsplineresolution*", "modeling/geometry_nodes/curve/spline_resolution.html#bpy-types-geometrynodeinputsplineresolution"),
("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"),
@@ -617,6 +619,7 @@ url_manual_mapping = (
("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"),
("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"),
("bpy.types.brushgpencilsettings.show_lasso*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-show-lasso"),
+ ("bpy.types.compositornodeconvertcolorspace*", "compositing/types/converter/color_space.html#bpy-types-compositornodeconvertcolorspace"),
("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"),
("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"),
("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"),
@@ -713,7 +716,9 @@ url_manual_mapping = (
("bpy.types.geometrynodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-geometrynodealigneulertovector"),
("bpy.types.geometrynodeattributestatistic*", "modeling/geometry_nodes/attribute/attribute_statistic.html#bpy-types-geometrynodeattributestatistic"),
("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"),
+ ("bpy.types.geometrynodegeometrytoinstance*", "modeling/geometry_nodes/geometry/geometry_to_instance.html#bpy-types-geometrynodegeometrytoinstance"),
("bpy.types.geometrynodeinputmaterialindex*", "modeling/geometry_nodes/material/material_index.html#bpy-types-geometrynodeinputmaterialindex"),
+ ("bpy.types.geometrynodeinputmeshedgeangle*", "modeling/geometry_nodes/mesh/edge_angle.html#bpy-types-geometrynodeinputmeshedgeangle"),
("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"),
("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"),
("bpy.types.geometrynodetranslateinstances*", "modeling/geometry_nodes/instances/translate_instances.html#bpy-types-geometrynodetranslateinstances"),
@@ -793,6 +798,7 @@ url_manual_mapping = (
("bpy.types.freestylesettings.use_culling*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-culling"),
("bpy.types.geometrynodeendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodeendpointselection"),
("bpy.types.geometrynodegeometryproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodegeometryproximity"),
+ ("bpy.types.geometrynodeinputmeshfacearea*", "modeling/geometry_nodes/mesh/face_area.html#bpy-types-geometrynodeinputmeshfacearea"),
("bpy.types.geometrynodeinputsplinecyclic*", "modeling/geometry_nodes/curve/is_spline_cyclic.html#bpy-types-geometrynodeinputsplinecyclic"),
("bpy.types.geometrynodeinstancestopoints*", "modeling/geometry_nodes/instances/instances_to_points.html#bpy-types-geometrynodeinstancestopoints"),
("bpy.types.geometrynodetransferattribute*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodetransferattribute"),
@@ -822,6 +828,7 @@ url_manual_mapping = (
("bpy.types.toolsettings.mesh_select_mode*", "modeling/meshes/selecting/introduction.html#bpy-types-toolsettings-mesh-select-mode"),
("bpy.types.toolsettings.use_snap_project*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-project"),
("bpy.types.transformorientationslot.type*", "editors/3dview/controls/orientation.html#bpy-types-transformorientationslot-type"),
+ ("bpy.types.unitsettings.temperature_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-temperature-unit"),
("bpy.types.vertexweightproximitymodifier*", "modeling/modifiers/modify/weight_proximity.html#bpy-types-vertexweightproximitymodifier"),
("bpy.types.view3doverlay.show_wireframes*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-wireframes"),
("bpy.types.view3dshading.background_type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-background-type"),
@@ -879,6 +886,7 @@ url_manual_mapping = (
("bpy.types.spacetexteditor.use_find_all*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-all"),
("bpy.types.toolsettings.snap_uv_element*", "editors/uv/controls/snapping.html#bpy-types-toolsettings-snap-uv-element"),
("bpy.types.toolsettings.use_snap_rotate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-rotate"),
+ ("bpy.types.unitsettings.system_rotation*", "scene_layout/scene/properties.html#bpy-types-unitsettings-system-rotation"),
("bpy.types.view3doverlay.display_handle*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-display-handle"),
("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"),
("bpy.ops.anim.channels_editable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-editable-toggle"),
@@ -923,11 +931,15 @@ url_manual_mapping = (
("bpy.types.freestylelineset.visibility*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-visibility"),
("bpy.types.freestylelinestyle.chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chaining"),
("bpy.types.freestylelinestyle.sort_key*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-key"),
+ ("bpy.types.geometrynodeaccumulatefield*", "modeling/geometry_nodes/utilities/accumulate_field.html#bpy-types-geometrynodeaccumulatefield"),
("bpy.types.geometrynodecurvesethandles*", "modeling/geometry_nodes/curve/set_handle_type.html#bpy-types-geometrynodecurvesethandles"),
("bpy.types.geometrynodecurvesplinetype*", "modeling/geometry_nodes/curve/set_spline_type.html#bpy-types-geometrynodecurvesplinetype"),
+ ("bpy.types.geometrynodeinputmeshisland*", "modeling/geometry_nodes/mesh/mesh_island.html#bpy-types-geometrynodeinputmeshisland"),
+ ("bpy.types.geometrynodemergebydistance*", "modeling/geometry_nodes/geometry/merge_by_distance.html#bpy-types-geometrynodemergebydistance"),
("bpy.types.geometrynodereplacematerial*", "modeling/geometry_nodes/material/replace_material.html#bpy-types-geometrynodereplacematerial"),
("bpy.types.geometrynoderotateinstances*", "modeling/geometry_nodes/instances/rotate_instances.html#bpy-types-geometrynoderotateinstances"),
("bpy.types.geometrynodesetsplinecyclic*", "modeling/geometry_nodes/curve/set_spline_cyclic.html#bpy-types-geometrynodesetsplinecyclic"),
+ ("bpy.types.geometrynodesplineparameter*", "modeling/geometry_nodes/curve/spline_parameter.html#bpy-types-geometrynodesplineparameter"),
("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"),
("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"),
("bpy.types.imagepaint.screen_grab_size*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-screen-grab-size"),
@@ -997,9 +1009,9 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"),
("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"),
("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"),
- ("bpy.types.geometrynodecurveparameter*", "modeling/geometry_nodes/curve/curve_parameter.html#bpy-types-geometrynodecurveparameter"),
("bpy.types.geometrynodedeletegeometry*", "modeling/geometry_nodes/geometry/delete_geometry.html#bpy-types-geometrynodedeletegeometry"),
("bpy.types.geometrynodeinputcurvetilt*", "modeling/geometry_nodes/curve/curve_tilt.html#bpy-types-geometrynodeinputcurvetilt"),
+ ("bpy.types.geometrynodeinputscenetime*", "modeling/geometry_nodes/input/scene_time.html#bpy-types-geometrynodeinputscenetime"),
("bpy.types.geometrynodepointstovolume*", "modeling/geometry_nodes/point/points_to_volume.html#bpy-types-geometrynodepointstovolume"),
("bpy.types.geometrynodescaleinstances*", "modeling/geometry_nodes/instances/scale_instances.html#bpy-types-geometrynodescaleinstances"),
("bpy.types.geometrynodesetcurveradius*", "modeling/geometry_nodes/curve/set_curve_radius.html#bpy-types-geometrynodesetcurveradius"),
@@ -1079,7 +1091,6 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"),
("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"),
("bpy.types.freestylelinestyle.rounds*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-rounds"),
- ("bpy.types.functionnodecomparefloats*", "modeling/geometry_nodes/utilities/compare_floats.html#bpy-types-functionnodecomparefloats"),
("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"),
("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"),
("bpy.types.geometrynodeinputposition*", "modeling/geometry_nodes/input/position.html#bpy-types-geometrynodeinputposition"),
@@ -1087,6 +1098,7 @@ url_manual_mapping = (
("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"),
("bpy.types.geometrynodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-geometrynodereplacestring"),
("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"),
+ ("bpy.types.geometrynodescaleelements*", "modeling/geometry_nodes/mesh/scale_elements.html#bpy-types-geometrynodescaleelements"),
("bpy.types.geometrynodesubdividemesh*", "modeling/geometry_nodes/mesh/subdivide_mesh.html#bpy-types-geometrynodesubdividemesh"),
("bpy.types.geometrynodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-geometrynodevaluetostring"),
("bpy.types.keyframe.handle_left_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left-type"),
@@ -1110,6 +1122,7 @@ url_manual_mapping = (
("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"),
("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"),
("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"),
+ ("bpy.types.shrinkwrapgpencilmodifier*", "grease_pencil/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapgpencilmodifier"),
("bpy.types.spaceclipeditor.show_grid*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-grid"),
("bpy.types.spaceoutliner.filter_text*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-text"),
("bpy.types.spacetexteditor.find_text*", "editors/text_editor.html#bpy-types-spacetexteditor-find-text"),
@@ -1118,6 +1131,8 @@ url_manual_mapping = (
("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"),
("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"),
("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"),
+ ("bpy.types.unitsettings.scale_length*", "scene_layout/scene/properties.html#bpy-types-unitsettings-scale-length"),
+ ("bpy.types.unitsettings.use_separate*", "scene_layout/scene/properties.html#bpy-types-unitsettings-use-separate"),
("bpy.types.viewlayer.use_motion_blur*", "render/layers/introduction.html#bpy-types-viewlayer-use-motion-blur"),
("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"),
("bpy.types.worldmistsettings.falloff*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-falloff"),
@@ -1150,13 +1165,14 @@ url_manual_mapping = (
("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"),
("bpy.types.assetmetadata.active_tag*", "editors/asset_browser.html#bpy-types-assetmetadata-active-tag"),
("bpy.types.bakesettings.cage_object*", "render/cycles/baking.html#bpy-types-bakesettings-cage-object"),
+ ("bpy.types.bakesettings.margin_type*", "render/cycles/baking.html#bpy-types-bakesettings-margin-type"),
("bpy.types.bone.use_relative_parent*", "animation/armatures/bones/properties/relations.html#bpy-types-bone-use-relative-parent"),
("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"),
("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"),
("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"),
("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"),
("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"),
- ("bpy.types.collection.lineart_usage*", "scene_layout/collections/properties.html#bpy-types-collection-lineart-usage"),
+ ("bpy.types.collection.lineart_usage*", "scene_layout/collections/collections.html#bpy-types-collection-lineart-usage"),
("bpy.types.colormanagedviewsettings*", "render/color_management.html#bpy-types-colormanagedviewsettings"),
("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"),
("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"),
@@ -1172,6 +1188,7 @@ url_manual_mapping = (
("bpy.types.freestylelineset.exclude*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-exclude"),
("bpy.types.freestylelinestyle.alpha*", "render/freestyle/view_layer/line_style/alpha.html#bpy-types-freestylelinestyle-alpha"),
("bpy.types.freestylelinestyle.color*", "render/freestyle/view_layer/line_style/color.html#bpy-types-freestylelinestyle-color"),
+ ("bpy.types.geometrynodefieldatindex*", "modeling/geometry_nodes/utilities/field_at_index.html#bpy-types-geometrynodefieldatindex"),
("bpy.types.geometrynodeinputtangent*", "modeling/geometry_nodes/curve/curve_tangent.html#bpy-types-geometrynodeinputtangent"),
("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"),
("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"),
@@ -1207,6 +1224,7 @@ url_manual_mapping = (
("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"),
("bpy.types.toolsettings.snap_target*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-target"),
("bpy.types.transformcacheconstraint*", "animation/constraints/transform/transform_cache.html#bpy-types-transformcacheconstraint"),
+ ("bpy.types.unitsettings.length_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-length-unit"),
("bpy.types.vertexweighteditmodifier*", "modeling/modifiers/modify/weight_edit.html#bpy-types-vertexweighteditmodifier"),
("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"),
("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"),
@@ -1229,7 +1247,7 @@ url_manual_mapping = (
("bpy.ops.object.vertex_group_clean*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-clean"),
("bpy.ops.poselib.create_pose_asset*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-create-pose-asset"),
("bpy.ops.preferences.theme_install*", "editors/preferences/themes.html#bpy-ops-preferences-theme-install"),
- ("bpy.ops.render.play-rendered-anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"),
+ ("bpy.ops.render.play_rendered_anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"),
("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"),
("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/image.html#bpy-ops-sequencer-image-strip-add"),
("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie.html#bpy-ops-sequencer-movie-strip-add"),
@@ -1253,6 +1271,7 @@ url_manual_mapping = (
("bpy.types.compositornodemovieclip*", "compositing/types/input/movie_clip.html#bpy-types-compositornodemovieclip"),
("bpy.types.compositornodenormalize*", "compositing/types/vector/normalize.html#bpy-types-compositornodenormalize"),
("bpy.types.compositornodepremulkey*", "compositing/types/converter/alpha_convert.html#bpy-types-compositornodepremulkey"),
+ ("bpy.types.compositornodescenetime*", "compositing/types/input/scene_time.html#bpy-types-compositornodescenetime"),
("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"),
("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"),
("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"),
@@ -1273,6 +1292,7 @@ url_manual_mapping = (
("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"),
("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/curve_spiral.html#bpy-types-geometrynodecurvespiral"),
("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"),
+ ("bpy.types.geometrynodeextrudemesh*", "modeling/geometry_nodes/mesh/extrude_mesh.html#bpy-types-geometrynodeextrudemesh"),
("bpy.types.geometrynodefilletcurve*", "modeling/geometry_nodes/curve/fillet_curve.html#bpy-types-geometrynodefilletcurve"),
("bpy.types.geometrynodeinputnormal*", "modeling/geometry_nodes/input/normal.html#bpy-types-geometrynodeinputnormal"),
("bpy.types.geometrynodeinputradius*", "modeling/geometry_nodes/input/radius.html#bpy-types-geometrynodeinputradius"),
@@ -1332,6 +1352,7 @@ url_manual_mapping = (
("bpy.ops.mesh.primitive_plane_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-plane-add"),
("bpy.ops.mesh.primitive_torus_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-torus-add"),
("bpy.ops.mesh.select_non_manifold*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-non-manifold"),
+ ("bpy.ops.object.attribute_convert*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-convert"),
("bpy.ops.object.constraints_clear*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-clear"),
("bpy.ops.object.quadriflow_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-quadriflow-remesh"),
("bpy.ops.object.vertex_group_copy*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy"),
@@ -1388,6 +1409,7 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-gap"),
("bpy.types.freestylesettings.mode*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-mode"),
("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"),
+ ("bpy.types.geometrynodedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodedomainsize"),
("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-geometrynodefloattoint"),
("bpy.types.geometrynodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-geometrynodeinputcolor"),
("bpy.types.geometrynodeinputindex*", "modeling/geometry_nodes/input/input_index.html#bpy-types-geometrynodeinputindex"),
@@ -1418,6 +1440,8 @@ url_manual_mapping = (
("bpy.types.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"),
("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"),
("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"),
+ ("bpy.types.unitsettings.mass_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-mass-unit"),
+ ("bpy.types.unitsettings.time_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-time-unit"),
("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"),
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
@@ -1435,6 +1459,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.stroke_caps_set*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-caps-set"),
("bpy.ops.gpencil.stroke_separate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-separate"),
("bpy.ops.gpencil.stroke_simplify*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify"),
+ ("bpy.ops.graph.blend_to_neighbor*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-blend-to-neighbor"),
("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"),
("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"),
("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"),
@@ -1447,6 +1472,7 @@ url_manual_mapping = (
("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"),
("bpy.ops.node.hide_socket_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-socket-toggle"),
("bpy.ops.node.tree_socket_remove*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-remove"),
+ ("bpy.ops.object.attribute_remove*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-remove"),
("bpy.ops.object.constraints_copy*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"),
("bpy.ops.object.gpencil_modifier*", "grease_pencil/modifiers/index.html#bpy-ops-object-gpencil-modifier"),
("bpy.ops.object.make_links_scene*", "scene_layout/object/editing/link_transfer/link_scene.html#bpy-ops-object-make-links-scene"),
@@ -1509,6 +1535,7 @@ url_manual_mapping = (
("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"),
("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodeedgesplit"),
("bpy.types.geometrynodefillcurve*", "modeling/geometry_nodes/curve/fill_curve.html#bpy-types-geometrynodefillcurve"),
+ ("bpy.types.geometrynodeflipfaces*", "modeling/geometry_nodes/mesh/flip_faces.html#bpy-types-geometrynodeflipfaces"),
("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"),
("bpy.types.geometrynodetrimcurve*", "modeling/geometry_nodes/curve/trim_curve.html#bpy-types-geometrynodetrimcurve"),
("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"),
@@ -1552,6 +1579,7 @@ url_manual_mapping = (
("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"),
("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"),
("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"),
+ ("bpy.ops.graph.equalize_handles*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-equalize-handles"),
("bpy.ops.mesh.bridge-edge-loops*", "modeling/meshes/editing/edge/bridge_edge_loops.html#bpy-ops-mesh-bridge-edge-loops"),
("bpy.ops.mesh.intersect_boolean*", "modeling/meshes/editing/face/intersect_boolean.html#bpy-ops-mesh-intersect-boolean"),
("bpy.ops.mesh.loop_multi_select*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-multi-select"),
@@ -1616,6 +1644,7 @@ url_manual_mapping = (
("bpy.types.motionpath.frame_end*", "animation/motion_paths.html#bpy-types-motionpath-frame-end"),
("bpy.types.noisegpencilmodifier*", "grease_pencil/modifiers/deform/noise.html#bpy-types-noisegpencilmodifier"),
("bpy.types.object.hide_viewport*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-viewport"),
+ ("bpy.types.object.rotation_mode*", "scene_layout/object/properties/transforms.html#bpy-types-object-rotation-mode"),
("bpy.types.object.show_in_front*", "scene_layout/object/properties/display.html#bpy-types-object-show-in-front"),
("bpy.types.posebone.rigify_type*", "addons/rigging/rigify/rig_types/index.html#bpy-types-posebone-rigify-type"),
("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"),
@@ -1718,6 +1747,7 @@ url_manual_mapping = (
("bpy.types.cyclesworldsettings*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings"),
("bpy.types.dashgpencilmodifier*", "grease_pencil/modifiers/generate/dash.html#bpy-types-dashgpencilmodifier"),
("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"),
+ ("bpy.types.functionnodecompare*", "modeling/geometry_nodes/utilities/compare.html#bpy-types-functionnodecompare"),
("bpy.types.geometrynodeboolean*", "modeling/geometry_nodes/input/boolean.html#bpy-types-geometrynodeboolean"),
("bpy.types.geometrynodeinputid*", "modeling/geometry_nodes/input/id.html#bpy-types-geometrynodeinputid"),
("bpy.types.geometrynodeinteger*", "modeling/geometry_nodes/input/integer.html#bpy-types-geometrynodeinteger"),
@@ -1744,6 +1774,7 @@ url_manual_mapping = (
("bpy.types.shadernodelightpath*", "render/shader_nodes/input/light_path.html#bpy-types-shadernodelightpath"),
("bpy.types.shadernodemixshader*", "render/shader_nodes/shader/mix.html#bpy-types-shadernodemixshader"),
("bpy.types.shadernodenormalmap*", "render/shader_nodes/vector/normal_map.html#bpy-types-shadernodenormalmap"),
+ ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"),
("bpy.types.shadernodewireframe*", "render/shader_nodes/input/wireframe.html#bpy-types-shadernodewireframe"),
("bpy.types.spacesequenceeditor*", "video_editing/index.html#bpy-types-spacesequenceeditor"),
("bpy.types.spline.resolution_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-resolution-u"),
@@ -1756,6 +1787,7 @@ url_manual_mapping = (
("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"),
("bpy.types.transformconstraint*", "animation/constraints/transform/transformation.html#bpy-types-transformconstraint"),
("bpy.types.triangulatemodifier*", "modeling/modifiers/generate/triangulate.html#bpy-types-triangulatemodifier"),
+ ("bpy.types.unitsettings.system*", "scene_layout/scene/properties.html#bpy-types-unitsettings-system"),
("bpy.types.viewlayer.use_solid*", "render/layers/introduction.html#bpy-types-viewlayer-use-solid"),
("bpy.types.volume.frame_offset*", "modeling/volumes/properties.html#bpy-types-volume-frame-offset"),
("bpy.types.windowmanager.addon*", "editors/preferences/addons.html#bpy-types-windowmanager-addon"),
@@ -1788,6 +1820,7 @@ url_manual_mapping = (
("bpy.ops.node.node_copy_color*", "interface/controls/nodes/sidebar.html#bpy-ops-node-node-copy-color"),
("bpy.ops.node.read_viewlayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-viewlayers"),
("bpy.ops.node.tree_socket_add*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-add"),
+ ("bpy.ops.object.attribute_add*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-add"),
("bpy.ops.object.data_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data.html#bpy-ops-object-data-transfer"),
("bpy.ops.object.modifier_copy*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy"),
("bpy.ops.object.select_camera*", "scene_layout/object/selecting.html#bpy-ops-object-select-camera"),
@@ -1819,12 +1852,12 @@ url_manual_mapping = (
("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.compositornodecomb*", "compositing/types/converter/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"),
("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"),
("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"),
- ("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"),
+ ("bpy.types.compositornodetime*", "compositing/types/input/time_curve.html#bpy-types-compositornodetime"),
("bpy.types.constraint.enabled*", "animation/constraints/interface/header.html#bpy-types-constraint-enabled"),
("bpy.types.curve.bevel_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-object"),
("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"),
@@ -1860,7 +1893,6 @@ url_manual_mapping = (
("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"),
("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"),
("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"),
- ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"),
("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"),
("bpy.types.shadernodergbcurve*", "modeling/geometry_nodes/color/rgb_curves.html#bpy-types-shadernodergbcurve"),
("bpy.types.shadernodeseparate*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodeseparate"),
@@ -1892,6 +1924,7 @@ url_manual_mapping = (
("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.layer_merge*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-merge"),
("bpy.ops.gpencil.stroke_flip*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-flip"),
("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"),
("bpy.ops.gpencil.stroke_trim*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-trim"),
@@ -1949,7 +1982,7 @@ url_manual_mapping = (
("bpy.types.collisionmodifier*", "physics/collision.html#bpy-types-collisionmodifier"),
("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"),
("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"),
- ("bpy.types.compositornodesep*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodesep"),
+ ("bpy.types.compositornodesep*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodesep"),
("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"),
("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"),
("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"),
@@ -2172,6 +2205,7 @@ url_manual_mapping = (
("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"),
("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"),
("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"),
+ ("bpy.types.geometrynodearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodearc"),
("bpy.types.imagepaint.mode*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-mode"),
("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"),
("bpy.types.materiallineart*", "render/materials/line_art.html#bpy-types-materiallineart"),
@@ -2289,6 +2323,7 @@ url_manual_mapping = (
("bpy.ops.fluid.free_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-free-mesh"),
("bpy.ops.font.select_all*", "modeling/texts/selecting.html#bpy-ops-font-select-all"),
("bpy.ops.gpencil.convert*", "grease_pencil/modes/object/convert_to_geometry.html#bpy-ops-gpencil-convert"),
+ ("bpy.ops.graph.breakdown*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-breakdown"),
("bpy.ops.mask.parent_set*", "movie_clip/masking/editing.html#bpy-ops-mask-parent-set"),
("bpy.ops.mask.select_all*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-all"),
("bpy.ops.mask.select_box*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-box"),
@@ -2475,6 +2510,7 @@ url_manual_mapping = (
("bpy.ops.sculpt.expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-expand"),
("bpy.ops.view3d.select*", "editors/3dview/selecting.html#bpy-ops-view3d-select"),
("bpy.ops.wm.debug_menu*", "advanced/operators.html#bpy-ops-wm-debug-menu"),
+ ("bpy.ops.wm.obj_export*", "files/import_export/obj.html#bpy-ops-wm-obj-export"),
("bpy.ops.wm.properties*", "files/data_blocks.html#bpy-ops-wm-properties"),
("bpy.ops.wm.usd_export*", "files/import_export/usd.html#bpy-ops-wm-usd-export"),
("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"),
@@ -2578,6 +2614,7 @@ url_manual_mapping = (
("bpy.types.node.name*", "interface/controls/nodes/sidebar.html#bpy-types-node-name"),
("bpy.types.nodeframe*", "interface/controls/nodes/frame.html#bpy-types-nodeframe"),
("bpy.types.nodegroup*", "interface/controls/nodes/groups.html#bpy-types-nodegroup"),
+ ("bpy.types.scene.muv*", "addons/uv/magic_uv.html#bpy-types-scene-muv"),
("bpy.types.spotlight*", "render/lights/light_object.html#bpy-types-spotlight"),
("bpy.types.textcurve*", "modeling/texts/index.html#bpy-types-textcurve"),
("bpy.types.udimtiles*", "modeling/meshes/uv/workflows/udims.html#bpy-types-udimtiles"),
@@ -2713,6 +2750,7 @@ url_manual_mapping = (
("bpy.ops.render*", "render/index.html#bpy-ops-render"),
("bpy.ops.script*", "advanced/scripting/index.html#bpy-ops-script"),
("bpy.ops.sculpt*", "sculpt_paint/sculpting/index.html#bpy-ops-sculpt"),
+ ("bpy.ops.uv.muv*", "addons/uv/magic_uv.html#bpy-ops-uv-muv"),
("bpy.ops.uv.pin*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pin"),
("bpy.ops.uv.rip*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip"),
("bpy.ops.view3d*", "editors/3dview/index.html#bpy-ops-view3d"),
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 8884b835130..6f4f862a3b8 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -20,12 +20,8 @@ class Params:
"legacy",
"select_mouse",
"select_mouse_value",
- "select_tweak",
"action_mouse",
- "action_tweak",
"tool_mouse",
- "tool_tweak",
- "tool_maybe_tweak",
"tool_maybe_tweak_value",
"context_menu_event",
"cursor_set_event",
@@ -72,13 +68,13 @@ class Params:
"use_fallback_tool_select_mouse",
# Shorthand for: `('CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value)`.
"select_mouse_value_fallback",
- # Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`.
+ # Shorthand for: `{"type": params.select_mouse, "value": 'CLICK_DRAG'}`.
"select_tweak_event",
# Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')`
"pie_value",
- # Shorthand for: `{"type": params.tool_tweak, "value": 'ANY'}`.
+ # Shorthand for: `{"type": params.tool_mouse, "value": 'CLICK_DRAG'}`.
"tool_tweak_event",
- # Shorthand for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`.
+ # Shorthand for: `{"type": params.tool_mouse, "value": params.tool_maybe_tweak_value}`.
#
# NOTE: This is typically used for active tool key-map items however it should never
# be used for selection tools (the default box-select tool for example).
@@ -122,24 +118,19 @@ class Params:
# Right mouse select.
self.select_mouse = 'RIGHTMOUSE'
self.select_mouse_value = 'PRESS'
- self.select_tweak = 'EVT_TWEAK_R'
self.action_mouse = 'LEFTMOUSE'
- self.action_tweak = 'EVT_TWEAK_L'
self.tool_mouse = 'LEFTMOUSE'
- self.tool_tweak = 'EVT_TWEAK_L'
if use_alt_tool_or_cursor:
- self.tool_maybe_tweak = 'LEFTMOUSE'
self.tool_maybe_tweak_value = 'PRESS'
else:
- self.tool_maybe_tweak = 'EVT_TWEAK_L'
- self.tool_maybe_tweak_value = 'ANY'
+ self.tool_maybe_tweak_value = 'CLICK_DRAG'
self.context_menu_event = {"type": 'W', "value": 'PRESS'}
# Use the "cursor" functionality for RMB select.
if use_alt_tool_or_cursor:
self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}
- self.cursor_tweak_event = {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True}
+ self.cursor_tweak_event = {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "alt": True}
else:
self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'}
self.cursor_tweak_event = None
@@ -151,13 +142,9 @@ class Params:
# events on the same mouse buttons.
self.select_mouse = 'LEFTMOUSE'
self.select_mouse_value = 'CLICK'
- self.select_tweak = 'EVT_TWEAK_L'
self.action_mouse = 'RIGHTMOUSE'
- self.action_tweak = 'EVT_TWEAK_R'
self.tool_mouse = 'LEFTMOUSE'
- self.tool_tweak = 'EVT_TWEAK_L'
- self.tool_maybe_tweak = 'EVT_TWEAK_L'
- self.tool_maybe_tweak_value = 'ANY'
+ self.tool_maybe_tweak_value = 'CLICK_DRAG'
if self.legacy:
self.context_menu_event = {"type": 'W', "value": 'PRESS'}
@@ -165,7 +152,7 @@ class Params:
self.context_menu_event = {"type": 'RIGHTMOUSE', "value": 'PRESS'}
self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True}
- self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True}
+ self.cursor_tweak_event = {"type": 'RIGHTMOUSE', "value": 'CLICK_DRAG', "shift": True}
# Use the "tool" functionality for LMB select.
if use_alt_tool_or_cursor:
@@ -199,15 +186,18 @@ class Params:
# Convenience variables:
self.use_fallback_tool_select_mouse = True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb
self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
- self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'}
+ self.select_tweak_event = {"type": self.select_mouse, "value": 'CLICK_DRAG'}
self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS'
- self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'}
- self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value}
+ self.tool_tweak_event = {"type": self.tool_mouse, "value": 'CLICK_DRAG'}
+ self.tool_maybe_tweak_event = {"type": self.tool_mouse, "value": self.tool_maybe_tweak_value}
# ------------------------------------------------------------------------------
# Constants
+from math import pi
+pi_2 = pi / 2.0
+
# Physical layout.
NUMBERS_1 = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO')
# Numeric order.
@@ -352,13 +342,13 @@ def _template_items_gizmo_tweak_value_click_drag():
("gizmogroup.gizmo_tweak",
{"type": 'LEFTMOUSE', "value": 'CLICK', **any_except("alt")}, None),
("gizmogroup.gizmo_tweak",
- {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None),
+ {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', **any_except("alt")}, None),
]
def _template_items_gizmo_tweak_value_drag():
return [
- ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None),
+ ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', **any_except("alt")}, None),
]
@@ -387,6 +377,11 @@ def _template_items_uv_select_mode(params):
]
else:
return [
+ # TODO(@campbellbarton): should this be kept?
+ # Seems it was included in the new key-map by accident, check on removing
+ # although it's not currently used for anything else.
+ op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}),
+
*_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),
@@ -439,7 +434,7 @@ def _template_items_tool_select(params, operator, cursor_operator, *, extend):
# For right mouse, set the cursor.
return [
(cursor_operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True), ("cursor_transform", True)]}),
]
@@ -861,7 +856,6 @@ def km_mask_editing(params):
items.extend([
("mask.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'},
{"properties": [("deselect_all", not params.legacy)]}),
- ("transform.translate", {"type": 'EVT_TWEAK_R', "value": 'ANY'}, None),
])
items.extend([
@@ -883,9 +877,9 @@ def km_mask_editing(params):
{"properties": [("deselect", True)]}),
("mask.select_box", {"type": 'B', "value": 'PRESS'}, None),
("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None),
- ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
- ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
@@ -905,7 +899,7 @@ def km_mask_editing(params):
("mask.copy_splines", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("mask.paste_splines", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None),
@@ -939,7 +933,7 @@ def km_markers(params):
items.extend([
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.move", {"type": params.select_tweak, "value": 'ANY'},
+ ("marker.move", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True)]}),
("marker.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("marker.select", {"type": params.select_mouse, "value": 'PRESS'}, None),
@@ -949,7 +943,7 @@ def km_markers(params):
{"properties": [("camera", True)]}),
("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("extend", True), ("camera", True)]}),
- ("marker.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("marker.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True)]}),
("marker.select_box", {"type": 'B', "value": 'PRESS'}, None),
*_template_items_select_actions(params, "marker.select_all"),
@@ -1066,10 +1060,10 @@ def km_outliner(params):
("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True},
{"properties": [("extend", True), ("extend_range", True), ("deselect_all", not params.legacy)]}),
("outliner.select_box", {"type": 'B', "value": 'PRESS'}, None),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}),
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True},
{"properties": [("direction", 'UP')]}),
@@ -1091,14 +1085,14 @@ def km_outliner(params):
{"properties": [("all", False)]}),
("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("all", True)]}),
- ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("all", False)]}),
# Fall through to generic context menu if the item(s) selected have no type specific actions.
("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
op_menu("OUTLINER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
op_menu_pie("OUTLINER_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
- ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None),
+ ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None),
("outliner.show_hierarchy", {"type": 'HOME', "value": 'PRESS'}, None),
("outliner.show_active", {"type": 'PERIOD', "value": 'PRESS'}, None),
("outliner.show_active", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
@@ -1176,9 +1170,9 @@ def km_uv_editor(params):
op_tool_optional(
("uv.select_circle", {"type": 'C', "value": 'PRESS'}, None),
(op_tool, "builtin.select_circle"), params),
- ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("uv.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("uv.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("uv.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("uv.select_linked_pick", {"type": 'L', "value": 'PRESS'},
@@ -1205,10 +1199,9 @@ def km_uv_editor(params):
if not params.legacy else
op_menu("IMAGE_MT_uvs_snap", {"type": 'S', "value": 'PRESS', "shift": True})
),
- op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}),
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_edit'),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
op_tool_optional(
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
(op_tool_cycle, "builtin.move"), params),
@@ -1370,7 +1363,6 @@ def km_view3d(params):
*(() if not params.use_pie_click_drag else
(("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)),
("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
- ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
# Numpad views.
("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'},
@@ -1407,7 +1399,7 @@ def km_view3d(params):
("view3d.view_roll", {"type": 'NUMPAD_6', "value": 'PRESS', "shift": True, "repeat": True},
{"properties": [("type", 'RIGHT')]}),
("view3d.view_orbit", {"type": 'NUMPAD_9', "value": 'PRESS'},
- {"properties": [("angle", 3.1415927), ("type", 'ORBITRIGHT')]}),
+ {"properties": [("angle", pi), ("type", 'ORBITRIGHT')]}),
("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS', "shift": True},
{"properties": [("type", 'FRONT'), ("align_active", True)]}),
("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS', "shift": True},
@@ -1421,28 +1413,28 @@ def km_view3d(params):
("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("type", 'BOTTOM'), ("align_active", True)]}),
*((
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH', "alt": True},
{"properties": [("type", 'TOP'), ("relative", True)]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'SOUTH', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'SOUTH', "alt": True},
{"properties": [("type", 'BOTTOM'), ("relative", True)]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'EAST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'EAST', "alt": True},
{"properties": [("type", 'RIGHT'), ("relative", True)]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'WEST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'WEST', "alt": True},
{"properties": [("type", 'LEFT'), ("relative", True)]}),
) if params.v3d_alt_mmb_drag_action == 'RELATIVE' else (
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH', "alt": True},
{"properties": [("type", 'TOP')]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'SOUTH', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'SOUTH', "alt": True},
{"properties": [("type", 'BOTTOM')]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'EAST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'EAST', "alt": True},
{"properties": [("type", 'RIGHT')]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'WEST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'WEST', "alt": True},
{"properties": [("type", 'LEFT')]}),
- # Match the pie menu.
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH_WEST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH_WEST', "alt": True},
{"properties": [("type", 'FRONT')]}),
- ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH_EAST', "alt": True},
+ ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH_EAST', "alt": True},
{"properties": [("type", 'BACK')]}),
+ # Match the pie menu.
)),
("view3d.view_center_pick", {"type": 'MIDDLEMOUSE', "value": 'CLICK', "alt": True}, None),
("view3d.ndof_orbit_zoom", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
@@ -1451,10 +1443,10 @@ def km_view3d(params):
("view3d.ndof_all", {"type": 'NDOF_MOTION', "value": 'ANY', "shift": True, "ctrl": True}, None),
("view3d.view_selected", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'},
{"properties": [("use_all_regions", False)]}),
+ ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CW', "value": 'PRESS'},
+ {"properties": [("angle", pi_2)]}),
("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'},
- {"properties": [("type", 'LEFT')]}),
- ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'},
- {"properties": [("type", 'RIGHT')]}),
+ {"properties": [("angle", -pi_2)]}),
("view3d.view_axis", {"type": 'NDOF_BUTTON_FRONT', "value": 'PRESS'},
{"properties": [("type", 'FRONT')]}),
("view3d.view_axis", {"type": 'NDOF_BUTTON_BACK', "value": 'PRESS'},
@@ -1482,9 +1474,9 @@ def km_view3d(params):
op_tool_optional(
("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None),
(op_tool, "builtin.select_box"), params),
- ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
op_tool_optional(
("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None),
@@ -1501,7 +1493,7 @@ def km_view3d(params):
("view3d.copybuffer", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("view3d.pastebuffer", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
# Transform.
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
op_tool_optional(
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
(op_tool_cycle, "builtin.move"), params),
@@ -1693,15 +1685,15 @@ def km_graph_editor(params):
("graph.select_box", {"type": 'B', "value": 'PRESS'}, None),
("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("graph.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True), ("mode", 'SET')]}),
- ("graph.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True},
+ ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("graph.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True},
+ ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
- ("graph.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("graph.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("graph.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("graph.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("graph.select_circle", {"type": 'C', "value": 'PRESS'}, None),
("graph.select_column", {"type": 'K', "value": 'PRESS'},
@@ -1749,7 +1741,7 @@ def km_graph_editor(params):
{"properties": [("only_active", False)]}),
("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
@@ -1977,11 +1969,11 @@ def km_node_editor(params):
])
items.extend([
- ("node.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("node.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True)]}),
- ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True},
+ ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
- ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
op_tool_optional(
("node.select_box", {"type": 'B', "value": 'PRESS'},
@@ -1990,16 +1982,16 @@ def km_node_editor(params):
op_tool_optional(
("node.select_circle", {"type": 'C', "value": 'PRESS'}, None),
(op_tool, "builtin.select_circle"), params),
- ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("detach", False)]}),
- ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("detach", True)]}),
- ("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("node.resize", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("node.add_reroute",
- {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None),
+ {"type": 'LEFTMOUSE' if params.legacy else 'RIGHTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None),
("node.links_cut",
- {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None),
- ("node.links_mute", {"type": 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True, "alt": True}, None),
+ {"type": 'LEFTMOUSE' if params.legacy else 'RIGHTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, None),
+ ("node.links_mute", {"type": 'RIGHTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
("node.backimage_zoom", {"type": 'V', "value": 'PRESS', "repeat": True},
@@ -2060,26 +2052,31 @@ def km_node_editor(params):
{"type": 'G', "value": 'PRESS'},
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
("node.translate_attach",
- {"type": 'EVT_TWEAK_L', "value": 'ANY'},
- {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
- ("node.translate_attach",
- {"type": params.select_tweak, "value": 'ANY'},
+ {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
+ # Avoid duplicating the previous item.
+ *([] if params.select_mouse == 'LEFTMOUSE' else (
+ ("node.translate_attach", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
+ {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
+ )),
("transform.translate", {"type": 'G', "value": 'PRESS'}, {"properties": [("view2d_edge_pan", True)]}),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
- {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'},
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
+ # Avoid duplicating the previous item.
+ *([] if params.select_mouse == 'LEFTMOUSE' else (
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
+ {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
+ )),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
("node.move_detach_links",
{"type": 'D', "value": 'PRESS', "alt": True},
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
("node.move_detach_links_release",
- {"type": params.action_tweak, "value": 'ANY', "alt": True},
+ {"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True},
{"properties": [("NODE_OT_translate_attach", [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])])]}),
("node.move_detach_links",
- {"type": params.select_tweak, "value": 'ANY', "alt": True},
+ {"type": params.select_mouse, "value": 'CLICK_DRAG', "alt": True},
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.use_snap')]}),
@@ -2108,7 +2105,7 @@ def km_info(params):
("info.select_pick", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
("info.select_pick", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True)]}),
- ("info.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("info.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("wait_for_input", False)]}),
*_template_items_select_actions(params, "info.select_all"),
("info.select_box", {"type": 'B', "value": 'PRESS'}, None),
@@ -2237,10 +2234,10 @@ def km_file_browser_main(params):
("file.next", {"type": 'BUTTON5MOUSE', "value": 'CLICK'}, None),
*_template_items_select_actions(params, "file.select_all"),
("file.select_box", {"type": 'B', "value": 'PRESS'}, None),
- ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties": [("mode", 'ADD')]}),
- ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
@@ -2342,15 +2339,15 @@ def km_dopesheet(params):
{"properties": [("axis_range", False)]}),
("action.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("action.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True), ("mode", 'SET')]}),
- ("action.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True},
+ ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("action.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True},
+ ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
- ("action.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("action.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("action.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("action.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("action.select_circle", {"type": 'C', "value": 'PRESS'}, None),
("action.select_column", {"type": 'K', "value": 'PRESS'},
@@ -2395,7 +2392,7 @@ def km_dopesheet(params):
("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
("transform.transform", {"type": 'G', "value": 'PRESS'},
{"properties": [("mode", 'TIME_TRANSLATE')]}),
- ("transform.transform", {"type": params.select_tweak, "value": 'ANY'},
+ ("transform.transform", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TIME_TRANSLATE')]}),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
@@ -2494,11 +2491,11 @@ def km_nla_editor(params):
{"properties": [("axis_range", False)]}),
("nla.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("nla.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True), ("mode", 'SET')]}),
- ("nla.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True},
+ ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("nla.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True},
+ ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
("nla.previewrange_set", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("nla.view_all", {"type": 'HOME', "value": 'PRESS'}, None),
@@ -2533,7 +2530,7 @@ def km_nla_editor(params):
("nla.fmodifier_add", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("transform.transform", {"type": 'G', "value": 'PRESS'},
{"properties": [("mode", 'TRANSLATION')]}),
- ("transform.transform", {"type": params.select_tweak, "value": 'ANY'},
+ ("transform.transform", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TRANSLATION')]}),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
@@ -2695,7 +2692,7 @@ def km_text(params):
("text.scroll_bar", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("text.scroll", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("text.scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
- ("text.selection_set", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("text.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("text.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
("text.scroll", {"type": 'WHEELUPMOUSE', "value": 'PRESS'},
@@ -2826,11 +2823,11 @@ def km_sequencer(params):
("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
("sequencer.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
- ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY'},
+ ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'},
{"properties": [("tweak", True), ("mode", 'SET')]}),
- ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True},
+ ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True},
+ ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
("sequencer.select_box", {"type": 'B', "value": 'PRESS'}, None),
("sequencer.select_box", {"type": 'B', "value": 'PRESS', "ctrl": True},
@@ -2843,7 +2840,7 @@ def km_sequencer(params):
("wm.context_set_int", {"type": 'O', "value": 'PRESS'},
{"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}),
("transform.seq_slide", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.seq_slide", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.seq_slide", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
@@ -2899,7 +2896,7 @@ def km_sequencerpreview(params):
op_menu_pie("SEQUENCER_MT_preview_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
# Edit.
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
op_tool_optional(
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
(op_tool_cycle, "builtin.move"), params),
@@ -3115,9 +3112,9 @@ def km_clip_editor(params):
("clip.select_box", {"type": 'B', "value": 'PRESS'}, None),
("clip.select_circle", {"type": 'C', "value": 'PRESS'}, None),
op_menu("CLIP_MT_select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}),
- ("clip.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ ("clip.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
- ("clip.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ ("clip.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
("clip.add_marker_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("clip.delete_marker", {"type": 'X', "value": 'PRESS', "shift": True}, None),
@@ -3144,7 +3141,7 @@ def km_clip_editor(params):
("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.use_mute_footage')]}),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True},
@@ -3213,7 +3210,7 @@ def km_clip_graph_editor(params):
("clip.graph_disable_markers", {"type": 'D', "value": 'PRESS', "shift": True},
{"properties": [("action", 'TOGGLE')]}),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
])
@@ -3395,10 +3392,10 @@ def km_animation_channels(params):
# Selection.
*_template_items_select_actions(params, "anim.channels_select_all"),
("anim.channels_select_box", {"type": 'B', "value": 'PRESS'}, None),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties": [("extend", True)]}),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("deselect", True)]}),
# Delete.
("anim.channels_delete", {"type": 'X', "value": 'PRESS'}, None),
@@ -3488,9 +3485,9 @@ def _grease_pencil_selection(params, use_select_mouse=True):
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
(op_tool, "builtin.select_box"), params),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
# In the Node Editor, lasso select needs ALT modifier too
# (as somehow CTRL+LMB drag gets taken for "cut" quite early).
@@ -3498,10 +3495,10 @@ def _grease_pencil_selection(params, use_select_mouse=True):
# as part of standard GP editing keymap. This hotkey combo doesn't seem
# to see much use under standard scenarios?
("gpencil.select_lasso",
- {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
("gpencil.select_lasso",
- {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
*_template_view3d_gpencil_select(
type=params.select_mouse,
@@ -3593,7 +3590,7 @@ def km_grease_pencil_stroke_edit_mode(params):
# Merge Layer
("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None),
# Transform tools
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
op_tool_optional(
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
(op_tool_cycle, "builtin.move"), params),
@@ -3765,7 +3762,7 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
# Box select
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
])
return keymap
@@ -3788,7 +3785,7 @@ def km_grease_pencil_stroke_paint_erase(params):
# Box select (used by eraser)
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
])
return keymap
@@ -4336,9 +4333,9 @@ def km_weight_paint_vertex_selection(params):
items.extend([
*_template_items_select_actions(params, "paint.vert_select_all"),
("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None),
- ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("mode", 'ADD')]}),
- ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None),
])
@@ -4536,7 +4533,7 @@ def km_paint_curve(params):
("paintcurve.draw", {"type": 'RET', "value": 'PRESS'}, None),
("paintcurve.draw", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None),
("transform.translate", {"type": 'G', "value": 'PRESS'}, None),
- ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
+ ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
])
@@ -4652,7 +4649,9 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal
return items
-def _template_view3d_select(*, type, value, legacy):
+def _template_view3d_select(*, type, value, legacy, exclude_mod=None):
+ # NOTE: `exclude_mod` is needed since we don't want this tool to exclude Control-RMB actions when this is used
+ # as a tool key-map with RMB-select and `use_fallback_tool_rmb` is enabled. See T92467.
return [(
"view3d.select",
{"type": type, "value": value, **{m: True for m in mods}},
@@ -4666,7 +4665,7 @@ def _template_view3d_select(*, type, value, legacy):
(("center", "enumerate"), ("ctrl", "alt")),
(("toggle", "enumerate"), ("shift", "alt")),
(("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")),
- )]
+ ) if exclude_mod is None or exclude_mod not in mods]
def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=True):
@@ -5450,6 +5449,22 @@ def km_font(params):
return keymap
+def km_sculpt_curves(params):
+ items = []
+ keymap = (
+ "Sculpt Curves",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("sculpt_curves.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ *_template_paint_radial_control("curves_sculpt"),
+ ])
+
+ return keymap
+
+
def km_object_non_modal(params):
items = []
keymap = (
@@ -6433,8 +6448,8 @@ def km_node_editor_tool_select_circle(params, *, fallback):
"node.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
- value='ANY' if fallback else 'PRESS',
+ type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
+ value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS',
properties=[("wait_for_input", False)])),
]},
)
@@ -6474,7 +6489,7 @@ def km_3d_view_tool_select(params, *, fallback):
*([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select(
params, "view3d.select", "view3d.cursor3d", extend="toggle")),
*([] if (not params.use_fallback_tool_rmb) else _template_view3d_select(
- type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)),
+ type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy, exclude_mod="ctrl")),
]},
)
@@ -6502,8 +6517,8 @@ def km_3d_view_tool_select_circle(params, *, fallback):
"view3d.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
- value='ANY' if fallback else 'PRESS',
+ type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
+ value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS',
properties=[("wait_for_input", False)])),
]},
)
@@ -6572,15 +6587,15 @@ def km_3d_view_tool_shear(params):
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("transform.shear",
- {"type": params.tool_tweak, "value": 'NORTH', **params.tool_modifier},
+ {"type": params.tool_mouse, "value": 'CLICK_DRAG', "direction": 'NORTH', **params.tool_modifier},
{"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}),
("transform.shear",
- {"type": params.tool_tweak, "value": 'SOUTH', **params.tool_modifier},
+ {"type": params.tool_mouse, "value": 'CLICK_DRAG', "direction": 'SOUTH', **params.tool_modifier},
{"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}),
# Use as fallback to catch diagonals too.
("transform.shear",
- {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier},
+ {"type": params.tool_mouse, "value": 'CLICK_DRAG', **params.tool_modifier},
{"properties": [("release_confirm", True), ("orient_axis_ortho", 'X')]}),
]},
)
@@ -7253,7 +7268,7 @@ def km_3d_view_tool_paint_gpencil_line(params):
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7268,7 +7283,7 @@ def km_3d_view_tool_paint_gpencil_polyline(params):
("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7285,7 +7300,7 @@ def km_3d_view_tool_paint_gpencil_box(params):
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7302,7 +7317,7 @@ def km_3d_view_tool_paint_gpencil_circle(params):
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7319,7 +7334,7 @@ def km_3d_view_tool_paint_gpencil_arc(params):
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7332,7 +7347,7 @@ def km_3d_view_tool_paint_gpencil_curve(params):
("gpencil.primitive_curve", params.tool_maybe_tweak_event,
{"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7344,7 +7359,7 @@ def km_3d_view_tool_paint_gpencil_cutter(params):
{"items": [
("gpencil.stroke_cutter", {"type": params.tool_mouse, "value": 'PRESS'}, None),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
]},
)
@@ -7414,8 +7429,8 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback):
"gpencil.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
- type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
- value='ANY' if fallback else 'PRESS',
+ type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
+ value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS',
properties=[("wait_for_input", False)])),
]},
)
@@ -7771,6 +7786,7 @@ def generate_keymaps(params=None):
km_lattice(params),
km_particle(params),
km_font(params),
+ km_sculpt_curves(params),
km_object_non_modal(params),
# Modal maps.
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 c9dea8ab2e7..e65ac32d088 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -7,11 +7,8 @@ class Params:
__slots__ = (
"select_mouse",
"select_mouse_value",
- "select_tweak",
"action_mouse",
- "action_tweak",
"tool_mouse",
- "tool_tweak",
"use_mouse_emulate_3_button",
)
@@ -24,9 +21,7 @@ class Params:
self.tool_mouse = 'LEFTMOUSE'
self.select_mouse = 'LEFTMOUSE'
self.select_mouse_value = 'CLICK'
- self.select_tweak = 'EVT_TWEAK_L'
- self.tool_tweak = 'EVT_TWEAK_L'
- self.action_tweak = 'EVT_TWEAK_R'
+ self.action_mouse = 'RIGHTMOUSE'
self.use_mouse_emulate_3_button = use_mouse_emulate_3_button
@@ -103,7 +98,7 @@ def _template_items_animation():
def _template_items_gizmo_tweak_value_drag():
return [
- ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
]
@@ -479,10 +474,10 @@ def km_outliner(params):
{"properties": [("extend", False), ("extend_range", True), ("deselect_all", True)]}),
("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True},
{"properties": [("extend", True), ("extend_range", True), ("deselect_all", True)]}),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}),
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties": [("tweak", True), ("mode", 'ADD')]}),
- ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("tweak", True), ("mode", 'SUB')]}),
("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True},
{"properties": [("direction", 'UP')]}),
@@ -504,13 +499,13 @@ def km_outliner(params):
{"properties": [("all", False)]}),
("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("all", True)]}),
- ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("all", False)]}),
# Fall through to generic context menu if the item(s) selected have no type specific actions.
("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
op_menu("OUTLINER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
- ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None),
+ ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None),
("outliner.show_hierarchy", {"type": 'A', "value": 'PRESS'}, None),
("outliner.show_active", {"type": 'PERIOD', "value": 'PRESS'}, None),
("outliner.show_active", {"type": 'F', "value": 'PRESS'}, None),
@@ -573,7 +568,7 @@ def km_uv_editor(params):
("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("deselect_all", False)]}),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True},
{"properties": [("extend", True)]}),
("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
@@ -742,7 +737,7 @@ def km_view3d(params):
# Menus.
op_menu_pie("VIEW3D_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
# Transform.
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
op_menu_pie("VIEW3D_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}),
op_menu_pie("VIEW3D_MT_orientations_pie", {"type": 'COMMA', "value": 'PRESS'}),
("view3d.toggle_xray", {"type": 'X', "value": 'PRESS', "alt": True}, None),
@@ -782,9 +777,9 @@ def km_mask_editing(params):
{"properties": [("deselect", True)]}),
("mask.select_box", {"type": 'Q', "value": 'PRESS'}, None),
("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None),
- ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
- ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
("mask.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
("mask.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
@@ -826,7 +821,7 @@ def km_markers(params):
items.extend([
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.move", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("marker.move", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("marker.duplicate", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
@@ -906,11 +901,11 @@ def km_graph_editor(params):
{"properties": [("axis_range", False)]}),
("graph.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties":[("tweak", True), ("axis_range", False), ("mode", 'SET')]}),
- ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties":[("tweak", True), ("axis_range", False), ("mode", 'ADD')]}),
- ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties":[("tweak", True), ("axis_range", False), ("mode", 'SUB')]}),
("graph.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
("graph.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
@@ -931,8 +926,8 @@ def km_graph_editor(params):
("graph.view_frame", {"type": 'A', "value": 'PRESS', "shift": True}, None),
("anim.channels_editable_toggle", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("transform.translate", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.transform", {"type": 'Y', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
@@ -1087,20 +1082,20 @@ def km_node_editor(params):
items.extend(node_select_ops('LEFTMOUSE'))
items.extend([
- ("node.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("node.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("tweak", True)]}),
- ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True},
+ ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True},
{"properties": [("mode", 'ADD')]}),
- ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
- ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("detach", False)]}),
- ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("detach", True)]}),
- ("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("node.add_reroute", {"type": params.action_tweak, "value": 'ANY', "shift": True}, None),
- ("node.links_cut", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, None),
- ("node.links_mute", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("node.resize", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("node.add_reroute", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True}, None),
+ ("node.links_cut", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, None),
+ ("node.links_mute", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None),
("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),
@@ -1147,15 +1142,15 @@ def km_node_editor(params):
("node.viewer_border", {"type": 'Z', "value": 'PRESS'}, None),
("node.clear_viewer_border", {"type": 'Z', "value": 'PRESS', "alt": True}, None),
("node.translate_attach", {"type": 'W', "value": 'PRESS'}, None),
- ("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("node.translate_attach", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None),
+ ("node.translate_attach", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("node.translate_attach", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True)]}),
("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
- ("node.move_detach_links_release", {"type": params.action_tweak, "value": 'ANY', "alt": True}, None),
- ("node.move_detach_links", {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True}, None),
+ ("node.move_detach_links_release", {"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True}, None),
+ ("node.move_detach_links", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "alt": True}, None),
("wm.context_toggle", {"type": 'X', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.use_snap')]}),
])
@@ -1177,7 +1172,7 @@ def km_info(params):
("info.select_pick", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("info.select_pick", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
- ("info.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("info.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("wait_for_input", False)]}),
("info.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True},
{"properties": [("action", 'SELECT')]}),
@@ -1300,8 +1295,8 @@ def km_file_browser_main(params):
("file.next", {"type": 'BUTTON5MOUSE', "value": 'CLICK'}, None),
("file.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
("file.select_box", {"type": 'Q', "value": 'PRESS'}, None),
- ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties": [("mode", 'ADD')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
@@ -1391,11 +1386,11 @@ def km_dopesheet(params):
{"properties": [("axis_range", False)]}),
("action.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'SET')]}),
- ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'ADD')]}),
- ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'SUB')]}),
("action.select_column", {"type": 'K', "value": 'PRESS'},
{"properties": [("mode", 'KEYS')]}),
@@ -1430,9 +1425,9 @@ def km_dopesheet(params):
("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
("transform.transform", {"type": 'W', "value": 'PRESS'},
{"properties": [("mode", 'TIME_TRANSLATE')]}),
- ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("transform.transform", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TIME_TRANSLATE')]}),
- ("transform.transform", {"type": 'EVT_TWEAK_M', "value": 'ANY'},
+ ("transform.transform", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TIME_TRANSLATE')]}),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
@@ -1518,11 +1513,11 @@ def km_nla_editor(params):
{"properties": [("axis_range", False)]}),
("nla.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
{"properties": [("axis_range", True)]}),
- ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties":[("tweak", True), ("mode", 'SET')]}),
- ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties":[("tweak", True), ("mode", 'ADD')]}),
- ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties":[("tweak", True), ("mode", 'SUB')]}),
("nla.view_all", {"type": 'A', "value": 'PRESS'}, None),
("nla.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
@@ -1542,9 +1537,9 @@ def km_nla_editor(params):
("nla.move_down", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
("transform.transform", {"type": 'W', "value": 'PRESS'},
{"properties": [("mode", 'TRANSLATION')]}),
- ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("transform.transform", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TRANSLATION')]}),
- ("transform.transform", {"type": 'EVT_TWEAK_M', "value": 'ANY'},
+ ("transform.transform", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("mode", 'TRANSLATION')]}),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
@@ -1701,7 +1696,7 @@ def km_text(params):
("text.scroll_bar", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("text.scroll", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("text.scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
- ("text.selection_set", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("text.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("text.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
("text.scroll", {"type": 'WHEELUPMOUSE', "value": 'PRESS'},
@@ -1823,19 +1818,19 @@ def km_sequencer(params):
("sequencer.select_linked_pick", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
("sequencer.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True}, None),
- ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties":[("tweak", True), ("mode", 'SET')]}),
- ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
{"properties":[("tweak", True), ("mode", 'ADD')]}),
- ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
+ ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
{"properties":[("tweak", True), ("mode", 'SUB')]}),
("sequencer.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None),
("sequencer.slip", {"type": 'R', "value": 'PRESS'}, None),
("wm.context_set_int", {"type": 'O', "value": 'PRESS'},
{"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}),
("transform.seq_slide", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.seq_slide", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("transform.seq_slide", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None),
+ ("transform.seq_slide", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("transform.seq_slide", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
@@ -2059,7 +2054,7 @@ def km_clip_editor(params):
("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.use_mute_footage')]}),
("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True},
@@ -2113,7 +2108,7 @@ def km_clip_graph_editor(params):
("clip.graph_disable_markers", {"type": 'D', "value": 'PRESS', "shift": True},
{"properties": [("action", 'TOGGLE')]}),
("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
])
@@ -2227,10 +2222,10 @@ def km_animation_channels(params):
("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
("anim.channels_select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True,},
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True,},
{"properties": [("extend", True)]}),
- ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True,},
+ ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True,},
{"properties": [("deselect", True)]}),
# Delete.
("anim.channels_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
@@ -2359,7 +2354,7 @@ def km_grease_pencil_stroke_edit_mode(params):
# Isolate layer
("gpencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
# Transform tools
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("wm.context_toggle", {"type": 'B', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.use_proportional_edit')]}),
# Vertex group menu
@@ -2482,7 +2477,7 @@ def km_grease_pencil_stroke_paint_erase(params):
# Box select (used by eraser)
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
# Lasso select
- ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
])
return keymap
@@ -3125,7 +3120,7 @@ def km_paint_curve(params):
("paintcurve.draw", {"type": 'RET', "value": 'PRESS'}, None),
("paintcurve.draw", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None),
("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
- ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
])
@@ -3408,9 +3403,6 @@ def km_sculpt(params):
("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True, "alt": True}, None),
# Remesh
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
- ("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
- # Remesh
- ("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
# Brush properties
@@ -4021,9 +4013,9 @@ def km_3d_view_tool_interactive_add(params):
"3D View Tool: Object, Add Primitive",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY'},
+ ("view3d.interactive_add", {"type": params.tool_mouse, "value": 'CLICK_DRAG'},
{"properties": [("wait_for_input", False)]}),
- ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True},
+ ("view3d.interactive_add", {"type": params.tool_mouse, "value": 'CLICK_DRAG', "ctrl": True},
{"properties": [("wait_for_input", False)]}),
]},
)
@@ -4187,29 +4179,28 @@ def keymap_transform_tool_mmb(keymap):
km_items_new = []
for kmi in km_items:
ty = kmi[1]["type"]
+ value = kmi[1]["value"]
if km_name.endswith(" (fallback)"):
if ty == 'RIGHTMOUSE':
kmi = (kmi[0], kmi[1].copy(), kmi[2])
kmi[1]["type"] = 'LEFTMOUSE'
km_items_new.append(kmi)
- elif ty == 'EVT_TWEAK_R':
- kmi = (kmi[0], kmi[1].copy(), kmi[2])
- kmi[1]["type"] = 'EVT_TWEAK_L'
- km_items_new.append(kmi)
else:
if ty == 'LEFTMOUSE':
- kmi = (kmi[0], kmi[1].copy(), kmi[2])
- kmi[1]["type"] = 'MIDDLEMOUSE'
- km_items_new.append(kmi)
- elif ty == 'EVT_TWEAK_L':
- kmi = (kmi[0], kmi[1].copy(), kmi[2])
- if kmi[1]["value"] == 'ANY':
+ if value == 'CLICK_DRAG':
+ kmi = (kmi[0], kmi[1].copy(), kmi[2])
+ if kmi[1].get("direction", 'ANY') == 'ANY':
+ kmi[1]["type"] = 'MIDDLEMOUSE'
+ kmi[1]["value"] = 'PRESS'
+ else:
+ # Directional tweaking can't be replaced by middle-mouse.
+ kmi[1]["type"] = 'MIDDLEMOUSE'
+ km_items_new.append(kmi)
+ else:
+ kmi = (kmi[0], kmi[1].copy(), kmi[2])
kmi[1]["type"] = 'MIDDLEMOUSE'
kmi[1]["value"] = 'PRESS'
- else:
- # Directional tweaking can't be replaced by middle-mouse.
- kmi[1]["type"] = 'EVT_TWEAK_M'
- km_items_new.append(kmi)
+ km_items_new.append(kmi)
km_items.extend(km_items_new)
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 9ba5480cf5a..a7f401afad1 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -963,21 +963,19 @@ class WM_OT_url_open(Operator):
return {'FINISHED'}
-# NOTE: needed for Python 3.10 since there are name-space issues with annotations.
-# This can be moved into the class as a static-method once Python 3.9x is dropped.
-def _wm_url_open_preset_type_items(_self, _context):
- return [item for (item, _) in WM_OT_url_open_preset.preset_items]
-
-
class WM_OT_url_open_preset(Operator):
"""Open a preset website in the web browser"""
bl_idname = "wm.url_open_preset"
bl_label = "Open Preset Website"
bl_options = {'INTERNAL'}
+ @staticmethod
+ def _wm_url_open_preset_type_items(_self, _context):
+ return [item for (item, _) in WM_OT_url_open_preset.preset_items]
+
type: EnumProperty(
name="Site",
- items=_wm_url_open_preset_type_items,
+ items=WM_OT_url_open_preset._wm_url_open_preset_type_items,
)
id: StringProperty(
@@ -997,11 +995,10 @@ class WM_OT_url_open_preset(Operator):
return "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
def _url_from_manual(self, _context):
- if bpy.app.version_cycle in {"rc", "release"}:
- manual_version = "%d.%d" % bpy.app.version[:2]
- else:
- manual_version = "dev"
- return "https://docs.blender.org/manual/en/" + manual_version + "/"
+ return "https://docs.blender.org/manual/en/%d.%d/" % bpy.app.version[:2]
+
+ def _url_from_api(self, _context):
+ return "https://docs.blender.org/api/%d.%d/" % bpy.app.version[:2]
# This list is: (enum_item, url) pairs.
# Allow dynamically extending.
@@ -1016,9 +1013,12 @@ class WM_OT_url_open_preset(Operator):
(('RELEASE_NOTES', "Release Notes",
"Read about what's new in this version of Blender"),
_url_from_release_notes),
- (('MANUAL', "Manual",
+ (('MANUAL', "User Manual",
"The reference manual for this version of Blender"),
_url_from_manual),
+ (('API', "Python API Reference",
+ "The API reference manual for this version of Blender"),
+ _url_from_api),
# Static URL's.
(('FUND', "Development Fund",
@@ -1233,11 +1233,7 @@ class WM_OT_doc_view(Operator):
bl_label = "View Documentation"
doc_id: doc_id
- if bpy.app.version_cycle in {"release", "rc", "beta"}:
- _prefix = ("https://docs.blender.org/api/%d.%d" %
- (bpy.app.version[0], bpy.app.version[1]))
- else:
- _prefix = ("https://docs.blender.org/api/master")
+ _prefix = "https://docs.blender.org/api/%d.%d" % bpy.app.version[:2]
def execute(self, _context):
url = _wm_doc_get_id(self.doc_id, do_url=True, url_prefix=self._prefix, report=self.report)
@@ -1283,12 +1279,6 @@ rna_vector_subtype_items = (
)
-# NOTE: needed for Python 3.10 since there are name-space issues with annotations.
-# This can be moved into the class as a static-method once Python 3.9x is dropped.
-def _wm_properties_edit_subtype_items(_self, _context):
- return WM_OT_properties_edit.subtype_items
-
-
class WM_OT_properties_edit(Operator):
"""Change a custom property's type, or adjust how it is displayed in the interface"""
bl_idname = "wm.properties_edit"
@@ -1395,7 +1385,7 @@ class WM_OT_properties_edit(Operator):
)
subtype: EnumProperty(
name="Subtype",
- items=_wm_properties_edit_subtype_items,
+ items=WM_OT_properties_edit.subtype_items,
)
# String properties.
diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py
index 548ce72c429..629399084ba 100644
--- a/release/scripts/startup/bl_ui/properties_animviz.py
+++ b/release/scripts/startup/bl_ui/properties_animviz.py
@@ -24,55 +24,55 @@ class MotionPathButtonsPanel:
layout.use_property_split = True
layout.use_property_decorate = False
- row = layout.row(align=True)
- row.prop(mps, "type")
- if mps.type == 'RANGE':
- if bones:
- row.operator("pose.paths_range_update", text="", icon='TIME')
- else:
- row.operator("object.paths_range_update", text="", icon='TIME')
-
+ # Display Range
+ col = layout.column(align=True)
+ col.prop(mps, "type")
+ col = layout.column(align=True)
if mps.type == 'CURRENT_FRAME':
- col = layout.column(align=True)
col.prop(mps, "frame_before", text="Frame Range Before")
col.prop(mps, "frame_after", text="After")
- col.prop(mps, "frame_step", text="Step")
- elif mps.type == 'RANGE':
- col = layout.column(align=True)
- col.prop(mps, "frame_start", text="Frame Range Start")
- col.prop(mps, "frame_end", text="End")
- col.prop(mps, "frame_step", text="Step")
+ col.prop(mps, "frame_step", text="Step")
+
+ # Calculation Range
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(mps, "range", text="Calculation Range")
if mpath:
col = layout.column(align=True)
- col.enabled = False
- if bones:
- col.prop(mpath, "frame_start", text="Bone Cache From")
- else:
- col.prop(mpath, "frame_start", text="Cache From")
- col.prop(mpath, "frame_end", text="To")
+ row = col.row(align=True)
+ row.enabled = False
+ row.prop(mpath, "frame_start", text="Cached Range")
+ row.prop(mpath, "frame_end", text="")
col = layout.column(align=True)
-
+ row = col.row(align=True)
if bones:
- col.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA')
+ row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA')
+ row.operator("pose.paths_clear", text="", icon='X').only_selected = True
+ row = col.row(align=True)
+ row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
+ row.operator("pose.paths_clear", text="", icon='X').only_selected = False
else:
- col.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA')
+ row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA')
+ row.operator("object.paths_clear", text="", icon='X').only_selected = True
+ row = col.row(align=True)
+ row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
+ row.operator("object.paths_clear", text="", icon='X').only_selected = False
else:
col = layout.column(align=True)
- col.label(text="Nothing to show yet...", icon='ERROR')
+ col.label(text="No Motion Path generated yet", icon='ERROR')
+ # Don't invoke settings popup because settings are right above
+ col.operator_context = 'EXEC_REGION_WIN'
if bones:
- col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA')
+ col.operator(
+ "pose.paths_calculate", text="Generate for selected bones", icon='BONE_DATA')
else:
- col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA')
-
- row = col.row(align=True)
- row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
- if bones:
- row.operator("pose.paths_clear", text="", icon='X')
- else:
- row.operator("object.paths_clear", text="", icon='X')
+ col.operator("object.paths_calculate", text="Generate", icon='OBJECT_DATA')
+ row = col.row(align=True)
+ row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
+ row.operator("object.paths_clear", text="", icon='X').only_selected = False
class MotionPathButtonsPanel_display:
diff --git a/release/scripts/startup/bl_ui/properties_data_curves.py b/release/scripts/startup/bl_ui/properties_data_curves.py
index 3e350575bc8..1bb5fc9afbe 100644
--- a/release/scripts/startup/bl_ui/properties_data_curves.py
+++ b/release/scripts/startup/bl_ui/properties_data_curves.py
@@ -35,6 +35,17 @@ class DATA_PT_context_curves(DataButtonsPanel, Panel):
layout.template_ID(space, "pin_id")
+class DATA_PT_curves_surface(DataButtonsPanel, Panel):
+ bl_label = "Surface"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ def draw(self, context):
+ layout = self.layout
+ ob = context.object
+
+ layout.prop(ob.data, "surface")
+
+
class CURVES_MT_add_attribute(Menu):
bl_label = "Add Attribute"
@@ -115,6 +126,7 @@ class DATA_PT_custom_props_curves(DataButtonsPanel, PropertyPanel, Panel):
classes = (
DATA_PT_context_curves,
DATA_PT_CURVES_attributes,
+ DATA_PT_curves_surface,
DATA_PT_custom_props_curves,
CURVES_MT_add_attribute,
CURVES_UL_attributes,
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index ca623797c49..9e40a8d364a 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -79,6 +79,8 @@ class UnifiedPaintPanel:
return tool_settings.gpencil_weight_paint
elif mode == 'VERTEX_GPENCIL':
return tool_settings.gpencil_vertex_paint
+ elif mode == 'SCULPT_CURVES':
+ return tool_settings.curves_sculpt
return None
@staticmethod
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 8e328e7cf2b..7b7fc9dcf77 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -663,6 +663,10 @@ class DOPESHEET_MT_context_menu(Menu):
layout.operator_menu_enum("action.mirror", "type", text="Mirror")
layout.operator_menu_enum("action.snap", "type", text="Snap")
+ if st.mode == 'DOPESHEET':
+ layout.separator()
+ layout.menu("VIEW3D_MT_motion_path")
+
class DOPESHEET_MT_channel_context_menu(Menu):
bl_label = "Dope Sheet Channel Context Menu"
diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index 6f9ef12c3b7..6bc11d51ca0 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -390,6 +390,9 @@ class GRAPH_MT_context_menu(Menu):
layout.operator_menu_enum("graph.mirror", "type", text="Mirror")
layout.operator_menu_enum("graph.snap", "type", text="Snap")
+ layout.separator()
+ layout.menu("VIEW3D_MT_motion_path")
+
class GRAPH_MT_pivot_pie(Menu):
bl_label = "Pivot Point"
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py
index 411db8781e1..3e4f7f6fbcb 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_common.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py
@@ -1038,9 +1038,6 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False):
if props is None:
print("Error:", gizmo_group, "could not access properties!")
else:
- for key in props.bl_rna.properties.keys():
- props.property_unset(key)
-
gizmo_properties = item.widget_properties
if gizmo_properties is not None:
if not isinstance(gizmo_properties, list):
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 338bf5a9d5d..f4aa4c50d76 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -2306,6 +2306,19 @@ class _defs_gpencil_weight:
)
+class _defs_curves_sculpt:
+
+ @staticmethod
+ def generate_from_brushes(context):
+ return generate_from_enum_ex(
+ context,
+ idname_prefix="builtin_brush.",
+ icon_prefix="ops.curves.sculpt_",
+ type= bpy.types.Brush,
+ attr="curves_sculpt_tool",
+ )
+
+
class _defs_gpencil_vertex:
@staticmethod
@@ -3065,6 +3078,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
else ()
),
],
+ 'SCULPT_CURVES': [
+ _defs_curves_sculpt.generate_from_brushes,
+ ],
}
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 493cad6d2db..3b6bdd01efa 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -448,8 +448,7 @@ class TOPBAR_MT_file_import(Menu):
def draw(self, _context):
if bpy.app.build_options.collada:
- self.layout.operator("wm.collada_import",
- text="Collada (Default) (.dae)")
+ self.layout.operator("wm.collada_import", text="Collada (.dae)")
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
if bpy.app.build_options.usd:
@@ -467,8 +466,7 @@ class TOPBAR_MT_file_export(Menu):
def draw(self, _context):
self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj)")
if bpy.app.build_options.collada:
- self.layout.operator("wm.collada_export",
- text="Collada (Default) (.dae)")
+ self.layout.operator("wm.collada_export", text="Collada (.dae)")
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
if bpy.app.build_options.usd:
@@ -690,8 +688,8 @@ class TOPBAR_MT_help(Menu):
layout.separator()
layout.operator(
- "wm.url_open", text="Python API Reference", icon='URL',
- ).url = bpy.types.WM_OT_doc_view._prefix
+ "wm.url_open_preset", text="Python API Reference", icon='URL',
+ ).type = 'API'
if show_developer:
layout.operator(
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index fc1911910c4..f80ad378b3c 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -1718,6 +1718,7 @@ class USERPREF_PT_ndof_settings(Panel):
if show_3dview_settings:
col.prop(props, "ndof_show_guide")
col.prop(props, "ndof_zoom_invert")
+ col.prop(props, "ndof_lock_camera_pan_zoom")
row = col.row(heading="Pan")
row.prop(props, "ndof_pan_yz_swap_axis", text="Swap Y and Z Axes")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 61ba6189be4..cd0306d31fd 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -468,6 +468,38 @@ class _draw_tool_settings_context_mode:
return True
+ @staticmethod
+ def SCULPT_CURVES(context, layout, tool):
+ if (tool is None) or (not tool.has_datablock):
+ return False
+
+ paint = context.tool_settings.curves_sculpt
+ layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
+
+ brush = paint.brush
+ if brush is None:
+ return False
+
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "size",
+ unified_name="use_unified_size",
+ text="Radius",
+ slider=True,
+ header=True
+ )
+
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "strength",
+ unified_name="use_unified_strength",
+ header=True
+ )
+
class VIEW3D_HT_header(Header):
bl_space_type = 'VIEW_3D'
@@ -1165,23 +1197,24 @@ class VIEW3D_MT_view_viewpoint(Menu):
def draw(self, _context):
layout = self.layout
+ i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D']
- layout.operator("view3d.view_camera", text="Camera")
+ layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_text_ctxt)
layout.separator()
- layout.operator("view3d.view_axis", text="Top").type = 'TOP'
- layout.operator("view3d.view_axis", text="Bottom").type = 'BOTTOM'
+ layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt).type = 'TOP'
+ layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt).type = 'BOTTOM'
layout.separator()
- layout.operator("view3d.view_axis", text="Front").type = 'FRONT'
- layout.operator("view3d.view_axis", text="Back").type = 'BACK'
+ layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt).type = 'FRONT'
+ layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt).type = 'BACK'
layout.separator()
- layout.operator("view3d.view_axis", text="Right").type = 'RIGHT'
- layout.operator("view3d.view_axis", text="Left").type = 'LEFT'
+ layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt).type = 'RIGHT'
+ layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt).type = 'LEFT'
class VIEW3D_MT_view_navigation(Menu):
@@ -1248,31 +1281,31 @@ class VIEW3D_MT_view_align_selected(Menu):
def draw(self, _context):
layout = self.layout
- props = layout.operator("view3d.view_axis", text="Top")
+ props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'TOP'
- props = layout.operator("view3d.view_axis", text="Bottom")
+ props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'BOTTOM'
layout.separator()
- props = layout.operator("view3d.view_axis", text="Front")
+ props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'FRONT'
- props = layout.operator("view3d.view_axis", text="Back")
+ props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'BACK'
layout.separator()
- props = layout.operator("view3d.view_axis", text="Right")
+ props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'RIGHT'
- props = layout.operator("view3d.view_axis", text="Left")
+ props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt)
props.align_active = True
props.type = 'LEFT'
@@ -1899,6 +1932,13 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
layout.operator("paint.vert_select_ungrouped", text="Ungrouped Vertices")
+class VIEW3D_MT_select_edit_curves(Menu):
+ bl_label = "Select"
+
+ def draw(self, _context):
+ pass
+
+
class VIEW3D_MT_angle_control(Menu):
bl_label = "Angle Control"
@@ -2344,6 +2384,25 @@ class VIEW3D_MT_object_clear(Menu):
layout.operator("object.origin_clear", text="Origin")
+class VIEW3D_MT_motion_path(Menu):
+ bl_label = "Motion Paths"
+
+ def draw(self, _context):
+ layout = self.layout
+ ob = _context.object
+ if ob.mode == 'OBJECT':
+ layout.operator("object.paths_calculate")
+ layout.operator("object.paths_update")
+ layout.operator("object.paths_update_visible")
+ layout.operator("object.paths_clear", text="Clear all").only_selected = False
+ layout.operator("object.paths_clear", text="Clear selected").only_selected = True
+ elif ob.mode == 'POSE':
+ layout.operator("pose.paths_calculate")
+ layout.operator("pose.paths_update")
+ layout.operator("pose.paths_clear", text="Clear all").only_selected = False
+ layout.operator("pose.paths_clear", text="Clear selected").only_selected = True
+
+
class VIEW3D_MT_object_context_menu(Menu):
bl_label = "Object Context Menu"
@@ -2545,6 +2604,7 @@ class VIEW3D_MT_object_context_menu(Menu):
layout.menu("VIEW3D_MT_mirror")
layout.menu("VIEW3D_MT_snap")
layout.menu("VIEW3D_MT_object_parent")
+ layout.menu("VIEW3D_MT_motion_path")
layout.operator_context = 'INVOKE_REGION_WIN'
if view and view.local_view:
@@ -3592,10 +3652,10 @@ class VIEW3D_MT_pose_context_menu(Menu):
layout.separator()
- layout.operator("pose.paths_calculate", text="Calculate Motion Paths")
- layout.operator("pose.paths_clear", text="Clear Motion Paths")
- layout.operator("pose.paths_update", text="Update Armature Motion Paths")
- layout.operator("object.paths_update_visible", text="Update All Motion Paths")
+ layout.operator("pose.paths_calculate")
+ layout.operator("pose.paths_update")
+ layout.operator("pose.paths_clear", text="Clear all").only_selected = False
+ layout.operator("pose.paths_clear", text="Clear selected").only_selected = True
layout.separator()
@@ -5123,6 +5183,13 @@ class VIEW3D_MT_edit_gpencil_showhide(Menu):
layout.operator("gpencil.hide", text="Hide Inactive Layers").unselected = True
+class VIEW3D_MT_edit_curves(Menu):
+ bl_label = "Curves"
+
+ def draw(self, _context):
+ pass
+
+
class VIEW3D_MT_object_mode_pie(Menu):
bl_label = "Mode"
@@ -7544,6 +7611,7 @@ classes = (
VIEW3D_MT_select_gpencil,
VIEW3D_MT_select_paint_mask,
VIEW3D_MT_select_paint_mask_vertex,
+ VIEW3D_MT_select_edit_curves,
VIEW3D_MT_angle_control,
VIEW3D_MT_mesh_add,
VIEW3D_MT_curve_add,
@@ -7576,6 +7644,7 @@ classes = (
VIEW3D_MT_object_quick_effects,
VIEW3D_MT_object_showhide,
VIEW3D_MT_object_cleanup,
+ VIEW3D_MT_motion_path,
VIEW3D_MT_make_single_user,
VIEW3D_MT_make_links,
VIEW3D_MT_brush_paint_modes,
@@ -7666,6 +7735,7 @@ classes = (
VIEW3D_MT_edit_armature_names,
VIEW3D_MT_edit_armature_delete,
VIEW3D_MT_edit_gpencil_transform,
+ VIEW3D_MT_edit_curves,
VIEW3D_MT_object_mode_pie,
VIEW3D_MT_view_pie,
VIEW3D_MT_transform_gizmo_pie,
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 21e20c3b734..a0205a2bcb1 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -142,6 +142,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeInputMeshEdgeVertices")
yield NodeItem("GeometryNodeInputMeshFaceArea")
yield NodeItem("GeometryNodeInputMeshFaceNeighbors")
+ yield NodeItem("GeometryNodeInputMeshFaceIsPlanar")
yield NodeItem("GeometryNodeInputMeshIsland")
yield NodeItem("GeometryNodeInputShadeSmooth")
yield NodeItem("GeometryNodeInputMeshVertexNeighbors")
@@ -164,6 +165,7 @@ def geometry_node_items(context):
yield NodeItem("GeometryNodeBoundBox")
yield NodeItem("GeometryNodeConvexHull")
yield NodeItem("GeometryNodeDeleteGeometry")
+ yield NodeItem("GeometryNodeDuplicateElements")
yield NodeItem("GeometryNodeGeometryToInstance")
yield NodeItem("GeometryNodeMergeByDistance")
yield NodeItem("GeometryNodeProximity")
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 0d63bb88de1..500f386dcc0 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -227,7 +227,7 @@ struct WriteAttributeLookup {
* - 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.
*
- * \note The lifetime of an output attribute should not be longer than the the lifetime of the
+ * \note The lifetime of an output attribute should not be longer than the lifetime of the
* geometry component it comes from, since it can keep a reference to the component for use in
* the #save method.
*/
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 21bbb4ce9ad..a8a851bb228 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -25,13 +25,13 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 3
+#define BLENDER_FILE_SUBVERSION 5
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
* was written with too new a version. */
#define BLENDER_FILE_MIN_VERSION 300
-#define BLENDER_FILE_MIN_SUBVERSION 42
+#define BLENDER_FILE_MIN_SUBVERSION 43
/** User readable version string. */
const char *BKE_blender_version_string(void);
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h
index c3a2aba18b5..4b84c0cfe23 100644
--- a/source/blender/blenkernel/BKE_brush.h
+++ b/source/blender/blenkernel/BKE_brush.h
@@ -8,13 +8,13 @@
* General operations for brushes.
*/
+#include "DNA_color_types.h"
#include "DNA_object_enums.h"
#ifdef __cplusplus
extern "C" {
#endif
-enum eCurveMappingPreset;
struct Brush;
struct ImBuf;
struct ImagePool;
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 52de39f3ed9..584568b4a5e 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -106,6 +106,7 @@ typedef enum eContextObjectMode {
CTX_MODE_EDIT_ARMATURE,
CTX_MODE_EDIT_METABALL,
CTX_MODE_EDIT_LATTICE,
+ CTX_MODE_EDIT_CURVES,
CTX_MODE_POSE,
CTX_MODE_SCULPT,
CTX_MODE_PAINT_WEIGHT,
diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h
index 2cce15fbfd6..88bb1c67fd1 100644
--- a/source/blender/blenkernel/BKE_curves.h
+++ b/source/blender/blenkernel/BKE_curves.h
@@ -2,9 +2,11 @@
#pragma once
+#include "DNA_curves_types.h"
+
/** \file
* \ingroup bke
- * \brief Low-level operations for curves.
+ * \brief Low-level operations for curves that cannot be defined in the C++ header yet.
*/
#ifdef __cplusplus
@@ -23,14 +25,10 @@ void *BKE_curves_add(struct Main *bmain, const char *name);
struct BoundBox *BKE_curves_boundbox_get(struct Object *ob);
-void BKE_curves_update_customdata_pointers(struct Curves *curves);
bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer);
/* Depsgraph */
-struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src,
- int totpoint,
- int totcurve);
struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference);
void BKE_curves_data_update(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
new file mode 100644
index 00000000000..f3d9090d16b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BKE_curves.h"
+
+/** \file
+ * \ingroup bke
+ * \brief Low-level operations for curves.
+ */
+
+#include <mutex>
+
+#include "BLI_float4x4.hh"
+#include "BLI_index_mask.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_access.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+namespace blender::bke {
+
+/**
+ * Contains derived data, caches, and other information not saved in files, besides a few pointers
+ * to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are
+ * accessed.
+ */
+class CurvesGeometryRuntime {
+ public:
+ /** Cache of evaluated positions. */
+ mutable Vector<float3> evaluated_position_cache;
+ mutable std::mutex position_cache_mutex;
+ mutable bool position_cache_dirty = true;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable Vector<float3> evaluated_tangents_cache;
+ mutable std::mutex tangent_cache_mutex;
+ mutable bool tangent_cache_dirty = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable Vector<float3> evaluated_normals_cache;
+ mutable std::mutex normal_cache_mutex;
+ mutable bool normal_cache_dirty = true;
+};
+
+/**
+ * A C++ class that wraps the DNA struct for better encapsulation and ease of use. It inherits
+ * directly from the struct rather than storing a pointer to avoid more complicated ownership
+ * handling.
+ */
+class CurvesGeometry : public ::CurvesGeometry {
+ public:
+ CurvesGeometry();
+ /**
+ * Create curves with the given size. Only the position attribute is created, along with the
+ * offsets.
+ */
+ CurvesGeometry(int point_size, int curve_size);
+ CurvesGeometry(const CurvesGeometry &other);
+ CurvesGeometry &operator=(const CurvesGeometry &other);
+ ~CurvesGeometry();
+
+ static CurvesGeometry &wrap(::CurvesGeometry &dna_struct)
+ {
+ CurvesGeometry *geometry = reinterpret_cast<CurvesGeometry *>(&dna_struct);
+ return *geometry;
+ }
+ static const CurvesGeometry &wrap(const ::CurvesGeometry &dna_struct)
+ {
+ const CurvesGeometry *geometry = reinterpret_cast<const CurvesGeometry *>(&dna_struct);
+ return *geometry;
+ }
+
+ /* --------------------------------------------------------------------
+ * Accessors.
+ */
+
+ int points_size() const;
+ int curves_size() const;
+ IndexRange points_range() const;
+ IndexRange curves_range() const;
+
+ /**
+ * The total number of points in the evaluated poly curve.
+ * This can depend on the resolution attribute if it exists.
+ */
+ int evaluated_points_size() const;
+
+ /**
+ * Access a range of indices of point data for a specific curve.
+ */
+ IndexRange range_for_curve(int index) const;
+
+ /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */
+ VArray<int8_t> curve_types() const;
+ /** Mutable access to curve types. Call #tag_topology_changed after changing any type. */
+ MutableSpan<int8_t> curve_types();
+
+ MutableSpan<float3> positions();
+ Span<float3> positions() const;
+
+ /**
+ * Calculate the largest and smallest position values, only including control points
+ * (rather than evaluated points). The existing values of `min` and `max` are taken into account.
+ *
+ * \return Whether there are any points. If the curve is empty, the inputs will be unaffected.
+ */
+ bool bounds_min_max(float3 &min, float3 &max) const;
+
+ /**
+ * The index of the first point in every curve. The size of this span is one larger than the
+ * number of curves. Consider using #range_for_curve rather than using the offsets directly.
+ */
+ Span<int> offsets() const;
+ MutableSpan<int> offsets();
+
+ VArray<bool> cyclic() const;
+ MutableSpan<bool> cyclic();
+
+ /* --------------------------------------------------------------------
+ * Operations.
+ */
+
+ /**
+ * Change the number of elements. New values for existing attributes should be properly
+ * initialized afterwards.
+ */
+ void resize(int point_size, int curve_size);
+
+ /** Call after deforming the position attribute. */
+ void tag_positions_changed();
+ /**
+ * Call after any operation that changes the topology
+ * (number of points, evaluated points, or the total count).
+ */
+ void tag_topology_changed();
+ /** Call after changing the "tilt" or "up" attributes. */
+ void tag_normals_changed();
+
+ void translate(const float3 &translation);
+ void transform(const float4x4 &matrix);
+
+ void update_customdata_pointers();
+
+ /* --------------------------------------------------------------------
+ * Attributes.
+ */
+
+ fn::GVArray adapt_domain(const fn::GVArray &varray,
+ AttributeDomain from,
+ AttributeDomain to) const;
+};
+
+Curves *curves_new_nomain(int point_size, int curves_size);
+
+/**
+ * Create a new curves data-block containing a single curve with the given length and type.
+ */
+Curves *curves_new_nomain_single(int point_size, CurveType type);
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 7c55c53ed66..f7767cc2a60 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -24,6 +24,7 @@
#include "FN_field.hh"
+struct Curves;
struct Collection;
struct Curve;
struct CurveEval;
@@ -412,10 +413,10 @@ struct GeometrySet {
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
- * Create a new geometry set that only contains the given curve.
+ * Create a new geometry set that only contains the given curves.
*/
- static GeometrySet create_with_curve(
- CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_curves(
+ Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
/**
@@ -435,9 +436,9 @@ struct GeometrySet {
*/
bool has_volume() const;
/**
- * Returns true when the geometry set has a curve component that has a curve.
+ * Returns true when the geometry set has a curves component that has a curves data-block.
*/
- bool has_curve() const;
+ bool has_curves() const;
/**
* Returns true when the geometry set has any data that is not an instance.
*/
@@ -460,9 +461,9 @@ struct GeometrySet {
*/
const Volume *get_volume_for_read() const;
/**
- * Returns a read-only curve or null.
+ * Returns a read-only curves data-block or null.
*/
- const CurveEval *get_curve_for_read() const;
+ const Curves *get_curves_for_read() const;
/**
* Returns a mutable mesh or null. No ownership is transferred.
@@ -477,9 +478,9 @@ struct GeometrySet {
*/
Volume *get_volume_for_write();
/**
- * Returns a mutable curve or null. No ownership is transferred.
+ * Returns a mutable curves data-block or null. No ownership is transferred.
*/
- CurveEval *get_curve_for_write();
+ Curves *get_curves_for_write();
/* Utility methods for replacement. */
/**
@@ -497,9 +498,9 @@ struct GeometrySet {
void replace_volume(Volume *volume,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
- * Clear the existing curve and replace it with the given one.
+ * Clear the existing curves data-block and replace it with the given one.
*/
- void replace_curve(CurveEval *curve,
+ void replace_curve(Curves *curves,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
private:
@@ -632,17 +633,59 @@ class PointCloudComponent : public GeometryComponent {
};
/**
- * A geometry component that stores curve data, in other words, a group of splines.
- * Curves are stored differently than other geometry components, because the data structure used
- * here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored here
- * instead, though the component does give access to a #Curve for interfacing with render engines
- * and other areas of Blender that expect to use a data-block with an #ID.
+ * Legacy runtime-only curves type.
+ * These curves are stored differently than other geometry components, because the data structure
+ * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored
+ * here instead, though the component does give access to a #Curve for interfacing with render
+ * engines and other areas of Blender that expect to use a data-block with an #ID.
*/
-class CurveComponent : public GeometryComponent {
+class CurveComponentLegacy : public GeometryComponent {
private:
CurveEval *curve_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+ public:
+ CurveComponentLegacy();
+ ~CurveComponentLegacy();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_curve() const;
+ /**
+ * Clear the component and replace it with the new curve.
+ */
+ void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ CurveEval *release();
+
+ const CurveEval *get_for_read() const;
+ CurveEval *get_for_write();
+
+ int attribute_domain_size(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;
+
+ blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray,
+ AttributeDomain from_domain,
+ AttributeDomain to_domain) const final;
+};
+
+/**
+ * A geometry component that stores a group of curves, corresponding the the #Curves and
+ * #CurvesGeometry types.
+ */
+class CurveComponent : public GeometryComponent {
+ private:
+ Curves *curves_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
/**
* Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws.
* This is necessary because Blender assumes that objects evaluate to an object data type, and
@@ -658,15 +701,15 @@ class CurveComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- bool has_curve() const;
+ bool has_curves() const;
/**
* Clear the component and replace it with the new curve.
*/
- void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
- CurveEval *release();
+ void replace(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ Curves *release();
- const CurveEval *get_for_read() const;
- CurveEval *get_for_write();
+ const Curves *get_for_read() const;
+ Curves *get_for_write();
int attribute_domain_size(AttributeDomain domain) const final;
@@ -693,7 +736,7 @@ class CurveComponent : public GeometryComponent {
/**
* Holds a reference to conceptually unique geometry or a pointer to object/collection data
- * that is is instanced with a transform in #InstancesComponent.
+ * that is instanced with a transform in #InstancesComponent.
*/
class InstanceReference {
public:
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index eb61bd32eee..4127030e96f 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -208,11 +208,13 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
* \param gpd: Grease pencil data-block
* \param gps: Stroke to sample
* \param dist: Distance of one segment
+ * \param sharp_threshold: Threshold for preserving sharp corners
*/
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
struct bGPDstroke *gps,
- float dist,
- bool select);
+ const float dist,
+ const bool select,
+ const float sharp_threshold);
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index b4dda3689e6..3f7d9498e39 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -169,7 +169,7 @@ struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop,
/*-------- Main Functions --------*/
/**
- * Get the Group property that contains the id properties for ID id.
+ * Get the Group property that contains the id properties for ID `id`.
*
* \param create_if_needed: Set to create the group property and attach it to id if it doesn't
* exist; otherwise the function will return NULL if there's no Group property attached to the ID.
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index e9abbb4575e..bd64b03cc7d 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -230,7 +230,7 @@ extern IDTypeInfo IDType_ID_SCE;
extern IDTypeInfo IDType_ID_LI;
extern IDTypeInfo IDType_ID_OB;
extern IDTypeInfo IDType_ID_ME;
-extern IDTypeInfo IDType_ID_CU;
+extern IDTypeInfo IDType_ID_CU_LEGACY;
extern IDTypeInfo IDType_ID_MB;
extern IDTypeInfo IDType_ID_MA;
extern IDTypeInfo IDType_ID_TE;
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 858bc2f5625..98301ca7a70 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -92,7 +92,13 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
*
* \param id_hierarchy_root: the override ID that is the root of the hierarchy. May be NULL, in
* which case it is assumed that the given `id_root_reference` is tagged for override, and its
- * newly created override will be used as hierarchy root.
+ * newly created override will be used as hierarchy root. Must be NULL if
+ * `id_hierarchy_root_reference` is not NULL.
+ *
+ * \param id_hierarchy_root_reference: the linked ID that is the root of the hierarchy. Must be
+ * tagged for override. May be NULL, in which case it is assumed that the given `id_root_reference`
+ * is tagged for override, and its newly created override will be used as hierarchy root. Must be
+ * NULL if `id_hierarchy_root` is not NULL.
*
* \param do_no_main: Create the new override data outside of Main database.
* Used for resyncing of linked overrides.
@@ -103,6 +109,7 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
struct Library *owner_library,
const struct ID *id_root_reference,
struct ID *id_hierarchy_root,
+ const struct ID *id_hierarchy_root_reference,
bool do_no_main);
/**
* Advanced 'smart' function to create fully functional overrides.
@@ -119,11 +126,18 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
* \param owner_library: the library in which the overrides should be created. Besides versioning
* and resync code path, this should always be NULL (i.e. the local .blend file).
*
- * \param id_root: The root ID to create an override from.
+ * \param id_root_reference: The linked root ID to create an override from. May be a sub-root of
+ * the overall hierarchy, in which case calling code is expected to have already tagged required
+ * 'path' of IDs leading from the given `id_hierarchy_root` to the given `id_root`.
+ *
+ * \param id_hierarchy_root_reference: The ID to be used a hierarchy root of the overrides to be
+ * created. Can be either the linked root ID of the whole override hierarchy, (typically the same
+ * as `id_root`, unless a sub-part only of the hierarchy is overridden), or the already existing
+ * override hierarchy root if part of the hierarchy is already overridden.
*
- * \param id_reference: Some reference ID used to do some post-processing after overrides have been
- * created, may be NULL. Typically, the Empty object instantiating the linked collection we
- * override, currently.
+ * \param id_instance_hint: Some ID used as hint/reference to do some post-processing after
+ * overrides have been created, may be NULL. Typically, the Empty object instantiating the linked
+ * collection we override, currently.
*
* \param r_id_root_override: if not NULL, the override generated for the given \a id_root.
*
@@ -133,8 +147,9 @@ bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Library *owner_library,
- struct ID *id_root,
- struct ID *id_reference,
+ struct ID *id_root_reference,
+ struct ID *id_hierarchy_root_reference,
+ struct ID *id_instance_hint,
struct ID **r_id_root_override);
/**
* Create a library override template.
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index 94b94303ec9..fd7d39fc250 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -220,7 +220,7 @@ IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper
* Use this function when `ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF`. In this case
* the #id_self parameter is required. Otherwise the #BKE_id_remapper_apply can be used.
*
- * \param id_self required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF.
+ * \param id_self: required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF.
* When remapping to id_self it will then be remapped to NULL.
*/
IDRemapperApplyResult BKE_id_remapper_apply_ex(const struct IDRemapper *id_remapper,
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index a84f6058d67..2373bb289cd 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -70,6 +70,13 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm,
const struct Mesh *me_settings);
/**
+ * Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used
+ * when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the
+ * evaluated vertices to the originals.
+ */
+void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh);
+
+/**
* Find the index of the loop in 'poly' which references vertex,
* returns -1 if not found
*/
@@ -332,8 +339,7 @@ int BKE_mesh_tessface_calc_ex(struct CustomData *fdata,
struct MVert *mvert,
int totface,
int totloop,
- int totpoly,
- bool do_face_nor_copy);
+ int totpoly);
void BKE_mesh_tessface_calc(struct Mesh *mesh);
/**
@@ -399,6 +405,9 @@ void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh);
* \note In order to clear the dirty flag, this function should be followed by a call to
* #BKE_mesh_vertex_normals_clear_dirty. This is separate so that normals are still tagged dirty
* while they are being assigned.
+ *
+ * \warning The memory returned by this function is not initialized if it was not previously
+ * allocated.
*/
float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3];
@@ -409,10 +418,24 @@ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3];
* \note In order to clear the dirty flag, this function should be followed by a call to
* #BKE_mesh_poly_normals_clear_dirty. This is separate so that normals are still tagged dirty
* while they are being assigned.
+ *
+ * \warning The memory returned by this function is not initialized if it was not previously
+ * allocated.
*/
float (*BKE_mesh_poly_normals_for_write(struct Mesh *mesh))[3];
/**
+ * Free any cached vertex or poly normals. Face corner (loop) normals are also derived data,
+ * but are not handled with the same method yet, so they are not included. It's important that this
+ * is called after the mesh changes size, since otherwise cached normal arrays might not be large
+ * enough (though it may be called indirectly by other functions).
+ *
+ * \note Normally it's preferred to call #BKE_mesh_normals_tag_dirty instead,
+ * but this can be used in specific situations to reset a mesh or reduce memory usage.
+ */
+void BKE_mesh_clear_derived_normals(struct Mesh *mesh);
+
+/**
* Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually.
*/
void BKE_mesh_vertex_normals_clear_dirty(struct Mesh *mesh);
@@ -919,7 +942,7 @@ void BKE_mesh_calc_relative_deform(const struct MPoly *mpoly,
const float (*vert_cos_org)[3],
float (*vert_cos_new)[3]);
-/* *** mesh_validate.c *** */
+/* *** mesh_validate.cc *** */
/**
* Validates and corrects a Mesh.
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 9a212cb1275..acdca23b21c 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -409,14 +409,18 @@ void BKE_modifier_session_uuid_generate(struct ModifierData *md);
bool BKE_modifier_unique_name(struct ListBase *modifiers, struct ModifierData *md);
+struct ModifierData *BKE_modifier_copy_ex(const struct ModifierData *md, int flag);
+
/**
* Callback's can use this to avoid copying every member.
*/
void BKE_modifier_copydata_generic(const struct ModifierData *md,
struct ModifierData *md_dst,
int flag);
-void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target);
-void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, int flag);
+void BKE_modifier_copydata(const struct ModifierData *md, struct ModifierData *target);
+void BKE_modifier_copydata_ex(const struct ModifierData *md,
+ struct ModifierData *target,
+ int flag);
bool BKE_modifier_depends_ontime(struct Scene *scene, struct ModifierData *md, int dag_eval_mode);
bool BKE_modifier_supports_mapping(struct ModifierData *md);
bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 933585b1f8f..315e24485fa 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -681,6 +681,7 @@ void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link);
bool nodeLinkIsHidden(const struct bNodeLink *link);
+bool nodeLinkIsSelected(const struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
void nodeToView(const struct bNode *node, float x, float y, float *rx, float *ry);
@@ -1514,6 +1515,8 @@ struct TexResult;
#define GEO_NODE_SCALE_ELEMENTS 1151
#define GEO_NODE_EXTRUDE_MESH 1152
#define GEO_NODE_MERGE_BY_DISTANCE 1153
+#define GEO_NODE_DUPLICATE_ELEMENTS 1154
+#define GEO_NODE_INPUT_MESH_FACE_IS_PLANAR 1155
/** \} */
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 1f37e95a023..8ab89b6c244 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -74,9 +74,11 @@ typedef enum ePaintMode {
PAINT_MODE_VERTEX_GPENCIL = 7,
PAINT_MODE_SCULPT_GPENCIL = 8,
PAINT_MODE_WEIGHT_GPENCIL = 9,
+ /** Curves. */
+ PAINT_MODE_SCULPT_CURVES = 10,
/** Keep last. */
- PAINT_MODE_INVALID = 10,
+ PAINT_MODE_INVALID = 11,
} ePaintMode;
#define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PAINT_MODE_SCULPT_UV)
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 846dcd3ca8e..42b4702ee44 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -10,6 +10,8 @@
#include "FN_generic_virtual_array.hh"
+#include "DNA_curves_types.h"
+
#include "BLI_float4x4.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_vector.hh"
@@ -18,6 +20,7 @@
#include "BKE_attribute_math.hh"
struct Curve;
+struct Curves;
struct ListBase;
class Spline;
@@ -49,12 +52,6 @@ using SplinePtr = std::unique_ptr<Spline>;
*/
class Spline {
public:
- enum class Type {
- Bezier,
- NURBS,
- Poly,
- };
-
enum NormalCalculationMode {
ZUp,
Minimum,
@@ -65,7 +62,7 @@ class Spline {
blender::bke::CustomDataAttributes attributes;
protected:
- Type type_;
+ CurveType type_;
bool is_cyclic_ = false;
/** Direction of the spline at each evaluated point. */
@@ -85,7 +82,7 @@ class Spline {
public:
virtual ~Spline() = default;
- Spline(const Type type) : type_(type)
+ Spline(const CurveType type) : type_(type)
{
}
Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
@@ -107,7 +104,7 @@ class Spline {
SplinePtr copy_without_attributes() const;
static void copy_base_settings(const Spline &src, Spline &dst);
- Spline::Type type() const;
+ CurveType type() const;
/** Return the number of control points. */
virtual int size() const = 0;
@@ -252,26 +249,13 @@ class Spline {
* 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_;
+ blender::Vector<int8_t> handle_types_left_;
+ blender::Vector<int8_t> handle_types_right_;
/* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
mutable blender::Vector<blender::float3> handle_positions_left_;
@@ -296,7 +280,7 @@ class BezierSpline final : public Spline {
mutable bool mapping_cache_dirty_ = true;
public:
- BezierSpline() : Spline(Type::Bezier)
+ BezierSpline() : Spline(CURVE_TYPE_BEZIER)
{
}
BezierSpline(const BezierSpline &other)
@@ -323,8 +307,8 @@ class BezierSpline final : public Spline {
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<int8_t> handle_types_left() const;
+ blender::MutableSpan<int8_t> handle_types_left();
blender::Span<blender::float3> handle_positions_left() const;
/**
* Get writable access to the handle position.
@@ -333,8 +317,8 @@ class BezierSpline final : public Spline {
* uninitialized memory while auto-generating handles.
*/
blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false);
- blender::Span<HandleType> handle_types_right() const;
- blender::MutableSpan<HandleType> handle_types_right();
+ blender::Span<int8_t> handle_types_right() const;
+ blender::MutableSpan<int8_t> handle_types_right();
blender::Span<blender::float3> handle_positions_right() const;
/**
* Get writable access to the handle position.
@@ -519,7 +503,7 @@ class NURBSpline final : public Spline {
mutable bool position_cache_dirty_ = true;
public:
- NURBSpline() : Spline(Type::NURBS)
+ NURBSpline() : Spline(CURVE_TYPE_NURBS)
{
}
NURBSpline(const NURBSpline &other)
@@ -586,7 +570,7 @@ class PolySpline final : public Spline {
blender::Vector<float> tilts_;
public:
- PolySpline() : Spline(Type::Poly)
+ PolySpline() : Spline(CURVE_TYPE_POLY)
{
}
PolySpline(const PolySpline &other)
@@ -658,7 +642,7 @@ struct CurveEval {
* \note If you are looping over all of the splines in the same scope anyway,
* it's better to avoid calling this function, in case there are many splines.
*/
- bool has_spline_with_type(const Spline::Type type) const;
+ bool has_spline_with_type(const CurveType type) const;
void resize(int size);
/**
@@ -708,3 +692,5 @@ struct CurveEval {
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve,
const ListBase &nurbs_list);
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve);
+std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves);
+Curves *curve_eval_to_curves(const CurveEval &curve_eval);
diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h
index a9ab5b91e1a..40e8ee2f999 100644
--- a/source/blender/blenkernel/BKE_subdiv_modifier.h
+++ b/source/blender/blenkernel/BKE_subdiv_modifier.h
@@ -24,18 +24,30 @@ void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings,
const struct SubsurfModifierData *smd,
bool use_render_params);
+bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierData *smd,
+ const struct Mesh *mesh);
+
+/**
+ * Return true if GPU subdivision evaluation is disabled by force due to incompatible mesh or
+ * modifier settings. This will only return true if GPU subdivision is enabled in the preferences
+ * and supported by the GPU. It is mainly useful for showing UI messages.
+ */
+bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(
+ const struct SubsurfModifierData *smd, const struct Mesh *mesh);
/**
* \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled
* modifier in the stack.
*/
bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const struct Scene *scene,
const struct Object *ob,
+ const struct Mesh *mesh,
const struct SubsurfModifierData *smd,
int required_mode,
bool skip_check_is_last);
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene,
const struct Object *ob,
+ const struct Mesh *mesh,
int required_mode);
extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv);
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index c73417d7740..77f01e7919d 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -90,7 +90,6 @@ typedef enum VolumeGridType {
VOLUME_GRID_INT,
VOLUME_GRID_INT64,
VOLUME_GRID_MASK,
- VOLUME_GRID_STRING,
VOLUME_GRID_VECTOR_FLOAT,
VOLUME_GRID_VECTOR_DOUBLE,
VOLUME_GRID_VECTOR_INT,
@@ -204,8 +203,6 @@ auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op)
return op.template operator()<openvdb::Vec3IGrid>();
case VOLUME_GRID_VECTOR_DOUBLE:
return op.template operator()<openvdb::Vec3dGrid>();
- case VOLUME_GRID_STRING:
- return op.template operator()<openvdb::StringGrid>();
case VOLUME_GRID_MASK:
return op.template operator()<openvdb::MaskGrid>();
case VOLUME_GRID_POINTS:
diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h
index cf4a8137638..3f92d6fa117 100644
--- a/source/blender/blenkernel/BKE_writeffmpeg.h
+++ b/source/blender/blenkernel/BKE_writeffmpeg.h
@@ -69,12 +69,8 @@ void BKE_ffmpeg_filepath_get(char *string,
void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset);
void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf);
-void BKE_ffmpeg_codec_settings_verify(struct RenderData *rd);
bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd);
-int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str);
-void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_);
-
void *BKE_ffmpeg_context_create(void);
void BKE_ffmpeg_context_free(void *context_v);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index bf720fa1341..a12a956cbf5 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -103,6 +103,7 @@ set(SRC
intern/cryptomatte.cc
intern/curve.cc
intern/curves.cc
+ intern/curves_geometry.cc
intern/curve_bevel.c
intern/curve_convert.c
intern/curve_decimate.c
@@ -130,6 +131,7 @@ set(SRC
intern/fmodifier.c
intern/freestyle.c
intern/geometry_component_curve.cc
+ intern/geometry_component_curves.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -199,7 +201,7 @@ set(SRC
intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_tessellate.c
- intern/mesh_validate.c
+ intern/mesh_validate.cc
intern/mesh_wrapper.c
intern/modifier.c
intern/movieclip.c
@@ -341,6 +343,7 @@ set(SRC
BKE_cryptomatte.hh
BKE_curve.h
BKE_curves.h
+ BKE_curves.hh
BKE_curve_to_mesh.hh
BKE_curveprofile.h
BKE_customdata.h
@@ -375,6 +378,7 @@ set(SRC
BKE_idprop.hh
BKE_idtype.h
BKE_image.h
+ BKE_image_partial_update.hh
BKE_image_save.h
BKE_ipo.h
BKE_kelvinlet.h
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 542be4027bc..39074a5c75f 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -546,6 +546,7 @@ static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer)
if (em) {
mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, me);
+ BKE_mesh_ensure_default_orig_index_customdata(mesh);
}
else {
mesh = BKE_mesh_copy_for_eval(me, true);
@@ -1142,12 +1143,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
* we need to apply these back onto the Mesh. If we have no
* Mesh then we need to build one. */
if (mesh_final == nullptr) {
- /* NOTE: this check on cdmask is a bit dodgy, it handles the issue at stake here (see T68211),
- * but other cases might require similar handling?
- * Could be a good idea to define a proper CustomData_MeshMask for that then. */
- if (deformed_verts == nullptr && allow_shared_mesh &&
- (final_datamask.lmask & CD_MASK_NORMAL) == 0 &&
- (final_datamask.pmask & CD_MASK_NORMAL) == 0) {
+ if (deformed_verts == nullptr && allow_shared_mesh) {
mesh_final = mesh_input;
}
else {
@@ -1364,6 +1360,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
em_input, &final_datamask, nullptr, mesh_input);
}
+ /* The mesh from edit mode should not have any original index layers already, since those
+ * are added during evaluation when necessary and are redundant on an original mesh. */
+ BLI_assert(CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr &&
+ CustomData_get_layer(&em_input->bm->edata, CD_ORIGINDEX) == nullptr &&
+ CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr);
+
/* Clear errors before evaluation. */
BKE_modifiers_clear_errors(ob);
@@ -1402,6 +1404,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) {
if (mesh_final == nullptr) {
mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input);
+ BKE_mesh_ensure_default_orig_index_customdata(mesh_final);
ASSERT_IS_VALID_MESH(mesh_final);
}
BLI_assert(deformed_verts != nullptr);
@@ -1510,7 +1513,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
else {
Mesh *me_orig = mesh_input;
- if (me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) {
+ /* Modifying the input mesh is weak, however as there can only be one object in edit mode
+ * even if multiple are sharing the same mesh this should be thread safe. */
+ if ((me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) && (ob->mode & OB_MODE_EDIT)) {
if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) {
BKE_mesh_runtime_reset_edit_data(me_orig);
}
@@ -1819,9 +1824,8 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
mesh_eval = BKE_object_get_evaluated_mesh(ob);
}
- if (mesh_eval != nullptr) {
- BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL));
- }
+ BKE_mesh_assert_normals_dirty_or_calculated(mesh_eval);
+
return mesh_eval;
}
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 6a999cde6eb..55aba1d22c3 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -1244,7 +1244,7 @@ void BKE_pose_update_constraint_flags(bPose *pose)
/* if we have a valid target, make sure that this will get updated on frame-change
* (needed for when there is no anim-data for this pose)
*/
- if ((data->tar) && (data->tar->type == OB_CURVE)) {
+ if ((data->tar) && (data->tar->type == OB_CURVES_LEGACY)) {
pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND;
}
}
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 0c9202400ad..861a89ea9d7 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -763,7 +763,7 @@ static bool fcurves_path_rename_fix(ID *owner_id,
if (fcu->rna_path != old_path) {
bActionGroup *agrp = fcu->grp;
is_changed = true;
- if ((agrp != NULL) && STREQ(oldName, agrp->name)) {
+ if (oldName != NULL && (agrp != NULL) && STREQ(oldName, agrp->name)) {
BLI_strncpy(agrp->name, newName, sizeof(agrp->name));
}
}
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index 57f64d7a0f8..1f8c6df6147 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -55,7 +55,7 @@ float BKE_anim_path_get_length(const CurveCache *curve_cache)
void BKE_anim_path_calc_data(Object *ob)
{
- if (ob == NULL || ob->type != OB_CURVE) {
+ if (ob == NULL || ob->type != OB_CURVES_LEGACY) {
return;
}
if (ob->runtime.curve_cache == NULL) {
@@ -222,7 +222,7 @@ bool BKE_where_on_path(const Object *ob,
float *r_radius,
float *r_weight)
{
- if (ob == NULL || ob->type != OB_CURVE) {
+ if (ob == NULL || ob->type != OB_CURVES_LEGACY) {
return false;
}
Curve *cu = ob->data;
diff --git a/source/blender/blenkernel/intern/anim_visualization.c b/source/blender/blenkernel/intern/anim_visualization.c
index 53a3a7e3712..f4c6a29c252 100644
--- a/source/blender/blenkernel/intern/anim_visualization.c
+++ b/source/blender/blenkernel/intern/anim_visualization.c
@@ -153,6 +153,11 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports,
if ((mpath->start_frame != mpath->end_frame) && (mpath->length > 0)) {
/* outer check ensures that we have some curve data for this path */
if (mpath->length == expected_length) {
+ /* The length might be the same, but the start and end could be different */
+ if (mpath->start_frame != avs->path_sf) {
+ mpath->start_frame = avs->path_sf;
+ mpath->end_frame = avs->path_ef;
+ }
/* return/use this as it is already valid length */
return mpath;
}
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 099588a0e14..361ab176abd 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -70,7 +70,7 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
ik_data = con->data;
/* Target can only be a curve. */
- if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) {
+ if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVES_LEGACY)) {
continue;
}
/* Skip if disabled. */
diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc
index 45889099567..1d862e5e4d4 100644
--- a/source/blender/blenkernel/intern/asset_library_test.cc
+++ b/source/blender/blenkernel/intern/asset_library_test.cc
@@ -52,7 +52,7 @@ TEST_F(AssetLibraryTest, bke_asset_library_load)
ASSERT_NE(nullptr, service);
/* Check that the catalogs defined in the library are actually loaded. This just tests one single
- * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for
+ * catalog, as that indicates the file has been loaded. Testing that loading went OK is for
* the asset catalog service tests. */
const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78");
AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie);
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 4eff878778a..bfc4c8fcde0 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -207,6 +207,16 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;
};
+template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size)
+{
+ return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
+}
+
+template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size)
+{
+ return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
+}
+
/**
* This provider is used to provide access to builtin attributes. It supports making internal types
* available as different types. For example, the vertex position attribute is stored as part of
diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c
index f06274c34d7..ce36bfe81be 100644
--- a/source/blender/blenkernel/intern/blendfile_link_append.c
+++ b/source/blender/blenkernel/intern/blendfile_link_append.c
@@ -927,8 +927,13 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d
* processed, so we need to recursively deal with them here. */
/* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it
* recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of
- * shape-key referencing the shape-key itself). */
- if (id != cb_data->id_self) {
+ * shape-key referencing the shape-key itself).
+ * NOTE: in case both IDs (owner and 'used' ones) are non-linkable, we can assume we can break
+ * the dependency here. Indeed, either they are both linked in another way (through their own
+ * meshes for shape keys e.g.), or this is an unsupported case (two shape-keys depending on
+ * each-other need to be also 'linked' in by their respective meshes, independent shape-keys
+ * are not allowed). ref T96048. */
+ if (id != cb_data->id_self && BKE_idtype_idcode_is_linkable(GS(cb_data->id_self->name))) {
BKE_library_foreach_ID_link(
cb_data->bmain, id, foreach_libblock_link_append_callback, data, IDWALK_NOP);
}
@@ -1449,7 +1454,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item;
/* We remove it from current Main, and add it to items to link... */
- /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */
+ /* Note that non-linkable IDs (like e.g. shape-keys) are also explicitly linked here... */
BLI_remlink(lbarray[lba_idx], id);
/* Usual special code for ShapeKeys snowflakes... */
Key *old_key = BKE_key_from_id(id);
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index f091ebe1e32..6ee6ff7f41d 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -144,10 +144,10 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
/* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided
* in IDType callbacks. Higher-level ID management code usually does not expect such things and
* does not deal properly with it. */
- /* NOTE: assert below ensures that the comment above is valid, and that that exception is
+ /* NOTE: assert below ensures that the comment above is valid, and that exception is
* acceptable for the time being. */
BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0);
- BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL);
+ BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == NULL);
}
if (force_local) {
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 57a95891a92..b840fb1e665 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -550,9 +550,8 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec
#define CAMERA_VIEWFRAME_NUM_PLANES 4
typedef struct CameraViewFrameData {
- float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes */
- float normal_tx[CAMERA_VIEWFRAME_NUM_PLANES][3];
- float dist_vals_sq[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance squared (signed) */
+ float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */
+ float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */
unsigned int tot;
/* Ortho camera only. */
@@ -569,8 +568,8 @@ static void camera_to_frame_view_cb(const float co[3], void *user_data)
CameraViewFrameData *data = (CameraViewFrameData *)user_data;
for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) {
- const float nd = dist_signed_squared_to_plane_v3(co, data->plane_tx[i]);
- CLAMP_MAX(data->dist_vals_sq[i], nd);
+ const float nd = plane_point_side_v3(data->plane_tx[i], co);
+ CLAMP_MAX(data->dist_vals[i], nd);
}
if (data->is_ortho) {
@@ -625,10 +624,11 @@ static void camera_frame_fit_data_init(const Scene *scene,
/* Rotate planes and get normals from them */
for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) {
mul_m4_v4(camera_rotmat_transposed_inversed, data->plane_tx[i]);
- normalize_v3_v3(data->normal_tx[i], data->plane_tx[i]);
+ /* Normalize. */
+ data->plane_tx[i][3] /= normalize_v3(data->plane_tx[i]);
}
- copy_v4_fl(data->dist_vals_sq, FLT_MAX);
+ copy_v4_fl(data->dist_vals, FLT_MAX);
data->tot = 0;
data->is_ortho = params->is_ortho;
if (params->is_ortho) {
@@ -653,14 +653,9 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
const float *cam_axis_x = data->camera_rotmat[0];
const float *cam_axis_y = data->camera_rotmat[1];
const float *cam_axis_z = data->camera_rotmat[2];
- float dists[CAMERA_VIEWFRAME_NUM_PLANES];
+ const float *dists = data->dist_vals;
float scale_diff;
- /* apply the dist-from-plane's to the transformed plane points */
- for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) {
- dists[i] = sqrtf_signed(data->dist_vals_sq[i]);
- }
-
if ((dists[0] + dists[2]) > (dists[1] + dists[3])) {
scale_diff = (dists[1] + dists[3]) *
(BLI_rctf_size_x(&params->viewplane) / BLI_rctf_size_y(&params->viewplane));
@@ -687,8 +682,8 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
/* apply the dist-from-plane's to the transformed plane points */
for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) {
float co[3];
- mul_v3_v3fl(co, data->normal_tx[i], sqrtf_signed(data->dist_vals_sq[i]));
- plane_from_point_normal_v3(plane_tx[i], co, data->normal_tx[i]);
+ mul_v3_v3fl(co, data->plane_tx[i], data->dist_vals[i]);
+ plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]);
}
if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) ||
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 69002a71f1d..2afe4dda35c 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -529,8 +529,8 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
float vec[3] = {0.0f, 0.0f, 0.0f};
float normal[3] = {0.0f, 0.0f, 0.0f};
float weightsum = 0.0f;
- const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
if (me_eval) {
+ const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
const MDeformVert *dvert = CustomData_get_layer(&me_eval->vdata, CD_MDEFORMVERT);
int numVerts = me_eval->totvert;
@@ -1493,7 +1493,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
{
bFollowPathConstraint *data = con->data;
- if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) {
+ if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) {
Curve *cu = ct->tar->data;
float vec[4], dir[3], radius;
float curvetime;
@@ -2479,7 +2479,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
#endif
if (VALID_CONS_TARGET(ct)) {
- if (ct->tar->type == OB_CURVE && ct->tar->runtime.curve_cache == NULL) {
+ if (ct->tar->type == OB_CURVES_LEGACY && ct->tar->runtime.curve_cache == NULL) {
unit_m4(ct->matrix);
return;
}
@@ -3867,7 +3867,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
bConstraintTarget *ct = targets->first;
/* only evaluate if there is a target and it is a curve */
- if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) {
+ if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) {
float obmat[4][4], ownLoc[3];
float curveMin[3], curveMax[3];
float targetMatrix[4][4];
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index d1374958763..28bcd961e26 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1148,7 +1148,7 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
switch (obedit->type) {
case OB_MESH:
return CTX_MODE_EDIT_MESH;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return CTX_MODE_EDIT_CURVE;
case OB_SURF:
return CTX_MODE_EDIT_SURFACE;
@@ -1160,6 +1160,8 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
return CTX_MODE_EDIT_METABALL;
case OB_LATTICE:
return CTX_MODE_EDIT_LATTICE;
+ case OB_CURVES:
+ return CTX_MODE_EDIT_CURVES;
}
}
else {
@@ -1227,6 +1229,7 @@ static const char *data_mode_strings[] = {
"armature_edit",
"mball_edit",
"lattice_edit",
+ "curves_edit",
"posemode",
"sculpt_mode",
"weightpaint",
diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc
index c6c0111780e..6be04b79761 100644
--- a/source/blender/blenkernel/intern/curve.cc
+++ b/source/blender/blenkernel/intern/curve.cc
@@ -115,6 +115,8 @@ static void curve_free_data(ID *id)
MEM_SAFE_FREE(curve->str);
MEM_SAFE_FREE(curve->strinfo);
MEM_SAFE_FREE(curve->tb);
+
+ delete curve->curve_eval;
}
static void curve_foreach_id(ID *id, LibraryForeachIDData *data)
@@ -293,14 +295,14 @@ static void curve_blend_read_expand(BlendExpander *expander, ID *id)
BLO_expand(expander, cu->textoncurve);
}
-IDTypeInfo IDType_ID_CU = {
- /* id_code */ ID_CU,
- /* id_filter */ FILTER_ID_CU,
- /* main_listbase_index */ INDEX_ID_CU,
+IDTypeInfo IDType_ID_CU_LEGACY = {
+ /* id_code */ ID_CU_LEGACY,
+ /* id_filter */ FILTER_ID_CU_LEGACY,
+ /* main_listbase_index */ INDEX_ID_CU_LEGACY,
/* struct_size */ sizeof(Curve),
/* name */ "Curve",
/* name_plural */ "curves",
- /* translation_context */ BLT_I18NCONTEXT_ID_CURVE,
+ /* translation_context */ BLT_I18NCONTEXT_ID_CURVE_LEGACY,
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* asset_type_info */ nullptr,
@@ -406,7 +408,7 @@ Curve *BKE_curve_add(Main *bmain, const char *name, int type)
Curve *cu;
/* We cannot use #BKE_id_new here as we need some custom initialization code. */
- cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU, name, 0);
+ cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU_LEGACY, name, 0);
BKE_curve_init(cu, type);
@@ -440,7 +442,7 @@ short BKE_curve_type_get(const Curve *cu)
}
if (!cu->type) {
- type = OB_CURVE;
+ type = OB_CURVES_LEGACY;
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
if (nu->pntsv > 1) {
@@ -473,7 +475,7 @@ void BKE_curve_type_test(Object *ob)
{
ob->type = BKE_curve_type_get((Curve *)ob->data);
- if (ob->type == OB_CURVE) {
+ if (ob->type == OB_CURVES_LEGACY) {
Curve *cu = (Curve *)ob->data;
if (CU_IS_2D(cu)) {
BKE_curve_dimension_update(cu);
diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c
index ffef22fb498..6f32f0f5e6f 100644
--- a/source/blender/blenkernel/intern/curve_bevel.c
+++ b/source/blender/blenkernel/intern/curve_bevel.c
@@ -228,7 +228,7 @@ static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
if (cu->bevobj == NULL) {
return;
}
- if (cu->bevobj->type != OB_CURVE) {
+ if (cu->bevobj->type != OB_CURVES_LEGACY) {
return;
}
diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c
index 285e6978522..129e930a21e 100644
--- a/source/blender/blenkernel/intern/curve_convert.c
+++ b/source/blender/blenkernel/intern/curve_convert.c
@@ -27,7 +27,7 @@ static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph)
Object *evaluated_object = DEG_get_evaluated_object(depsgraph, object);
BKE_vfont_to_curve_nubase(evaluated_object, FO_EDIT, &new_curve->nurb);
- new_curve->type = OB_CURVE;
+ new_curve->type = OB_CURVES_LEGACY;
new_curve->flag &= ~CU_3D;
BKE_curve_dimension_update(new_curve);
@@ -55,7 +55,7 @@ static Curve *curve_from_curve_object(Object *object, Depsgraph *depsgraph, bool
Curve *BKE_curve_new_from_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers)
{
- if (!ELEM(object->type, OB_FONT, OB_CURVE)) {
+ if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) {
return NULL;
}
diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c
index f76e4202994..fb082fccc0b 100644
--- a/source/blender/blenkernel/intern/curve_deform.c
+++ b/source/blender/blenkernel/intern/curve_deform.c
@@ -211,7 +211,7 @@ static void curve_deform_coords_impl(const Object *ob_curve,
bool use_dverts = false;
int cd_dvert_offset;
- if (ob_curve->type != OB_CURVE) {
+ if (ob_curve->type != OB_CURVES_LEGACY) {
return;
}
@@ -404,7 +404,7 @@ void BKE_curve_deform_co(const Object *ob_curve,
CurveDeform cd;
float quat[4];
- if (ob_curve->type != OB_CURVE) {
+ if (ob_curve->type != OB_CURVES_LEGACY) {
unit_m3(r_mat);
return;
}
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 7fb833e67f8..d6525a11cff 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -13,6 +13,8 @@
#include "BKE_anonymous_attribute.hh"
#include "BKE_curve.h"
+#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_spline.hh"
using blender::Array;
@@ -23,8 +25,15 @@ using blender::Map;
using blender::MutableSpan;
using blender::Span;
using blender::StringRefNull;
+using blender::VArray;
+using blender::VArray_Span;
using blender::Vector;
using blender::bke::AttributeIDRef;
+using blender::bke::OutputAttribute;
+using blender::bke::OutputAttribute_Typed;
+using blender::bke::ReadAttributeLookup;
+using blender::fn::GVArray;
+using blender::fn::GVArray_GSpan;
blender::Span<SplinePtr> CurveEval::splines() const
{
@@ -36,7 +45,7 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
-bool CurveEval::has_spline_with_type(const Spline::Type type) const
+bool CurveEval::has_spline_with_type(const CurveType type) const
{
for (const SplinePtr &spline : this->splines()) {
if (spline->type() == type) {
@@ -160,24 +169,24 @@ void CurveEval::mark_cache_invalid()
}
}
-static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
+static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {
case HD_FREE:
- return BezierSpline::HandleType::Free;
+ return BEZIER_HANDLE_FREE;
case HD_AUTO:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case HD_VECT:
- return BezierSpline::HandleType::Vector;
+ return BEZIER_HANDLE_VECTOR;
case HD_ALIGN:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
case HD_AUTO_ANIM:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case HD_ALIGN_DOUBLESIDE:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
}
BLI_assert_unreachable();
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
}
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
@@ -220,8 +229,8 @@ static SplinePtr spline_from_dna_bezier(const Nurb &nurb)
MutableSpan<float3> positions = spline->positions();
MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true);
MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true);
- MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left();
- MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right();
+ MutableSpan<int8_t> handle_types_left = spline->handle_types_left();
+ MutableSpan<int8_t> handle_types_right = spline->handle_types_right();
MutableSpan<float> radii = spline->radii();
MutableSpan<float> tilts = spline->tilts();
@@ -336,6 +345,186 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve));
}
+static void copy_attributes_between_components(const GeometryComponent &src_component,
+ GeometryComponent &dst_component,
+ Span<std::string> skip)
+{
+ src_component.attribute_foreach(
+ [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (id.is_named() && skip.contains(id.name())) {
+ return true;
+ }
+
+ GVArray src_attribute = src_component.attribute_try_get_for_read(
+ id, meta_data.domain, meta_data.data_type);
+ if (!src_attribute) {
+ return true;
+ }
+ GVArray_GSpan src_attribute_data{src_attribute};
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ id, meta_data.domain, meta_data.data_type);
+ if (!dst_attribute) {
+ return true;
+ }
+ dst_attribute.varray().set_all(src_attribute_data.data());
+ dst_attribute.save();
+ return true;
+ });
+}
+
+std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
+{
+ CurveComponent src_component;
+ src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly);
+ const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
+ curves.geometry);
+
+ VArray_Span<float> nurbs_weights{
+ src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
+ VArray_Span<int> nurbs_orders{
+ src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
+ VArray_Span<int8_t> nurbs_knots_modes{
+ src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
+
+ VArray_Span<int8_t> handle_types_right{
+ src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
+ VArray_Span<int8_t> handle_types_left{
+ src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
+
+ /* Create splines with the correct size and type. */
+ VArray<int8_t> curve_types = geometry.curve_types();
+ std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>();
+ for (const int curve_index : curve_types.index_range()) {
+ const IndexRange point_range = geometry.range_for_curve(curve_index);
+
+ std::unique_ptr<Spline> spline;
+ switch (curve_types[curve_index]) {
+ case CURVE_TYPE_POLY: {
+ spline = std::make_unique<PolySpline>();
+ spline->resize(point_range.size());
+ break;
+ }
+ case CURVE_TYPE_BEZIER: {
+ std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>();
+ bezier_spline->resize(point_range.size());
+ bezier_spline->handle_types_left().copy_from(handle_types_left.slice(point_range));
+ bezier_spline->handle_types_right().copy_from(handle_types_right.slice(point_range));
+
+ spline = std::move(bezier_spline);
+ break;
+ }
+ case CURVE_TYPE_NURBS: {
+ std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>();
+ nurb_spline->resize(point_range.size());
+ nurb_spline->weights().copy_from(nurbs_weights.slice(point_range));
+ nurb_spline->set_order(nurbs_orders[curve_index]);
+ nurb_spline->knots_mode = static_cast<NURBSpline::KnotsMode>(
+ nurbs_knots_modes[curve_index]);
+
+ spline = std::move(nurb_spline);
+ break;
+ }
+ case CURVE_TYPE_CATMULL_ROM:
+ /* Not supported yet. */
+ BLI_assert_unreachable();
+ continue;
+ }
+ spline->positions().fill(float3(0));
+ spline->tilts().fill(0.0f);
+ spline->radii().fill(1.0f);
+ curve_eval->add_spline(std::move(spline));
+ }
+
+ CurveComponentLegacy dst_component;
+ dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable);
+
+ copy_attributes_between_components(src_component,
+ dst_component,
+ {"curve_type",
+ "nurbs_weight",
+ "nurbs_order",
+ "knots_mode",
+ "handle_type_right",
+ "handle_type_left"});
+
+ return curve_eval;
+}
+
+Curves *curve_eval_to_curves(const CurveEval &curve_eval)
+{
+ Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(),
+ curve_eval.splines().size());
+ CurveComponent dst_component;
+ dst_component.replace(curves, GeometryOwnershipType::Editable);
+
+ blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry);
+ geometry.offsets().copy_from(curve_eval.control_point_offsets());
+ MutableSpan<int8_t> curve_types = geometry.curve_types();
+
+ OutputAttribute_Typed<float> nurbs_weight;
+ OutputAttribute_Typed<int> nurbs_order;
+ OutputAttribute_Typed<int8_t> nurbs_knots_mode;
+ if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) {
+ nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight",
+ ATTR_DOMAIN_POINT);
+ nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order",
+ ATTR_DOMAIN_CURVE);
+ nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode",
+ ATTR_DOMAIN_CURVE);
+ }
+ OutputAttribute_Typed<int8_t> handle_type_right;
+ OutputAttribute_Typed<int8_t> handle_type_left;
+ if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) {
+ handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>(
+ "handle_type_right", ATTR_DOMAIN_POINT);
+ handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left",
+ ATTR_DOMAIN_POINT);
+ }
+
+ for (const int curve_index : curve_eval.splines().index_range()) {
+ const Spline &spline = *curve_eval.splines()[curve_index];
+ curve_types[curve_index] = curve_eval.splines()[curve_index]->type();
+
+ const IndexRange point_range = geometry.range_for_curve(curve_index);
+
+ switch (spline.type()) {
+ case CURVE_TYPE_POLY:
+ break;
+ case CURVE_TYPE_BEZIER: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(spline);
+ handle_type_right.as_span().slice(point_range).copy_from(src.handle_types_right());
+ handle_type_left.as_span().slice(point_range).copy_from(src.handle_types_left());
+ break;
+ }
+ case CURVE_TYPE_NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(spline);
+ nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode);
+ nurbs_order.as_span()[curve_index] = src.order();
+ nurbs_weight.as_span().slice(point_range).copy_from(src.weights());
+ break;
+ }
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ }
+
+ nurbs_weight.save();
+ nurbs_order.save();
+ nurbs_knots_mode.save();
+ handle_type_right.save();
+ handle_type_left.save();
+
+ CurveComponentLegacy src_component;
+ src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly);
+
+ copy_attributes_between_components(src_component, dst_component, {});
+
+ return curves;
+}
+
void CurveEval::assert_valid_point_attributes() const
{
#ifdef DEBUG
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 51edf4a6591..5d80ef47908 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -172,7 +172,8 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
}
- if (fill_caps && profile.is_cyclic()) {
+ const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic();
+ if (has_caps) {
const int poly_size = info.spline_edge_len * info.profile_edge_len;
const int cap_loop_offset = info.loop_offset + poly_size * 4;
const int cap_poly_offset = info.poly_offset + poly_size;
@@ -225,7 +226,7 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Mark edge loops from sharp vector control points sharp. */
- if (profile.type() == Spline::Type::Bezier) {
+ if (profile.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile);
Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : IndexRange(bezier_spline.size())) {
@@ -256,7 +257,8 @@ static inline int spline_extrude_loop_size(const Spline &curve,
const bool fill_caps)
{
const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
- const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0;
+ const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
+ const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0;
return tube + caps;
}
@@ -265,7 +267,8 @@ static inline int spline_extrude_poly_size(const Spline &curve,
const bool fill_caps)
{
const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size();
- const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0;
+ const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
+ const int caps = has_caps ? 2 : 0;
return tube + caps;
}
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index ccc20d5118a..838f7f28e93 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -14,16 +14,18 @@
#include "DNA_material_types.h"
#include "DNA_object_types.h"
+#include "BLI_bounds.hh"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vector.hh"
#include "BLI_rand.hh"
+#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_idtype.h"
@@ -44,11 +46,11 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::RandomNumberGenerator;
+using blender::Span;
static const char *ATTR_POSITION = "position";
-static const char *ATTR_RADIUS = "radius";
-static void curves_random(Curves *curves);
+static void update_custom_data_pointers(Curves &curves);
static void curves_init_data(ID *id)
{
@@ -57,50 +59,36 @@ static void curves_init_data(ID *id)
MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id);
- CustomData_reset(&curves->geometry.point_data);
- CustomData_reset(&curves->geometry.curve_data);
-
- CustomData_add_layer_named(&curves->geometry.point_data,
- CD_PROP_FLOAT3,
- CD_CALLOC,
- nullptr,
- curves->geometry.point_size,
- ATTR_POSITION);
- CustomData_add_layer_named(&curves->geometry.point_data,
- CD_PROP_FLOAT,
- CD_CALLOC,
- nullptr,
- curves->geometry.point_size,
- ATTR_RADIUS);
-
- BKE_curves_update_customdata_pointers(curves);
-
- curves_random(curves);
+ new (&curves->geometry) blender::bke::CurvesGeometry();
}
static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
{
+ using namespace blender;
+
Curves *curves_dst = (Curves *)id_dst;
const Curves *curves_src = (const Curves *)id_src;
curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
- curves_dst->geometry.point_size = curves_src->geometry.point_size;
- curves_dst->geometry.curve_size = curves_src->geometry.curve_size;
+ const bke::CurvesGeometry &src = bke::CurvesGeometry::wrap(curves_src->geometry);
+ bke::CurvesGeometry &dst = bke::CurvesGeometry::wrap(curves_dst->geometry);
+
+ /* We need special handling here because the generic ID management code has already done a
+ * shallow copy from the source to the destination, and because the copy-on-write functionality
+ * isn't supported more generically yet. */
+
+ dst.point_size = src.point_size;
+ dst.curve_size = src.curve_size;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
- CustomData_copy(&curves_src->geometry.point_data,
- &curves_dst->geometry.point_data,
- CD_MASK_ALL,
- alloc_type,
- curves_dst->geometry.point_size);
- CustomData_copy(&curves_src->geometry.curve_data,
- &curves_dst->geometry.curve_data,
- CD_MASK_ALL,
- alloc_type,
- curves_dst->geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves_dst);
-
- curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets));
+ CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size);
+ CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size);
+
+ dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets));
+
+ dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__);
+
+ dst.update_customdata_pointers();
curves_dst->batch_cache = nullptr;
}
@@ -110,12 +98,9 @@ static void curves_free_data(ID *id)
Curves *curves = (Curves *)id;
BKE_animdata_free(&curves->id, false);
- BKE_curves_batch_cache_free(curves);
-
- CustomData_free(&curves->geometry.point_data, curves->geometry.point_size);
- CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size);
+ blender::bke::CurvesGeometry::wrap(curves->geometry).~CurvesGeometry();
- MEM_SAFE_FREE(curves->geometry.offsets);
+ BKE_curves_batch_cache_free(curves);
MEM_SAFE_FREE(curves->mat);
}
@@ -126,6 +111,7 @@ static void curves_foreach_id(ID *id, LibraryForeachIDData *data)
for (int i = 0; i < curves->totcol; i++) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER);
}
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->surface, IDWALK_CB_NOP);
}
static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address)
@@ -157,7 +143,7 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre
CD_MASK_ALL,
&curves->id);
- BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets);
+ BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets);
BLO_write_pointer_array(writer, curves->totcol, curves->mat);
if (curves->adt) {
@@ -182,9 +168,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id)
/* Geometry */
CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size);
CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves);
+ update_custom_data_pointers(*curves);
- BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets);
+ BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets);
+
+ curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
/* Materials */
BLO_read_pointer_array(reader, (void **)&curves->mat);
@@ -196,6 +184,7 @@ static void curves_blend_read_lib(BlendLibReader *reader, ID *id)
for (int a = 0; a < curves->totcol; a++) {
BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]);
}
+ BLO_read_id_address(reader, curves->id.lib, &curves->surface);
}
static void curves_blend_read_expand(BlendExpander *expander, ID *id)
@@ -204,6 +193,7 @@ static void curves_blend_read_expand(BlendExpander *expander, ID *id)
for (int a = 0; a < curves->totcol; a++) {
BLO_expand(expander, curves->mat[a]);
}
+ BLO_expand(expander, curves->surface);
}
IDTypeInfo IDType_ID_CV = {
@@ -236,53 +226,9 @@ IDTypeInfo IDType_ID_CV = {
/*lib_override_apply_post */ nullptr,
};
-static void curves_random(Curves *curves)
+static void update_custom_data_pointers(Curves &curves)
{
- CurvesGeometry &geometry = curves->geometry;
- const int numpoints = 8;
-
- geometry.curve_size = 500;
-
- geometry.curve_size = 500;
- geometry.point_size = geometry.curve_size * numpoints;
-
- curves->geometry.offsets = (int *)MEM_calloc_arrayN(
- curves->geometry.curve_size + 1, sizeof(int), __func__);
- CustomData_realloc(&geometry.point_data, geometry.point_size);
- CustomData_realloc(&geometry.curve_data, geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves);
-
- MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1};
- MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size};
- MutableSpan<float> radii{geometry.radius, geometry.point_size};
-
- for (const int i : offsets.index_range()) {
- geometry.offsets[i] = numpoints * i;
- }
-
- RandomNumberGenerator rng;
-
- for (int i = 0; i < geometry.curve_size; i++) {
- const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
- MutableSpan<float3> curve_positions = positions.slice(curve_range);
- MutableSpan<float> curve_radii = radii.slice(curve_range);
-
- const float theta = 2.0f * M_PI * rng.get_float();
- const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
-
- float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)};
- no = blender::math::normalize(no);
-
- float3 co = no;
- for (int key = 0; key < numpoints; key++) {
- float t = key / (float)(numpoints - 1);
- curve_positions[key] = co;
- curve_radii[key] = 0.02f * (1.0f - t);
-
- float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f;
- co += (offset + no) / numpoints;
- }
- }
+ blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers();
}
void *BKE_curves_add(Main *bmain, const char *name)
@@ -304,18 +250,13 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
- float min[3], max[3];
- INIT_MINMAX(min, max);
-
- float(*curves_co)[3] = curves->geometry.position;
- float *curves_radius = curves->geometry.radius;
- for (int a = 0; a < curves->geometry.point_size; a++) {
- float *co = curves_co[a];
- float radius = (curves_radius) ? curves_radius[a] : 0.0f;
- const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
- const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
- DO_MIN(co_min, min);
- DO_MAX(co_max, max);
+ blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry);
+
+ float3 min(FLT_MAX);
+ float3 max(-FLT_MAX);
+ if (!geometry.bounds_min_max(min, max)) {
+ min = float3(-1);
+ max = float3(1);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
@@ -324,46 +265,11 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
return ob->runtime.bb;
}
-void BKE_curves_update_customdata_pointers(Curves *curves)
-{
- curves->geometry.position = (float(*)[3])CustomData_get_layer_named(
- &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION);
- curves->geometry.radius = (float *)CustomData_get_layer_named(
- &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS);
-}
-
bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer)
{
return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION);
}
-/* Dependency Graph */
-
-Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve)
-{
- Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
-
- STRNCPY(curves_dst->id.name, curves_src->id.name);
- curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
- curves_dst->totcol = curves_src->totcol;
-
- curves_dst->geometry.point_size = totpoint;
- curves_dst->geometry.curve_size = totcurve;
- CustomData_copy(&curves_src->geometry.point_data,
- &curves_dst->geometry.point_data,
- CD_MASK_ALL,
- CD_CALLOC,
- totpoint);
- CustomData_copy(&curves_src->geometry.curve_data,
- &curves_dst->geometry.curve_data,
- CD_MASK_ALL,
- CD_CALLOC,
- totcurve);
- BKE_curves_update_customdata_pointers(curves_dst);
-
- return curves_dst;
-}
-
Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference)
{
int flags = LIB_ID_COPY_LOCALIZE;
@@ -409,16 +315,16 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
curves = BKE_curves_copy_for_eval(curves, true);
}
- /* Ensure we are not overwriting referenced data. */
- CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data,
- CD_PROP_FLOAT3,
- ATTR_POSITION,
- curves->geometry.point_size);
- BKE_curves_update_customdata_pointers(curves);
-
/* Created deformed coordinates array on demand. */
- mti->deformVerts(
- md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size);
+ blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
+ curves->geometry);
+ MutableSpan<float3> positions = geometry.positions();
+
+ mti->deformVerts(md,
+ &mectx,
+ nullptr,
+ reinterpret_cast<float(*)[3]>(positions.data()),
+ curves->geometry.point_size);
}
}
@@ -457,3 +363,24 @@ void BKE_curves_batch_cache_free(Curves *curves)
BKE_curves_batch_cache_free_cb(curves);
}
}
+
+namespace blender::bke {
+
+Curves *curves_new_nomain(const int point_size, const int curves_size)
+{
+ Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
+ CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry);
+ geometry.resize(point_size, curves_size);
+ return curves;
+}
+
+Curves *curves_new_nomain_single(const int point_size, const CurveType type)
+{
+ Curves *curves = curves_new_nomain(point_size, 1);
+ CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry);
+ geometry.offsets().last() = point_size;
+ geometry.curve_types().first() = type;
+ return curves;
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
new file mode 100644
index 00000000000..3eea579230a
--- /dev/null
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bounds.hh"
+
+#include "DNA_curves_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+
+namespace blender::bke {
+
+static const std::string ATTR_POSITION = "position";
+static const std::string ATTR_RADIUS = "radius";
+static const std::string ATTR_CURVE_TYPE = "curve_type";
+static const std::string ATTR_CYCLIC = "cyclic";
+
+/* -------------------------------------------------------------------- */
+/** \name Constructors/Destructor
+ * \{ */
+
+CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0)
+{
+}
+
+CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size)
+{
+ this->point_size = point_size;
+ this->curve_size = curve_size;
+ CustomData_reset(&this->point_data);
+ CustomData_reset(&this->curve_data);
+
+ CustomData_add_layer_named(&this->point_data,
+ CD_PROP_FLOAT3,
+ CD_DEFAULT,
+ nullptr,
+ this->point_size,
+ ATTR_POSITION.c_str());
+
+ this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__);
+
+ this->update_customdata_pointers();
+
+ this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
+}
+
+/**
+ * \note Expects `dst` to be initialized, since the original attributes must be freed.
+ */
+static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
+{
+ CustomData_free(&dst.point_data, dst.point_size);
+ CustomData_free(&dst.curve_data, dst.curve_size);
+ dst.point_size = src.point_size;
+ dst.curve_size = src.curve_size;
+ CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size);
+ CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size);
+
+ MEM_SAFE_FREE(dst.curve_offsets);
+ dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__);
+ dst.offsets().copy_from(src.offsets());
+
+ dst.tag_topology_changed();
+
+ dst.update_customdata_pointers();
+}
+
+CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
+ : CurvesGeometry(other.point_size, other.curve_size)
+{
+ copy_curves_geometry(*this, other);
+}
+
+CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
+{
+ if (this != &other) {
+ copy_curves_geometry(*this, other);
+ }
+ return *this;
+}
+
+CurvesGeometry::~CurvesGeometry()
+{
+ CustomData_free(&this->point_data, this->point_size);
+ CustomData_free(&this->curve_data, this->curve_size);
+ MEM_SAFE_FREE(this->curve_offsets);
+ MEM_delete(this->runtime);
+ this->runtime = nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Accessors
+ * \{ */
+
+int CurvesGeometry::points_size() const
+{
+ return this->point_size;
+}
+int CurvesGeometry::curves_size() const
+{
+ return this->curve_size;
+}
+IndexRange CurvesGeometry::points_range() const
+{
+ return IndexRange(this->points_size());
+}
+IndexRange CurvesGeometry::curves_range() const
+{
+ return IndexRange(this->curves_size());
+}
+
+int CurvesGeometry::evaluated_points_size() const
+{
+ /* TODO: Implement when there are evaluated points. */
+ return 0;
+}
+
+IndexRange CurvesGeometry::range_for_curve(const int index) const
+{
+ const int offset = this->curve_offsets[index];
+ const int offset_next = this->curve_offsets[index + 1];
+ return {offset, offset_next - offset};
+}
+
+VArray<int8_t> CurvesGeometry::curve_types() const
+{
+ if (const int8_t *data = (const int8_t *)CustomData_get_layer_named(
+ &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str())) {
+ return VArray<int8_t>::ForSpan({data, this->curve_size});
+ }
+ return VArray<int8_t>::ForSingle(CURVE_TYPE_CATMULL_ROM, this->curve_size);
+}
+
+MutableSpan<int8_t> CurvesGeometry::curve_types()
+{
+ int8_t *data = (int8_t *)CustomData_add_layer_named(&this->curve_data,
+ CD_PROP_INT8,
+ CD_CALLOC,
+ nullptr,
+ this->curve_size,
+ ATTR_CURVE_TYPE.c_str());
+ return {data, this->curve_size};
+}
+
+MutableSpan<float3> CurvesGeometry::positions()
+{
+ this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
+ &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size);
+ return {(float3 *)this->position, this->point_size};
+}
+Span<float3> CurvesGeometry::positions() const
+{
+ return {(const float3 *)this->position, this->point_size};
+}
+
+MutableSpan<int> CurvesGeometry::offsets()
+{
+ return {this->curve_offsets, this->curve_size + 1};
+}
+Span<int> CurvesGeometry::offsets() const
+{
+ return {this->curve_offsets, this->curve_size + 1};
+}
+
+VArray<bool> CurvesGeometry::cyclic() const
+{
+ const bool *data = (const bool *)CustomData_get_layer_named(
+ &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str());
+ if (data != nullptr) {
+ return VArray<bool>::ForSpan(Span(data, this->curve_size));
+ }
+ return VArray<bool>::ForSingle(false, this->curve_size);
+}
+
+MutableSpan<bool> CurvesGeometry::cyclic()
+{
+ bool *data = (bool *)CustomData_add_layer_named(
+ &this->curve_data, CD_PROP_BOOL, CD_CALLOC, nullptr, this->curve_size, ATTR_CYCLIC.c_str());
+ return {data, this->curve_size};
+}
+
+void CurvesGeometry::resize(const int point_size, const int curve_size)
+{
+ if (point_size != this->point_size) {
+ CustomData_realloc(&this->point_data, point_size);
+ this->point_size = point_size;
+ }
+ if (curve_size != this->curve_size) {
+ CustomData_realloc(&this->curve_data, curve_size);
+ this->curve_size = curve_size;
+ this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curve_size + 1));
+ }
+ this->tag_topology_changed();
+ this->update_customdata_pointers();
+}
+
+void CurvesGeometry::tag_positions_changed()
+{
+ this->runtime->position_cache_dirty = true;
+ this->runtime->tangent_cache_dirty = true;
+ this->runtime->normal_cache_dirty = true;
+}
+void CurvesGeometry::tag_topology_changed()
+{
+ this->runtime->position_cache_dirty = true;
+ this->runtime->tangent_cache_dirty = true;
+ this->runtime->normal_cache_dirty = true;
+}
+void CurvesGeometry::tag_normals_changed()
+{
+ this->runtime->normal_cache_dirty = true;
+}
+
+void CurvesGeometry::translate(const float3 &translation)
+{
+ MutableSpan<float3> positions = this->positions();
+ threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
+ for (float3 &position : positions.slice(range)) {
+ position += translation;
+ }
+ });
+}
+
+void CurvesGeometry::transform(const float4x4 &matrix)
+{
+ MutableSpan<float3> positions = this->positions();
+ threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
+ for (float3 &position : positions.slice(range)) {
+ position = matrix * position;
+ }
+ });
+}
+
+static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
+{
+ Span<float3> positions = curves.positions();
+ if (curves.radius) {
+ Span<float> radii{curves.radius, curves.points_size()};
+ return bounds::min_max_with_radii(positions, radii);
+ }
+ return bounds::min_max(positions);
+}
+
+bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
+{
+ const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
+ if (!bounds) {
+ return false;
+ }
+ min = math::min(bounds->min, min);
+ max = math::max(bounds->max, max);
+ return true;
+}
+
+void CurvesGeometry::update_customdata_pointers()
+{
+ this->position = (float(*)[3])CustomData_get_layer_named(
+ &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str());
+ this->radius = (float *)CustomData_get_layer_named(
+ &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str());
+ this->curve_type = (int8_t *)CustomData_get_layer_named(
+ &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Domain Interpolation
+ * \{ */
+
+/**
+ * Mix together all of a curve's control point values.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ attribute_math::DefaultMixer<T> mixer(r_values);
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ for (const int i_point : curves.range_for_curve(i_curve)) {
+ mixer.mix_in(i_curve, old_values[i_point]);
+ }
+ }
+ mixer.finalize();
+}
+
+/**
+ * A curve is selected if all of its control points were selected.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<>
+void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
+ const VArray<bool> &old_values,
+ MutableSpan<bool> r_values)
+{
+ r_values.fill(true);
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ for (const int i_point : curves.range_for_curve(i_curve)) {
+ if (!old_values[i_point]) {
+ r_values[i_curve] = false;
+ break;
+ }
+ }
+ }
+}
+
+static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves,
+ const GVArray &varray)
+{
+ GVArray new_varray;
+ attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(curves.curves_size());
+ adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
+ new_varray = VArray<T>::ForContainer(std::move(values));
+ }
+ });
+ return new_varray;
+}
+
+/**
+ * Copy the value from a curve to all of its points.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ r_values.slice(curves.range_for_curve(i_curve)).fill(old_values[i_curve]);
+ }
+}
+
+static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves,
+ const GVArray &varray)
+{
+ GVArray new_varray;
+ attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<T> values(curves.points_size());
+ adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
+ new_varray = VArray<T>::ForContainer(std::move(values));
+ });
+ return new_varray;
+}
+
+fn::GVArray CurvesGeometry::adapt_domain(const fn::GVArray &varray,
+ const AttributeDomain from,
+ const AttributeDomain to) const
+{
+ if (!varray) {
+ return {};
+ }
+ if (varray.is_empty()) {
+ return {};
+ }
+ if (from == to) {
+ return varray;
+ }
+
+ if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) {
+ return adapt_curve_domain_point_to_curve(*this, varray);
+ }
+ if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) {
+ return adapt_curve_domain_curve_to_point(*this, varray);
+ }
+
+ BLI_assert_unreachable();
+ return {};
+}
+
+/** \} */
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc
index 867bdcd06bd..4492f8bbc64 100644
--- a/source/blender/blenkernel/intern/customdata.cc
+++ b/source/blender/blenkernel/intern/customdata.cc
@@ -1777,7 +1777,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_PROP_INT8 */
- {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
+ {sizeof(int8_t), "MInt8Property", 1, N_("Int8"), nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */ /* UNUSED */
{-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 47: CD_PROP_COLOR */
@@ -1959,10 +1959,10 @@ const CustomData_MeshMasks CD_MASK_BMESH = {
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
};
const CustomData_MeshMasks CD_MASK_EVERYTHING = {
- /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
- CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
- CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX |
- CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE),
+ /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT |
+ CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO |
+ CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK |
+ CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE),
/* emask */
(CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE |
CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
@@ -1971,7 +1971,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PREVIEW_MCOL |
CD_MASK_PROP_ALL),
/* pmask */
- (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_FACEMAP |
+ (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP |
CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS),
/* lmask */
(CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV |
diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c
index 6f1026f170f..1818e5a9490 100644
--- a/source/blender/blenkernel/intern/data_transfer.c
+++ b/source/blender/blenkernel/intern/data_transfer.c
@@ -263,7 +263,6 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
/* This should be ensured by cddata_masks we pass to code generating/giving us me_src now. */
BLI_assert(CustomData_get_layer(&me_src->ldata, CD_NORMAL) != NULL);
- BLI_assert(CustomData_get_layer(&me_src->pdata, CD_NORMAL) != NULL);
(void)me_src;
float(*loop_nors_dst)[3];
@@ -318,15 +317,12 @@ static void data_transfer_dtdata_type_postprocess(Object *UNUSED(ob_src),
const int num_polys_dst = me_dst->totpoly;
MLoop *loops_dst = me_dst->mloop;
const int num_loops_dst = me_dst->totloop;
- CustomData *pdata_dst = &me_dst->pdata;
CustomData *ldata_dst = &me_dst->ldata;
- const float(*poly_nors_dst)[3] = CustomData_get_layer(pdata_dst, CD_NORMAL);
+ const float(*poly_nors_dst)[3] = BKE_mesh_poly_normals_ensure(me_dst);
float(*loop_nors_dst)[3] = CustomData_get_layer(ldata_dst, CD_NORMAL);
short(*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL);
- BLI_assert(poly_nors_dst);
-
if (!custom_nors_dst) {
custom_nors_dst = CustomData_add_layer(
ldata_dst, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, num_loops_dst);
@@ -1379,7 +1375,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH));
if (me_dst) {
- dirty_nors_dst = (me_dst->runtime.cd_dirty_vert & CD_NORMAL) != 0;
+ dirty_nors_dst = BKE_mesh_vertex_normals_are_dirty(me_dst);
/* Never create needed custom layers on passed destination mesh
* (assumed to *not* be ob_dst->data, aka modifier case). */
use_create = false;
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index 793af80ec74..f0894ee04e2 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -590,7 +590,7 @@ static float displist_calc_taper(Depsgraph *depsgraph,
Object *taperobj,
float fac)
{
- if (taperobj == nullptr || taperobj->type != OB_CURVE) {
+ if (taperobj == nullptr || taperobj->type != OB_CURVES_LEGACY) {
return 1.0;
}
@@ -865,7 +865,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(
*cu, ob->runtime.curve_cache->deformed_nurbs);
- geometry_set.replace_curve(curve_eval.release());
+ geometry_set.replace_curve(curve_eval_to_curves(*curve_eval));
}
for (; md; md = md->next) {
@@ -1263,7 +1263,7 @@ static GeometrySet evaluate_curve_type_object(Depsgraph *depsgraph,
const bool for_render,
ListBase *r_dispbase)
{
- BLI_assert(ELEM(ob->type, OB_CURVE, OB_FONT));
+ BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT));
const Curve *cu = (const Curve *)ob->data;
ListBase *deformed_nurbs = &ob->runtime.curve_cache->deformed_nurbs;
@@ -1473,7 +1473,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
Object *ob,
const bool for_render)
{
- BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT));
+ BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT));
Curve &cow_curve = *(Curve *)ob->data;
BKE_object_free_derived_caches(ob);
@@ -1490,14 +1490,14 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
else {
GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase);
- if (geometry.has_curve()) {
+ if (geometry.has_curves()) {
/* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval
* added to the curve here, it will also contain a copy of the original curve's data. This is
* essential, because it maintains the expected behavior for evaluated curve data from before
* the CurveEval data type was introduced, when an evaluated object's curve data was just a
* copy of the original curve and everything else ended up in #CurveCache. */
CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>();
- cow_curve.curve_eval = curve_component.get_for_write();
+ cow_curve.curve_eval = curves_to_curve_eval(*curve_component.get_for_read()).release();
BKE_object_eval_assign_data(ob, &cow_curve.id, false);
}
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 2f760597e1a..f2915a97746 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -143,7 +143,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
BLI_rng_srandom(eff->pd->rng, eff->pd->seed + cfra);
}
- if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) {
+ if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVES_LEGACY) {
Curve *cu = eff->ob->data;
if (cu->flag & CU_PATH) {
if (eff->ob->runtime.curve_cache == NULL ||
@@ -161,7 +161,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
}
else if (eff->pd->shape == PFIELD_SHAPE_SURFACE) {
eff->surmd = (SurfaceModifierData *)BKE_modifiers_findby_type(eff->ob, eModifierType_Surface);
- if (eff->ob->type == OB_CURVE) {
+ if (eff->ob->type == OB_CURVES_LEGACY) {
eff->flag |= PE_USE_NORMAL_DATA;
}
}
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index ef2fa2266c4..6f2760e91a6 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -3292,7 +3292,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
mpolys = me->mpoly;
mloops = me->mloop;
- /* Get size (dimension) but considering scaling scaling. */
+ /* Get size (dimension) but considering scaling. */
copy_v3_v3(cell_size_scaled, fds->cell_size);
mul_v3_v3(cell_size_scaled, ob->scale);
madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 5921f853389..0926d65b306 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -23,18 +23,18 @@ using blender::fn::GVArray_GSpan;
/** \name Geometry Component Implementation
* \{ */
-CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
+CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
{
}
-CurveComponent::~CurveComponent()
+CurveComponentLegacy::~CurveComponentLegacy()
{
this->clear();
}
-GeometryComponent *CurveComponent::copy() const
+GeometryComponent *CurveComponentLegacy::copy() const
{
- CurveComponent *new_component = new CurveComponent();
+ CurveComponentLegacy *new_component = new CurveComponentLegacy();
if (curve_ != nullptr) {
new_component->curve_ = new CurveEval(*curve_);
new_component->ownership_ = GeometryOwnershipType::Owned;
@@ -42,30 +42,23 @@ GeometryComponent *CurveComponent::copy() const
return new_component;
}
-void CurveComponent::clear()
+void CurveComponentLegacy::clear()
{
BLI_assert(this->is_mutable());
if (curve_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
- if (curve_for_render_ != nullptr) {
- /* The curve created by this component should not have any edit mode data. */
- BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
- BKE_id_free(nullptr, curve_for_render_);
- curve_for_render_ = nullptr;
- }
-
curve_ = nullptr;
}
}
-bool CurveComponent::has_curve() const
+bool CurveComponentLegacy::has_curve() const
{
return curve_ != nullptr;
}
-void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
+void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
@@ -73,7 +66,7 @@ void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
ownership_ = ownership;
}
-CurveEval *CurveComponent::release()
+CurveEval *CurveComponentLegacy::release()
{
BLI_assert(this->is_mutable());
CurveEval *curve = curve_;
@@ -81,12 +74,12 @@ CurveEval *CurveComponent::release()
return curve;
}
-const CurveEval *CurveComponent::get_for_read() const
+const CurveEval *CurveComponentLegacy::get_for_read() const
{
return curve_;
}
-CurveEval *CurveComponent::get_for_write()
+CurveEval *CurveComponentLegacy::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
@@ -96,17 +89,17 @@ CurveEval *CurveComponent::get_for_write()
return curve_;
}
-bool CurveComponent::is_empty() const
+bool CurveComponentLegacy::is_empty() const
{
return curve_ == nullptr;
}
-bool CurveComponent::owns_direct_data() const
+bool CurveComponentLegacy::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
-void CurveComponent::ensure_owns_direct_data()
+void CurveComponentLegacy::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
@@ -115,32 +108,13 @@ void CurveComponent::ensure_owns_direct_data()
}
}
-const Curve *CurveComponent::get_curve_for_render() const
-{
- if (curve_ == nullptr) {
- return nullptr;
- }
- if (curve_for_render_ != nullptr) {
- return curve_for_render_;
- }
- std::lock_guard lock{curve_for_render_mutex_};
- if (curve_for_render_ != nullptr) {
- return curve_for_render_;
- }
-
- curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr);
- curve_for_render_->curve_eval = curve_;
-
- return curve_for_render_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access Helper Functions
* \{ */
-int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const
{
if (curve_ == nullptr) {
return 0;
@@ -334,9 +308,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra
} // namespace blender::bke
-GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
- const AttributeDomain from_domain,
- const AttributeDomain to_domain) const
+GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(
+ const GVArray &varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
if (!varray) {
return {};
@@ -361,14 +336,15 @@ GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
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);
+ CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(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);
+ const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>(
+ component);
return curve_component.get_for_read();
}
@@ -377,98 +353,6 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen
namespace blender::bke {
/* -------------------------------------------------------------------- */
-/** \name Curve Normals Access
- * \{ */
-
-static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
-{
- Span<int> offsets = spline.control_point_offsets();
- Span<float3> evaluated_normals = spline.evaluated_normals();
- for (const int i : IndexRange(spline.size())) {
- normals[i] = evaluated_normals[offsets[i]];
- }
-}
-
-static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
-{
- normals.copy_from(spline.evaluated_normals());
-}
-
-/**
- * Because NURBS control points are not necessarily on the path, the normal at the control points
- * is not well defined, so create a temporary poly spline to find the normals. This requires extra
- * copying currently, but may be more efficient in the future if attributes have some form of CoW.
- */
-static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
-{
- PolySpline poly_spline;
- poly_spline.resize(spline.size());
- poly_spline.positions().copy_from(spline.positions());
- poly_spline.tilts().copy_from(spline.tilts());
- normals.copy_from(poly_spline.evaluated_normals());
-}
-
-static Array<float3> curve_normal_point_domain(const CurveEval &curve)
-{
- Span<SplinePtr> splines = curve.splines();
- Array<int> offsets = curve.control_point_offsets();
- const int total_size = offsets.last();
- Array<float3> normals(total_size);
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
- switch (splines[i]->type()) {
- case Spline::Type::Bezier:
- calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
- break;
- case Spline::Type::Poly:
- calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
- break;
- case Spline::Type::NURBS:
- calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
- break;
- }
- }
- });
- return normals;
-}
-
-VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
-{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
- return nullptr;
- }
-
- if (domain == ATTR_DOMAIN_POINT) {
- const Span<SplinePtr> splines = curve->splines();
-
- /* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
- * This is only possible when there is only one poly spline. */
- if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
- const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
- return VArray<float3>::ForSpan(spline.evaluated_normals());
- }
-
- Array<float3> normals = curve_normal_point_domain(*curve);
- return VArray<float3>::ForContainer(std::move(normals));
- }
-
- if (domain == ATTR_DOMAIN_CURVE) {
- Array<float3> point_normals = curve_normal_point_domain(*curve);
- VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
- return component.attribute_try_adapt_domain<float3>(
- std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- }
-
- return nullptr;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Builtin Spline Attributes
*
* Attributes with a value for every spline, stored contiguously or in every spline separately.
@@ -877,15 +761,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> {
{
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;
- }
+ spline.positions()[indices.point_index] = value;
}
void set_all(Span<float3> src) final
@@ -894,20 +770,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> {
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));
- }
+ spline.positions().copy_from(src.slice(offset, next_offset - offset));
}
}
@@ -955,7 +818,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
const PointIndices indices = lookup_point_indices(offsets_, index);
const Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == Spline::Type::Bezier) {
+ if (spline.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] :
bezier_spline.handle_positions_left()[indices.point_index];
@@ -967,13 +830,13 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
const PointIndices indices = lookup_point_indices(offsets_, index);
Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == Spline::Type::Bezier) {
+ if (spline.type() == CURVE_TYPE_BEZIER) {
BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
if (is_right_) {
- bezier_spline.set_handle_position_right(indices.point_index, value);
+ bezier_spline.handle_positions_right()[indices.point_index] = value;
}
else {
- bezier_spline.set_handle_position_left(indices.point_index, value);
+ bezier_spline.handle_positions_left()[indices.point_index] = value;
}
bezier_spline.mark_cache_invalid();
}
@@ -983,18 +846,18 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
for (const int spline_index : splines_.index_range()) {
Spline &spline = *splines_[spline_index];
- if (spline.type() == Spline::Type::Bezier) {
+ if (spline.type() == CURVE_TYPE_BEZIER) {
const int offset = offsets_[spline_index];
BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
if (is_right_) {
for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.set_handle_position_right(i, src[offset + i]);
+ bezier_spline.handle_positions_right()[i] = src[offset + i];
}
}
else {
for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.set_handle_position_left(i, src[offset + i]);
+ bezier_spline.handle_positions_left()[i] = src[offset + i];
}
}
bezier_spline.mark_cache_invalid();
@@ -1024,7 +887,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
Array<Span<float3>> spans(splines.size());
for (const int i : spans.index_range()) {
- if (splines[i]->type() == Spline::Type::Bezier) {
+ if (splines[i]->type() == CURVE_TYPE_BEZIER) {
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
spans[i] = is_right ? bezier_spline.handle_positions_right() :
bezier_spline.handle_positions_left();
@@ -1214,7 +1077,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
/* 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_spline_with_type(Spline::Type::Bezier)) {
+ if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
@@ -1255,7 +1118,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
- if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return {};
}
@@ -1273,7 +1136,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
- if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return {};
}
@@ -1304,7 +1167,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return false;
}
- return curve->has_spline_with_type(Spline::Type::Bezier) &&
+ return curve->has_spline_with_type(CURVE_TYPE_BEZIER) &&
component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
}
};
@@ -1324,7 +1187,8 @@ 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;
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
+ CD_MASK_PROP_INT8;
public:
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
@@ -1569,7 +1433,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
} // namespace blender::bke
-const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
+const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers()
+ const
{
static blender::bke::ComponentAttributeProviders providers =
blender::bke::create_attribute_providers_for_curve();
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
new file mode 100644
index 00000000000..5723d110aa0
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -0,0 +1,521 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_task.hh"
+
+#include "DNA_ID_enums.h"
+#include "DNA_curve_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_curve.h"
+#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_spline.hh"
+
+#include "attribute_access_intern.hh"
+
+using blender::fn::GVArray;
+
+/* -------------------------------------------------------------------- */
+/** \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 (curves_ != nullptr) {
+ new_component->curves_ = BKE_curves_copy_for_eval(curves_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void CurveComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (curves_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, curves_);
+ }
+ if (curve_for_render_ != nullptr) {
+ /* The curve created by this component should not have any edit mode data. */
+ BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
+ BKE_id_free(nullptr, curve_for_render_);
+ curve_for_render_ = nullptr;
+ }
+
+ curves_ = nullptr;
+ }
+}
+
+bool CurveComponent::has_curves() const
+{
+ return curves_ != nullptr;
+}
+
+void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ curves_ = curves;
+ ownership_ = ownership;
+}
+
+Curves *CurveComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ Curves *curves = curves_;
+ curves_ = nullptr;
+ return curves;
+}
+
+const Curves *CurveComponent::get_for_read() const
+{
+ return curves_;
+}
+
+Curves *CurveComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ curves_ = BKE_curves_copy_for_eval(curves_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return curves_;
+}
+
+bool CurveComponent::is_empty() const
+{
+ return curves_ == 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) {
+ curves_ = BKE_curves_copy_for_eval(curves_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
+const Curve *CurveComponent::get_curve_for_render() const
+{
+ if (curves_ == nullptr) {
+ return nullptr;
+ }
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+ std::lock_guard lock{curve_for_render_mutex_};
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+
+ curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr);
+ curve_for_render_->curve_eval = curves_to_curve_eval(*curves_).release();
+
+ return curve_for_render_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Curve Normals Access
+ * \{ */
+
+namespace blender::bke {
+
+static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
+{
+ Span<int> offsets = spline.control_point_offsets();
+ Span<float3> evaluated_normals = spline.evaluated_normals();
+ for (const int i : IndexRange(spline.size())) {
+ normals[i] = evaluated_normals[offsets[i]];
+ }
+}
+
+static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
+{
+ normals.copy_from(spline.evaluated_normals());
+}
+
+/**
+ * Because NURBS control points are not necessarily on the path, the normal at the control points
+ * is not well defined, so create a temporary poly spline to find the normals. This requires extra
+ * copying currently, but may be more efficient in the future if attributes have some form of CoW.
+ */
+static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
+{
+ PolySpline poly_spline;
+ poly_spline.resize(spline.size());
+ poly_spline.positions().copy_from(spline.positions());
+ poly_spline.tilts().copy_from(spline.tilts());
+ normals.copy_from(poly_spline.evaluated_normals());
+}
+
+static Array<float3> curve_normal_point_domain(const CurveEval &curve)
+{
+ Span<SplinePtr> splines = curve.splines();
+ Array<int> offsets = curve.control_point_offsets();
+ const int total_size = offsets.last();
+ Array<float3> normals(total_size);
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
+ switch (splines[i]->type()) {
+ case CURVE_TYPE_BEZIER:
+ calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
+ break;
+ case CURVE_TYPE_POLY:
+ calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
+ break;
+ case CURVE_TYPE_NURBS:
+ calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
+ break;
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ });
+ return normals;
+}
+
+VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
+{
+ if (component.is_empty()) {
+ return nullptr;
+ }
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<float3> normals = curve_normal_point_domain(*curve);
+ return VArray<float3>::ForContainer(std::move(normals));
+ }
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ Array<float3> point_normals = curve_normal_point_domain(*curve);
+ VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
+ return component.attribute_try_adapt_domain<float3>(
+ std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+ }
+
+ return nullptr;
+}
+
+} // namespace blender::bke
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access Helper Functions
+ * \{ */
+
+int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ if (curves_ == nullptr) {
+ return 0;
+ }
+ const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
+ curves_->geometry);
+ if (domain == ATTR_DOMAIN_POINT) {
+ return geometry.points_size();
+ }
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return geometry.curves_size();
+ }
+ return 0;
+}
+
+GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
+{
+ return blender::bke::CurvesGeometry::wrap(curves_->geometry)
+ .adapt_domain(varray, from_domain, to_domain);
+}
+
+static Curves *get_curves_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 Curves *get_curves_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();
+}
+
+static void tag_component_topology_changed(GeometryComponent &component)
+{
+ Curves *curves = get_curves_from_component_for_write(component);
+ if (curves) {
+ blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed();
+ }
+}
+
+static void tag_component_positions_changed(GeometryComponent &component)
+{
+ Curves *curves = get_curves_from_component_for_write(component);
+ if (curves) {
+ blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed();
+ }
+}
+
+static void tag_component_normals_changed(GeometryComponent &component)
+{
+ Curves *curves = get_curves_from_component_for_write(component);
+ if (curves) {
+ blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed();
+ }
+}
+
+/** \} */
+
+namespace blender::bke {
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Provider Declaration
+ * \{ */
+
+/**
+ * In this function all the attribute providers for a curves 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 CustomDataAccessInfo curve_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ Curves *curves = get_curves_from_component_for_write(component);
+ return curves ? &curves->geometry.curve_data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const Curves *curves = get_curves_from_component_for_read(component);
+ return curves ? &curves->geometry.curve_data : nullptr;
+ },
+ [](GeometryComponent &component) {
+ Curves *curves = get_curves_from_component_for_write(component);
+ if (curves) {
+ blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers();
+ }
+ }};
+ static CustomDataAccessInfo point_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ Curves *curves = get_curves_from_component_for_write(component);
+ return curves ? &curves->geometry.point_data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const Curves *curves = get_curves_from_component_for_read(component);
+ return curves ? &curves->geometry.point_data : nullptr;
+ },
+ [](GeometryComponent &component) {
+ Curves *curves = get_curves_from_component_for_write(component);
+ if (curves) {
+ blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_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>,
+ make_array_write_attribute<float3>,
+ tag_component_positions_changed);
+
+ 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 BuiltinCustomDataLayerProvider id("id",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_INT32,
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<int>,
+ make_array_write_attribute<int>,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider tilt("tilt",
+ 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>,
+ tag_component_normals_changed);
+
+ static BuiltinCustomDataLayerProvider handle_right("handle_right",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ tag_component_positions_changed);
+
+ static BuiltinCustomDataLayerProvider handle_left("handle_left",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ tag_component_positions_changed);
+
+ static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_INT8,
+ CD_PROP_INT8,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<int8_t>,
+ make_array_write_attribute<int8_t>,
+ tag_component_topology_changed);
+
+ static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_INT8,
+ CD_PROP_INT8,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<int8_t>,
+ make_array_write_attribute<int8_t>,
+ tag_component_topology_changed);
+
+ static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
+ 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>,
+ tag_component_positions_changed);
+
+ static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
+ ATTR_DOMAIN_CURVE,
+ CD_PROP_INT32,
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ curve_access,
+ make_array_read_attribute<int>,
+ make_array_write_attribute<int>,
+ tag_component_topology_changed);
+
+ static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
+ ATTR_DOMAIN_CURVE,
+ CD_PROP_INT8,
+ CD_PROP_INT8,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ curve_access,
+ make_array_read_attribute<int8_t>,
+ make_array_write_attribute<int8_t>,
+ tag_component_topology_changed);
+
+ static BuiltinCustomDataLayerProvider resolution("resolution",
+ ATTR_DOMAIN_CURVE,
+ CD_PROP_INT32,
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ curve_access,
+ make_array_read_attribute<int>,
+ make_array_write_attribute<int>,
+ tag_component_positions_changed);
+
+ static BuiltinCustomDataLayerProvider cyclic("cyclic",
+ ATTR_DOMAIN_CURVE,
+ CD_PROP_BOOL,
+ CD_PROP_BOOL,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ curve_access,
+ make_array_read_attribute<bool>,
+ make_array_write_attribute<bool>,
+ tag_component_topology_changed);
+
+ static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access);
+ static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
+
+ return ComponentAttributeProviders({&position,
+ &radius,
+ &id,
+ &tilt,
+ &handle_right,
+ &handle_left,
+ &handle_type_right,
+ &handle_type_left,
+ &nurbs_order,
+ &nurbs_weight,
+ &resolution,
+ &cyclic},
+ {&curve_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 b83a8b1ee94..0cb2b0e812b 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -439,18 +439,6 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
}
};
-template<typename T>
-static GVArray make_array_read_attribute(const void *data, const int domain_size)
-{
- return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
-}
-
-template<typename T>
-static GVMutableArray make_array_write_attribute(void *data, const int domain_size)
-{
- return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
-}
-
static ComponentAttributeProviders create_attribute_providers_for_instances()
{
static InstancePositionAttributeProvider position;
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 2f8ff944420..104166df913 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -854,18 +854,6 @@ static GVMutableArray make_derived_write_attribute(void *data, const int domain_
MutableSpan<StructT>((StructT *)data, domain_size));
}
-template<typename T>
-static GVArray make_array_read_attribute(const void *data, const int domain_size)
-{
- return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
-}
-
-template<typename T>
-static GVMutableArray make_array_write_attribute(void *data, const int domain_size)
-{
- return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
-}
-
static float3 get_vertex_position(const MVert &vert)
{
return float3(vert.co);
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index f6f3c4e1b4e..3db4db307a3 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -117,18 +117,6 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
-template<typename T>
-static GVArray make_array_read_attribute(const void *data, const int domain_size)
-{
- return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
-}
-
-template<typename T>
-static GVMutableArray make_array_write_attribute(void *data, const int domain_size)
-{
- return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
-}
-
/**
* In this function all the attribute providers for a point cloud component are created. Most data
* in this function is statically allocated, because it does not change over time.
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 9c7cfa04e0b..ca372ba8f38 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -7,6 +7,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
+#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
@@ -186,8 +187,9 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
have_minmax |= BKE_volume_min_max(volume, *r_min, *r_max);
}
- const CurveEval *curve = this->get_curve_for_read();
- if (curve != nullptr) {
+ const Curves *curves = this->get_curves_for_read();
+ if (curves != nullptr) {
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
/* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
have_minmax |= curve->bounds_min_max(*r_min, *r_max, true);
}
@@ -258,7 +260,7 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
-const CurveEval *GeometrySet::get_curve_for_read() const
+const Curves *GeometrySet::get_curves_for_read() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
@@ -282,10 +284,10 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
-bool GeometrySet::has_curve() const
+bool GeometrySet::has_curves() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
- return component != nullptr && component->has_curve();
+ return component != nullptr && component->has_curves();
}
bool GeometrySet::has_realized_data() const
@@ -302,8 +304,8 @@ bool GeometrySet::has_realized_data() const
bool GeometrySet::is_empty() const
{
- return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || this->has_volume() ||
- this->has_instances());
+ return !(this->has_mesh() || this->has_curves() || this->has_pointcloud() ||
+ this->has_volume() || this->has_instances());
}
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
@@ -327,12 +329,12 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
-GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
+GeometrySet GeometrySet::create_with_curves(Curves *curves, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
- if (curve != nullptr) {
+ if (curves != nullptr) {
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- component.replace(curve, ownership);
+ component.replace(curves, ownership);
}
return geometry_set;
}
@@ -351,18 +353,18 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
-void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
+void GeometrySet::replace_curve(Curves *curves, GeometryOwnershipType ownership)
{
- if (curve == nullptr) {
+ if (curves == nullptr) {
this->remove<CurveComponent>();
return;
}
- if (curve == this->get_curve_for_read()) {
+ if (curves == this->get_curves_for_read()) {
return;
}
this->remove<CurveComponent>();
CurveComponent &component = this->get_component_for_write<CurveComponent>();
- component.replace(curve, ownership);
+ component.replace(curves, ownership);
}
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
@@ -411,7 +413,7 @@ Volume *GeometrySet::get_volume_for_write()
return component == nullptr ? nullptr : component->get_for_write();
}
-CurveEval *GeometrySet::get_curve_for_write()
+Curves *GeometrySet::get_curves_for_write()
{
CurveComponent *component = this->get_component_ptr<CurveComponent>();
return component == nullptr ? nullptr : component->get_for_write();
@@ -632,7 +634,7 @@ bool BKE_object_has_geometry_set_instances(const Object *ob)
is_instance = ob->type != OB_VOLUME;
break;
case GEO_COMPONENT_TYPE_CURVE:
- is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT);
+ is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT);
break;
}
if (is_instance) {
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 5ce45f6df5a..ee6b77e6463 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -443,7 +443,7 @@ static void gpencil_convert_spline(Main *bmain,
}
if (sample > 0.0f) {
- BKE_gpencil_stroke_sample(gpd, gps, sample, false);
+ BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
}
/* Recalc fill geometry. */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index 365171b300f..865bcebee25 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -202,8 +202,8 @@ static int stroke_march_next_point(const bGPDstroke *gps,
int next_point_index = index_next_pt;
bGPDspoint *pt = nullptr;
- if (!(next_point_index < gps->totpoints)) {
- return -1;
+ if (next_point_index == gps->totpoints) {
+ next_point_index = 0;
}
copy_v3_v3(step_start, current);
@@ -211,15 +211,33 @@ static int stroke_march_next_point(const bGPDstroke *gps,
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
- while (remaining_till_next < remaining_march) {
+ while (remaining_till_next < remaining_march && next_point_index) {
remaining_march -= remaining_till_next;
pt = &gps->points[next_point_index];
+ if (pt->flag & GP_SPOINT_TEMP_TAG) {
+ pt = &gps->points[next_point_index];
+ copy_v3_v3(result, &pt->x);
+ *pressure = gps->points[next_point_index].pressure;
+ *strength = gps->points[next_point_index].strength;
+ memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
+
+ *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
+ *index_to = next_point_index;
+ *ratio_result = 1.0f;
+ next_point_index++;
+ return next_point_index == 0 ? gps->totpoints : next_point_index;
+ }
+ next_point_index++;
copy_v3_v3(point, &pt->x);
copy_v3_v3(step_start, point);
- next_point_index++;
if (!(next_point_index < gps->totpoints)) {
- next_point_index = gps->totpoints - 1;
- break;
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ next_point_index = 0;
+ }
+ else {
+ next_point_index = gps->totpoints - 1;
+ break;
+ }
}
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
@@ -232,35 +250,37 @@ static int stroke_march_next_point(const bGPDstroke *gps,
*strength = gps->points[next_point_index].strength;
memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
- *index_from = next_point_index - 1;
+ *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
*index_to = next_point_index;
*ratio_result = 1.0f;
return 0;
}
+ *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
+ *index_to = next_point_index;
+
float ratio = remaining_march / remaining_till_next;
interp_v3_v3v3(result, step_start, point, ratio);
+ *ratio_result = ratio;
+
*pressure = interpf(
- gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
+ gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio);
*strength = interpf(
- gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
+ gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio);
interp_v4_v4v4(vert_color,
- gps->points[next_point_index - 1].vert_color,
+ gps->points[*index_from].vert_color,
gps->points[next_point_index].vert_color,
ratio);
- *index_from = next_point_index - 1;
- *index_to = next_point_index;
- *ratio_result = ratio;
-
- return next_point_index;
+ return next_point_index == 0 ? gps->totpoints : next_point_index;
}
static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
const int index_next_pt,
const float *current,
const float dist,
+ const float sharp_threshold,
float *result)
{
float remaining_till_next = 0.0f;
@@ -270,8 +290,8 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
int next_point_index = index_next_pt;
bGPDspoint *pt = nullptr;
- if (!(next_point_index < gps->totpoints)) {
- return -1;
+ if (next_point_index == gps->totpoints) {
+ next_point_index = 0;
}
copy_v3_v3(step_start, current);
@@ -279,15 +299,29 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
- while (remaining_till_next < remaining_march) {
+ while (remaining_till_next < remaining_march && next_point_index) {
remaining_march -= remaining_till_next;
pt = &gps->points[next_point_index];
+ if (next_point_index < gps->totpoints - 1 &&
+ angle_v3v3v3(&gps->points[next_point_index - 1].x,
+ &gps->points[next_point_index].x,
+ &gps->points[next_point_index + 1].x) < sharp_threshold) {
+ copy_v3_v3(result, &pt->x);
+ pt->flag |= GP_SPOINT_TEMP_TAG;
+ next_point_index++;
+ return next_point_index == 0 ? gps->totpoints : next_point_index;
+ }
+ next_point_index++;
copy_v3_v3(point, &pt->x);
copy_v3_v3(step_start, point);
- next_point_index++;
if (!(next_point_index < gps->totpoints)) {
- next_point_index = gps->totpoints - 1;
- break;
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ next_point_index = 0;
+ }
+ else {
+ next_point_index = gps->totpoints - 1;
+ break;
+ }
}
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
@@ -296,15 +330,16 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
if (remaining_till_next < remaining_march) {
pt = &gps->points[next_point_index];
copy_v3_v3(result, &pt->x);
+ /* Stroke marching only terminates here. */
return 0;
}
float ratio = remaining_march / remaining_till_next;
interp_v3_v3v3(result, step_start, point, ratio);
- return next_point_index;
+ return next_point_index == 0 ? gps->totpoints : next_point_index;
}
-static int stroke_march_count(const bGPDstroke *gps, const float dist)
+static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold)
{
int point_count = 0;
float point[3];
@@ -315,8 +350,13 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist)
copy_v3_v3(point, &pt->x);
point_count++;
+ /* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG);
+ }
+
while ((next_point_index = stroke_march_next_point_no_interp(
- gps, next_point_index, point, dist, point)) > -1) {
+ gps, next_point_index, point, dist, sharp_threshold, point)) > -1) {
point_count++;
if (next_point_index == 0) {
break; /* last point finished */
@@ -394,7 +434,11 @@ static void stroke_interpolate_deform_weights(
}
}
-bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
+bool BKE_gpencil_stroke_sample(bGPdata *gpd,
+ bGPDstroke *gps,
+ const float dist,
+ const bool select,
+ const float sharp_threshold)
{
bGPDspoint *pt = gps->points;
bGPDspoint *pt1 = nullptr;
@@ -406,7 +450,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
return false;
}
/* TODO: Implement feature point preservation. */
- int count = stroke_march_count(gps, dist);
+ int count = stroke_march_count(gps, dist, sharp_threshold);
bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count,
"gp_stroke_points_sampled");
@@ -491,6 +535,8 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
gps->totpoints = i;
+ gps->flag &= (~GP_STROKE_CYCLIC);
+
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -2052,27 +2098,30 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
MDeformVert *dvert_final = nullptr;
MDeformVert *dvert_next = nullptr;
int totnewpoints, oldtotpoints;
- int i2;
+
+ bool cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
for (int s = 0; s < level; s++) {
- totnewpoints = gps->totpoints - 1;
+ totnewpoints = gps->totpoints;
+ if (!cyclic) {
+ totnewpoints--;
+ }
/* duplicate points in a temp area */
- temp_points = (bGPDspoint *)MEM_dupallocN(gps->points);
+ temp_points = gps->points;
oldtotpoints = gps->totpoints;
/* resize the points arrays */
gps->totpoints += totnewpoints;
- gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ gps->points = (bGPDspoint *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->points), __func__);
if (gps->dvert != nullptr) {
- temp_dverts = (MDeformVert *)MEM_dupallocN(gps->dvert);
- gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ temp_dverts = gps->dvert;
+ gps->dvert = (MDeformVert *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->dvert), __func__);
}
/* move points from last to first to new place */
- i2 = gps->totpoints - 1;
- for (int i = oldtotpoints - 1; i > 0; i--) {
+ for (int i = 0; i < oldtotpoints; i++) {
bGPDspoint *pt = &temp_points[i];
- bGPDspoint *pt_final = &gps->points[i2];
+ bGPDspoint *pt_final = &gps->points[i * 2];
copy_v3_v3(&pt_final->x, &pt->x);
pt_final->pressure = pt->pressure;
@@ -2085,18 +2134,16 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
if (gps->dvert != nullptr) {
dvert = &temp_dverts[i];
- dvert_final = &gps->dvert[i2];
+ dvert_final = &gps->dvert[i * 2];
dvert_final->totweight = dvert->totweight;
dvert_final->dw = dvert->dw;
}
- i2 -= 2;
}
/* interpolate mid points */
- i2 = 1;
- for (int i = 0; i < oldtotpoints - 1; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *next = &temp_points[i + 1];
- bGPDspoint *pt_final = &gps->points[i2];
+ for (int i = cyclic ? 0 : 1, j = cyclic ? oldtotpoints - 1 : 0; i < oldtotpoints; j = i, i++) {
+ bGPDspoint *pt = &temp_points[j];
+ bGPDspoint *next = &temp_points[i];
+ bGPDspoint *pt_final = &gps->points[j * 2 + 1];
/* add a half way point */
interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
@@ -2109,9 +2156,9 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
if (gps->dvert != nullptr) {
- dvert = &temp_dverts[i];
- dvert_next = &temp_dverts[i + 1];
- dvert_final = &gps->dvert[i2];
+ dvert = &temp_dverts[j];
+ dvert_next = &temp_dverts[i];
+ dvert_final = &gps->dvert[j * 2 + 1];
dvert_final->totweight = dvert->totweight;
dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
@@ -2126,8 +2173,6 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
}
}
}
-
- i2 += 2;
}
MEM_SAFE_FREE(temp_points);
@@ -2135,20 +2180,18 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
/* Move points to smooth stroke (not simple type). */
if (type != GP_SUBDIV_SIMPLE) {
- /* duplicate points in a temp area with the new subdivide data */
- temp_points = (bGPDspoint *)MEM_dupallocN(gps->points);
-
+ float mid[3];
/* extreme points are not changed */
- for (int i = 0; i < gps->totpoints - 2; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *next = &temp_points[i + 1];
- bGPDspoint *pt_final = &gps->points[i + 1];
+ for (int i = cyclic ? 0 : 2, j = cyclic ? gps->totpoints - 2 : 0; i < gps->totpoints - 2;
+ j = i, i += 2) {
+ bGPDspoint *prev = &gps->points[j + 1];
+ bGPDspoint *pt = &gps->points[i];
+ bGPDspoint *next = &gps->points[i + 1];
/* move point */
- interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ interp_v3_v3v3(mid, &prev->x, &next->x, 0.5f);
+ interp_v3_v3v3(&pt->x, mid, &pt->x, 0.5f);
}
- /* free temp memory */
- MEM_SAFE_FREE(temp_points);
}
}
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index 2551bb12511..5b9dfa55c45 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -59,7 +59,7 @@ static void id_type_init(void)
INIT_TYPE(ID_LI);
INIT_TYPE(ID_OB);
INIT_TYPE(ID_ME);
- INIT_TYPE(ID_CU);
+ INIT_TYPE(ID_CU_LEGACY);
INIT_TYPE(ID_MB);
INIT_TYPE(ID_MA);
INIT_TYPE(ID_TE);
@@ -215,7 +215,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
CASE_IDFILTER(CF);
- CASE_IDFILTER(CU);
+ CASE_IDFILTER(CU_LEGACY);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
CASE_IDFILTER(CV);
@@ -264,7 +264,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
CASE_IDFILTER(CF);
- CASE_IDFILTER(CU);
+ CASE_IDFILTER(CU_LEGACY);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
CASE_IDFILTER(CV);
@@ -312,7 +312,7 @@ int BKE_idtype_idcode_to_index(const short idcode)
CASE_IDINDEX(BR);
CASE_IDINDEX(CA);
CASE_IDINDEX(CF);
- CASE_IDINDEX(CU);
+ CASE_IDINDEX(CU_LEGACY);
CASE_IDINDEX(GD);
CASE_IDINDEX(GR);
CASE_IDINDEX(CV);
@@ -371,7 +371,7 @@ short BKE_idtype_idcode_from_index(const int index)
CASE_IDCODE(BR);
CASE_IDCODE(CA);
CASE_IDCODE(CF);
- CASE_IDCODE(CU);
+ CASE_IDCODE(CU_LEGACY);
CASE_IDCODE(GD);
CASE_IDCODE(GR);
CASE_IDCODE(CV);
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index fa63f99d3f1..8a212ed0d7d 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -3569,6 +3569,7 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c
/* Must copy image user changes to CoW data-block. */
DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE);
}
+ BKE_image_partial_update_mark_full_update(ima);
}
}
@@ -3609,6 +3610,7 @@ static void image_free_tile(Image *ima, ImageTile *tile)
}
}
}
+ BKE_image_partial_update_mark_full_update(ima);
if (BKE_image_is_multiview(ima)) {
const int totviews = BLI_listbase_count(&ima->views);
@@ -3949,6 +3951,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
}
}
}
+ BKE_image_partial_update_mark_full_update(ima);
return tile;
}
@@ -4014,6 +4017,7 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu
}
}
}
+ BKE_image_partial_update_mark_full_update(ima);
}
static int tile_sort_cb(const void *a, const void *b)
diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc
index 5675641deb4..c4a43d8b023 100644
--- a/source/blender/blenkernel/intern/image_gpu.cc
+++ b/source/blender/blenkernel/intern/image_gpu.cc
@@ -322,19 +322,25 @@ static void image_gpu_texture_partial_update_changes_available(
Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes)
{
while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
- const int tile_offset_x = changes.changed_region.region.xmin;
- const int tile_offset_y = changes.changed_region.region.ymin;
- const int tile_width = min_ii(changes.tile_data.tile_buffer->x,
- BLI_rcti_size_x(&changes.changed_region.region));
- const int tile_height = min_ii(changes.tile_data.tile_buffer->y,
- BLI_rcti_size_y(&changes.changed_region.region));
+ /* Calculate the clipping region with the tile buffer.
+ * TODO(jbakker): should become part of ImageTileData to deduplicate with image engine. */
+ rcti buffer_rect;
+ BLI_rcti_init(
+ &buffer_rect, 0, changes.tile_data.tile_buffer->x, 0, changes.tile_data.tile_buffer->y);
+ rcti clipped_update_region;
+ const bool has_overlap = BLI_rcti_isect(
+ &buffer_rect, &changes.changed_region.region, &clipped_update_region);
+ if (!has_overlap) {
+ continue;
+ }
+
image_update_gputexture_ex(image,
changes.tile_data.tile,
changes.tile_data.tile_buffer,
- tile_offset_x,
- tile_offset_y,
- tile_width,
- tile_height);
+ clipped_update_region.xmin,
+ clipped_update_region.ymin,
+ BLI_rcti_size_x(&clipped_update_region),
+ BLI_rcti_size_y(&clipped_update_region));
}
}
@@ -431,7 +437,8 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
if (ibuf_intern == nullptr) {
ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr);
if (ibuf_intern == nullptr) {
- return image_gpu_texture_error_create(textarget);
+ *tex = image_gpu_texture_error_create(textarget);
+ return *tex;
}
}
diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc
index 9d5635f49ab..4606a14ab69 100644
--- a/source/blender/blenkernel/intern/image_partial_update.cc
+++ b/source/blender/blenkernel/intern/image_partial_update.cc
@@ -198,8 +198,8 @@ struct TileChangeset {
tile_width = image_buffer->x;
tile_height = image_buffer->y;
- int chunk_x_len = tile_width / CHUNK_SIZE;
- int chunk_y_len = tile_height / CHUNK_SIZE;
+ int chunk_x_len = (tile_width + CHUNK_SIZE - 1) / CHUNK_SIZE;
+ int chunk_y_len = (tile_height + CHUNK_SIZE - 1) / CHUNK_SIZE;
init_chunks(chunk_x_len, chunk_y_len);
return true;
}
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index e5c1cf96f8c..abd6505456e 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -1090,7 +1090,7 @@ static char *get_rna_access(ID *id,
propname = particle_adrcodes_to_paths(adrcode, &dummy_index);
break;
- case ID_CU: /* curve */
+ case ID_CU_LEGACY: /* curve */
/* this used to be a 'dummy' curve which got evaluated on the fly...
* now we've got real var for this!
*/
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index b28d9db92cf..e28094c0abc 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -281,7 +281,7 @@ Key *BKE_key_add(Main *bmain, ID *id) /* common function */
key->elemsize = sizeof(float[KEYELEM_FLOAT_LEN_COORD]);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
el = key->elemstr;
el[0] = KEYELEM_ELEM_SIZE_CURVE;
@@ -659,7 +659,7 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int
*ofs = sizeof(float[KEYELEM_FLOAT_LEN_COORD]);
*poinsize = *ofs;
break;
- case ID_CU:
+ case ID_CU_LEGACY:
if (mode == KEY_MODE_BPOINT) {
*ofs = sizeof(float[KEYELEM_FLOAT_LEN_BPOINT]);
*step = KEYELEM_ELEM_LEN_BPOINT;
@@ -1524,7 +1524,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
tot = lt->pntsu * lt->pntsv * lt->pntsw;
size = tot * sizeof(float[KEYELEM_FLOAT_LEN_COORD]);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = ob->data;
tot = BKE_keyblock_curve_element_count(&cu->nurb);
@@ -1570,7 +1570,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
MEM_freeN(weights);
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
cp_cu_key(ob->data, key, actkb, kb, 0, tot, out, tot);
}
}
@@ -1582,7 +1582,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
else if (ob->type == OB_LATTICE) {
do_latt_key(ob, key, out, tot);
}
- else if (ob->type == OB_CURVE) {
+ else if (ob->type == OB_CURVES_LEGACY) {
do_curve_key(ob, key, out, tot);
}
else if (ob->type == OB_SURF) {
@@ -1714,7 +1714,7 @@ bool BKE_key_idtype_support(const short id_type)
{
switch (id_type) {
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_LT:
return true;
default:
@@ -1729,7 +1729,7 @@ Key **BKE_key_from_id_p(ID *id)
Mesh *me = (Mesh *)id;
return &me->key;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)id;
if (cu->vfont == NULL) {
return &cu->key;
@@ -2269,7 +2269,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve
Lattice *lt = ob->data;
BLI_assert((lt->pntsu * lt->pntsv * lt->pntsw) == kb->totelem);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = ob->data;
BLI_assert(BKE_keyblock_curve_element_count(&cu->nurb) == kb->totelem);
}
@@ -2293,7 +2293,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve
copy_v3_v3(fp, *co);
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = (Curve *)ob->data;
Nurb *nu;
BezTriple *bezt;
@@ -2335,7 +2335,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v
tot = lt->pntsu * lt->pntsv * lt->pntsw;
elemsize = lt->key->elemsize;
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = (Curve *)ob->data;
elemsize = cu->key->elemsize;
tot = BKE_keyblock_curve_element_count(&cu->nurb);
@@ -2366,7 +2366,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3]
Lattice *lt = (Lattice *)ob->data;
tot = lt->pntsu * lt->pntsv * lt->pntsw;
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = (Curve *)ob->data;
tot = BKE_nurbList_verts_count(&cu->nurb);
}
@@ -2383,7 +2383,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3]
copy_v3_v3(*co, fp);
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = (Curve *)ob->data;
Nurb *nu;
BezTriple *bezt;
@@ -2422,7 +2422,7 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, const float (*ofs
add_v3_v3(fp, *ofs);
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = (Curve *)ob->data;
Nurb *nu;
BezTriple *bezt;
diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c
index 057309d0896..0903c2a2cac 100644
--- a/source/blender/blenkernel/intern/layer_utils.c
+++ b/source/blender/blenkernel/intern/layer_utils.c
@@ -67,9 +67,11 @@ Object **BKE_view_layer_array_selected_objects_params(
}
FOREACH_SELECTED_OBJECT_END;
- object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array));
- /* We always need a valid allocation (prevent crash on free). */
- if (object_array == NULL) {
+ if (object_array != NULL) {
+ BLI_array_trim(object_array);
+ }
+ else {
+ /* We always need a valid allocation (prevent crash on free). */
object_array = MEM_mallocN(0, __func__);
}
*r_len = BLI_array_len(object_array);
@@ -121,9 +123,11 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
}
FOREACH_BASE_IN_MODE_END;
- base_array = MEM_reallocN(base_array, sizeof(*base_array) * BLI_array_len(base_array));
/* We always need a valid allocation (prevent crash on free). */
- if (base_array == NULL) {
+ if (base_array != NULL) {
+ BLI_array_trim(base_array);
+ }
+ else {
base_array = MEM_mallocN(0, __func__);
}
*r_len = BLI_array_len(base_array);
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index cf25af1c637..ba5556c8b2d 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -234,7 +234,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
for (id = lb->first; id; id = id_next) {
id_next = id->next;
/* NOTE: in case we delete a library, we also delete all its datablocks! */
- if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) {
+ if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) {
BLI_remlink(lb, id);
BLI_addtail(&tagged_deleted_ids, id);
/* Do not tag as no_main now, we want to unlink it first (lower-level ID management
@@ -290,7 +290,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
for (id = lb->first; id; id = id_next) {
id_next = id->next;
/* NOTE: in case we delete a library, we also delete all its datablocks! */
- if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) {
+ if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) {
id->tag |= tag;
BKE_id_remapper_add(remapper, id, NULL);
}
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 02cdd6fcd20..922c1beda38 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -72,12 +72,19 @@ static void lib_override_library_property_operation_clear(
IDOverrideLibraryPropertyOperation *opop);
/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
-BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id)
+BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id)
{
+ if (r_owner_id != NULL) {
+ *r_owner_id = id;
+ }
if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) {
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
if (id_type->owner_get != NULL) {
- return id_type->owner_get(bmain, id)->override_library;
+ ID *owner_id = id_type->owner_get(bmain, id);
+ if (r_owner_id != NULL) {
+ *r_owner_id = owner_id;
+ }
+ return owner_id->override_library;
}
BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter");
}
@@ -146,6 +153,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
id_us_plus(dst_id->override_library->reference);
dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
+ dst_id->override_library->flag = src_id->override_library->flag;
if (do_full_copy) {
BLI_duplicatelist(&dst_id->override_library->properties,
@@ -313,17 +321,36 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
return local_id;
}
+/* TODO: Make this static local function instead? API is becoming complex, and it's not used
+ * outside of this file anyway. */
bool BKE_lib_override_library_create_from_tag(Main *bmain,
Library *owner_library,
const ID *id_root_reference,
ID *id_hierarchy_root,
+ const ID *id_hierarchy_root_reference,
const bool do_no_main)
{
- BLI_assert(id_root_reference != NULL);
- BLI_assert(id_hierarchy_root != NULL || (id_root_reference->tag & LIB_TAG_DOIT) != 0);
- BLI_assert(id_hierarchy_root == NULL ||
- (ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) &&
- id_hierarchy_root->override_library->reference == id_root_reference));
+ BLI_assert(id_root_reference != NULL && ID_IS_LINKED(id_root_reference));
+ /* If we do not have any hierarchy root given, then the root reference must be tagged for
+ * override. */
+ BLI_assert(id_hierarchy_root != NULL || id_hierarchy_root_reference != NULL ||
+ (id_root_reference->tag & LIB_TAG_DOIT) != 0);
+ /* At least one of the hierarchy root pointers must be NULL, passing both is useless and can
+ * create confusion. */
+ BLI_assert(ELEM(NULL, id_hierarchy_root, id_hierarchy_root_reference));
+
+ if (id_hierarchy_root != NULL) {
+ /* If the hierarchy root is given, it must be a valid existing override (used during partial
+ * resync process mainly). */
+ BLI_assert((ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) &&
+ id_hierarchy_root->override_library->reference->lib == id_root_reference->lib));
+ }
+ if (!ELEM(id_hierarchy_root_reference, NULL, id_root_reference)) {
+ /* If the reference hierarchy root is given, it must be from the same library as the reference
+ * root, and also tagged for override. */
+ BLI_assert((id_hierarchy_root_reference->lib == id_root_reference->lib &&
+ (id_hierarchy_root_reference->tag & LIB_TAG_DOIT) != 0));
+ }
const Library *reference_library = id_root_reference->lib;
@@ -379,7 +406,12 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
/* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole
* existing linked IDs usages. */
if (success) {
- if (id_root_reference->newid != NULL) {
+ if (id_hierarchy_root_reference != NULL) {
+ id_hierarchy_root = id_hierarchy_root_reference->newid;
+ }
+ else if (id_root_reference->newid != NULL &&
+ (id_hierarchy_root == NULL ||
+ id_hierarchy_root->override_library->reference == id_root_reference)) {
id_hierarchy_root = id_root_reference->newid;
}
BLI_assert(id_hierarchy_root != NULL);
@@ -832,8 +864,8 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
continue;
}
- Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
- ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
+ Library *reference_lib = lib_override_get(bmain, id_owner, NULL)->reference->lib;
+ ID *to_id_reference = lib_override_get(bmain, to_id, NULL)->reference;
if (to_id_reference->lib != reference_lib) {
/* We do not override data-blocks from other libraries, nor do we process them. */
continue;
@@ -879,12 +911,13 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
static bool lib_override_library_create_do(Main *bmain,
Scene *scene,
Library *owner_library,
- ID *id_root)
+ ID *id_root_reference,
+ ID *id_hierarchy_root_reference)
{
BKE_main_relations_create(bmain, 0);
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = scene,
- .id_root = id_root,
+ .id_root = id_root_reference,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING,
.is_override = false,
@@ -898,8 +931,18 @@ static bool lib_override_library_create_do(Main *bmain,
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
- const bool success = BKE_lib_override_library_create_from_tag(
- bmain, owner_library, id_root, NULL, false);
+ bool success = false;
+ if (id_hierarchy_root_reference->lib != id_root_reference->lib) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+ BLI_assert(id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib);
+ success = BKE_lib_override_library_create_from_tag(
+ bmain, owner_library, id_root_reference, id_hierarchy_root_reference, NULL, false);
+ }
+ else {
+ success = BKE_lib_override_library_create_from_tag(
+ bmain, owner_library, id_root_reference, NULL, id_hierarchy_root_reference, false);
+ }
return success;
}
@@ -909,7 +952,7 @@ static void lib_override_library_create_post_process(Main *bmain,
ViewLayer *view_layer,
const Library *owner_library,
ID *id_root,
- ID *id_reference,
+ ID *id_instance_hint,
Collection *residual_storage,
const bool is_resync)
{
@@ -933,8 +976,8 @@ static void lib_override_library_create_post_process(Main *bmain,
(!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) {
switch (GS(id_root->name)) {
case ID_GR: {
- Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
- (Object *)id_reference :
+ Object *ob_reference = id_instance_hint != NULL && GS(id_instance_hint->name) == ID_OB ?
+ (Object *)id_instance_hint :
NULL;
Collection *collection_new = ((Collection *)id_root->newid);
if (is_resync && BKE_collection_is_in_scene(collection_new)) {
@@ -943,10 +986,10 @@ static void lib_override_library_create_post_process(Main *bmain,
if (ob_reference != NULL) {
BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
}
- else if (id_reference != NULL) {
- BLI_assert(GS(id_reference->name) == ID_GR);
+ else if (id_instance_hint != NULL) {
+ BLI_assert(GS(id_instance_hint->name) == ID_GR);
BKE_collection_add_from_collection(
- bmain, scene, ((Collection *)id_reference), collection_new);
+ bmain, scene, ((Collection *)id_instance_hint), collection_new);
}
else {
BKE_collection_add_from_collection(
@@ -1039,26 +1082,32 @@ bool BKE_lib_override_library_create(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
Library *owner_library,
- ID *id_root,
- ID *id_reference,
+ ID *id_root_reference,
+ ID *id_hierarchy_root_reference,
+ ID *id_instance_hint,
ID **r_id_root_override)
{
if (r_id_root_override != NULL) {
*r_id_root_override = NULL;
}
- const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root);
+ if (id_hierarchy_root_reference == NULL) {
+ id_hierarchy_root_reference = id_root_reference;
+ }
+
+ const bool success = lib_override_library_create_do(
+ bmain, scene, owner_library, id_root_reference, id_hierarchy_root_reference);
if (!success) {
return success;
}
if (r_id_root_override != NULL) {
- *r_id_root_override = id_root->newid;
+ *r_id_root_override = id_root_reference->newid;
}
lib_override_library_create_post_process(
- bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false);
+ bmain, scene, view_layer, owner_library, id_root_reference, id_instance_hint, NULL, false);
/* Cleanup. */
BKE_main_id_newptr_and_tag_clear(bmain);
@@ -1102,11 +1151,18 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
BLI_assert(entry != NULL);
- if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- /* This ID has already been processed. */
- BLI_assert(id->override_library != NULL);
- *r_best_level = curr_level;
- return id->override_library->hierarchy_root;
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ /* This ID has already been processed. */
+ *r_best_level = curr_level;
+ return id->override_library->hierarchy_root;
+ }
+
+ BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ ID *id_owner;
+ int best_level_placeholder = 0;
+ lib_override_get(bmain, id, &id_owner);
+ return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder);
}
/* This way we won't process again that ID, should we encounter it again through another
* relationship hierarchy. */
@@ -1140,7 +1196,17 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
}
}
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(best_root_id_candidate)) {
+ BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ ID *id_owner;
+ int best_level_placeholder = 0;
+ lib_override_get(bmain, best_root_id_candidate, &id_owner);
+ best_root_id_candidate = lib_override_root_find(
+ bmain, id_owner, curr_level + 1, &best_level_placeholder);
+ }
+
BLI_assert(best_root_id_candidate != NULL);
+ BLI_assert((best_root_id_candidate->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) == 0);
*r_best_level = best_level_candidate;
return best_root_id_candidate;
@@ -1430,7 +1496,7 @@ static bool lib_override_library_resync(Main *bmain,
/* While this should not happen in typical cases (and won't be properly supported here),
* user is free to do all kind of very bad things, including having different local
* overrides of a same linked ID in a same hierarchy. */
- IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, NULL);
ID *reference_id = id_override_library->reference;
if (GS(reference_id->name) != GS(id->name)) {
switch (GS(id->name)) {
@@ -1502,7 +1568,7 @@ static bool lib_override_library_resync(Main *bmain,
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
const bool success = BKE_lib_override_library_create_from_tag(
- bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, true);
+ bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, NULL, true);
if (!success) {
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
@@ -2127,7 +2193,7 @@ static void lib_override_library_main_resync_on_library_indirect_level(
"ID override %s from library level %d still found as needing resync, when all "
"IDs from that level should have been processed after tackling library level %d",
id->name,
- id->lib != NULL ? id->lib->temp_index : 0,
+ ID_IS_LINKED(id) ? id->lib->temp_index : 0,
library_indirect_level);
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
}
@@ -2136,6 +2202,10 @@ static void lib_override_library_main_resync_on_library_indirect_level(
BLI_ghash_free(id_roots, NULL, MEM_freeN);
+ /* In some fairly rare (and degenerate) cases, some root ID from other liboverrides may have been
+ * freed, and therefore set to NULL. Attempt to fix this as best as possible. */
+ BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
+
if (do_reports_recursive_resync_timing) {
reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time;
}
diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c
index 809235ad24c..dc164313788 100644
--- a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c
+++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c
@@ -42,7 +42,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
const bool is_override_instancing_object = ob_proxy_group != NULL;
ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id :
&ob_proxy->proxy->id;
- ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
+ ID *id_instance_hint = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
/* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
* sure this is a valid state, but for now just abort the overriding process. */
@@ -81,7 +81,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
FOREACH_MAIN_ID_END;
return BKE_lib_override_library_create(
- bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL);
+ bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL);
}
static void lib_override_library_proxy_convert_do(Main *bmain,
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index ba009072db8..5de8704e13b 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -402,7 +402,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner)
return FILTER_ID_ALL;
case ID_ME:
return FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM;
- case ID_CU:
+ case ID_CU_LEGACY:
return FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_VF;
case ID_MB:
return FILTER_ID_MA;
@@ -418,7 +418,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner)
return FILTER_ID_OB | FILTER_ID_IM;
case ID_KE:
/* Warning! key->from, could be more types in future? */
- return FILTER_ID_ME | FILTER_ID_CU | FILTER_ID_LT;
+ return FILTER_ID_ME | FILTER_ID_CU_LEGACY | FILTER_ID_LT;
case ID_SCR:
return FILTER_ID_SCE;
case ID_WO:
@@ -448,7 +448,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner)
case ID_WS:
return FILTER_ID_SCE;
case ID_CV:
- return FILTER_ID_MA;
+ return FILTER_ID_MA | FILTER_ID_OB;
case ID_PT:
return FILTER_ID_MA;
case ID_VO:
@@ -490,7 +490,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
/* Exception: ID_KE aren't available as filter_id. */
if (id_type_used == ID_KE) {
- return ELEM(id_type_owner, ID_ME, ID_CU, ID_LT);
+ return ELEM(id_type_owner, ID_ME, ID_CU_LEGACY, ID_LT);
}
/* Exception: ID_SCR aren't available as filter_id. */
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 9329a09f1b6..24e7178dd63 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -380,7 +380,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
case ID_ME:
multires_force_sculpt_rebuild(ob);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
BKE_curve_type_test(ob);
break;
default:
@@ -573,7 +573,7 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_
bmain, NULL, (Collection *)old_id, (Collection *)new_id);
break;
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_MB:
case ID_CV:
case ID_PT:
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index b25432780ed..03e03dacfbc 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -563,7 +563,7 @@ ListBase *which_libbase(Main *bmain, short type)
return &(bmain->objects);
case ID_ME:
return &(bmain->meshes);
- case ID_CU:
+ case ID_CU_LEGACY:
return &(bmain->curves);
case ID_MB:
return &(bmain->metaballs);
@@ -670,7 +670,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
lb[INDEX_ID_CF] = &(bmain->cachefiles);
lb[INDEX_ID_ME] = &(bmain->meshes);
- lb[INDEX_ID_CU] = &(bmain->curves);
+ lb[INDEX_ID_CU_LEGACY] = &(bmain->curves);
lb[INDEX_ID_MB] = &(bmain->metaballs);
lb[INDEX_ID_CV] = &(bmain->hair_curves);
lb[INDEX_ID_PT] = &(bmain->pointclouds);
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index c559c6fe807..7d01a92e829 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -313,7 +313,7 @@ Material ***BKE_object_material_array_p(Object *ob)
Mesh *me = ob->data;
return &(me->mat);
}
- if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) {
+ if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
Curve *cu = ob->data;
return &(cu->mat);
}
@@ -346,7 +346,7 @@ short *BKE_object_material_len_p(Object *ob)
Mesh *me = ob->data;
return &(me->totcol);
}
- if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) {
+ if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
Curve *cu = ob->data;
return &(cu->totcol);
}
@@ -381,7 +381,7 @@ Material ***BKE_id_material_array_p(ID *id)
switch (GS(id->name)) {
case ID_ME:
return &(((Mesh *)id)->mat);
- case ID_CU:
+ case ID_CU_LEGACY:
return &(((Curve *)id)->mat);
case ID_MB:
return &(((MetaBall *)id)->mat);
@@ -407,7 +407,7 @@ short *BKE_id_material_len_p(ID *id)
switch (GS(id->name)) {
case ID_ME:
return &(((Mesh *)id)->totcol);
- case ID_CU:
+ case ID_CU_LEGACY:
return &(((Curve *)id)->totcol);
case ID_MB:
return &(((MetaBall *)id)->totcol);
@@ -434,7 +434,7 @@ static void material_data_index_remove_id(ID *id, short index)
case ID_ME:
BKE_mesh_material_index_remove((Mesh *)id, index);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
BKE_curve_material_index_remove((Curve *)id, index);
break;
case ID_MB:
@@ -468,7 +468,7 @@ bool BKE_object_material_slot_used(Object *object, short actcol)
switch (GS(ob_data->name)) {
case ID_ME:
return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1);
- case ID_CU:
+ case ID_CU_LEGACY:
return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1);
case ID_MB:
/* Meta-elements don't support materials at the moment. */
@@ -489,7 +489,7 @@ static void material_data_index_clear_id(ID *id)
case ID_ME:
BKE_mesh_material_index_clear((Mesh *)id);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
BKE_curve_material_index_clear((Curve *)id);
break;
case ID_MB:
@@ -1062,7 +1062,7 @@ void BKE_object_material_remap(Object *ob, const unsigned int *remap)
if (ob->type == OB_MESH) {
BKE_mesh_material_remap(ob->data, remap, ob->totcol);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
BKE_curve_material_remap(ob->data, remap, ob->totcol);
}
else if (ob->type == OB_GPENCIL) {
@@ -1314,7 +1314,7 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob)
}
/* check indices from mesh */
- if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) {
+ if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
material_data_index_remove_id((ID *)ob->data, actcol - 1);
if (ob->runtime.curve_cache) {
BKE_displist_free(&ob->runtime.curve_cache->disp);
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 6ca5babbf13..37564f9334f 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -59,6 +59,8 @@
#include "BLO_read_write.h"
+using blender::float3;
+
static void mesh_clear_geometry(Mesh *mesh);
static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata);
@@ -1106,18 +1108,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src,
mesh_tessface_clear_intern(me_dst, false);
}
- me_dst->runtime.cd_dirty_poly = me_src->runtime.cd_dirty_poly;
- me_dst->runtime.cd_dirty_vert = me_src->runtime.cd_dirty_vert;
-
- /* Ensure that when no normal layers exist, they are marked dirty, because
- * normals might not have been included in the mask of copied layers. */
- if (!CustomData_has_layer(&me_dst->vdata, CD_NORMAL)) {
- me_dst->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
- if (!CustomData_has_layer(&me_dst->pdata, CD_NORMAL)) {
- me_dst->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
- }
-
/* The destination mesh should at least have valid primary CD layers,
* even in cases where the source mesh does not. */
mesh_ensure_cdlayers_primary(me_dst, do_tessface);
@@ -1204,6 +1194,23 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm,
return mesh;
}
+static void ensure_orig_index_layer(CustomData &data, const int size)
+{
+ if (CustomData_has_layer(&data, CD_ORIGINDEX)) {
+ return;
+ }
+ int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_DEFAULT, nullptr, size);
+ range_vn_i(indices, size, 0);
+}
+
+void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh)
+{
+ BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
+ ensure_orig_index_layer(mesh->vdata, mesh->totvert);
+ ensure_orig_index_layer(mesh->edata, mesh->totedge);
+ ensure_orig_index_layer(mesh->pdata, mesh->totpoly);
+}
+
BoundBox *BKE_mesh_boundbox_get(Object *ob)
{
/* This is Object-level data access,
@@ -1968,8 +1975,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac
nullptr);
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
-
- mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL;
}
void BKE_mesh_calc_normals_split(Mesh *mesh)
@@ -2147,6 +2152,10 @@ static void split_faces_split_new_verts(Mesh *mesh,
MVert *mvert = mesh->mvert;
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh);
+ /* Normals were already calculated at the beginning of this operation, we rely on that to update
+ * them partially here. */
+ BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh));
+
/* Remember new_verts is a single linklist, so its items are in reversed order... */
MVert *new_mv = &mvert[mesh->totvert - 1];
for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) {
@@ -2157,7 +2166,6 @@ static void split_faces_split_new_verts(Mesh *mesh,
copy_v3_v3(vert_normals[i], new_verts->vnor);
}
}
- BKE_mesh_vertex_normals_clear_dirty(mesh);
}
/* Perform actual split of edges. */
@@ -2228,6 +2236,10 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
/* Update pointers to a newly allocated memory. */
BKE_mesh_update_customdata_pointers(mesh, false);
+ /* Update normals manually to avoid recalculation after this operation. */
+ mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals,
+ sizeof(float[3]) * mesh->totvert);
+
/* Perform actual split of vertices and edges. */
split_faces_split_new_verts(mesh, new_verts, num_new_verts);
if (do_edges) {
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index ec66cd0d84d..eee1d3b9eec 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -292,7 +292,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_edge_offset[mi] = e;
r_info->mesh_poly_offset[mi] = f;
/* Get matrix that transforms a coordinate in objects[mi]'s local space
- * to the target space space. */
+ * to the target space. */
const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() :
clean_obmat(*obmats[mi]);
r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index 86e5f2b3cfe..1542f7119d1 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -739,14 +739,14 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1);
if (nurblist.first) {
- Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVE);
+ Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVES_LEGACY);
cu->flag |= CU_3D;
cu->nurb = nurblist;
id_us_min(&((Mesh *)ob->data)->id);
ob->data = cu;
- ob->type = OB_CURVE;
+ ob->type = OB_CURVES_LEGACY;
BKE_object_free_derived_caches(ob);
}
@@ -886,7 +886,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object)
{
/* Clear edit mode pointers that were explicitly copied to the temporary curve. */
ID *final_object_data = static_cast<ID *>(temp_object->data);
- if (GS(final_object_data->name) == ID_CU) {
+ if (GS(final_object_data->name) == ID_CU_LEGACY) {
Curve &curve = *reinterpret_cast<Curve *>(final_object_data);
curve.editfont = nullptr;
curve.editnurb = nullptr;
@@ -901,7 +901,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object)
*/
static void curve_to_mesh_eval_ensure(Object &object)
{
- BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU);
+ BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU_LEGACY);
Curve &curve = *static_cast<Curve *>(object.data);
/* Clear all modifiers for the bevel object.
*
@@ -953,11 +953,11 @@ static const Mesh *get_evaluated_mesh_from_object(const Object *object)
return nullptr;
}
-static const CurveEval *get_evaluated_curve_from_object(const Object *object)
+static const Curves *get_evaluated_curves_from_object(const Object *object)
{
GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval;
if (geometry_set_eval) {
- return geometry_set_eval->get_curve_for_read();
+ return geometry_set_eval->get_curves_for_read();
}
return nullptr;
}
@@ -968,8 +968,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
if (mesh) {
return BKE_mesh_copy_for_eval(mesh, false);
}
- const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object);
- if (curve) {
+ const Curves *curves = get_evaluated_curves_from_object(evaluated_object);
+ if (curves) {
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
return blender::bke::curve_to_wire_mesh(*curve);
}
return nullptr;
@@ -1110,7 +1111,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
Mesh *new_mesh = nullptr;
switch (object->type) {
case OB_FONT:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
new_mesh = mesh_new_from_curve_type_object(object);
break;
@@ -1182,7 +1183,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
Object *object,
bool preserve_all_data_layers)
{
- BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
+ BLI_assert(ELEM(object->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL, OB_MESH));
Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
if (mesh == nullptr) {
@@ -1222,6 +1223,9 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true);
+ /* Anonymous attributes shouldn't exist on original data. */
+ BKE_mesh_anonymous_attributes_remove(mesh_in_bmain);
+
/* User-count is required because so far mesh was in a limbo, where library management does
* not perform any user management (i.e. copy of a mesh will not increase users of materials). */
BKE_library_foreach_ID_link(
@@ -1478,17 +1482,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
tmp.cd_flag = mesh_src->cd_flag;
tmp.runtime.deformed_only = mesh_src->runtime.deformed_only;
- tmp.runtime.cd_dirty_poly = mesh_src->runtime.cd_dirty_poly;
- tmp.runtime.cd_dirty_vert = mesh_src->runtime.cd_dirty_vert;
-
- /* Ensure that when no normal layers exist, they are marked dirty, because
- * normals might not have been included in the mask of copied layers. */
- if (!CustomData_has_layer(&tmp.vdata, CD_NORMAL)) {
- tmp.runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
- if (!CustomData_has_layer(&tmp.pdata, CD_NORMAL)) {
- tmp.runtime.cd_dirty_poly |= CD_MASK_NORMAL;
- }
+ /* Clear the normals completely, since the new vertex / polygon count might be different. */
+ BKE_mesh_clear_derived_normals(&tmp);
if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) {
KeyBlock *kb;
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index 3d801b301f9..1c2a903d8c3 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -94,53 +94,72 @@ static void add_v3_v3_atomic(float r[3], const float a[3])
void BKE_mesh_normals_tag_dirty(Mesh *mesh)
{
- mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
+ mesh->runtime.vert_normals_dirty = true;
+ mesh->runtime.poly_normals_dirty = true;
}
float (*BKE_mesh_vertex_normals_for_write(Mesh *mesh))[3]
{
- CustomData_duplicate_referenced_layer(&mesh->vdata, CD_NORMAL, mesh->totvert);
- return (float(*)[3])CustomData_add_layer(
- &mesh->vdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totvert);
+ if (mesh->runtime.vert_normals == nullptr) {
+ mesh->runtime.vert_normals = (float(*)[3])MEM_malloc_arrayN(
+ mesh->totvert, sizeof(float[3]), __func__);
+ }
+
+ BLI_assert(MEM_allocN_len(mesh->runtime.vert_normals) >= sizeof(float[3]) * mesh->totvert);
+
+ return mesh->runtime.vert_normals;
}
float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3]
{
- CustomData_duplicate_referenced_layer(&mesh->pdata, CD_NORMAL, mesh->totpoly);
- return (float(*)[3])CustomData_add_layer(
- &mesh->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totpoly);
+ if (mesh->runtime.poly_normals == nullptr) {
+ mesh->runtime.poly_normals = (float(*)[3])MEM_malloc_arrayN(
+ mesh->totpoly, sizeof(float[3]), __func__);
+ }
+
+ BLI_assert(MEM_allocN_len(mesh->runtime.poly_normals) >= sizeof(float[3]) * mesh->totpoly);
+
+ return mesh->runtime.poly_normals;
}
void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh)
{
- mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL;
+ mesh->runtime.vert_normals_dirty = false;
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
}
void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh)
{
- mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL;
+ mesh->runtime.poly_normals_dirty = false;
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
}
bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh)
{
- return mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL;
+ return mesh->runtime.vert_normals_dirty;
}
bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh)
{
- return mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL;
+ return mesh->runtime.poly_normals_dirty;
+}
+
+void BKE_mesh_clear_derived_normals(Mesh *mesh)
+{
+ MEM_SAFE_FREE(mesh->runtime.vert_normals);
+ MEM_SAFE_FREE(mesh->runtime.poly_normals);
+
+ mesh->runtime.vert_normals_dirty = true;
+ mesh->runtime.poly_normals_dirty = true;
}
void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh)
{
- if (!(mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL)) {
- BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0);
+ if (!mesh->runtime.vert_normals_dirty) {
+ BLI_assert(mesh->runtime.vert_normals || mesh->totvert == 0);
}
- if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL)) {
- BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0);
+ if (!mesh->runtime.poly_normals_dirty) {
+ BLI_assert(mesh->runtime.poly_normals || mesh->totpoly == 0);
}
}
@@ -201,14 +220,13 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert,
* \{ */
struct MeshCalcNormalsData_PolyAndVertex {
- /** Write into vertex normals #MVert.no. */
- MVert *mvert;
+ const MVert *mvert;
const MLoop *mloop;
const MPoly *mpoly;
/** Polygon normal output. */
float (*pnors)[3];
- /** Vertex normal output (may be freed, copied into #MVert.no). */
+ /** Vertex normal output. */
float (*vnors)[3];
};
@@ -279,7 +297,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn(
{
MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
- MVert *mv = &data->mvert[vidx];
+ const MVert *mv = &data->mvert[vidx];
float *no = data->vnors[vidx];
if (UNLIKELY(normalize_v3(no) == 0.0f)) {
@@ -288,7 +306,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn(
}
}
-static void mesh_calc_normals_poly_and_vertex(MVert *mvert,
+static void mesh_calc_normals_poly_and_vertex(const MVert *mvert,
const int mvert_len,
const MLoop *mloop,
const int UNUSED(mloop_len),
@@ -301,36 +319,22 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert,
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 1024;
- float(*vnors)[3] = r_vert_normals;
- bool free_vnors = false;
-
- /* First go through and calculate normals for all the polys. */
- if (vnors == nullptr) {
- vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__);
- free_vnors = true;
- }
- else {
- memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len);
- }
+ memset(r_vert_normals, 0, sizeof(*r_vert_normals) * (size_t)mvert_len);
MeshCalcNormalsData_PolyAndVertex data = {};
data.mpoly = mpoly;
data.mloop = mloop;
data.mvert = mvert;
data.pnors = r_poly_normals;
- data.vnors = vnors;
+ data.vnors = r_vert_normals;
- /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */
+ /* Compute poly normals, accumulating them into vertex normals. */
BLI_task_parallel_range(
0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings);
- /* Normalize and validate computed vertex normals (`vnors`). */
+ /* Normalize and validate computed vertex normals. */
BLI_task_parallel_range(
0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings);
-
- if (free_vnors) {
- MEM_freeN(vnors);
- }
}
/** \} */
@@ -342,8 +346,8 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert,
const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
{
if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) {
- BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0);
- return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL);
+ BLI_assert(mesh->runtime.vert_normals != nullptr || mesh->totvert == 0);
+ return mesh->runtime.vert_normals;
}
if (mesh->totvert == 0) {
@@ -353,9 +357,9 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex;
BLI_mutex_lock(normals_mutex);
if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) {
- BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL));
+ BLI_assert(mesh->runtime.vert_normals != nullptr);
BLI_mutex_unlock(normals_mutex);
- return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL);
+ return mesh->runtime.vert_normals;
}
float(*vert_normals)[3];
@@ -388,8 +392,8 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
{
if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
- BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0);
- return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL);
+ BLI_assert(mesh->runtime.poly_normals != nullptr || mesh->totpoly == 0);
+ return mesh->runtime.poly_normals;
}
if (mesh->totpoly == 0) {
@@ -399,9 +403,9 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex;
BLI_mutex_lock(normals_mutex);
if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
- BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL));
+ BLI_assert(mesh->runtime.poly_normals != nullptr);
BLI_mutex_unlock(normals_mutex);
- return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL);
+ return mesh->runtime.poly_normals;
}
float(*poly_normals)[3];
diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c
index 204441d5326..7bd52abeb0d 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.c
@@ -88,6 +88,11 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
runtime->bvh_cache = NULL;
runtime->shrinkwrap_data = NULL;
+ runtime->vert_normals_dirty = true;
+ runtime->poly_normals_dirty = true;
+ runtime->vert_normals = NULL;
+ runtime->poly_normals = NULL;
+
mesh_runtime_init_mutexes(mesh);
}
@@ -101,6 +106,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh)
BKE_mesh_runtime_clear_geometry(mesh);
BKE_mesh_batch_cache_free(mesh);
BKE_mesh_runtime_clear_edit_data(mesh);
+ BKE_mesh_clear_derived_normals(mesh);
}
/**
diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c
index 96b588779f8..ae52e31cb9b 100644
--- a/source/blender/blenkernel/intern/mesh_tessellate.c
+++ b/source/blender/blenkernel/intern/mesh_tessellate.c
@@ -144,8 +144,7 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata,
MVert *mvert,
int totface,
int totloop,
- int totpoly,
- const bool do_face_nor_copy)
+ int totpoly)
{
#define USE_TESSFACE_SPEEDUP
#define USE_TESSFACE_QUADS
@@ -347,18 +346,6 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata,
CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface);
CustomData_from_bmeshpoly(fdata, ldata, totface);
- if (do_face_nor_copy) {
- /* If polys have a normals layer, copying that to faces can help
- * avoid the need to recalculate normals later. */
- if (CustomData_has_layer(pdata, CD_NORMAL)) {
- float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL);
- float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface);
- for (mface_index = 0; mface_index < totface; mface_index++) {
- copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]);
- }
- }
- }
-
/* NOTE: quad detection issue - fourth vertidx vs fourth loopidx:
* Polygons take care of their loops ordering, hence not of their vertices ordering.
* Currently, our tfaces' fourth vertex index might be 0 even for a quad.
@@ -395,16 +382,13 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata,
void BKE_mesh_tessface_calc(Mesh *mesh)
{
- mesh->totface = BKE_mesh_tessface_calc_ex(
- &mesh->fdata,
- &mesh->ldata,
- &mesh->pdata,
- mesh->mvert,
- mesh->totface,
- mesh->totloop,
- mesh->totpoly,
- /* Calculate normals right after, don't copy from polys here. */
- false);
+ mesh->totface = BKE_mesh_tessface_calc_ex(&mesh->fdata,
+ &mesh->ldata,
+ &mesh->pdata,
+ mesh->mvert,
+ mesh->totface,
+ mesh->totloop,
+ mesh->totpoly);
BKE_mesh_update_customdata_pointers(mesh, true);
}
diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.cc
index 53e19e6d16d..fb526354305 100644
--- a/source/blender/blenkernel/intern/mesh_validate.c
+++ b/source/blender/blenkernel/intern/mesh_validate.cc
@@ -5,10 +5,10 @@
* \ingroup bke
*/
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "CLG_log.h"
@@ -41,25 +41,25 @@ static CLG_LogRef LOG = {"bke.mesh"};
/** \name Internal functions
* \{ */
-typedef union {
+union EdgeUUID {
uint32_t verts[2];
int64_t edval;
-} EdgeUUID;
+};
-typedef struct SortFace {
+struct SortFace {
EdgeUUID es[4];
uint index;
-} SortFace;
+};
/* Used to detect polys (faces) using exactly the same vertices. */
/* Used to detect loops used by no (disjoint) or more than one (intersect) polys. */
-typedef struct SortPoly {
+struct SortPoly {
int *verts;
int numverts;
int loopstart;
uint index;
bool invalid; /* Poly index. */
-} SortPoly;
+};
static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2)
{
@@ -106,7 +106,8 @@ static int int64_cmp(const void *v1, const void *v2)
static int search_face_cmp(const void *v1, const void *v2)
{
- const SortFace *sfa = v1, *sfb = v2;
+ const SortFace *sfa = static_cast<const SortFace *>(v1);
+ const SortFace *sfb = static_cast<const SortFace *>(v2);
if (sfa->es[0].edval > sfb->es[0].edval) {
return 1;
@@ -147,8 +148,8 @@ static int int_cmp(const void *v1, const void *v2)
static int search_poly_cmp(const void *v1, const void *v2)
{
- const SortPoly *sp1 = v1;
- const SortPoly *sp2 = v2;
+ const SortPoly *sp1 = static_cast<const SortPoly *>(v1);
+ const SortPoly *sp2 = static_cast<const SortPoly *>(v2);
/* Reject all invalid polys at end of list! */
if (sp1->invalid || sp2->invalid) {
@@ -168,8 +169,8 @@ static int search_poly_cmp(const void *v1, const void *v2)
static int search_polyloop_cmp(const void *v1, const void *v2)
{
- const SortPoly *sp1 = v1;
- const SortPoly *sp2 = v2;
+ const SortPoly *sp1 = static_cast<const SortPoly *>(v1);
+ const SortPoly *sp2 = static_cast<const SortPoly *>(v2);
/* Reject all invalid polys at end of list! */
if (sp1->invalid || sp2->invalid) {
@@ -275,7 +276,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge);
- BLI_assert(!(do_fixes && mesh == NULL));
+ BLI_assert(!(do_fixes && mesh == nullptr));
fix_flag.as_flag = 0;
free_flag.as_flag = 0;
@@ -288,7 +289,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
recalc_flag.edges = do_fixes;
}
- const float(*vert_normals)[3] = NULL;
+ const float(*vert_normals)[3] = nullptr;
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
if (!BKE_mesh_vertex_normals_are_dirty(mesh)) {
vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
@@ -394,7 +395,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
MFace *mf;
MFace *mf_prev;
- SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces");
+ SortFace *sort_faces = (SortFace *)MEM_callocN(sizeof(SortFace) * totface, "search faces");
SortFace *sf;
SortFace *sf_prev;
uint totsortface = 0;
@@ -547,7 +548,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
{
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
- SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys");
+ SortPoly *sort_polys = (SortPoly *)MEM_callocN(sizeof(SortPoly) * totpoly,
+ "mesh validate's sort_polys");
SortPoly *prev_sp, *sp = sort_polys;
int prev_end;
@@ -586,7 +588,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
/* Poly itself is valid, for now. */
int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */
sp->invalid = false;
- sp->verts = v = MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly");
+ sp->verts = v = (int *)MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly");
sp->numverts = mp->totloop;
sp->loopstart = mp->loopstart;
@@ -725,7 +727,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
/* Third check pass, testing loops used by none or more than one poly. */
qsort(sort_polys, totpoly, sizeof(SortPoly), search_polyloop_cmp);
sp = sort_polys;
- prev_sp = NULL;
+ prev_sp = nullptr;
prev_end = 0;
for (i = 0; i < totpoly; i++, sp++) {
/* Free this now, we don't need it anymore, and avoid us another loop! */
@@ -733,7 +735,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
MEM_freeN(sp->verts);
}
- /* Note above prev_sp: in following code, we make sure it is always valid poly (or NULL). */
+ /* Note above prev_sp: in following code, we make sure it is always valid poly (or nullptr).
+ */
if (sp->invalid) {
if (do_fixes) {
REMOVE_POLY_TAG((&mpolys[sp->index]));
@@ -792,7 +795,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
MEM_freeN(sort_polys);
}
- BLI_edgehash_free(edge_hash, NULL);
+ BLI_edgehash_free(edge_hash, nullptr);
/* fix deform verts */
if (dverts) {
@@ -907,7 +910,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
if (free_flag.mselect) {
MEM_freeN(mesh->mselect);
- mesh->mselect = NULL;
+ mesh->mselect = nullptr;
mesh->totselect = 0;
}
}
@@ -1192,7 +1195,7 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
MLoop *l;
int a, b;
/* New loops idx! */
- int *new_idx = MEM_mallocN(sizeof(int) * me->totloop, __func__);
+ int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__);
for (a = b = 0, p = me->mpoly; a < me->totpoly; a++, p++) {
bool invalid = false;
@@ -1262,7 +1265,7 @@ void BKE_mesh_strip_loose_edges(Mesh *me)
MEdge *e;
MLoop *l;
int a, b;
- uint *new_idx = MEM_mallocN(sizeof(int) * me->totedge, __func__);
+ uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * me->totedge, __func__);
for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) {
if (e->v1 != e->v2) {
@@ -1322,7 +1325,8 @@ static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, sh
static int vergedgesort(const void *v1, const void *v2)
{
- const struct EdgeSort *x1 = v1, *x2 = v2;
+ const struct EdgeSort *x1 = static_cast<const struct EdgeSort *>(v1);
+ const struct EdgeSort *x2 = static_cast<const struct EdgeSort *>(v2);
if (x1->v1 > x2->v1) {
return 1;
@@ -1380,12 +1384,12 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
if (totedge == 0) {
/* flag that mesh has edges */
- (*r_medge) = MEM_callocN(0, __func__);
+ (*r_medge) = (MEdge *)MEM_callocN(0, __func__);
(*r_totedge) = 0;
return;
}
- ed = edsort = MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort");
+ ed = edsort = (EdgeSort *)MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort");
for (a = totface, mface = allface; a > 0; a--, mface++) {
to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2);
@@ -1411,7 +1415,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
}
totedge_final++;
- medge = MEM_callocN(sizeof(MEdge) * totedge_final, __func__);
+ medge = (MEdge *)MEM_callocN(sizeof(MEdge) * totedge_final, __func__);
for (a = totedge, med = medge, ed = edsort; a > 1; a--, ed++) {
/* edge is unique when it differs from next edge, or is last */
@@ -1433,7 +1437,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
med++;
}
else {
- /* equal edge, we merge the drawflag */
+ /* Equal edge, merge the draw-flag. */
(ed + 1)->is_draw |= ed->is_draw;
}
}
@@ -1469,7 +1473,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
}
}
- BLI_edgehash_free(hash, NULL);
+ BLI_edgehash_free(hash, nullptr);
*r_medge = medge;
*r_totedge = totedge_final;
@@ -1499,7 +1503,7 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old)
return;
}
- medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge);
+ medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge);
me->medge = medge;
me->totedge = totedge;
@@ -1548,11 +1552,11 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh)
/* write new edges into a temporary CustomData */
CustomData edgeData;
CustomData_reset(&edgeData);
- CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges);
- CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges);
+ CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, nullptr, numEdges);
+ CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, nullptr, numEdges);
- MEdge *med = CustomData_get_layer(&edgeData, CD_MEDGE);
- int *index = CustomData_get_layer(&edgeData, CD_ORIGINDEX);
+ MEdge *med = (MEdge *)CustomData_get_layer(&edgeData, CD_MEDGE);
+ int *index = (int *)CustomData_get_layer(&edgeData, CD_ORIGINDEX);
EdgeSetIterator *ehi = BLI_edgesetIterator_new(eh);
for (int i = 0; BLI_edgesetIterator_isDone(ehi) == false;
@@ -1569,7 +1573,7 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh)
mesh->edata = edgeData;
mesh->totedge = numEdges;
- mesh->medge = CustomData_get_layer(&mesh->edata, CD_MEDGE);
+ mesh->medge = (MEdge *)CustomData_get_layer(&mesh->edata, CD_MEDGE);
BLI_edgeset_free(eh);
}
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c
index 267020fb675..f9fcaa0dceb 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.c
+++ b/source/blender/blenkernel/intern/mesh_wrapper.c
@@ -115,6 +115,16 @@ static void mesh_wrapper_ensure_mdata_isolated(void *userdata)
BMEditMesh *em = me->edit_mesh;
BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra);
+ /* Adding original index layers assumes that all BMesh mesh wrappers are created from
+ * original edit mode meshes (the only case where adding original indices makes sense).
+ * If that assumption is broken, the layers might be incorrect in that they might not
+ * actually be "original".
+ *
+ * There is also a performance aspect, where this also assumes that original indices are
+ * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
+ * harmful. */
+ BKE_mesh_ensure_default_orig_index_customdata(me);
+
EditMeshData *edit_data = me->runtime.edit_data;
if (edit_data->vertexCos) {
BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 46fea5ae115..5af8dfc2b72 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -131,7 +131,7 @@ void BKE_modifier_panel_expand(ModifierData *md)
/***/
-ModifierData *BKE_modifier_new(int type)
+static ModifierData *modifier_allocate_and_init(int type)
{
const ModifierTypeInfo *mti = BKE_modifier_get_info(type);
ModifierData *md = MEM_callocN(mti->structSize, mti->structName);
@@ -152,6 +152,13 @@ ModifierData *BKE_modifier_new(int type)
mti->initData(md);
}
+ return md;
+}
+
+ModifierData *BKE_modifier_new(int type)
+{
+ ModifierData *md = modifier_allocate_and_init(type);
+
BKE_modifier_session_uuid_generate(md);
return md;
@@ -315,6 +322,16 @@ void BKE_modifiers_foreach_tex_link(Object *ob, TexWalkFunc walk, void *userData
}
}
+ModifierData *BKE_modifier_copy_ex(const ModifierData *md, int flag)
+{
+ ModifierData *md_dst = modifier_allocate_and_init(md->type);
+
+ BLI_strncpy(md_dst->name, md->name, sizeof(md_dst->name));
+ BKE_modifier_copydata_ex(md, md_dst, flag);
+
+ return md_dst;
+}
+
void BKE_modifier_copydata_generic(const ModifierData *md_src,
ModifierData *md_dst,
const int UNUSED(flag))
@@ -348,7 +365,7 @@ static void modifier_copy_data_id_us_cb(void *UNUSED(userData),
}
}
-void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int flag)
+void BKE_modifier_copydata_ex(const ModifierData *md, ModifierData *target, const int flag)
{
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
@@ -378,7 +395,7 @@ void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int
}
}
-void BKE_modifier_copydata(ModifierData *md, ModifierData *target)
+void BKE_modifier_copydata(const ModifierData *md, ModifierData *target)
{
BKE_modifier_copydata_ex(md, target, 0);
}
@@ -651,7 +668,7 @@ ModifierData *BKE_modifiers_get_virtual_modifierlist(const Object *ob,
virtualModifierData->amd.deformflag = ((bArmature *)(ob->parent->data))->deformflag;
md = &virtualModifierData->amd.modifier;
}
- else if (ob->parent->type == OB_CURVE && ob->partype == PARSKEL) {
+ else if (ob->parent->type == OB_CURVES_LEGACY && ob->partype == PARSKEL) {
virtualModifierData->cmd.object = ob->parent;
virtualModifierData->cmd.defaxis = ob->trackflag + 1;
virtualModifierData->cmd.modifier.next = md;
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index c8e82302787..96bfcb0311b 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -1562,13 +1562,15 @@ static void socket_id_user_increment(bNodeSocket *sock)
}
}
-static void socket_id_user_decrement(bNodeSocket *sock)
+/** \return True if the socket had an ID default value. */
+static bool socket_id_user_decrement(bNodeSocket *sock)
{
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value;
if (default_value->value != nullptr) {
id_us_min(&default_value->value->id);
+ return true;
}
break;
}
@@ -1576,6 +1578,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value;
if (default_value->value != nullptr) {
id_us_min(&default_value->value->id);
+ return true;
}
break;
}
@@ -1584,6 +1587,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
sock->default_value;
if (default_value->value != nullptr) {
id_us_min(&default_value->value->id);
+ return true;
}
break;
}
@@ -1591,6 +1595,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
if (default_value->value != nullptr) {
id_us_min(&default_value->value->id);
+ return true;
}
break;
}
@@ -1598,6 +1603,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
if (default_value->value != nullptr) {
id_us_min(&default_value->value->id);
+ return true;
}
break;
}
@@ -1613,6 +1619,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case SOCK_GEOMETRY:
break;
}
+ return false;
}
void nodeModifySocketType(bNodeTree *ntree,
@@ -1945,6 +1952,8 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node)
}
}
+ BLI_freelistN(&node->internal_links);
+
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
node_socket_free(sock, true);
MEM_freeN(sock);
@@ -2431,6 +2440,11 @@ bool nodeLinkIsHidden(const bNodeLink *link)
return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock);
}
+bool nodeLinkIsSelected(const bNodeLink *link)
+{
+ return (link->fromnode->flag & NODE_SELECT) || (link->tonode->flag & NODE_SELECT);
+}
+
/* Adjust the indices of links connected to the given multi input socket after deleting the link at
* `deleted_index`. This function also works if the link has not yet been deleted. */
static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree,
@@ -2972,6 +2986,8 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
* do to ID user refcounting and removal of animdation data then. */
BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0);
+ bool node_has_id = false;
+
if (do_id_user) {
/* Free callback for NodeCustomGroup. */
if (node->typeinfo->freefunc_api) {
@@ -2984,13 +3000,14 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
/* Do user counting. */
if (node->id) {
id_us_min(node->id);
+ node_has_id = true;
}
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- socket_id_user_decrement(sock);
+ node_has_id |= socket_id_user_decrement(sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
- socket_id_user_decrement(sock);
+ node_has_id |= socket_id_user_decrement(sock);
}
}
@@ -3007,6 +3024,12 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
}
}
+ if (node_has_id) {
+ if (bmain != nullptr) {
+ DEG_relations_tag_update(bmain);
+ }
+ }
+
nodeUnlinkNode(ntree, node);
node_unlink_attached(ntree, node);
@@ -4735,6 +4758,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_to_points();
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
+ register_node_type_geo_duplicate_elements();
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_dual_mesh();
register_node_type_geo_edge_split();
@@ -4753,6 +4777,7 @@ static void registerGeometryNodes()
register_node_type_geo_input_mesh_edge_neighbors();
register_node_type_geo_input_mesh_edge_vertices();
register_node_type_geo_input_mesh_face_area();
+ register_node_type_geo_input_mesh_face_is_planar();
register_node_type_geo_input_mesh_face_neighbors();
register_node_type_geo_input_mesh_island();
register_node_type_geo_input_mesh_vertex_neighbors();
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 579e61750f0..985c9edac1a 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -73,7 +73,7 @@
#include "BKE_constraint.h"
#include "BKE_crazyspace.h"
#include "BKE_curve.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_duplilist.h"
@@ -1385,8 +1385,14 @@ ModifierData *BKE_object_active_modifier(const Object *ob)
bool BKE_object_supports_modifiers(const Object *ob)
{
- return (
- ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME));
+ return (ELEM(ob->type,
+ OB_MESH,
+ OB_CURVES_LEGACY,
+ OB_SURF,
+ OB_FONT,
+ OB_LATTICE,
+ OB_POINTCLOUD,
+ OB_VOLUME));
}
bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
@@ -1402,7 +1408,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) {
return (mti->modifyGeometrySet != nullptr);
}
- if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
+ if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;
}
@@ -1599,9 +1605,7 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst,
continue;
}
- ModifierData *md_dst = BKE_modifier_new(md_src->type);
- BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name));
- BKE_modifier_copydata_ex(md_src, md_dst, flag_subdata);
+ ModifierData *md_dst = BKE_modifier_copy_ex(md_src, flag_subdata);
BLI_addtail(&ob_dst->modifiers, md_dst);
}
@@ -1873,7 +1877,7 @@ bool BKE_object_is_in_editmode(const Object *ob)
case OB_LATTICE:
return ((Lattice *)ob->data)->editlatt != nullptr;
case OB_SURF:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return ((Curve *)ob->data)->editnurb != nullptr;
case OB_GPENCIL:
/* Grease Pencil object has no edit mode data. */
@@ -1895,7 +1899,7 @@ bool BKE_object_data_is_in_editmode(const ID *id)
switch (type) {
case ID_ME:
return ((const Mesh *)id)->edit_mesh != nullptr;
- case ID_CU:
+ case ID_CU_LEGACY:
return ((((const Curve *)id)->editnurb != nullptr) ||
(((const Curve *)id)->editfont != nullptr));
case ID_MB:
@@ -1921,7 +1925,7 @@ char *BKE_object_data_editmode_flush_ptr_get(struct ID *id)
}
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
if (((Curve *)id)->vfont != nullptr) {
EditFont *ef = ((Curve *)id)->editfont;
if (ef != nullptr) {
@@ -2064,7 +2068,7 @@ static const char *get_obdata_defname(int type)
switch (type) {
case OB_MESH:
return DATA_("Mesh");
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return DATA_("Curve");
case OB_SURF:
return DATA_("Surf");
@@ -2135,8 +2139,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
switch (type) {
case OB_MESH:
return BKE_mesh_add(bmain, name);
- case OB_CURVE:
- return BKE_curve_add(bmain, name, OB_CURVE);
+ case OB_CURVES_LEGACY:
+ return BKE_curve_add(bmain, name, OB_CURVES_LEGACY);
case OB_SURF:
return BKE_curve_add(bmain, name, OB_SURF);
case OB_FONT:
@@ -2177,7 +2181,7 @@ int BKE_object_obdata_to_type(const ID *id)
switch (GS(id->name)) {
case ID_ME:
return OB_MESH;
- case ID_CU:
+ case ID_CU_LEGACY:
return BKE_curve_type_get((const Curve *)id);
case ID_MB:
return OB_MBALL;
@@ -2658,7 +2662,7 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica
id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
if (dupflag & USER_DUP_CURVE) {
id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags);
}
@@ -3222,7 +3226,7 @@ static void give_parvert(Object *par, int nr, float vec[3])
"object position can be wrong now");
}
}
- else if (ELEM(par->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(par->type, OB_CURVES_LEGACY, OB_SURF)) {
ListBase *nurb;
/* Unless there's some weird depsgraph failure the cache should exist. */
@@ -3295,7 +3299,7 @@ void BKE_object_get_parent_matrix(Object *ob, Object *par, float r_parentmat[4][
switch (ob->partype & PARTYPE) {
case PAROBJECT: {
bool ok = false;
- if (par->type == OB_CURVE) {
+ if (par->type == OB_CURVES_LEGACY) {
if ((((Curve *)par->data)->flag & CU_PATH) && (ob_parcurve(ob, par, tmat))) {
ok = true;
}
@@ -3591,7 +3595,7 @@ BoundBox *BKE_object_boundbox_get(Object *ob)
case OB_MESH:
bb = BKE_mesh_boundbox_get(ob);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
bb = BKE_curve_boundbox_get(ob);
@@ -3760,7 +3764,7 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us
bool changed = false;
switch (ob->type) {
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT:
case OB_SURF: {
BoundBox bb = *BKE_curve_boundbox_get(ob);
@@ -3938,7 +3942,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
ListBase *lb = object_duplilist(depsgraph, scene, ob);
LISTBASE_FOREACH (DupliObject *, dob, lb) {
- if ((use_hidden == false) && (dob->no_draw != 0)) {
+ if (((use_hidden == false) && (dob->no_draw != 0)) || dob->ob_data == nullptr) {
/* pass */
}
else {
@@ -4189,7 +4193,7 @@ bool BKE_object_obdata_texspace_get(Object *ob, char **r_texflag, float **r_loc,
BKE_mesh_texspace_get_reference((Mesh *)ob->data, r_texflag, r_loc, r_size);
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)ob->data;
BKE_curve_texspace_ensure(cu);
if (r_texflag) {
@@ -4557,7 +4561,7 @@ KeyBlock *BKE_object_shapekey_insert(Main *bmain,
case OB_MESH:
key = insert_meshkey(bmain, ob, name, from_mix);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
key = insert_curvekey(bmain, ob, name, from_mix);
break;
@@ -4629,7 +4633,7 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb)
case OB_MESH:
BKE_keyblock_convert_to_mesh(key->refkey, (Mesh *)ob->data);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
BKE_keyblock_convert_to_curve(
key->refkey, (Curve *)ob->data, BKE_curve_nurbs_get((Curve *)ob->data));
@@ -4844,7 +4848,7 @@ int BKE_object_is_deform_modified(Scene *scene, Object *ob)
flag |= eModifierMode_Realtime | eModifierMode_Render;
}
- if (ob->type == OB_CURVE) {
+ if (ob->type == OB_CURVES_LEGACY) {
Curve *cu = (Curve *)ob->data;
if (cu->taperobj != nullptr && object_deforms_in_time(cu->taperobj)) {
flag |= eModifierMode_Realtime | eModifierMode_Render;
@@ -4921,7 +4925,7 @@ bool BKE_object_supports_material_slots(struct Object *ob)
{
return ELEM(ob->type,
OB_MESH,
- OB_CURVE,
+ OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
OB_MBALL,
@@ -5155,7 +5159,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot)
BLI_kdtree_3d_balance(tree);
break;
}
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF: {
/* TODO: take deformation into account */
Curve *cu = (Curve *)ob->data;
@@ -5462,7 +5466,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
}
/* for curve following objects, parented curve has to be updated too */
- if (ob->type == OB_CURVE) {
+ if (ob->type == OB_CURVES_LEGACY) {
Curve *cu = (Curve *)ob->data;
BKE_animsys_evaluate_animdata(
&cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 18dd61004f5..009a7bd70be 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -851,7 +851,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
dupli->ob_data = (ID *)volume;
}
}
- if (!ELEM(ctx->object->type, OB_CURVE, OB_FONT) || geometry_set_is_instance) {
+ if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT) || geometry_set_is_instance) {
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
if (curve_component != nullptr) {
const Curve *curve = curve_component->get_curve_for_render();
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 2b0e04d0bd0..3bc2139ca0c 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -181,7 +181,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_displist_make_mball(depsgraph, scene, ob);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT: {
bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
@@ -300,7 +300,7 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data)
BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data,
BKE_LATTICE_BATCH_DIRTY_ALL);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL);
break;
case ID_MB:
@@ -364,7 +364,7 @@ void BKE_object_data_select_update(Depsgraph *depsgraph, ID *object_data)
case ID_ME:
BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_SELECT);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
BKE_curve_batch_cache_dirty_tag((Curve *)object_data, BKE_CURVE_BATCH_DIRTY_SELECT);
break;
case ID_LT:
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index ffd0a03fc51..d42c8ea37d5 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -327,6 +327,9 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode)
case PAINT_MODE_WEIGHT_GPENCIL:
paint_ptr = (Paint **)&ts->gp_weightpaint;
break;
+ case PAINT_MODE_SCULPT_CURVES:
+ paint_ptr = (Paint **)&ts->curves_sculpt;
+ break;
case PAINT_MODE_INVALID:
break;
}
@@ -362,6 +365,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode)
return &ts->gp_sculptpaint->paint;
case PAINT_MODE_WEIGHT_GPENCIL:
return &ts->gp_weightpaint->paint;
+ case PAINT_MODE_SCULPT_CURVES:
+ return &ts->curves_sculpt->paint;
case PAINT_MODE_INVALID:
return NULL;
default:
@@ -394,6 +399,8 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode)
return rna_enum_brush_gpencil_sculpt_types_items;
case PAINT_MODE_WEIGHT_GPENCIL:
return rna_enum_brush_gpencil_weight_types_items;
+ case PAINT_MODE_SCULPT_CURVES:
+ return rna_enum_brush_curves_sculpt_tool_items;
case PAINT_MODE_INVALID:
break;
}
@@ -422,6 +429,8 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode)
return "gpencil_sculpt_tool";
case PAINT_MODE_WEIGHT_GPENCIL:
return "gpencil_weight_tool";
+ case PAINT_MODE_SCULPT_CURVES:
+ return "curves_sculpt_tool";
case PAINT_MODE_INVALID:
break;
}
@@ -453,6 +462,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
return &ts->gp_sculptpaint->paint;
case OB_MODE_WEIGHT_GPENCIL:
return &ts->gp_weightpaint->paint;
+ case OB_MODE_SCULPT_CURVES:
+ return &ts->curves_sculpt->paint;
case OB_MODE_EDIT:
return ts->uvsculpt ? &ts->uvsculpt->paint : NULL;
default:
@@ -573,6 +584,8 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref)
return PAINT_MODE_SCULPT_GPENCIL;
case CTX_MODE_WEIGHT_GPENCIL:
return PAINT_MODE_WEIGHT_GPENCIL;
+ case CTX_MODE_SCULPT_CURVES:
+ return PAINT_MODE_SCULPT_CURVES;
}
}
else if (tref->space_type == SPACE_IMAGE) {
@@ -641,6 +654,10 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL;
}
+ else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
+ paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
+ }
else {
BLI_assert_unreachable();
}
@@ -668,6 +685,8 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode)
return offsetof(Brush, gpencil_sculpt_tool);
case PAINT_MODE_WEIGHT_GPENCIL:
return offsetof(Brush, gpencil_weight_tool);
+ case PAINT_MODE_SCULPT_CURVES:
+ return offsetof(Brush, curves_sculpt_tool);
case PAINT_MODE_INVALID:
break; /* We don't use these yet. */
}
@@ -1028,6 +1047,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
(Paint *)ts->vpaint,
(Paint *)ts->wpaint,
(Paint *)ts->uvsculpt,
+ (Paint *)ts->curves_sculpt,
(Paint *)&ts->imapaint));
#ifdef DEBUG
struct Paint paint_test = **r_paint;
@@ -1075,6 +1095,10 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
UvSculpt *data = MEM_callocN(sizeof(*data), __func__);
paint = &data->paint;
}
+ else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) {
+ CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__);
+ paint = &data->paint;
+ }
else if (*r_paint == &ts->imapaint.paint) {
paint = &ts->imapaint.paint;
}
diff --git a/source/blender/blenkernel/intern/paint_toolslots.c b/source/blender/blenkernel/intern/paint_toolslots.c
index 04b70aae199..f35755021d2 100644
--- a/source/blender/blenkernel/intern/paint_toolslots.c
+++ b/source/blender/blenkernel/intern/paint_toolslots.c
@@ -98,6 +98,9 @@ void BKE_paint_toolslots_init_from_main(struct Main *bmain)
if (ts->gp_weightpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint);
}
+ if (ts->curves_sculpt) {
+ paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint);
+ }
}
}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index 2a4e0715293..3ee46fc4f15 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -11,6 +11,7 @@
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
+#include "BLI_bounds.hh"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_vec_types.hh"
@@ -254,68 +255,28 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
return pointcloud;
}
-struct MinMaxResult {
- float3 min;
- float3 max;
-};
-
-static MinMaxResult min_max_no_radii(Span<float3> positions)
+static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds(
+ const PointCloud &pointcloud)
{
- using namespace blender::math;
-
- return blender::threading::parallel_reduce(
- positions.index_range(),
- 1024,
- MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)},
- [&](IndexRange range, const MinMaxResult &init) {
- MinMaxResult result = init;
- for (const int i : range) {
- min_max(positions[i], result.min, result.max);
- }
- return result;
- },
- [](const MinMaxResult &a, const MinMaxResult &b) {
- return MinMaxResult{min(a.min, b.min), max(a.max, b.max)};
- });
-}
-
-static MinMaxResult min_max_with_radii(Span<float3> positions, Span<float> radii)
-{
- using namespace blender::math;
-
- return blender::threading::parallel_reduce(
- positions.index_range(),
- 1024,
- MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)},
- [&](IndexRange range, const MinMaxResult &init) {
- MinMaxResult result = init;
- for (const int i : range) {
- result.min = min(positions[i] - radii[i], result.min);
- result.max = max(positions[i] + radii[i], result.max);
- }
- return result;
- },
- [](const MinMaxResult &a, const MinMaxResult &b) {
- return MinMaxResult{min(a.min, b.min), max(a.max, b.max)};
- });
+ Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint};
+ if (pointcloud.radius) {
+ Span<float> radii{pointcloud.radius, pointcloud.totpoint};
+ return blender::bounds::min_max_with_radii(positions, radii);
+ }
+ return blender::bounds::min_max(positions);
}
bool BKE_pointcloud_minmax(const PointCloud *pointcloud, float r_min[3], float r_max[3])
{
- using namespace blender::math;
+ using namespace blender;
- if (!pointcloud->totpoint) {
+ const std::optional<bounds::MinMaxResult<float3>> min_max = point_cloud_bounds(*pointcloud);
+ if (!min_max) {
return false;
}
- Span<float3> positions{reinterpret_cast<float3 *>(pointcloud->co), pointcloud->totpoint};
- const MinMaxResult min_max = (pointcloud->radius) ?
- min_max_with_radii(positions,
- {pointcloud->radius, pointcloud->totpoint}) :
- min_max_no_radii(positions);
-
- copy_v3_v3(r_min, min(min_max.min, float3(r_min)));
- copy_v3_v3(r_max, max(min_max.max, float3(r_max)));
+ copy_v3_v3(r_min, math::min(min_max->min, float3(r_min)));
+ copy_v3_v3(r_max, math::max(min_max->max, float3(r_max)));
return true;
}
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 7827c40e2c2..baf2f0bac8a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -317,12 +317,6 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
scene_dst->r.avicodecdata->lpParms = MEM_dupallocN(scene_dst->r.avicodecdata->lpParms);
}
- if (scene_src->r.ffcodecdata.properties) {
- /* intentionally check sce_dst not sce_src. */ /* XXX ??? comment outdated... */
- scene_dst->r.ffcodecdata.properties = IDP_CopyProperty_ex(scene_src->r.ffcodecdata.properties,
- flag_subdata);
- }
-
if (scene_src->display.shading.prop) {
scene_dst->display.shading.prop = IDP_CopyProperty(scene_src->display.shading.prop);
}
@@ -393,10 +387,6 @@ static void scene_free_data(ID *id)
MEM_freeN(scene->r.avicodecdata);
scene->r.avicodecdata = NULL;
}
- if (scene->r.ffcodecdata.properties) {
- IDP_FreeProperty(scene->r.ffcodecdata.properties);
- scene->r.ffcodecdata.properties = NULL;
- }
scene_free_markers(scene, do_id_user);
BLI_freelistN(&scene->transform_spaces);
@@ -718,6 +708,16 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
reader,
&toolsett_old->gp_weightpaint->paint));
}
+ if (toolsett->curves_sculpt) {
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->curves_sculpt->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->curves_sculpt->paint));
+ }
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
toolsett->gp_sculpt.guide.reference_object,
@@ -972,6 +972,10 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
BLO_write_struct(writer, GpWeightPaint, tos->gp_weightpaint);
BKE_paint_blend_write(writer, &tos->gp_weightpaint->paint);
}
+ if (tos->curves_sculpt) {
+ BLO_write_struct(writer, CurvesSculpt, tos->curves_sculpt);
+ BKE_paint_blend_write(writer, &tos->curves_sculpt->paint);
+ }
/* write grease-pencil custom ipo curve to file */
if (tos->gp_interpolate.custom_ipo) {
BKE_curvemapping_blend_write(writer, tos->gp_interpolate.custom_ipo);
@@ -1014,9 +1018,6 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
BLO_write_raw(writer, (size_t)sce->r.avicodecdata->cbParms, sce->r.avicodecdata->lpParms);
}
}
- if (sce->r.ffcodecdata.properties) {
- IDP_BlendWrite(writer, sce->r.ffcodecdata.properties);
- }
/* writing dynamic list of TimeMarkers to the blend file */
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
@@ -1148,6 +1149,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_vertexpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_sculptpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_weightpaint);
+ direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->curves_sculpt);
BKE_paint_blend_read_data(reader, sce, &sce->toolsettings->imapaint.paint);
@@ -1256,11 +1258,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
BLO_read_data_address(reader, &sce->r.avicodecdata->lpFormat);
BLO_read_data_address(reader, &sce->r.avicodecdata->lpParms);
}
- if (sce->r.ffcodecdata.properties) {
- BLO_read_data_address(reader, &sce->r.ffcodecdata.properties);
- IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties);
- }
-
BLO_read_list(reader, &(sce->markers));
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
BLO_read_data_address(reader, &marker->prop);
@@ -1406,6 +1403,9 @@ static void scene_blend_read_lib(BlendLibReader *reader, ID *id)
if (sce->toolsettings->gp_weightpaint) {
BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->gp_weightpaint->paint);
}
+ if (sce->toolsettings->curves_sculpt) {
+ BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->curves_sculpt->paint);
+ }
if (sce->toolsettings->sculpt) {
BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->sculpt->gravity_object);
@@ -1726,6 +1726,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
ts->gp_weightpaint = MEM_dupallocN(ts->gp_weightpaint);
BKE_paint_copy(&ts->gp_weightpaint->paint, &ts->gp_weightpaint->paint, flag);
}
+ if (ts->curves_sculpt) {
+ ts->curves_sculpt = MEM_dupallocN(ts->curves_sculpt);
+ BKE_paint_copy(&ts->curves_sculpt->paint, &ts->curves_sculpt->paint, flag);
+ }
BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag);
ts->particle.paintcursor = NULL;
@@ -1781,6 +1785,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_paint_free(&toolsettings->gp_weightpaint->paint);
MEM_freeN(toolsettings->gp_weightpaint);
}
+ if (toolsettings->curves_sculpt) {
+ BKE_paint_free(&toolsettings->curves_sculpt->paint);
+ MEM_freeN(toolsettings->curves_sculpt);
+ }
BKE_paint_free(&toolsettings->imapaint.paint);
/* free Grease Pencil interpolation curve */
@@ -1873,10 +1881,6 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
sce_copy->r.avicodecdata->lpParms = MEM_dupallocN(sce_copy->r.avicodecdata->lpParms);
}
- if (sce->r.ffcodecdata.properties) { /* intentionally check scen not sce. */
- sce_copy->r.ffcodecdata.properties = IDP_CopyProperty(sce->r.ffcodecdata.properties);
- }
-
BKE_sound_reset_scene_runtime(sce_copy);
/* grease pencil */
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 7b8e5a1409a..38066f95084 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -2967,7 +2967,7 @@ static void curve_surf_to_softbody(Object *ob)
totvert = BKE_nurbList_verts_count(&cu->nurb);
if (ob->softflag & OB_SB_EDGES) {
- if (ob->type == OB_CURVE) {
+ if (ob->type == OB_CURVES_LEGACY) {
totspring = totvert - BLI_listbase_count(&cu->nurb);
}
}
@@ -3320,7 +3320,7 @@ static void softbody_reset(Object *ob, SoftBody *sb, float (*vertexCos)[3], int
break;
case OB_LATTICE:
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
break;
default:
@@ -3537,7 +3537,7 @@ void sbObjectStep(struct Depsgraph *depsgraph,
case OB_LATTICE:
lattice_to_softbody(ob);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
curve_surf_to_softbody(ob);
break;
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index d1c4756a3b9..dc5b1d28539 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -23,7 +23,7 @@ using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
-Spline::Type Spline::type() const
+CurveType Spline::type() const
{
return type_;
}
@@ -34,15 +34,18 @@ void Spline::copy_base_settings(const Spline &src, Spline &dst)
dst.is_cyclic_ = src.is_cyclic_;
}
-static SplinePtr create_spline(const Spline::Type type)
+static SplinePtr create_spline(const CurveType type)
{
switch (type) {
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
return std::make_unique<PolySpline>();
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
return std::make_unique<BezierSpline>();
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
return std::make_unique<NURBSpline>();
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ return {};
}
BLI_assert_unreachable();
return {};
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index 57f1d73d55e..3c2ac1dae9c 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -93,11 +93,11 @@ Span<float> BezierSpline::tilts() const
{
return tilts_;
}
-Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
+Span<int8_t> BezierSpline::handle_types_left() const
{
return handle_types_left_;
}
-MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
+MutableSpan<int8_t> BezierSpline::handle_types_left()
{
return handle_types_left_;
}
@@ -114,11 +114,11 @@ MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only)
return handle_positions_left_;
}
-Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
+Span<int8_t> BezierSpline::handle_types_right() const
{
return handle_types_right_;
}
-MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
+MutableSpan<int8_t> BezierSpline::handle_types_right()
{
return handle_types_right_;
}
@@ -187,7 +187,7 @@ void BezierSpline::ensure_auto_handles() const
for (const int i : IndexRange(this->size())) {
using namespace blender;
- if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
+ if (ELEM(BEZIER_HANDLE_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 = math::length(prev_diff);
@@ -203,23 +203,23 @@ void BezierSpline::ensure_auto_handles() const
/* This magic number is unfortunate, but comes from elsewhere in Blender. */
const float len = math::length(dir) * 2.5614f;
if (len != 0.0f) {
- if (handle_types_left_[i] == HandleType::Auto) {
+ if (handle_types_left_[i] == BEZIER_HANDLE_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) {
+ if (handle_types_right_[i] == BEZIER_HANDLE_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) {
+ if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) {
const float3 prev = previous_position(positions_, is_cyclic_, i);
handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f);
}
- if (handle_types_right_[i] == HandleType::Vector) {
+ if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) {
const float3 next = next_position(positions_, is_cyclic_, i);
handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f);
}
@@ -257,8 +257,8 @@ void BezierSpline::transform(const blender::float4x4 &matrix)
}
static void set_handle_position(const float3 &position,
- const BezierSpline::HandleType type,
- const BezierSpline::HandleType type_other,
+ const HandleType type,
+ const HandleType type_other,
const float3 &new_value,
float3 &handle,
float3 &handle_other)
@@ -266,12 +266,12 @@ static void set_handle_position(const float3 &position,
using namespace blender::math;
/* Don't bother when the handle positions are calculated automatically anyway. */
- if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) {
+ if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
return;
}
handle = new_value;
- if (type_other == BezierSpline::HandleType::Align) {
+ if (type_other == BEZIER_HANDLE_ALIGN) {
/* Keep track of the old length of the opposite handle. */
const float length = distance(handle_other, position);
/* Set the other handle to directly opposite from the current handle. */
@@ -283,8 +283,8 @@ static void set_handle_position(const float3 &position,
void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
{
set_handle_position(positions_[index],
- handle_types_right_[index],
- handle_types_left_[index],
+ static_cast<HandleType>(handle_types_right_[index]),
+ static_cast<HandleType>(handle_types_left_[index]),
value,
handle_positions_right_[index],
handle_positions_left_[index]);
@@ -293,8 +293,8 @@ void BezierSpline::set_handle_position_right(const int index, const blender::flo
void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
{
set_handle_position(positions_[index],
- handle_types_left_[index],
- handle_types_right_[index],
+ static_cast<HandleType>(handle_types_right_[index]),
+ static_cast<HandleType>(handle_types_left_[index]),
value,
handle_positions_left_[index],
handle_positions_right_[index]);
@@ -302,8 +302,8 @@ void BezierSpline::set_handle_position_left(const int index, const blender::floa
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);
+ return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
+ ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
}
bool BezierSpline::segment_is_vector(const int index) const
@@ -313,15 +313,15 @@ 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;
+ return handle_types_right_.last() == BEZIER_HANDLE_VECTOR &&
+ handle_types_left_.first() == BEZIER_HANDLE_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;
+ return handle_types_right_[index] == BEZIER_HANDLE_VECTOR &&
+ handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR;
}
void BezierSpline::mark_cache_invalid()
diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c
index fd56582f5e3..50135110a64 100644
--- a/source/blender/blenkernel/intern/subdiv_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_mesh.c
@@ -72,6 +72,9 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx)
static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices)
{
+ if (!ctx->have_displacement) {
+ return;
+ }
ctx->accumulated_counters = MEM_calloc_arrayN(
num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters");
}
@@ -424,18 +427,17 @@ static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx,
const float v,
MVert *subdiv_vert)
{
+ /* Accumulate displacement. */
Subdiv *subdiv = ctx->subdiv;
const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
float dummy_P[3], dPdu[3], dPdv[3], D[3];
BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
- /* Accumulate displacement if needed. */
- if (ctx->have_displacement) {
- /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero
- * locations as a default calloc(). */
- BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
- add_v3_v3(subdiv_vert->co, D);
- }
+ /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero
+ * locations as a default calloc(). */
+ BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
+ add_v3_v3(subdiv_vert->co, D);
+
if (ctx->accumulated_counters) {
++ctx->accumulated_counters[subdiv_vertex_index];
}
@@ -554,12 +556,13 @@ static void evaluate_vertex_and_apply_displacement_interpolate(
add_v3_v3(subdiv_vert->co, D);
}
-static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *foreach_context,
- void *UNUSED(tls),
- const int ptex_face_index,
- const float u,
- const float v,
- const int subdiv_vertex_index)
+static void subdiv_mesh_vertex_displacement_every_corner_or_edge(
+ const SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int ptex_face_index,
+ const float u,
+ const float v,
+ const int subdiv_vertex_index)
{
SubdivMeshContext *ctx = foreach_context->user_data;
Mesh *subdiv_mesh = ctx->subdiv_mesh;
@@ -568,31 +571,32 @@ static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *
subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert);
}
-static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context,
- void *tls,
- const int ptex_face_index,
- const float u,
- const float v,
- const int UNUSED(coarse_vertex_index),
- const int UNUSED(coarse_poly_index),
- const int UNUSED(coarse_corner),
- const int subdiv_vertex_index)
+static void subdiv_mesh_vertex_displacement_every_corner(
+ const SubdivForeachContext *foreach_context,
+ void *tls,
+ const int ptex_face_index,
+ const float u,
+ const float v,
+ const int UNUSED(coarse_vertex_index),
+ const int UNUSED(coarse_poly_index),
+ const int UNUSED(coarse_corner),
+ const int subdiv_vertex_index)
{
- subdiv_mesh_vertex_every_corner_or_edge(
+ subdiv_mesh_vertex_displacement_every_corner_or_edge(
foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index);
}
-static void subdiv_mesh_vertex_every_edge(const SubdivForeachContext *foreach_context,
- void *tls,
- const int ptex_face_index,
- const float u,
- const float v,
- const int UNUSED(coarse_edge_index),
- const int UNUSED(coarse_poly_index),
- const int UNUSED(coarse_corner),
- const int subdiv_vertex_index)
+static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context,
+ void *tls,
+ const int ptex_face_index,
+ const float u,
+ const float v,
+ const int UNUSED(coarse_edge_index),
+ const int UNUSED(coarse_poly_index),
+ const int UNUSED(coarse_corner),
+ const int subdiv_vertex_index)
{
- subdiv_mesh_vertex_every_corner_or_edge(
+ subdiv_mesh_vertex_displacement_every_corner_or_edge(
foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index);
}
@@ -1078,12 +1082,8 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context,
foreach_context->topology_info = subdiv_mesh_topology_info;
/* Every boundary geometry. Used for displacement averaging. */
if (subdiv_context->have_displacement) {
- foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
- foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
- }
- else {
- foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
- foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
+ foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner;
+ foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge;
}
foreach_context->vertex_corner = subdiv_mesh_vertex_corner;
foreach_context->vertex_edge = subdiv_mesh_vertex_edge;
diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c
index dd35388f230..34dfdaf7595 100644
--- a/source/blender/blenkernel/intern/subdiv_modifier.c
+++ b/source/blender/blenkernel/intern/subdiv_modifier.c
@@ -54,23 +54,20 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene,
return md;
}
-bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene,
- const Object *ob,
- const SubsurfModifierData *smd,
- int required_mode,
- bool skip_check_is_last)
+bool BKE_subsurf_modifier_use_custom_loop_normals(const SubsurfModifierData *smd, const Mesh *mesh)
{
- if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) {
- return false;
- }
+ return (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh->flag & ME_AUTOSMOOTH) &&
+ CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
+}
- if (!skip_check_is_last) {
- ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
- if (md != (const ModifierData *)smd) {
- return false;
- }
- }
+static bool subsurf_modifier_use_autosmooth_or_split_normals(const SubsurfModifierData *smd,
+ const Mesh *mesh)
+{
+ return (mesh->flag & ME_AUTOSMOOTH) || BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh);
+}
+static bool is_subdivision_evaluation_possible_on_gpu(void)
+{
/* Only OpenGL is supported for OpenSubdiv evaluation for now. */
if (GPU_backend_get_type() != GPU_BACKEND_OPENGL) {
return false;
@@ -88,8 +85,52 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene,
return true;
}
+bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfModifierData *smd,
+ const Mesh *mesh)
+{
+ if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) {
+ /* GPU subdivision is explicitly disabled, so we don't force it. */
+ return false;
+ }
+
+ if (!is_subdivision_evaluation_possible_on_gpu()) {
+ /* The GPU type is not compatible with the subdivision. */
+ return false;
+ }
+
+ return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh);
+}
+
+bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene,
+ const Object *ob,
+ const Mesh *mesh,
+ const SubsurfModifierData *smd,
+ int required_mode,
+ bool skip_check_is_last)
+{
+ if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) {
+ return false;
+ }
+
+ /* Deactivate GPU subdivision if autosmooth or custom split normals are used as those are
+ * complicated to support on GPU, and should really be separate workflows. */
+ if (subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh)) {
+ return false;
+ }
+
+ if (!skip_check_is_last) {
+ ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
+ if (md != (const ModifierData *)smd) {
+ return false;
+ }
+ }
+
+ return is_subdivision_evaluation_possible_on_gpu();
+}
+
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene,
const Object *ob,
+ const Mesh *mesh,
int required_mode)
{
ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
@@ -103,7 +144,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene,
}
return BKE_subsurf_modifier_can_do_gpu_subdiv_ex(
- scene, ob, (SubsurfModifierData *)md, required_mode, true);
+ scene, ob, mesh, (SubsurfModifierData *)md, required_mode, true);
}
void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL;
diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c
index cb5e351e78a..5f751da1ee1 100644
--- a/source/blender/blenkernel/intern/vfont.c
+++ b/source/blender/blenkernel/intern/vfont.c
@@ -811,8 +811,8 @@ static bool vfont_to_curve(Object *ob,
#define MARGIN_X_MIN (xof_scale + tb_scale.x)
#define MARGIN_Y_MIN (yof_scale + tb_scale.y)
- /* remark: do calculations including the trailing '\0' of a string
- * because the cursor can be at that location */
+ /* NOTE: do calculations including the trailing '\0' of a string
+ * because the cursor can be at that location. */
BLI_assert(ob == NULL || ob->type == OB_FONT);
@@ -905,8 +905,8 @@ static bool vfont_to_curve(Object *ob,
custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK | CU_CHINFO_OVERFLOW);
}
- for (i = 0; i <= slen; i++) {
- makebreak:
+ i = 0;
+ while (i <= slen) {
/* Characters in the list */
info = &custrinfo[i];
ascii = mem[i];
@@ -941,19 +941,16 @@ static bool vfont_to_curve(Object *ob,
che = find_vfont_char(vfd, ascii);
BLI_rw_mutex_unlock(&vfont_rwlock);
- /*
- * The character wasn't in the current curve base so load it
+ /* The character wasn't in the current curve base so load it.
* But if the font is built-in then do not try loading since
- * whole font is in the memory already
- */
+ * whole font is in the memory already. */
if (che == NULL && BKE_vfont_is_builtin(vfont) == false) {
BLI_rw_mutex_lock(&vfont_rwlock, THREAD_LOCK_WRITE);
/* Check it once again, char might have been already load
- * between previous BLI_rw_mutex_unlock() and this BLI_rw_mutex_lock().
+ * between previous #BLI_rw_mutex_unlock() and this #BLI_rw_mutex_lock().
*
* Such a check should not be a bottleneck since it wouldn't
- * happen often once all the chars are load.
- */
+ * happen often once all the chars are load. */
if ((che = find_vfont_char(vfd, ascii)) == NULL) {
che = BKE_vfontdata_char_from_freetypefont(vfont, ascii);
}
@@ -985,8 +982,23 @@ static bool vfont_to_curve(Object *ob,
}
else if (x_used > x_available) {
// CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]);
- for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) {
- bool dobreak = false;
+ bool dobreak = false;
+ for (j = i; (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) {
+
+ /* Special case when there are no breaks possible. */
+ if (UNLIKELY(j == 0)) {
+ if (i == slen) {
+ /* Use the behavior of zero a height text-box when a break cannot be inserted.
+ *
+ * Typically when a text-box has any height and overflow is set to scale
+ * the text will wrap to fit the width as necessary. When wrapping isn't
+ * possible it's important to use the same code-path as zero-height lines.
+ * Without this exception a single word will not scale-to-fit (see: T95116). */
+ tb_scale.h = 0.0f;
+ }
+ break;
+ }
+
if (ELEM(mem[j], ' ', '-')) {
ct -= (i - (j - 1));
cnr -= (i - (j - 1));
@@ -1001,24 +1013,18 @@ static bool vfont_to_curve(Object *ob,
ct[1].dobreak = 1;
custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
dobreak = true;
+ break;
}
- else if (chartransdata[j].dobreak) {
- // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]);
- ct->dobreak = 1;
- custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
- ct -= 1;
- cnr -= 1;
- i--;
- xof = ct->xof;
- dobreak = true;
- }
- if (dobreak) {
- if (tb_scale.h == 0.0f) {
- /* NOTE: If underlined text is truncated away, the extra space is also truncated. */
- custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
- }
- goto makebreak;
+ BLI_assert(chartransdata[j].dobreak == 0);
+ }
+
+ if (dobreak) {
+ if (tb_scale.h == 0.0f) {
+ /* NOTE: If underlined text is truncated away, the extra space is also truncated. */
+ custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
}
+ /* Since a break was added, re-run this loop with `i` at it's new value. */
+ continue;
}
}
}
@@ -1063,22 +1069,12 @@ static bool vfont_to_curve(Object *ob,
current_line_length = 0.0f;
}
- /* XXX(campbell): has been unused for years, need to check if this is useful, r4613 r5282. */
-#if 0
- if (ascii == '\n') {
- xof = xof_scale;
- }
- else {
- xof = MARGIN_X_MIN;
- }
-#else
xof = MARGIN_X_MIN;
-#endif
lnr++;
cnr = 0;
wsnr = 0;
}
- else if (ascii == 9) { /* TAB */
+ else if (ascii == '\t') { /* Tab character. */
float tabfac;
ct->xof = xof;
@@ -1106,7 +1102,7 @@ static bool vfont_to_curve(Object *ob,
sb->w = xof * font_size;
}
- if (ascii == 32) {
+ if (ascii == ' ') { /* Space character. */
wsfac = cu->wordspace;
wsnr++;
}
@@ -1114,7 +1110,7 @@ static bool vfont_to_curve(Object *ob,
wsfac = 1.0f;
}
- /* Set the width of the character */
+ /* Set the width of the character. */
twidth = char_width(cu, che, info);
xof += (twidth * wsfac * (1.0f + (info->kern / 40.0f))) + xtrax;
@@ -1124,7 +1120,9 @@ static bool vfont_to_curve(Object *ob,
}
}
ct++;
+ i++;
}
+
current_line_length += xof + twidth - MARGIN_X_MIN;
longest_line_length = MAX2(current_line_length, longest_line_length);
@@ -1213,7 +1211,7 @@ static bool vfont_to_curve(Object *ob,
}
}
- /* top-baseline is default, in this case, do nothing */
+ /* Top-baseline is default, in this case, do nothing. */
if (cu->align_y != CU_ALIGN_Y_TOP_BASELINE) {
if (tb_scale.h != 0.0f) {
/* We need to loop all the text-boxes even the "full" ones.
@@ -1238,7 +1236,7 @@ static bool vfont_to_curve(Object *ob,
}
textbox_scale(&tb_scale, &cu->tb[tb_index], 1.0f / font_size);
- /* The initial Y origin of the textbox is hardcoded to 1.0f * text scale. */
+ /* The initial Y origin of the text-box is hard-coded to 1.0f * text scale. */
const float textbox_y_origin = 1.0f;
float yoff = 0.0f;
@@ -1302,8 +1300,8 @@ static bool vfont_to_curve(Object *ob,
MEM_freeN(i_textbox_array);
/* TEXT ON CURVE */
- /* NOTE: Only OB_CURVE objects could have a path. */
- if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) {
+ /* NOTE: Only #OB_CURVES_LEGACY objects could have a path. */
+ if (cu->textoncurve && cu->textoncurve->type == OB_CURVES_LEGACY) {
BLI_assert(cu->textoncurve->runtime.curve_cache != NULL);
if (cu->textoncurve->runtime.curve_cache != NULL &&
cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) {
@@ -1393,8 +1391,8 @@ static bool vfont_to_curve(Object *ob,
ctime = timeofs + distfac * (ct->xof - minx);
CLAMP(ctime, 0.0f, 1.0f);
- /* calc the right loc AND the right rot separately */
- /* vec, tvec need 4 items */
+ /* Calculate the right loc AND the right rot separately. */
+ /* `vec`, `tvec` need 4 items. */
BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
@@ -1455,7 +1453,7 @@ static bool vfont_to_curve(Object *ob,
break;
}
cnr = ct->charnr;
- /* seek for char with lnr en cnr */
+ /* Seek for char with `lnr` & `cnr`. */
ef->pos = 0;
ct = chartransdata;
for (i = 0; i < slen; i++) {
@@ -1473,7 +1471,7 @@ static bool vfont_to_curve(Object *ob,
}
}
- /* cursor first */
+ /* Cursor first. */
if (ef) {
float si, co;
@@ -1519,13 +1517,13 @@ static bool vfont_to_curve(Object *ob,
}
/* Only do that check in case we do have an object, otherwise all materials get erased every
- * time that code is called without an object... */
+ * time that code is called without an object. */
if (ob != NULL && (info->mat_nr > (ob->totcol))) {
// CLOG_ERROR(
// &LOG, "Illegal material index (%d) in text object, setting to 0", info->mat_nr);
info->mat_nr = 0;
}
- /* We do not want to see any character for \n or \r */
+ /* We don't want to see any character for '\n'. */
if (cha != '\n') {
BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
}
@@ -1601,8 +1599,7 @@ static bool vfont_to_curve(Object *ob,
*
* Keep in mind that there is no single number that will make all fit to the end.
* In a way, our ultimate goal is to get the highest scale that still leads to the
- * number of extra lines to zero.
- */
+ * number of extra lines to zero. */
if (iter_data->status == VFONT_TO_CURVE_INIT) {
bool valid = true;
@@ -1635,7 +1632,7 @@ static bool vfont_to_curve(Object *ob,
iter_data->bisect.max = iter_data->scale_to_fit;
}
else {
- /* It fits inside the textbox, scale it up. */
+ /* It fits inside the text-box, scale it up. */
iter_data->bisect.min = iter_data->scale_to_fit;
valid = true;
}
@@ -1697,7 +1694,7 @@ finally:
}
}
- /* Store the effective scale, to use for the textbox lines. */
+ /* Store the effective scale, to use for the text-box lines. */
cu->fsize_realtime = font_size;
return ok;
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index ff3f20479f8..07db0328f56 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -1331,9 +1331,6 @@ VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid)
if (grid.isType<openvdb::Vec3dGrid>()) {
return VOLUME_GRID_VECTOR_DOUBLE;
}
- if (grid.isType<openvdb::StringGrid>()) {
- return VOLUME_GRID_STRING;
- }
if (grid.isType<openvdb::MaskGrid>()) {
return VOLUME_GRID_MASK;
}
@@ -1369,7 +1366,6 @@ int BKE_volume_grid_channels(const VolumeGrid *grid)
case VOLUME_GRID_VECTOR_DOUBLE:
case VOLUME_GRID_VECTOR_INT:
return 3;
- case VOLUME_GRID_STRING:
case VOLUME_GRID_POINTS:
case VOLUME_GRID_UNKNOWN:
return 0;
@@ -1610,13 +1606,8 @@ struct CreateGridWithChangedResolutionOp {
template<typename GridType> typename openvdb::GridBase::Ptr operator()()
{
- if constexpr (std::is_same_v<GridType, openvdb::StringGrid>) {
- return {};
- }
- else {
- return create_grid_with_changed_resolution(static_cast<const GridType &>(grid),
- resolution_factor);
- }
+ return create_grid_with_changed_resolution(static_cast<const GridType &>(grid),
+ resolution_factor);
}
};
diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc
index ca18d6c3446..e7620be6401 100644
--- a/source/blender/blenkernel/intern/volume_render.cc
+++ b/source/blender/blenkernel/intern/volume_render.cc
@@ -63,7 +63,6 @@ static void extract_dense_float_voxels(const VolumeGridType grid_type,
case VOLUME_GRID_VECTOR_INT:
return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>(
grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
- case VOLUME_GRID_STRING:
case VOLUME_GRID_POINTS:
case VOLUME_GRID_UNKNOWN:
/* Zero channels to copy. */
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 3ee75ca21ce..b9d013d4756 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -42,6 +42,7 @@
* like M_SQRT1_2 leading to warnings with MSVC */
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
+# include <libavutil/channel_layout.h>
# include <libavutil/imgutils.h>
# include <libavutil/opt.h>
# include <libavutil/rational.h>
@@ -101,8 +102,6 @@ typedef struct FFMpegContext {
printf
static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value);
-static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value);
-static void ffmpeg_set_expert_options(RenderData *rd);
static void ffmpeg_filepath_get(FFMpegContext *context,
char *string,
const struct RenderData *rd,
@@ -414,99 +413,6 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixe
return context->current_frame;
}
-static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary)
-{
- char name[128];
- char *param;
-
- PRINT("FFMPEG expert option: %s: ", prop->name);
-
- BLI_strncpy(name, prop->name, sizeof(name));
-
- param = strchr(name, ':');
-
- if (param) {
- *param++ = '\0';
- }
-
- switch (prop->type) {
- case IDP_STRING:
- PRINT("%s.\n", IDP_String(prop));
- av_dict_set(dictionary, name, IDP_String(prop), 0);
- break;
- case IDP_FLOAT:
- PRINT("%g.\n", IDP_Float(prop));
- ffmpeg_dict_set_float(dictionary, prop->name, IDP_Float(prop));
- break;
- case IDP_INT:
- PRINT("%d.\n", IDP_Int(prop));
-
- if (param) {
- if (IDP_Int(prop)) {
- av_dict_set(dictionary, name, param, 0);
- }
- else {
- return;
- }
- }
- else {
- ffmpeg_dict_set_int(dictionary, prop->name, IDP_Int(prop));
- }
- break;
- }
-}
-
-static int ffmpeg_proprty_valid(AVCodecContext *c, const char *prop_name, IDProperty *curr)
-{
- int valid = 1;
-
- if (STREQ(prop_name, "video")) {
- if (STREQ(curr->name, "bf")) {
- /* flash codec doesn't support b frames */
- valid &= c->codec_id != AV_CODEC_ID_FLV1;
- }
- }
-
- return valid;
-}
-
-static void set_ffmpeg_properties(RenderData *rd,
- AVCodecContext *c,
- const char *prop_name,
- AVDictionary **dictionary)
-{
- IDProperty *prop;
- IDProperty *curr;
-
- /* TODO(sergey): This is actually rather stupid, because changing
- * codec settings in render panel would also set expert options.
- *
- * But we need ti here in order to get rid of deprecated settings
- * when opening old files in new blender.
- *
- * For as long we don't allow editing properties in the interface
- * it's all good. bug if we allow editing them, we'll need to
- * replace it with some smarter code which would port settings
- * from deprecated to new one.
- */
- ffmpeg_set_expert_options(rd);
-
- if (!rd->ffcodecdata.properties) {
- return;
- }
-
- prop = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, prop_name);
- if (!prop) {
- return;
- }
-
- for (curr = prop->data.group.first; curr; curr = curr->next) {
- if (ffmpeg_proprty_valid(c, prop_name, curr)) {
- set_ffmpeg_property_option(curr, dictionary);
- }
- }
-}
-
static AVRational calc_time_base(uint den, double num, int codec_id)
{
/* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer
@@ -561,7 +467,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodec *codec;
+ const AVCodec *codec;
AVDictionary *opts = NULL;
error[0] = '\0';
@@ -574,21 +480,15 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
/* Set up the codec context */
- context->video_codec = avcodec_alloc_context3(NULL);
- AVCodecContext *c = context->video_codec;
- c->codec_id = codec_id;
- c->codec_type = AVMEDIA_TYPE_VIDEO;
-
- codec = avcodec_find_encoder(c->codec_id);
+ codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Couldn't find valid video codec\n");
- avcodec_free_context(&c);
context->video_codec = NULL;
return NULL;
}
- /* Load codec defaults into 'c'. */
- avcodec_get_context_defaults3(c, codec);
+ context->video_codec = avcodec_alloc_context3(codec);
+ AVCodecContext *c = context->video_codec;
/* Get some values from the current render settings */
@@ -702,6 +602,13 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
+ if (codec_id == AV_CODEC_ID_DNXHD) {
+ if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
+ /* Set the block decision algorithm to be of the highest quality ("rd" == 2). */
+ c->mb_decision = 2;
+ }
+ }
+
if (codec_id == AV_CODEC_ID_FFV1) {
c->pix_fmt = AV_PIX_FMT_RGB32;
}
@@ -738,8 +645,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
255);
st->avg_frame_rate = av_inv_q(c->time_base);
- set_ffmpeg_properties(rd, c, "video", &opts);
-
if (codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
c->thread_count = 0;
}
@@ -804,8 +709,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodec *codec;
- AVDictionary *opts = NULL;
+ const AVCodec *codec;
error[0] = '\0';
@@ -815,24 +719,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
}
st->id = 1;
- context->audio_codec = avcodec_alloc_context3(NULL);
- AVCodecContext *c = context->audio_codec;
- c->thread_count = BLI_system_thread_count();
- c->thread_type = FF_THREAD_SLICE;
-
- c->codec_id = codec_id;
- c->codec_type = AVMEDIA_TYPE_AUDIO;
-
- codec = avcodec_find_encoder(c->codec_id);
+ codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Couldn't find valid audio codec\n");
- avcodec_free_context(&c);
context->audio_codec = NULL;
return NULL;
}
- /* Load codec defaults into 'c'. */
- avcodec_get_context_defaults3(c, codec);
+ context->audio_codec = avcodec_alloc_context3(codec);
+ AVCodecContext *c = context->audio_codec;
+ c->thread_count = BLI_system_thread_count();
+ c->thread_type = FF_THREAD_SLICE;
c->sample_rate = rd->ffcodecdata.audio_mixrate;
c->bit_rate = context->ffmpeg_audio_bitrate * 1000;
@@ -900,19 +797,15 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
- set_ffmpeg_properties(rd, c, "audio", &opts);
-
- int ret = avcodec_open2(c, codec, &opts);
+ int ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret));
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
- av_dict_free(&opts);
avcodec_free_context(&c);
context->audio_codec = NULL;
return NULL;
}
- av_dict_free(&opts);
/* need to prevent floating point exception when using vorbis audio codec,
* initialize this value in the same way as it's done in FFmpeg itself (sergey) */
@@ -958,15 +851,6 @@ static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value)
av_dict_set(dict, key, buffer, 0);
}
-static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value)
-{
- char buffer[32];
-
- BLI_snprintf(buffer, sizeof(buffer), "%.8f", value);
-
- av_dict_set(dict, key, buffer, 0);
-}
-
static void ffmpeg_add_metadata_callback(void *data,
const char *propname,
char *propvalue,
@@ -985,8 +869,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
{
/* Handle to the output file */
AVFormatContext *of;
- AVOutputFormat *fmt;
- AVDictionary *opts = NULL;
+ const AVOutputFormat *fmt;
char name[FILE_MAX], error[1024];
const char **exts;
@@ -1023,11 +906,13 @@ static int start_ffmpeg_impl(FFMpegContext *context,
rectx,
recty);
+ /* Sanity checks for the output file extensions. */
exts = get_file_extensions(context->ffmpeg_type);
if (!exts) {
BKE_report(reports, RPT_ERROR, "No valid formats found");
return 0;
}
+
fmt = av_guess_format(NULL, exts[0], NULL);
if (!fmt) {
BKE_report(reports, RPT_ERROR, "No valid formats found");
@@ -1036,66 +921,55 @@ static int start_ffmpeg_impl(FFMpegContext *context,
of = avformat_alloc_context();
if (!of) {
- BKE_report(reports, RPT_ERROR, "Error opening output file");
+ BKE_report(reports, RPT_ERROR, "Can't allocate ffmpeg format context");
return 0;
}
- /* Returns after this must 'goto fail;' */
-
- of->oformat = fmt;
-
- /* Only bother with setting packet size & mux rate when CRF is not used. */
- if (context->ffmpeg_crf == 0) {
- of->packet_size = rd->ffcodecdata.mux_packet_size;
- if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
- ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate);
- }
- else {
- av_dict_set(&opts, "muxrate", "0", 0);
- }
- }
-
- ffmpeg_dict_set_int(&opts, "preload", (int)(0.5 * AV_TIME_BASE));
-
- of->max_delay = (int)(0.7 * AV_TIME_BASE);
-
- fmt->audio_codec = context->ffmpeg_audio_codec;
+ enum AVCodecID audio_codec = context->ffmpeg_audio_codec;
+ enum AVCodecID video_codec = context->ffmpeg_codec;
of->url = av_strdup(name);
- /* set the codec to the user's selection */
+ /* Check if we need to force change the codec because of file type codec restrictions */
switch (context->ffmpeg_type) {
- case FFMPEG_AVI:
- case FFMPEG_MOV:
- case FFMPEG_MKV:
- fmt->video_codec = context->ffmpeg_codec;
- break;
case FFMPEG_OGG:
- fmt->video_codec = AV_CODEC_ID_THEORA;
+ video_codec = AV_CODEC_ID_THEORA;
break;
case FFMPEG_DV:
- fmt->video_codec = AV_CODEC_ID_DVVIDEO;
+ video_codec = AV_CODEC_ID_DVVIDEO;
break;
case FFMPEG_MPEG1:
- fmt->video_codec = AV_CODEC_ID_MPEG1VIDEO;
+ video_codec = AV_CODEC_ID_MPEG1VIDEO;
break;
case FFMPEG_MPEG2:
- fmt->video_codec = AV_CODEC_ID_MPEG2VIDEO;
+ video_codec = AV_CODEC_ID_MPEG2VIDEO;
break;
case FFMPEG_H264:
- fmt->video_codec = AV_CODEC_ID_H264;
+ video_codec = AV_CODEC_ID_H264;
break;
case FFMPEG_XVID:
- fmt->video_codec = AV_CODEC_ID_MPEG4;
+ video_codec = AV_CODEC_ID_MPEG4;
break;
case FFMPEG_FLV:
- fmt->video_codec = AV_CODEC_ID_FLV1;
+ video_codec = AV_CODEC_ID_FLV1;
break;
- case FFMPEG_MPEG4:
default:
- fmt->video_codec = context->ffmpeg_codec;
+ /* These containers are not restricted to any specific codec types.
+ * Currently we expect these to be .avi, .mov, .mkv, and .mp4.
+ */
+ video_codec = context->ffmpeg_codec;
break;
}
- if (fmt->video_codec == AV_CODEC_ID_DVVIDEO) {
+
+ /* Returns after this must 'goto fail;' */
+
+# if LIBAVFORMAT_VERSION_MAJOR >= 59
+ of->oformat = fmt;
+# else
+ /* *DEPRECATED* 2022/08/01 For FFMPEG (<5.0) remove this else branch and the `ifdef` above. */
+ of->oformat = (AVOutputFormat *)fmt;
+# endif
+
+ if (video_codec == AV_CODEC_ID_DVVIDEO) {
if (rectx != 720) {
BKE_report(reports, RPT_ERROR, "Render width has to be 720 pixels for DV!");
goto fail;
@@ -1111,7 +985,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
}
if (context->ffmpeg_type == FFMPEG_DV) {
- fmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
+ audio_codec = AV_CODEC_ID_PCM_S16LE;
if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE &&
rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) {
BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!");
@@ -1119,9 +993,9 @@ static int start_ffmpeg_impl(FFMpegContext *context,
}
}
- if (fmt->video_codec != AV_CODEC_ID_NONE) {
+ if (video_codec != AV_CODEC_ID_NONE) {
context->video_stream = alloc_video_stream(
- context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error));
+ context, rd, video_codec, of, rectx, recty, error, sizeof(error));
PRINT("alloc video stream %p\n", context->video_stream);
if (!context->video_stream) {
if (error[0]) {
@@ -1137,8 +1011,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
}
if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
- context->audio_stream = alloc_audio_stream(
- context, rd, fmt->audio_codec, of, error, sizeof(error));
+ context->audio_stream = alloc_audio_stream(context, rd, audio_codec, of, error, sizeof(error));
if (!context->audio_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
@@ -1175,7 +1048,6 @@ static int start_ffmpeg_impl(FFMpegContext *context,
context->outfile = of;
av_dump_format(of, 0, name, 1);
- av_dict_free(&opts);
return 1;
@@ -1192,7 +1064,6 @@ fail:
context->audio_stream = NULL;
}
- av_dict_free(&opts);
avformat_free_context(of);
return 0;
}
@@ -1526,198 +1397,17 @@ void BKE_ffmpeg_end(void *context_v)
end_ffmpeg_impl(context, false);
}
-/* properties */
-
-void BKE_ffmpeg_property_del(RenderData *rd, void *type, void *prop_)
-{
- struct IDProperty *prop = (struct IDProperty *)prop_;
- IDProperty *group;
-
- if (!rd->ffcodecdata.properties) {
- return;
- }
-
- group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type);
- if (group && prop) {
- IDP_FreeFromGroup(group, prop);
- }
-}
-
-static IDProperty *BKE_ffmpeg_property_add(RenderData *rd,
- const char *type,
- const AVOption *o,
- const AVOption *parent)
-{
- AVCodecContext c;
- IDProperty *group;
- IDProperty *prop;
- IDPropertyTemplate val;
- int idp_type;
- char name[256];
-
- val.i = 0;
-
- avcodec_get_context_defaults3(&c, NULL);
-
- if (!rd->ffcodecdata.properties) {
- rd->ffcodecdata.properties = IDP_New(IDP_GROUP, &val, "ffmpeg");
- }
-
- group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type);
-
- if (!group) {
- group = IDP_New(IDP_GROUP, &val, type);
- IDP_AddToGroup(rd->ffcodecdata.properties, group);
- }
-
- if (parent) {
- BLI_snprintf(name, sizeof(name), "%s:%s", parent->name, o->name);
- }
- else {
- BLI_strncpy(name, o->name, sizeof(name));
- }
-
- PRINT("ffmpeg_property_add: %s %s\n", type, name);
-
- prop = IDP_GetPropertyFromGroup(group, name);
- if (prop) {
- return prop;
- }
-
- switch (o->type) {
- case AV_OPT_TYPE_INT:
- case AV_OPT_TYPE_INT64:
- val.i = o->default_val.i64;
- idp_type = IDP_INT;
- break;
- case AV_OPT_TYPE_DOUBLE:
- case AV_OPT_TYPE_FLOAT:
- val.f = o->default_val.dbl;
- idp_type = IDP_FLOAT;
- break;
- case AV_OPT_TYPE_STRING:
- val.string.str =
- (char
- *)" ";
- val.string.len = 80;
- idp_type = IDP_STRING;
- break;
- case AV_OPT_TYPE_CONST:
- val.i = 1;
- idp_type = IDP_INT;
- break;
- default:
- return NULL;
- }
- prop = IDP_New(idp_type, &val, name);
- IDP_AddToGroup(group, prop);
- return prop;
-}
-
-/* not all versions of ffmpeg include that, so here we go ... */
-
-int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char *str)
-{
- AVCodecContext c;
- const AVOption *o = NULL;
- const AVOption *p = NULL;
- char name_[128];
- char *name;
- char *param;
- IDProperty *prop = NULL;
-
- avcodec_get_context_defaults3(&c, NULL);
-
- BLI_strncpy(name_, str, sizeof(name_));
-
- name = name_;
- while (*name == ' ') {
- name++;
- }
-
- param = strchr(name, ':');
-
- if (!param) {
- param = strchr(name, ' ');
- }
- if (param) {
- *param++ = '\0';
- while (*param == ' ') {
- param++;
- }
- }
-
- o = av_opt_find(&c, name, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
- if (!o) {
- PRINT("Ignoring unknown expert option %s\n", str);
- return 0;
- }
- if (param && o->type == AV_OPT_TYPE_CONST) {
- return 0;
- }
- if (param && o->type != AV_OPT_TYPE_CONST && o->unit) {
- p = av_opt_find(&c, param, o->unit, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
- if (p) {
- prop = BKE_ffmpeg_property_add(rd, (char *)type, p, o);
- }
- else {
- PRINT("Ignoring unknown expert option %s\n", str);
- }
- }
- else {
- prop = BKE_ffmpeg_property_add(rd, (char *)type, o, NULL);
- }
-
- if (!prop) {
- return 0;
- }
-
- if (param && !p) {
- switch (prop->type) {
- case IDP_INT:
- IDP_Int(prop) = atoi(param);
- break;
- case IDP_FLOAT:
- IDP_Float(prop) = atof(param);
- break;
- case IDP_STRING:
- strncpy(IDP_String(prop), param, prop->len);
- break;
- }
- }
- return 1;
-}
-
-static void ffmpeg_set_expert_options(RenderData *rd)
-{
- int codec_id = rd->ffcodecdata.codec;
-
- if (rd->ffcodecdata.properties) {
- IDP_FreePropertyContent(rd->ffcodecdata.properties);
- }
-
- if (codec_id == AV_CODEC_ID_DNXHD) {
- if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
- BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd");
- }
- }
-}
-
void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
{
- int isntsc = (rd->frs_sec != 25);
-
- if (rd->ffcodecdata.properties) {
- IDP_FreePropertyContent(rd->ffcodecdata.properties);
- }
+ bool is_ntsc = (rd->frs_sec != 25);
switch (preset) {
case FFMPEG_PRESET_VCD:
rd->ffcodecdata.type = FFMPEG_MPEG1;
rd->ffcodecdata.video_bitrate = 1150;
rd->xsch = 352;
- rd->ysch = isntsc ? 240 : 288;
- rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
+ rd->ysch = is_ntsc ? 240 : 288;
+ rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 1150;
rd->ffcodecdata.rc_min_rate = 1150;
rd->ffcodecdata.rc_buffer_size = 40 * 8;
@@ -1729,8 +1419,8 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
rd->ffcodecdata.type = FFMPEG_MPEG2;
rd->ffcodecdata.video_bitrate = 2040;
rd->xsch = 480;
- rd->ysch = isntsc ? 480 : 576;
- rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
+ rd->ysch = is_ntsc ? 480 : 576;
+ rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 2516;
rd->ffcodecdata.rc_min_rate = 0;
rd->ffcodecdata.rc_buffer_size = 224 * 8;
@@ -1747,7 +1437,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
rd->ysch = isntsc ? 480 : 576;
# endif
- rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
+ rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 9000;
rd->ffcodecdata.rc_min_rate = 0;
rd->ffcodecdata.rc_buffer_size = 224 * 8;
@@ -1758,14 +1448,14 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
case FFMPEG_PRESET_DV:
rd->ffcodecdata.type = FFMPEG_DV;
rd->xsch = 720;
- rd->ysch = isntsc ? 480 : 576;
+ rd->ysch = is_ntsc ? 480 : 576;
break;
case FFMPEG_PRESET_H264:
rd->ffcodecdata.type = FFMPEG_AVI;
rd->ffcodecdata.codec = AV_CODEC_ID_H264;
rd->ffcodecdata.video_bitrate = 6000;
- rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
+ rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 9000;
rd->ffcodecdata.rc_min_rate = 0;
rd->ffcodecdata.rc_buffer_size = 224 * 8;
@@ -1786,7 +1476,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
}
rd->ffcodecdata.video_bitrate = 6000;
- rd->ffcodecdata.gop_size = isntsc ? 18 : 15;
+ rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15;
rd->ffcodecdata.rc_max_rate = 9000;
rd->ffcodecdata.rc_min_rate = 0;
rd->ffcodecdata.rc_buffer_size = 224 * 8;
@@ -1794,8 +1484,6 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
rd->ffcodecdata.mux_rate = 10080000;
break;
}
-
- ffmpeg_set_expert_options(rd);
}
void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf)
@@ -1841,11 +1529,6 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf)
}
}
-void BKE_ffmpeg_codec_settings_verify(RenderData *rd)
-{
- ffmpeg_set_expert_options(rd);
-}
-
bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd)
{
int codec = rd->ffcodecdata.codec;
diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h
index d3bb3401d7e..80e865ded62 100644
--- a/source/blender/blenlib/BLI_array.h
+++ b/source/blender/blenlib/BLI_array.h
@@ -146,6 +146,17 @@ void _bli_array_grow_func(void **arr_p,
*/
#define BLI_array_fake_user(arr) ((void)_##arr##_len, (void)_##arr##_static)
+/**
+ * Trim excess items from the array (when they exist).
+ */
+#define BLI_array_trim(arr) \
+ { \
+ if (_bli_array_totalsize_dynamic(arr) != _##arr##_len) { \
+ arr = MEM_reallocN(arr, sizeof(*arr) * _##arr##_len); \
+ } \
+ } \
+ ((void)0)
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index a580f12851e..91dfc81ae27 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -278,18 +278,20 @@ class Array {
}
/**
- * Return a reference to the last element in the array.
- * This invokes undefined behavior when the array is empty.
+ * Return a reference to the nth last element.
+ * This invokes undefined behavior when the array is too short.
*/
- const T &last() const
+ const T &last(const int64_t n = 0) const
{
- BLI_assert(size_ > 0);
- return *(data_ + size_ - 1);
+ BLI_assert(n >= 0);
+ BLI_assert(n < size_);
+ return *(data_ + size_ - 1 - n);
}
- T &last()
+ T &last(const int64_t n = 0)
{
- BLI_assert(size_ > 0);
- return *(data_ + size_ - 1);
+ BLI_assert(n >= 0);
+ BLI_assert(n < size_);
+ return *(data_ + size_ - 1 - n);
}
/**
diff --git a/source/blender/blenlib/BLI_bounds.hh b/source/blender/blenlib/BLI_bounds.hh
new file mode 100644
index 00000000000..d20382ed500
--- /dev/null
+++ b/source/blender/blenlib/BLI_bounds.hh
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * Generic algorithms for finding the largest and smallest elements in a span.
+ */
+
+#include <optional>
+
+#include "BLI_math_vector.hh"
+#include "BLI_task.hh"
+
+namespace blender::bounds {
+
+template<typename T> struct MinMaxResult {
+ T min;
+ T max;
+};
+
+/**
+ * Find the smallest and largest values element-wise in the span.
+ */
+template<typename T> static std::optional<MinMaxResult<T>> min_max(Span<T> values)
+{
+ if (values.is_empty()) {
+ return std::nullopt;
+ }
+ return threading::parallel_reduce(
+ values.index_range(),
+ 1024,
+ MinMaxResult<T>(),
+ [&](IndexRange range, const MinMaxResult<T> &init) {
+ MinMaxResult<T> result = init;
+ for (const int i : range) {
+ math::min_max(values[i], result.min, result.max);
+ }
+ return result;
+ },
+ [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) {
+ return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
+ });
+}
+
+/**
+ * Find the smallest and largest values element-wise in the span, adding the radius to each element
+ * first. The template type T is expected to have an addition operator implemented with RadiusT.
+ */
+template<typename T, typename RadiusT>
+static std::optional<MinMaxResult<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii)
+{
+ BLI_assert(values.size() == radii.size());
+ if (values.is_empty()) {
+ return std::nullopt;
+ }
+ return threading::parallel_reduce(
+ values.index_range(),
+ 1024,
+ MinMaxResult<T>(),
+ [&](IndexRange range, const MinMaxResult<T> &init) {
+ MinMaxResult<T> result = init;
+ for (const int i : range) {
+ result.min = math::min(values[i] - radii[i], result.min);
+ result.max = math::max(values[i] + radii[i], result.max);
+ }
+ return result;
+ },
+ [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) {
+ return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)};
+ });
+}
+
+} // namespace blender::bounds
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
index 339f02dce0f..51bf8d06cf1 100644
--- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -3,7 +3,25 @@
#pragma once
#ifdef WITH_TBB
-# include <tbb/enumerable_thread_specific.h>
+
+# ifdef WITH_TBB
+/* Quiet top level deprecation message, unrelated to API usage here. */
+# if defined(WIN32) && !defined(NOMINMAX)
+/* TBB includes Windows.h which will define min/max macros causing issues
+ * when we try to use std::min and std::max later on. */
+# define NOMINMAX
+# define TBB_MIN_MAX_CLEANUP
+# endif
+# include <tbb/enumerable_thread_specific.h>
+# ifdef WIN32
+/* We cannot keep this defined, since other parts of the code deal with this on their own, leading
+ * to multiple define warnings unless we un-define this, however we can only undefine this if we
+ * were the ones that made the definition earlier. */
+# ifdef TBB_MIN_MAX_CLEANUP
+# undef NOMINMAX
+# endif
+# endif
+# endif
#endif
#include <atomic>
diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh
index 40b20dbd84f..334634613a2 100644
--- a/source/blender/blenlib/BLI_hash_tables.hh
+++ b/source/blender/blenlib/BLI_hash_tables.hh
@@ -8,6 +8,7 @@
* This file contains code that can be shared between different hash table implementations.
*/
+#include <algorithm>
#include <cmath>
#include "BLI_allocator.hh"
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index 7f4e7be543b..3decd8b9441 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -209,6 +209,18 @@ class IndexMask {
return indices_.is_empty();
}
+ bool contained_in(const IndexRange range) const
+ {
+ if (indices_.is_empty()) {
+ return true;
+ }
+ if (range.size() < indices_.size()) {
+ return false;
+ }
+ return indices_.first() >= range.first() && indices_.last() <= range.last();
+ }
+
+ IndexMask slice(int64_t start, int64_t size) const;
IndexMask slice(IndexRange slice) const;
/**
* Create a sub-mask that is also shifted to the beginning.
@@ -229,6 +241,30 @@ class IndexMask {
* so that the first index in the output is zero.
*/
IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const;
+
+ /**
+ * Get a new mask that contains all the indices that are not in the current mask.
+ * If necessary, the indices referenced by the new mask are inserted in #r_new_indices.
+ */
+ IndexMask invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const;
+
+ /**
+ * Get all contiguous index ranges within the mask.
+ */
+ Vector<IndexRange> extract_ranges() const;
+
+ /**
+ * Similar to #extract ranges, but works on the inverted mask. So the returned ranges are
+ * in-between the indices in the mask.
+ *
+ * Using this method is generally more efficient than first inverting the index mask and then
+ * extracting the ranges.
+ *
+ * If #r_skip_amounts is passed in, it will contain the number of indices that have been skipped
+ * before each range in the return value starts.
+ */
+ Vector<IndexRange> extract_ranges_invert(const IndexRange full_range,
+ Vector<int64_t> *r_skip_amounts) const;
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_index_mask_ops.hh b/source/blender/blenlib/BLI_index_mask_ops.hh
new file mode 100644
index 00000000000..48a1f27a2fa
--- /dev/null
+++ b/source/blender/blenlib/BLI_index_mask_ops.hh
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * This is separate from `BLI_index_mask.hh` because it includes headers just `IndexMask` shouldn't
+ * depend on.
+ */
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_index_mask.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+
+namespace blender::index_mask_ops {
+
+namespace detail {
+IndexMask find_indices_based_on_predicate__merge(
+ IndexMask indices_to_check,
+ threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks,
+ Vector<int64_t> &r_indices);
+} // namespace detail
+
+/**
+ * Evaluate the #predicate for all indices in #indices_to_check and return a mask that contains all
+ * indices where the predicate was true.
+ *
+ * #r_indices indices is only used if necessary.
+ */
+template<typename Predicate>
+inline IndexMask find_indices_based_on_predicate(const IndexMask indices_to_check,
+ const int64_t parallel_grain_size,
+ Vector<int64_t> &r_indices,
+ const Predicate &predicate)
+{
+ /* Evaluate predicate in parallel. Since the size of the final mask is not known yet, many
+ * smaller vectors have to be filled with all indices where the predicate is true. Those smaller
+ * vectors are joined afterwards. */
+ threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> sub_masks;
+ threading::parallel_for(
+ indices_to_check.index_range(), parallel_grain_size, [&](const IndexRange range) {
+ const IndexMask sub_mask = indices_to_check.slice(range);
+ Vector<int64_t> masked_indices;
+ for (const int64_t i : sub_mask) {
+ if (predicate(i)) {
+ masked_indices.append(i);
+ }
+ }
+ if (!masked_indices.is_empty()) {
+ sub_masks.local().append(std::move(masked_indices));
+ }
+ });
+
+ /* This part doesn't have to be in the header. */
+ return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices);
+}
+
+} // namespace blender::index_mask_ops
diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh
new file mode 100644
index 00000000000..6a988eda8a9
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_base.hh
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <type_traits>
+
+#include "BLI_math_base_safe.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_utildefines.h"
+
+#ifdef WITH_GMP
+# include "BLI_math_mpq.hh"
+#endif
+
+namespace blender::math {
+
+template<typename T> inline bool is_zero(const T &a)
+{
+ return a == T(0);
+}
+
+template<typename T> inline bool is_any_zero(const T &a)
+{
+ return is_zero(a);
+}
+
+template<typename T> inline T abs(const T &a)
+{
+ return std::abs(a);
+}
+
+template<typename T> inline T min(const T &a, const T &b)
+{
+ return std::min(a, b);
+}
+
+template<typename T> inline T max(const T &a, const T &b)
+{
+ return std::max(a, b);
+}
+
+template<typename T> inline T clamp(const T &a, const T &min, const T &max)
+{
+ return std::clamp(a, min, max);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T mod(const T &a, const T &b)
+{
+ return std::fmod(a, b);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T safe_mod(const T &a, const T &b)
+{
+ return (b != 0) ? std::fmod(a, b) : 0;
+}
+
+template<typename T> inline void min_max(const T &value, T &min, T &max)
+{
+ min = math::min(value, min);
+ max = math::max(value, max);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T safe_divide(const T &a, const T &b)
+{
+ return (b != 0) ? a / b : T(0.0f);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T floor(const T &a)
+{
+ return std::floor(a);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T ceil(const T &a)
+{
+ return std::ceil(a);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(const T &a)
+{
+ return a - std::floor(a);
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T interpolate(const T &a, const T &b, const T &t)
+{
+ return a * (1 - t) + b * t;
+}
+
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T midpoint(const T &a, const T &b)
+{
+ return (a + b) * T(0.5);
+}
+
+} // namespace blender::math
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 3d2ac5688ff..4bba84f2e29 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -303,6 +303,9 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4],
const float bbmin[3],
const float bbmax[3]);
+/** Returns the distance between two 2D line segments. */
+float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3]);
+
float closest_to_ray_v3(float r_close[3],
const float p[3],
const float ray_orig[3],
diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh
index 8e897870098..389307e331d 100644
--- a/source/blender/blenlib/BLI_math_vec_types.hh
+++ b/source/blender/blenlib/BLI_math_vec_types.hh
@@ -14,6 +14,10 @@
#include "BLI_utildefines.h"
+#ifdef WITH_GMP
+# include "BLI_math_mpq.hh"
+#endif
+
namespace blender {
/* clang-format off */
@@ -60,16 +64,6 @@ template<typename T> uint64_t vector_hash(const T &vec)
return result;
}
-template<typename T> inline bool is_any_zero(const T &a)
-{
- for (int i = 0; i < T::type_length; i++) {
- if (a[i] == T::base_type(0)) {
- return true;
- }
- }
- return false;
-}
-
} // namespace math
template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> {
@@ -349,7 +343,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
friend vec_base operator/(const vec_base &a, const vec_base &b)
{
- BLI_assert(!math::is_any_zero(b));
+ for (int i = 0; i < Size; i++) {
+ BLI_assert(b[i] != T(0));
+ }
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] / b[i]);
}
@@ -361,7 +357,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
friend vec_base operator/(T a, const vec_base &b)
{
- BLI_assert(!math::is_any_zero(b));
+ for (int i = 0; i < Size; i++) {
+ BLI_assert(b[i] != T(0));
+ }
BLI_VEC_OP_IMPL(ret, i, ret[i] = a / b[i]);
}
@@ -505,7 +503,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
BLI_INT_OP(T) friend vec_base operator%(const vec_base &a, const vec_base &b)
{
- BLI_assert(!math::is_any_zero(b));
+ for (int i = 0; i < Size; i++) {
+ BLI_assert(b[i] != T(0));
+ }
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] % b[i]);
}
@@ -579,4 +579,13 @@ using double2 = vec_base<double, 2>;
using double3 = vec_base<double, 3>;
using double4 = vec_base<double, 4>;
+template<typename T>
+inline constexpr bool is_math_float_type = (std::is_floating_point_v<T>
+#ifdef WITH_GMP
+ || std::is_same_v<T, mpq_class>
+#endif
+);
+
+template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>;
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh
index d2ef2a1c5c8..7c848eeb145 100644
--- a/source/blender/blenlib/BLI_math_vector.hh
+++ b/source/blender/blenlib/BLI_math_vector.hh
@@ -15,10 +15,6 @@
#include "BLI_span.hh"
#include "BLI_utildefines.h"
-#ifdef WITH_GMP
-# include "BLI_math_mpq.hh"
-#endif
-
namespace blender::math {
#ifndef NDEBUG
@@ -33,277 +29,303 @@ namespace blender::math {
# define BLI_ASSERT_UNIT(v) (void)(v)
#endif
-#define bT typename T::base_type
-
-#ifdef WITH_GMP
-# define BLI_ENABLE_IF_FLT_VEC(T) \
- BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type> || \
- std::is_same_v<typename T::base_type, mpq_class>))
-#else
-# define BLI_ENABLE_IF_FLT_VEC(T) BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type>))
-#endif
-
-#define BLI_ENABLE_IF_INT_VEC(T) BLI_ENABLE_IF((std::is_integral_v<typename T::base_type>))
-
-template<typename T> inline bool is_zero(const T &a)
+template<typename T, int Size> inline bool is_zero(const vec_base<T, Size> &a)
{
- for (int i = 0; i < T::type_length; i++) {
- if (a[i] != bT(0)) {
+ for (int i = 0; i < Size; i++) {
+ if (a[i] != T(0)) {
return false;
}
}
return true;
}
-template<typename T> inline T abs(const T &a)
+template<typename T, int Size> inline bool is_any_zero(const vec_base<T, Size> &a)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ for (int i = 0; i < Size; i++) {
+ if (a[i] == T(0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template<typename T, int Size> inline vec_base<T, Size> abs(const vec_base<T, Size> &a)
+{
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = a[i] >= 0 ? a[i] : -a[i];
}
return result;
}
-template<typename T> inline T min(const T &a, const T &b)
+template<typename T, int Size>
+inline vec_base<T, Size> min(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = a[i] < b[i] ? a[i] : b[i];
}
return result;
}
-template<typename T> inline T max(const T &a, const T &b)
+template<typename T, int Size>
+inline vec_base<T, Size> max(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = a[i] > b[i] ? a[i] : b[i];
}
return result;
}
-template<typename T> inline T clamp(const T &a, const T &min_v, const T &max_v)
+template<typename T, int Size>
+inline T clamp(const vec_base<T, Size> &a,
+ const vec_base<T, Size> &min,
+ const vec_base<T, Size> &max)
{
- T result = a;
- for (int i = 0; i < T::type_length; i++) {
- CLAMP(result[i], min_v[i], max_v[i]);
+ vec_base<T, Size> result = a;
+ for (int i = 0; i < Size; i++) {
+ std::clamp(result[i], min[i], max[i]);
}
return result;
}
-template<typename T> inline T clamp(const T &a, const bT &min_v, const bT &max_v)
+template<typename T, int Size>
+inline vec_base<T, Size> clamp(const vec_base<T, Size> &a, const T &min, const T &max)
{
- T result = a;
- for (int i = 0; i < T::type_length; i++) {
- CLAMP(result[i], min_v, max_v);
+ vec_base<T, Size> result = a;
+ for (int i = 0; i < Size; i++) {
+ std::clamp(result[i], min, max);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
BLI_assert(b[i] != 0);
result[i] = std::fmod(a[i], b[i]);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, bT b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const T &b)
{
BLI_assert(b != 0);
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = std::fmod(a[i], b);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T safe_mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = (b[i] != 0) ? std::fmod(a[i], b[i]) : 0;
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, bT b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T safe_mod(const vec_base<T, Size> &a, const T &b)
{
if (b == 0) {
- return T(0.0f);
+ return vec_base<T, Size>(0);
}
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = std::fmod(a[i], b);
}
return result;
}
-template<typename T> inline void min_max(const T &vector, T &min_vec, T &max_vec)
+template<typename T, int Size>
+inline void min_max(const vec_base<T, Size> &vector,
+ vec_base<T, Size> &min,
+ vec_base<T, Size> &max)
{
- min_vec = min(vector, min_vec);
- max_vec = max(vector, max_vec);
+ min = math::min(vector, min);
+ max = math::max(vector, max);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = (b[i] == 0) ? 0 : a[i] / b[i];
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const bT b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const T &b)
{
- return (b != 0) ? a / b : T(0.0f);
+ return (b != 0) ? a / b : vec_base<T, Size>(0.0f);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T floor(const T &a)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> floor(const vec_base<T, Size> &a)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = std::floor(a[i]);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T ceil(const T &a)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> ceil(const vec_base<T, Size> &a)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = std::ceil(a[i]);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T fract(const T &a)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> fract(const vec_base<T, Size> &a)
{
- T result;
- for (int i = 0; i < T::type_length; i++) {
+ vec_base<T, Size> result;
+ for (int i = 0; i < Size; i++) {
result[i] = a[i] - std::floor(a[i]);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT dot(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T dot(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
- bT result = a[0] * b[0];
- for (int i = 1; i < T::type_length; i++) {
+ T result = a[0] * b[0];
+ for (int i = 1; i < Size; i++) {
result += a[i] * b[i];
}
return result;
}
-template<typename T> inline bT length_manhattan(const T &a)
+template<typename T, int Size> inline T length_manhattan(const vec_base<T, Size> &a)
{
- bT result = std::abs(a[0]);
- for (int i = 1; i < T::type_length; i++) {
+ T result = std::abs(a[0]);
+ for (int i = 1; i < Size; i++) {
result += std::abs(a[i]);
}
return result;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length_squared(const T &a)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T length_squared(const vec_base<T, Size> &a)
{
return dot(a, a);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length(const T &a)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T length(const vec_base<T, Size> &a)
{
return std::sqrt(length_squared(a));
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_manhattan(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T distance_manhattan(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
return length_manhattan(a - b);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_squared(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T distance_squared(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
return length_squared(a - b);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline T distance(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
return length(a - b);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T reflect(const T &incident, const T &normal)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> reflect(const vec_base<T, Size> &incident,
+ const vec_base<T, Size> &normal)
{
BLI_ASSERT_UNIT(normal);
return incident - 2.0 * dot(normal, incident) * normal;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)>
-inline T refract(const T &incident, const T &normal, const bT eta)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> refract(const vec_base<T, Size> &incident,
+ const vec_base<T, Size> &normal,
+ const T &eta)
{
float dot_ni = dot(normal, incident);
float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni);
if (k < 0.0f) {
- return T(0.0f);
+ return vec_base<T, Size>(0.0f);
}
return eta * incident - (eta * dot_ni + sqrt(k)) * normal;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T project(const T &p, const T &v_proj)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> project(const vec_base<T, Size> &p, const vec_base<T, Size> &v_proj)
{
if (UNLIKELY(is_zero(v_proj))) {
- return T(0.0f);
+ return vec_base<T, Size>(0.0f);
}
return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj));
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)>
-inline T normalize_and_get_length(const T &v, bT &out_length)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> normalize_and_get_length(const vec_base<T, Size> &v, T &out_length)
{
out_length = length_squared(v);
/* A larger value causes normalize errors in a scaled down models with camera extreme close. */
- constexpr bT threshold = std::is_same_v<bT, double> ? 1.0e-70 : 1.0e-35f;
+ constexpr T threshold = std::is_same_v<T, double> ? 1.0e-70 : 1.0e-35f;
if (out_length > threshold) {
out_length = sqrt(out_length);
return v / out_length;
}
/* Either the vector is small or one of it's values contained `nan`. */
out_length = 0.0;
- return T(0.0);
+ return vec_base<T, Size>(0.0);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T normalize(const T &v)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> normalize(const vec_base<T, Size> &v)
{
- bT len;
+ T len;
return normalize_and_get_length(v, len);
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))>
-inline T cross(const T &a, const T &b)
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, 3> cross(const vec_base<T, 3> &a, const vec_base<T, 3> &b)
{
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
-template<typename T,
- BLI_ENABLE_IF((std::is_same_v<bT, float>)),
- BLI_ENABLE_IF((T::type_length == 3))>
-inline T cross_high_precision(const T &a, const T &b)
+inline vec_base<float, 3> cross_high_precision(const vec_base<float, 3> &a,
+ const vec_base<float, 3> &b)
{
return {(float)((double)a.y * b.z - (double)a.z * b.y),
(float)((double)a.z * b.x - (double)a.x * b.z),
(float)((double)a.x * b.y - (double)a.y * b.x)};
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))>
-inline T cross_poly(Span<T> poly)
+template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, 3> cross_poly(Span<vec_base<T, 3>> poly)
{
/* Newell's Method. */
int nv = static_cast<int>(poly.size());
if (nv < 3) {
- return T(0, 0, 0);
+ return vec_base<T, 3>(0, 0, 0);
}
- const T *v_prev = &poly[nv - 1];
- const T *v_curr = &poly[0];
- T n(0, 0, 0);
+ const vec_base<T, 3> *v_prev = &poly[nv - 1];
+ const vec_base<T, 3> *v_curr = &poly[0];
+ vec_base<T, 3> n(0, 0, 0);
for (int i = 0; i < nv;) {
n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]);
n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]);
@@ -317,25 +339,31 @@ inline T cross_poly(Span<T> poly)
return n;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T interpolate(const T &a, const T &b, bT t)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> interpolate(const vec_base<T, Size> &a,
+ const vec_base<T, Size> &b,
+ const T &t)
{
return a * (1 - t) + b * t;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T midpoint(const T &a, const T &b)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> midpoint(const vec_base<T, Size> &a, const vec_base<T, Size> &b)
{
return (a + b) * 0.5;
}
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)>
-inline T faceforward(const T &vector, const T &incident, const T &reference)
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+inline vec_base<T, Size> faceforward(const vec_base<T, Size> &vector,
+ const vec_base<T, Size> &incident,
+ const vec_base<T, Size> &reference)
{
return (dot(reference, incident) < 0) ? vector : -vector;
}
-template<typename T> inline int dominant_axis(const T &a)
+template<typename T> inline int dominant_axis(const vec_base<T, 3> &a)
{
- T b = abs(a);
+ vec_base<T, 3> b = abs(a);
return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2));
}
@@ -348,14 +376,13 @@ template<typename T> struct isect_result {
LINE_LINE_EXACT = 1,
LINE_LINE_CROSS = 2,
} kind;
- bT lambda;
+ typename T::base_type lambda;
};
-template<typename T, BLI_ENABLE_IF_FLT_VEC(T)>
-isect_result<T> isect_seg_seg(const T &v1, const T &v2, const T &v3, const T &v4);
-
-#undef BLI_ENABLE_IF_FLT_VEC
-#undef BLI_ENABLE_IF_INT_VEC
-#undef bT
+template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
+isect_result<vec_base<T, Size>> isect_seg_seg(const vec_base<T, Size> &v1,
+ const vec_base<T, Size> &v2,
+ const vec_base<T, Size> &v3,
+ const vec_base<T, Size> &v4);
} // namespace blender::math
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index dd6b8463d84..a7cad5461b4 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -544,3 +544,31 @@ Container &move_assign_container(Container &dst, Container &&src) noexcept(
}
} // namespace blender
+
+namespace blender::detail {
+
+template<typename Func> struct ScopedDeferHelper {
+ Func func;
+
+ ~ScopedDeferHelper()
+ {
+ func();
+ }
+};
+
+} // namespace blender::detail
+
+#define BLI_SCOPED_DEFER_NAME1(a, b) a##b
+#define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b)
+#define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__)
+
+/**
+ * Execute the given function when the current scope ends. This can be used to cheaply implement
+ * some RAII-like behavior for C types that don't support it. Long term, the types we want to use
+ * this with should either be converted to C++ or get a proper C++ API. Until then, this function
+ * can help avoid common resource leakages.
+ */
+#define BLI_SCOPED_DEFER(function_to_defer) \
+ auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \
+ blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \
+ BLI_SCOPED_DEFER_NAME(helper){std::move(BLI_SCOPED_DEFER_NAME(func))};
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index b4427b1dc2a..7b3e3e983f0 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -298,7 +298,7 @@ bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL();
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL();
/**
* Replaces "#" character sequence in last slash-separated component of `path`
- * with frame as decimal integer, with leading zeroes as necessary, to make digits digits.
+ * with frame as decimal integer, with leading zeroes as necessary, to make digits.
*/
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL();
/**
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index d82f21a57ff..9ab096094de 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -307,13 +307,14 @@ template<typename T> class Span {
}
/**
- * Returns a reference to the last element in the array. This invokes undefined behavior when the
- * array is empty.
+ * Returns a reference to the nth last element. This invokes undefined behavior when the span is
+ * too short.
*/
- constexpr const T &last() const
+ constexpr const T &last(const int64_t n = 0) const
{
- BLI_assert(size_ > 0);
- return data_[size_ - 1];
+ BLI_assert(n >= 0);
+ BLI_assert(n < size_);
+ return data_[size_ - 1 - n];
}
/**
@@ -673,13 +674,14 @@ template<typename T> class MutableSpan {
}
/**
- * Returns a reference to the last element. This invokes undefined behavior when the array is
- * empty.
+ * Returns a reference to the nth last element. This invokes undefined behavior when the span is
+ * too short.
*/
- constexpr T &last() const
+ constexpr T &last(const int64_t n = 0) const
{
- BLI_assert(size_ > 0);
- return data_[size_ - 1];
+ BLI_assert(n >= 0);
+ BLI_assert(n < size_);
+ return data_[size_ - 1 - n];
}
/**
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index d5d33b8a000..da9ab9c313e 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -639,18 +639,20 @@ class Vector {
}
/**
- * Return a reference to the last element in the vector.
- * This invokes undefined behavior when the vector is empty.
+ * Return a reference to the nth last element.
+ * This invokes undefined behavior when the vector is too short.
*/
- const T &last() const
+ const T &last(const int64_t n = 0) const
{
- BLI_assert(this->size() > 0);
- return *(end_ - 1);
+ BLI_assert(n >= 0);
+ BLI_assert(n < this->size());
+ return *(end_ - 1 - n);
}
- T &last()
+ T &last(const int64_t n = 0)
{
- BLI_assert(this->size() > 0);
- return *(end_ - 1);
+ BLI_assert(n >= 0);
+ BLI_assert(n < this->size());
+ return *(end_ - 1 - n);
}
/**
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index d697590b946..16fd706c99d 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -667,7 +667,7 @@ template<typename T> class VArrayCommon {
}
/**
- * Returns the internally used span of the virtual array. This invokes undefined behavior is the
+ * Returns the internally used span of the virtual array. This invokes undefined behavior if the
* virtual array is not stored as a span internally.
*/
Span<T> get_internal_span() const
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 29015084679..6e3e84f6495 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -162,6 +162,7 @@ set(SRC
BLI_bitmap_draw_2d.h
BLI_blenlib.h
BLI_boxpack_2d.h
+ BLI_bounds.hh
BLI_buffer.h
BLI_color.hh
BLI_compiler_attrs.h
@@ -202,6 +203,7 @@ set(SRC
BLI_heap.h
BLI_heap_simple.h
BLI_index_mask.hh
+ BLI_index_mask_ops.hh
BLI_index_range.hh
BLI_inplace_priority_queue.hh
BLI_iterator.h
@@ -220,6 +222,7 @@ set(SRC
BLI_map.hh
BLI_map_slots.hh
BLI_math.h
+ BLI_math_base.hh
BLI_math_base.h
BLI_math_base_safe.h
BLI_math_bits.h
@@ -238,6 +241,7 @@ set(SRC
BLI_math_vec_mpq_types.hh
BLI_math_vec_types.hh
BLI_math_vector.h
+ BLI_math_vector.hh
BLI_memarena.h
BLI_memblock.h
BLI_memiter.h
@@ -306,6 +310,9 @@ set(SRC
BLI_winstuff.h
PIL_time.h
PIL_time_utildefines.h
+
+ # Without these files listed, they aren't known to CMake.
+ ../../../extern/json/include/json.hpp
)
set(LIB
@@ -394,6 +401,7 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
+ tests/BLI_bounds_test.cc
tests/BLI_color_test.cc
tests/BLI_delaunay_2d_test.cc
tests/BLI_disjoint_set_test.cc
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
index cb0ba763c94..b7dbd7d679c 100644
--- a/source/blender/blenlib/intern/delaunay_2d.cc
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -1691,7 +1691,7 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco,
BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
UNUSED_VARS_NDEBUG(vc);
- auto isect = isect_seg_seg<vec2<T>>(va->co.exact, vb->co.exact, curco.exact, v2->co.exact);
+ auto isect = isect_seg_seg(va->co.exact, vb->co.exact, curco.exact, v2->co.exact);
T &lambda = isect.lambda;
switch (isect.kind) {
case isect_result<vec2<T>>::LINE_LINE_CROSS: {
@@ -2556,10 +2556,10 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state)
if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) {
continue; /* Don't count hits on edges between faces in same region. */
}
- auto isect = isect_seg_seg<vec2<T>>(ray_end.exact,
- mid.exact,
- e->symedges[0].vert->co.exact,
- e->symedges[1].vert->co.exact);
+ auto isect = isect_seg_seg(ray_end.exact,
+ mid.exact,
+ e->symedges[0].vert->co.exact,
+ e->symedges[1].vert->co.exact);
switch (isect.kind) {
case isect_result<vec2<T>>::LINE_LINE_CROSS: {
hits++;
diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc
index b55de6a9264..1e301bc5fb9 100644
--- a/source/blender/blenlib/intern/index_mask.cc
+++ b/source/blender/blenlib/intern/index_mask.cc
@@ -1,9 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_index_mask.hh"
+#include "BLI_index_mask_ops.hh"
namespace blender {
+IndexMask IndexMask::slice(int64_t start, int64_t size) const
+{
+ return this->slice(IndexRange(start, size));
+}
+
IndexMask IndexMask::slice(IndexRange slice) const
{
return IndexMask(indices_.slice(slice));
@@ -30,4 +36,161 @@ IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r
return IndexMask(r_new_indices.as_span());
}
+IndexMask IndexMask::invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const
+{
+ BLI_assert(this->contained_in(full_range));
+ if (full_range.size() == indices_.size()) {
+ return {};
+ }
+ if (indices_.is_empty()) {
+ return full_range;
+ }
+ r_new_indices.clear();
+
+ const Vector<IndexRange> ranges = this->extract_ranges_invert(full_range, nullptr);
+ for (const IndexRange &range : ranges) {
+ for (const int64_t index : range) {
+ r_new_indices.append(index);
+ }
+ }
+ return r_new_indices.as_span();
+}
+
+Vector<IndexRange> IndexMask::extract_ranges() const
+{
+ Vector<IndexRange> ranges;
+ int64_t range_start = 0;
+ while (range_start < indices_.size()) {
+ int64_t current_range_end = range_start + 1;
+ int64_t step_size = 1;
+
+ while (true) {
+ const int64_t possible_range_end = current_range_end + step_size;
+ if (possible_range_end > indices_.size()) {
+ break;
+ }
+ if (!this->slice(range_start, possible_range_end - range_start).is_range()) {
+ break;
+ }
+ current_range_end = possible_range_end;
+ step_size *= 2;
+ }
+
+ /* This step size was tried already, no need to try it again. */
+ step_size /= 2;
+
+ while (step_size > 0) {
+ const int64_t possible_range_end = current_range_end + step_size;
+ step_size /= 2;
+ if (possible_range_end > indices_.size()) {
+ continue;
+ }
+ if (!this->slice(range_start, possible_range_end - range_start).is_range()) {
+ continue;
+ }
+ current_range_end = possible_range_end;
+ }
+
+ ranges.append(IndexRange{indices_[range_start], current_range_end - range_start});
+ range_start = current_range_end;
+ }
+ return ranges;
+}
+
+Vector<IndexRange> IndexMask::extract_ranges_invert(const IndexRange full_range,
+ Vector<int64_t> *r_skip_amounts) const
+{
+ BLI_assert(this->contained_in(full_range));
+ const Vector<IndexRange> ranges = this->extract_ranges();
+ Vector<IndexRange> inverted_ranges;
+
+ int64_t skip_amount = 0;
+ int64_t next_start = full_range.start();
+ for (const int64_t i : ranges.index_range()) {
+ const IndexRange range = ranges[i];
+ if (range.start() > next_start) {
+ inverted_ranges.append({next_start, range.start() - next_start});
+ if (r_skip_amounts != nullptr) {
+ r_skip_amounts->append(skip_amount);
+ }
+ }
+ next_start = range.one_after_last();
+ skip_amount += range.size();
+ }
+ if (next_start < full_range.one_after_last()) {
+ inverted_ranges.append({next_start, full_range.one_after_last() - next_start});
+ if (r_skip_amounts != nullptr) {
+ r_skip_amounts->append(skip_amount);
+ }
+ }
+ return inverted_ranges;
+}
+
} // namespace blender
+
+namespace blender::index_mask_ops::detail {
+
+IndexMask find_indices_based_on_predicate__merge(
+ IndexMask indices_to_check,
+ threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks,
+ Vector<int64_t> &r_indices)
+{
+ /* Gather vectors that have been generated by possibly multiple threads. */
+ Vector<Vector<int64_t> *> all_vectors;
+ int64_t result_mask_size = 0;
+ for (Vector<Vector<int64_t>> &local_sub_masks : sub_masks) {
+ for (Vector<int64_t> &sub_mask : local_sub_masks) {
+ all_vectors.append(&sub_mask);
+ result_mask_size += sub_mask.size();
+ }
+ }
+
+ if (all_vectors.is_empty()) {
+ /* Special case when the predicate was false for all elements. */
+ return {};
+ }
+ if (result_mask_size == indices_to_check.size()) {
+ /* Special case when the predicate was true for all elements. */
+ return indices_to_check;
+ }
+ if (all_vectors.size() == 1) {
+ /* Special case when all indices for which the predicate is true happen to be in a single
+ * vector. */
+ r_indices = std::move(*all_vectors[0]);
+ return r_indices.as_span();
+ }
+
+ /* Indices in separate vectors don't overlap. So it is ok to sort the vectors just by looking at
+ * the first element. */
+ std::sort(all_vectors.begin(),
+ all_vectors.end(),
+ [](const Vector<int64_t> *a, const Vector<int64_t> *b) { return (*a)[0] < (*b)[0]; });
+
+ /* Precompute the offsets for the individual vectors, so that the indices can be copied into the
+ * final vector in parallel. */
+ Vector<int64_t> offsets;
+ offsets.reserve(all_vectors.size() + 1);
+ offsets.append(0);
+ for (Vector<int64_t> *vector : all_vectors) {
+ offsets.append(offsets.last() + vector->size());
+ }
+
+ r_indices.resize(result_mask_size);
+
+ /* Fill the final index mask in parallel again. */
+ threading::parallel_for(all_vectors.index_range(), 100, [&](const IndexRange all_vectors_range) {
+ for (const int64_t vector_index : all_vectors_range) {
+ Vector<int64_t> &vector = *all_vectors[vector_index];
+ const int64_t offset = offsets[vector_index];
+ threading::parallel_for(vector.index_range(), 1024, [&](const IndexRange range) {
+ initialized_copy_n(vector.data() + range.start(),
+ range.size(),
+ r_indices.data() + offset + range.start());
+ });
+ }
+ });
+
+ return r_indices.as_span();
+}
+
+} // namespace blender::index_mask_ops::detail
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index f96c80185b1..bc3ed099fd5 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -903,6 +903,18 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4],
/** \} */
+float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3])
+{
+ if (isect_seg_seg_v2_simple(a1, a2, b1, b2)) {
+ return 0.0f;
+ }
+ const float d1 = dist_squared_to_line_segment_v2(a1, b1, b2);
+ const float d2 = dist_squared_to_line_segment_v2(a2, b1, b2);
+ const float d3 = dist_squared_to_line_segment_v2(b1, a1, a2);
+ const float d4 = dist_squared_to_line_segment_v2(b2, a1, a2);
+ return sqrtf(min_ffff(d1, d2, d3, d4));
+}
+
void closest_on_tri_to_point_v3(
float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
{
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 6e2e9787ebe..70030fc2bdf 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -2185,7 +2185,7 @@ static void finish_patch_cell_graph(const IMesh &tm,
* There will be a vector of \a nshapes winding numbers in each cell, one per
* input shape.
* As one crosses a patch into a new cell, the original shape (mesh part)
- * that that patch was part of dictates which winding number changes.
+ * that patch was part of dictates which winding number changes.
* The shape_fn(triangle_number) function should return the shape that the
* triangle is part of.
* Also, as soon as the winding numbers for a cell are set, use bool_optype
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index 976c1b0226f..75fa628e701 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -307,8 +307,9 @@ size_t BLI_str_unescape_ex(char *__restrict dst,
{
size_t len = 0;
bool is_complete = true;
+ const size_t max_strlen = dst_maxncpy - 1; /* Account for trailing zero byte. */
for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) {
- if (UNLIKELY(len == dst_maxncpy)) {
+ if (UNLIKELY(len == max_strlen)) {
is_complete = false;
break;
}
diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc
index 6d12b54099a..74eeb5e4e5e 100644
--- a/source/blender/blenlib/tests/BLI_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_test.cc
@@ -231,9 +231,11 @@ TEST(array, Last)
{
Array<int> array = {5, 7, 8, 9};
EXPECT_EQ(array.last(), 9);
+ EXPECT_EQ(array.last(1), 8);
array.last() = 1;
EXPECT_EQ(array[3], 1);
EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1);
+ EXPECT_EQ(const_cast<const Array<int> &>(array).last(2), 7);
}
TEST(array, Reinitialize)
diff --git a/source/blender/blenlib/tests/BLI_bounds_test.cc b/source/blender/blenlib/tests/BLI_bounds_test.cc
new file mode 100644
index 00000000000..9c123d4705c
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_bounds_test.cc
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_math_base.hh"
+
+#include "BLI_array.hh"
+#include "BLI_bounds.hh"
+
+namespace blender::tests {
+
+TEST(bounds, Empty)
+{
+ Span<float2> empty_span{};
+ EXPECT_TRUE(empty_span.is_empty());
+ auto result = bounds::min_max(empty_span);
+ EXPECT_EQ(result, std::nullopt);
+}
+
+TEST(bounds, MinMax)
+{
+ Array<float2> data = {float2(0, 1), float2(3, -1), float2(0, -2), float2(-1, 1)};
+ auto result = bounds::min_max(data.as_span());
+ EXPECT_EQ(result->min, float2(-1, -2));
+ EXPECT_EQ(result->max, float2(3, 1));
+}
+
+TEST(bounds, MinMaxFloat)
+{
+ Array<float> data = {1.0f, 3.0f, 0.0f, -1.0f};
+ auto result = bounds::min_max(data.as_span());
+ EXPECT_EQ(result->min, -1.0f);
+ EXPECT_EQ(result->max, 3.0f);
+}
+
+TEST(bounds, MinMaxRadii)
+{
+ Array<int2> data = {int2(0, 1), int2(3, -1), int2(0, -2), int2(-1, 1)};
+ Array<int> radii = {5, 1, 1, 4};
+ auto result = bounds::min_max_with_radii(data.as_span(), radii.as_span());
+ EXPECT_EQ(result->min, int2(-5, -4));
+ EXPECT_EQ(result->max, int2(5, 6));
+}
+
+TEST(bounds, Large)
+{
+ Array<int2> data(10000);
+ for (const int64_t i : data.index_range()) {
+ data[i] = int2(i, i);
+ }
+
+ auto result = bounds::min_max(data.as_span());
+ EXPECT_EQ(result->min, int2(0, 0));
+ EXPECT_EQ(result->max, int2(9999, 9999));
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc
index 179c1c58cc4..86ae31cedcc 100644
--- a/source/blender/blenlib/tests/BLI_index_mask_test.cc
+++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc
@@ -64,4 +64,153 @@ TEST(index_mask, SliceAndOffset)
}
}
+TEST(index_mask, ExtractRanges)
+{
+ {
+ Vector<int64_t> indices = {1, 2, 3, 5, 7, 8};
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), 3);
+ EXPECT_EQ(ranges[0], IndexRange(1, 3));
+ EXPECT_EQ(ranges[1], IndexRange(5, 1));
+ EXPECT_EQ(ranges[2], IndexRange(7, 2));
+ }
+ {
+ Vector<int64_t> indices;
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), 0);
+ }
+ {
+ Vector<int64_t> indices = {5, 6, 7, 8, 9, 10};
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), 1);
+ EXPECT_EQ(ranges[0], IndexRange(5, 6));
+ }
+ {
+ Vector<int64_t> indices = {1, 3, 6, 8};
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), 4);
+ EXPECT_EQ(ranges[0], IndexRange(1, 1));
+ EXPECT_EQ(ranges[1], IndexRange(3, 1));
+ EXPECT_EQ(ranges[2], IndexRange(6, 1));
+ EXPECT_EQ(ranges[3], IndexRange(8, 1));
+ }
+ {
+ Vector<int64_t> indices;
+ IndexRange range1{4, 10};
+ IndexRange range2{20, 30};
+ IndexRange range3{100, 1};
+ IndexRange range4{150, 100};
+ for (const IndexRange &range : {range1, range2, range3, range4}) {
+ for (const int64_t i : range) {
+ indices.append(i);
+ }
+ }
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), 4);
+ EXPECT_EQ(ranges[0], range1);
+ EXPECT_EQ(ranges[1], range2);
+ EXPECT_EQ(ranges[2], range3);
+ EXPECT_EQ(ranges[3], range4);
+ }
+ {
+ const int64_t max_test_range_size = 50;
+ Vector<int64_t> indices;
+ int64_t offset = 0;
+ for (const int64_t range_size : IndexRange(1, max_test_range_size)) {
+ for (const int i : IndexRange(range_size)) {
+ indices.append(offset + i);
+ }
+ offset += range_size + 1;
+ }
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges();
+ EXPECT_EQ(ranges.size(), max_test_range_size);
+ for (const int64_t range_size : IndexRange(1, max_test_range_size)) {
+ const IndexRange range = ranges[range_size - 1];
+ EXPECT_EQ(range.size(), range_size);
+ }
+ }
+}
+
+TEST(index_mask, Invert)
+{
+ {
+ Vector<int64_t> indices;
+ Vector<int64_t> new_indices;
+ IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices);
+ EXPECT_EQ(inverted_mask.size(), 10);
+ EXPECT_TRUE(new_indices.is_empty());
+ }
+ {
+ Vector<int64_t> indices = {3, 4, 5, 6};
+ Vector<int64_t> new_indices;
+ IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(3, 4), new_indices);
+ EXPECT_TRUE(inverted_mask.is_empty());
+ }
+ {
+ Vector<int64_t> indices = {5};
+ Vector<int64_t> new_indices;
+ IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices);
+ EXPECT_EQ(inverted_mask.size(), 9);
+ EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({0, 1, 2, 3, 4, 6, 7, 8, 9}));
+ }
+ {
+ Vector<int64_t> indices = {0, 1, 2, 6, 7, 9};
+ Vector<int64_t> new_indices;
+ IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices);
+ EXPECT_EQ(inverted_mask.size(), 4);
+ EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({3, 4, 5, 8}));
+ }
+}
+
+TEST(index_mask, ExtractRangesInvert)
+{
+ {
+ Vector<int64_t> indices;
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10), nullptr);
+ EXPECT_EQ(ranges.size(), 1);
+ EXPECT_EQ(ranges[0], IndexRange(10));
+ }
+ {
+ Vector<int64_t> indices = {1, 2, 3, 6, 7};
+ Vector<int64_t> skip_amounts;
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10),
+ &skip_amounts);
+ EXPECT_EQ(ranges.size(), 3);
+ EXPECT_EQ(ranges[0], IndexRange(0, 1));
+ EXPECT_EQ(ranges[1], IndexRange(4, 2));
+ EXPECT_EQ(ranges[2], IndexRange(8, 2));
+ EXPECT_EQ(skip_amounts[0], 0);
+ EXPECT_EQ(skip_amounts[1], 3);
+ EXPECT_EQ(skip_amounts[2], 5);
+ }
+ {
+ Vector<int64_t> indices = {0, 1, 2, 3, 4};
+ Vector<int64_t> skip_amounts;
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5),
+ &skip_amounts);
+ EXPECT_TRUE(ranges.is_empty());
+ EXPECT_TRUE(skip_amounts.is_empty());
+ }
+ {
+ Vector<int64_t> indices = {5, 6, 7, 10, 11};
+ Vector<int64_t> skip_amounts;
+ Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5, 20),
+ &skip_amounts);
+ EXPECT_EQ(ranges.size(), 2);
+ EXPECT_EQ(ranges[0], IndexRange(8, 2));
+ EXPECT_EQ(ranges[1], IndexRange(12, 13));
+ EXPECT_EQ(skip_amounts[0], 3);
+ EXPECT_EQ(skip_amounts[1], 5);
+ }
+}
+
+TEST(index_mask, ContainedIn)
+{
+ EXPECT_TRUE(IndexMask({3, 4, 5}).contained_in(IndexRange(10)));
+ EXPECT_TRUE(IndexMask().contained_in(IndexRange(5, 0)));
+ EXPECT_FALSE(IndexMask({3}).contained_in(IndexRange(3)));
+ EXPECT_FALSE(IndexMask({4, 5, 6}).contained_in(IndexRange(5, 10)));
+ EXPECT_FALSE(IndexMask({5, 6}).contained_in(IndexRange()));
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_math_base_test.cc b/source/blender/blenlib/tests/BLI_math_base_test.cc
index 33acefeeac2..62f2b2775d0 100644
--- a/source/blender/blenlib/tests/BLI_math_base_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_base_test.cc
@@ -3,6 +3,10 @@
#include "testing/testing.h"
#include "BLI_math.h"
+#include "BLI_math_base.hh"
+#include "BLI_math_vector.hh"
+
+namespace blender::tests {
/* In tests below, when we are using -1.0f as max_diff value, we actually turn the function into a
* pure-ULP one. */
@@ -131,3 +135,20 @@ TEST(math_base, FloorPowerOf10)
EXPECT_NEAR(floor_power_of_10(100.1f), 100.0f, 1e-4f);
EXPECT_NEAR(floor_power_of_10(99.9f), 10.0f, 1e-4f);
}
+
+TEST(math_base, MinVectorAndFloat)
+{
+ EXPECT_EQ(math::min(1.0f, 2.0f), 1.0f);
+}
+
+TEST(math_base, ClampInt)
+{
+ EXPECT_EQ(math::clamp(111, -50, 101), 101);
+}
+
+TEST(math_base, Midpoint)
+{
+ EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f);
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc
index 07eb6b29a20..7590d77525b 100644
--- a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc
@@ -146,4 +146,29 @@ TEST(math_vec_types, VectorTypeConversion)
EXPECT_EQ(d[1], -1.0);
}
+TEST(math_vec_types, Divide)
+{
+ float2 a(1.0f, 2.0f);
+ float2 b(0.5f, 2.0f);
+ float2 result = a / b;
+ EXPECT_FLOAT_EQ(result.x, 2.0f);
+ EXPECT_FLOAT_EQ(result.y, 1.0f);
+}
+
+TEST(math_vec_types, DivideFloatByVector)
+{
+ float a = 2.0f;
+ float2 b(0.5f, 2.0f);
+ float2 result = a / b;
+ EXPECT_FLOAT_EQ(result.x, 4.0f);
+ EXPECT_FLOAT_EQ(result.y, 1.0f);
+}
+
+TEST(math_vec_types, DivideFloatByVectorSmall)
+{
+ float2 result = 2.0f / float2(2.0f);
+ EXPECT_FLOAT_EQ(result.x, 1.0f);
+ EXPECT_FLOAT_EQ(result.y, 1.0f);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
index 993434ddeba..ab716e5d011 100644
--- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc
+++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
@@ -176,4 +176,29 @@ static_assert(!is_same_any_v<int, float, bool>);
static_assert(!is_same_any_v<int, float>);
static_assert(!is_same_any_v<int>);
+TEST(memory_utils, ScopedDefer1)
+{
+ int a = 0;
+ {
+ BLI_SCOPED_DEFER([&]() { a -= 5; });
+ {
+ BLI_SCOPED_DEFER([&]() { a *= 10; });
+ a = 5;
+ }
+ }
+ EXPECT_EQ(a, 45);
+}
+
+TEST(memory_utils, ScopedDefer2)
+{
+ std::string s;
+ {
+ BLI_SCOPED_DEFER([&]() { s += "A"; });
+ BLI_SCOPED_DEFER([&]() { s += "B"; });
+ BLI_SCOPED_DEFER([&]() { s += "C"; });
+ BLI_SCOPED_DEFER([&]() { s += "D"; });
+ }
+ EXPECT_EQ(s, "DCBA");
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc
index 35fb22b3257..0bd34250deb 100644
--- a/source/blender/blenlib/tests/BLI_span_test.cc
+++ b/source/blender/blenlib/tests/BLI_span_test.cc
@@ -247,6 +247,8 @@ TEST(span, FirstLast)
Span<int> a_span(a);
EXPECT_EQ(a_span.first(), 6);
EXPECT_EQ(a_span.last(), 9);
+ EXPECT_EQ(a_span.last(1), 8);
+ EXPECT_EQ(a_span.last(2), 7);
}
TEST(span, FirstLast_OneElement)
@@ -255,6 +257,7 @@ TEST(span, FirstLast_OneElement)
Span<int> a_span(&a, 1);
EXPECT_EQ(a_span.first(), 3);
EXPECT_EQ(a_span.last(), 3);
+ EXPECT_EQ(a_span.last(0), 3);
}
TEST(span, Get)
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 40cda20c395..29b6d2b41fe 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -447,6 +447,9 @@ TEST(vector, Last)
{
Vector<int> a{3, 5, 7};
EXPECT_EQ(a.last(), 7);
+ EXPECT_EQ(a.last(0), 7);
+ EXPECT_EQ(a.last(1), 5);
+ EXPECT_EQ(a.last(2), 3);
}
TEST(vector, AppendNTimes)
diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c
index 10150f56098..e1527201e22 100644
--- a/source/blender/blenloader/intern/blend_validate.c
+++ b/source/blender/blenloader/intern/blend_validate.c
@@ -181,5 +181,20 @@ bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports)
BKE_main_unlock(bmain);
+ /* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here.
+ */
+ LISTBASE_FOREACH_MUTABLE (Key *, shapekey, &bmain->shapekeys) {
+ if (shapekey->from != NULL) {
+ continue;
+ }
+
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted",
+ shapekey->id.name,
+ shapekey->from);
+ BKE_id_delete(bmain, shapekey);
+ }
+
return is_valid;
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 84625fea6fc..9539436cf69 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -320,15 +320,22 @@ static void oldnewmap_increase_size(OldNewMap *onm)
/* Public OldNewMap API */
-static OldNewMap *oldnewmap_new(void)
+static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp)
{
- OldNewMap *onm = MEM_callocN(sizeof(*onm), "OldNewMap");
+ memset(onm, 0x0, sizeof(*onm));
- onm->capacity_exp = DEFAULT_SIZE_EXP;
+ onm->capacity_exp = capacity_exp;
onm->entries = MEM_malloc_arrayN(
ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries");
onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map");
oldnewmap_clear_map(onm);
+}
+
+static OldNewMap *oldnewmap_new(void)
+{
+ OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap");
+
+ oldnewmap_init_data(onm, DEFAULT_SIZE_EXP);
return onm;
}
@@ -395,9 +402,10 @@ static void oldnewmap_clear(OldNewMap *onm)
}
}
- onm->capacity_exp = DEFAULT_SIZE_EXP;
- oldnewmap_clear_map(onm);
- onm->nentries = 0;
+ MEM_freeN(onm->entries);
+ MEM_freeN(onm->map);
+
+ oldnewmap_init_data(onm, DEFAULT_SIZE_EXP);
}
static void oldnewmap_free(OldNewMap *onm)
@@ -2913,7 +2921,7 @@ static const char *dataname(short id_code)
return "Data from MA";
case ID_TE:
return "Data from TE";
- case ID_CU:
+ case ID_CU_LEGACY:
return "Data from CU";
case ID_GR:
return "Data from GR";
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 6bf3402eafe..ac59e3efc72 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -1644,7 +1644,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
BLI_addtail((ListBase *)&ob->modifiers, lmd);
ob->partype = PAROBJECT;
}
- else if (parent->type == OB_CURVE && ob->partype == PARCURVE) {
+ else if (parent->type == OB_CURVES_LEGACY && ob->partype == PARCURVE) {
CurveModifierData *cmd;
cmd = (CurveModifierData *)BKE_modifier_new(eModifierType_Curve);
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index f14029d6555..2a840ea585a 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -2544,6 +2544,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 301, 7) ||
+ (bmain->versionfile == 302 && !MAIN_VERSION_ATLEAST(bmain, 302, 4))) {
+ /* Duplicate value for two flags that mistakenly had the same numeric value. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_WeightVGProximity) {
+ WeightVGProximityModifierData *wpmd = (WeightVGProximityModifierData *)md;
+ if (wpmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) {
+ wpmd->proximity_flags |= MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE;
+ }
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc
index 6aac76642d5..281769410bd 100644
--- a/source/blender/blenloader/intern/versioning_common.cc
+++ b/source/blender/blenloader/intern/versioning_common.cc
@@ -56,7 +56,7 @@ ID *do_versions_rename_id(Main *bmain,
ListBase *lb = which_libbase(bmain, id_type);
ID *id = nullptr;
LISTBASE_FOREACH (ID *, idtest, lb) {
- if (idtest->lib == nullptr) {
+ if (!ID_IS_LINKED(idtest)) {
if (STREQ(idtest->name + 2, name_src)) {
id = idtest;
}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index cd4efa95d5e..2908b2b151b 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -1356,7 +1356,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
bFollowPathConstraint *data = con->data;
Object *obc = blo_do_versions_newlibadr(fd, lib, data->tar);
- if (obc && obc->type == OB_CURVE) {
+ if (obc && obc->type == OB_CURVES_LEGACY) {
Curve *cu = blo_do_versions_newlibadr(fd, lib, obc->data);
if (cu) {
cu->flag |= CU_PATH;
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 8685a0fa62d..e933964221b 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -33,6 +33,7 @@
#include "readfile.h" /* Own include. */
+#include "WM_types.h"
#include "wm_event_types.h"
/* Don't use translation strings in versioning!
@@ -363,10 +364,12 @@ static void do_version_select_mouse(UserDef *userdef, wmKeyMapItem *kmi)
kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE;
break;
case EVT_TWEAK_S:
- kmi->type = (left) ? EVT_TWEAK_L : EVT_TWEAK_R;
+ kmi->type = (left) ? LEFTMOUSE : RIGHTMOUSE;
+ kmi->val = KM_CLICK_DRAG;
break;
case EVT_TWEAK_A:
- kmi->type = (left) ? EVT_TWEAK_R : EVT_TWEAK_L;
+ kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE;
+ kmi->val = KM_CLICK_DRAG;
break;
default:
break;
@@ -385,6 +388,39 @@ static bool keymap_item_has_invalid_wm_context_data_path(wmKeyMapItem *kmi,
return false;
}
+static bool keymap_item_update_tweak_event(wmKeyMapItem *kmi, void *UNUSED(user_data))
+{
+ /* Tweak events for L M R mouse-buttons. */
+ enum {
+ EVT_TWEAK_L = 0x5002,
+ EVT_TWEAK_M = 0x5003,
+ EVT_TWEAK_R = 0x5004,
+ };
+ switch (kmi->type) {
+ case EVT_TWEAK_L:
+ kmi->type = LEFTMOUSE;
+ break;
+ case EVT_TWEAK_M:
+ kmi->type = MIDDLEMOUSE;
+ break;
+ case EVT_TWEAK_R:
+ kmi->type = RIGHTMOUSE;
+ break;
+ default:
+ kmi->direction = KM_ANY;
+ return false;
+ }
+
+ if (kmi->val >= KM_DIRECTION_N && kmi->val <= KM_DIRECTION_NW) {
+ kmi->direction = kmi->val;
+ }
+ else {
+ kmi->direction = KM_ANY;
+ }
+ kmi->val = KM_CLICK_DRAG;
+ return false;
+}
+
void blo_do_versions_userdef(UserDef *userdef)
{
/* #UserDef & #Main happen to have the same struct member. */
@@ -948,6 +984,20 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
+ if (!USER_VERSION_ATLEAST(300, 43)) {
+ userdef->ndof_flag |= NDOF_CAMERA_PAN_ZOOM;
+ }
+
+ if (!USER_VERSION_ATLEAST(302, 5)) {
+ BKE_keyconfig_pref_filter_items(userdef,
+ &((struct wmKeyConfigFilterItemParams){
+ .check_item = true,
+ .check_diff_item_add = true,
+ }),
+ keymap_item_update_tweak_event,
+ NULL);
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 495054923f9..490328106ca 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -580,7 +580,7 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren
/**
* END the mywrite wrapper
- * \return 1 if write failed
+ * \return True if write failed
* \return unknown global variable otherwise
* \warning Talks to other functions with global parameters
*/
@@ -1256,12 +1256,12 @@ static bool do_history(const char *name, ReportList *reports)
int hisnr = U.versions;
if (U.versions == 0) {
- return 0;
+ return false;
}
if (strlen(name) < 2) {
BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short");
- return 1;
+ return true;
}
while (hisnr > 1) {
@@ -1287,7 +1287,7 @@ static bool do_history(const char *name, ReportList *reports)
}
}
- return 0;
+ return false;
}
/** \} */
@@ -1334,7 +1334,7 @@ bool BLO_write_file(Main *mainvar,
if (ww.open(&ww, tempname) == false) {
BKE_reportf(
reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno));
- return 0;
+ return false;
}
if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
@@ -1426,7 +1426,7 @@ bool BLO_write_file(Main *mainvar,
BKE_report(reports, RPT_ERROR, strerror(errno));
remove(tempname);
- return 0;
+ return false;
}
/* file save to temporary file was successful */
@@ -1435,13 +1435,13 @@ bool BLO_write_file(Main *mainvar,
const bool err_hist = do_history(filepath, reports);
if (err_hist) {
BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)");
- return 0;
+ return false;
}
}
if (BLI_rename(tempname, filepath) != 0) {
BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)");
- return 0;
+ return false;
}
if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
@@ -1449,7 +1449,7 @@ bool BLO_write_file(Main *mainvar,
BLO_main_validate_libraries(mainvar, reports);
}
- return 1;
+ return true;
}
bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index a7873d5d2d0..ebb0f604df7 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -92,7 +92,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_CAMERA "Camera"
#define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile"
#define BLT_I18NCONTEXT_ID_COLLECTION "Collection"
-#define BLT_I18NCONTEXT_ID_CURVE "Curve"
+#define BLT_I18NCONTEXT_ID_CURVE_LEGACY "Curve"
#define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
#define BLT_I18NCONTEXT_ID_GPENCIL "GPencil"
#define BLT_I18NCONTEXT_ID_CURVES "Curves"
@@ -129,6 +129,9 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip"
#define BLT_I18NCONTEXT_ID_MASK "Mask"
+/* Editors-types contexts. */
+#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D"
+
/* Helper for bpy.app.i18n object... */
typedef struct {
const char *c_id;
@@ -154,7 +157,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_COLLECTION, "id_collection"), \
- BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "id_curve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVES, "id_curves"), \
@@ -191,6 +194,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORLD, "id_world"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORKSPACE, "id_workspace"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \
{ \
NULL, NULL, NULL \
} \
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 77b0781bcb6..31492cd5c13 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -9,6 +9,7 @@ set(INC
../depsgraph
../makesdna
../../../intern/atomic
+ ../../../intern/clog
../../../intern/eigen
../../../intern/guardedalloc
../../../extern/rangetree
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
index ae6b4da6003..ecdc86d31cf 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
@@ -54,6 +54,19 @@
*
* This has the effect from the users POV of leaving the mesh un-touched,
* and only editing the active shape key-block.
+ *
+ * \subsection other_notes Other Notes
+ *
+ * Other details noted here which might not be so obvious:
+ *
+ * - The #CD_SHAPEKEY layer is only used in edit-mode,
+ * and the #Mesh.key is only used in object-mode.
+ * Although the #CD_SHAPEKEY custom-data layer is converted into #Key data-blocks for each
+ * undo-step while in edit-mode.
+ * - The #CD_SHAPE_KEYINDEX layer is used to check if vertices existed when entering edit-mode.
+ * Values of the indices are only used for shape-keys when the #CD_SHAPEKEY layer can't be found,
+ * allowing coordinates from the #Key to be used to prevent data-loss.
+ * These indices are also used to maintain correct indices for hook modifiers and vertex parents.
*/
#include "DNA_key_types.h"
@@ -84,6 +97,10 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* For element checking. */
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"bmesh.mesh.convert"};
+
using blender::Array;
using blender::IndexRange;
using blender::Span;
@@ -195,10 +212,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (!me || !me->totvert) {
if (me && is_new) { /* No verts? still copy custom-data layout. */
- CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, 0);
- CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_ASSIGN, 0);
- CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_ASSIGN, 0);
- CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_ASSIGN, 0);
+ CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0);
+ CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@@ -553,8 +570,89 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
return vertMap;
}
+/* -------------------------------------------------------------------- */
+/** \name Edit-Mesh to Shape Key Conversion
+ *
+ * There are some details relating to using data from shape keys that need to be
+ * considered carefully for shape key synchronization logic.
+ *
+ * Key Block Usage
+ * ***************
+ *
+ * Key blocks (data in #Mesh.key must be used carefully).
+ *
+ * They can be used to query which key blocks are relative to the basis
+ * since it's not possible to add/remove/reorder key blocks while in edit-mode.
+ *
+ * Key Block Coordinates
+ * =====================
+ *
+ * Key blocks locations must *not* be used. This was done from v2.67 to 3.0,
+ * causing bugs T35170 & T44415.
+ *
+ * Shape key synchronizing could work under the assumption that the key-block is
+ * fixed-in-place when entering edit-mode allowing them to be used as a reference when exiting.
+ * It often does work but isn't reliable since for e.g. rendering may flush changes
+ * from the edit-mesh to the key-block (there are a handful of other situations where
+ * changes may be flushed, see #ED_editors_flush_edits and related functions).
+ * When using undo, it's not known if the data in key-block is from the past or future,
+ * so just don't use this data as it causes pain and suffering for users and developers alike.
+ *
+ * Instead, use the shape-key values stored in #CD_SHAPEKEY since they are reliably
+ * based on the original locations, unless explicitly manipulated.
+ * It's important to write the final shape-key values back to the #CD_SHAPEKEY so applying
+ * the difference between the original-basis and the new coordinates isn't done multiple times.
+ * Therefore #ED_editors_flush_edits and other flushing calls will update both the #Mesh.key
+ * and the edit-mode #CD_SHAPEKEY custom-data layers.
+ *
+ * WARNING: There is an exception to the rule of ignoring coordinates in the destination:
+ * that is when shape-key data in `bm` can't be found (which is itself an error/exception).
+ * In this case our own rule is violated as the alternative is loosing the shape-data entirely.
+ *
+ * Flushing Coordinates Back to the #BMesh
+ * ---------------------------------------
+ *
+ * The edit-mesh may be flushed back to the #Mesh and #Key used to generate it.
+ * When this is done, the new values are written back to the #BMesh's #CD_SHAPEKEY as well.
+ * This is necessary when editing basis-shapes so the difference in shape keys
+ * is not applied multiple times. If it were important to avoid it could be skipped while
+ * exiting edit-mode (as the entire #BMesh is freed in that case), however it's just copying
+ * back a `float[3]` so the work to check if it's necessary isn't worth the overhead.
+ *
+ * In general updating the #BMesh's #CD_SHAPEKEY makes shake-key logic easier to reason about
+ * since it means flushing data back to the mesh has the same behavior as exiting and entering
+ * edit-mode (a more common operation). Meaning there is one less corner-case to have to consider.
+ *
+ * Exceptional Cases
+ * *****************
+ *
+ * There are some situations that should not happen in typical usage but are
+ * still handled in this code, since failure to handle them could loose user-data.
+ * These could be investigated further since if they never happen in practice,
+ * we might consider removing them. However, the possibility of an mesh directly
+ * being modified by Python or some other low level logic that changes key-blocks
+ * means there is a potential this to happen so keeping code to these cases remain supported.
+ *
+ * - Custom Data & Mesh Key Block Synchronization.
+ * Key blocks in `me->key->block` should always have an associated
+ * #CD_SHAPEKEY layer in `bm->vdata`.
+ * If they don't there are two fall-backs for setting the location,
+ * - Use the value from the original shape key
+ * WARNING: this is technically incorrect! (see note on "Key Block Usage").
+ * - Use the current vertex location,
+ * Also not correct but it's better then having it zeroed for e.g.
+ *
+ * - Missing key-index layer.
+ * In this case the basis key wont apply it's deltas to other keys and in the case
+ * a shape-key layer is missing, its coordinates will be initialized from the edit-mesh
+ * vertex locations instead of attempting to remap the shape-keys coordinates.
+ *
+ * \note These cases are considered abnormal and shouldn't occur in typical usage.
+ * A warning is logged in this case to help troubleshooting bugs with shape-keys.
+ * \{ */
+
/**
- * Returns custom-data shapekey index from a keyblock or -1
+ * Returns custom-data shape-key index from a key-block or -1
* \note could split this out into a more generic function.
*/
static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
@@ -573,6 +671,196 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
return -1;
}
+/**
+ * Update `key` with shape key data stored in `bm`.
+ *
+ * \param bm: The source BMesh.
+ * \param key: The destination key.
+ * \param mvert: The destination vertex array (in some situations it's coordinates are updated).
+ */
+static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert)
+{
+ KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1));
+
+ /* It's unlikely this ever remains false, check for correctness. */
+ bool actkey_has_layer = false;
+
+ /* Go through and find any shape-key custom-data layers
+ * that might not have corresponding KeyBlocks, and add them if necessary. */
+ for (int i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY) {
+ continue;
+ }
+
+ KeyBlock *currkey;
+ for (currkey = (KeyBlock *)key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ break;
+ }
+ }
+
+ if (currkey) {
+ if (currkey == actkey) {
+ actkey_has_layer = true;
+ }
+ }
+ else {
+ currkey = BKE_keyblock_add(key, bm->vdata.layers[i].name);
+ currkey->uid = bm->vdata.layers[i].uid;
+ }
+ }
+
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ BMIter iter;
+ BMVert *eve;
+ float(*ofs)[3] = nullptr;
+
+ /* Editing the basis key updates others. */
+ if ((key->type == KEY_RELATIVE) &&
+ /* The shape-key coordinates used from entering edit-mode are used. */
+ (actkey_has_layer == true) &&
+ /* Original key-indices are only used to check the vertex existed when entering edit-mode. */
+ (cd_shape_keyindex_offset != -1) &&
+ /* Offsets are only needed if the current shape is a basis for others. */
+ BKE_keyblock_is_basis(key, bm->shapenr - 1)) {
+
+ BLI_assert(actkey != nullptr); /* Assured by `actkey_has_layer` check. */
+ const int actkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, actkey);
+
+ /* Since `actkey_has_layer == true`, this must never fail. */
+ BLI_assert(actkey_uuid != -1);
+
+ const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, actkey_uuid);
+
+ ofs = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float[3]) * bm->totvert, __func__));
+ int i;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ /* Check the vertex existed when entering edit-mode (otherwise don't apply an offset). */
+ if (keyi != ORIGINDEX_NONE) {
+ float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
+ /* Could use 'eve->co' or the destination #MVert.co, they're the same at this point. */
+ sub_v3_v3v3(ofs[i], eve->co, co_orig);
+ }
+ else {
+ /* If there are new vertices in the mesh, we can't propagate the offset
+ * because it will only work for the existing vertices and not the new
+ * ones, creating a mess when doing e.g. subdivide + translate. */
+ MEM_freeN(ofs);
+ ofs = nullptr;
+ break;
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (KeyBlock *, currkey, &key->block) {
+ int keyi;
+ float(*currkey_data)[3];
+
+ const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
+ const int cd_shape_offset = (currkey_uuid == -1) ?
+ -1 :
+ CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid);
+
+ /* Common case, the layer data is available, use it where possible. */
+ if (cd_shape_offset != -1) {
+ const bool apply_offset = (ofs != nullptr) && (currkey != actkey) &&
+ (bm->shapenr - 1 == currkey->relative);
+
+ if (currkey->data && (currkey->totelem == bm->totvert)) {
+ /* Use memory in-place. */
+ }
+ else {
+ currkey->data = MEM_reallocN(currkey->data, key->elemsize * bm->totvert);
+ currkey->totelem = bm->totvert;
+ }
+ currkey_data = (float(*)[3])currkey->data;
+
+ int i;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
+
+ if (currkey == actkey) {
+ copy_v3_v3(currkey_data[i], eve->co);
+
+ if (actkey != key->refkey) {
+ /* Without this, the real mesh coordinates (uneditable) as soon as you create
+ * the Basis shape, see: T30771 for details. */
+ if (cd_shape_keyindex_offset != -1) {
+ keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if (keyi != ORIGINDEX_NONE) {
+ copy_v3_v3(mvert[i].co, co_orig);
+ }
+ }
+ }
+ }
+ else {
+ copy_v3_v3(currkey_data[i], co_orig);
+ }
+
+ /* Propagate edited basis offsets to other shapes. */
+ if (apply_offset) {
+ add_v3_v3(currkey_data[i], ofs[i]);
+ }
+
+ /* Apply back new coordinates shape-keys that have offset into #BMesh.
+ * Otherwise, in case we call again #BM_mesh_bm_to_me on same #BMesh,
+ * we'll apply diff from previous call to #BM_mesh_bm_to_me,
+ * to shape-key values from original creation of the #BMesh. See T50524. */
+ copy_v3_v3(co_orig, currkey_data[i]);
+ }
+ }
+ else {
+ /* No original layer data, use fallback information. */
+ if (currkey->data && (cd_shape_keyindex_offset != -1)) {
+ CLOG_WARN(&LOG,
+ "Found shape-key but no CD_SHAPEKEY layers to read from, "
+ "using existing shake-key data where possible");
+ }
+ else {
+ CLOG_WARN(&LOG,
+ "Found shape-key but no CD_SHAPEKEY layers to read from, "
+ "using basis shape-key data");
+ }
+
+ currkey_data = static_cast<float(*)[3]>(
+ MEM_mallocN(key->elemsize * bm->totvert, "currkey->data"));
+
+ int i;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+
+ if ((currkey->data != nullptr) && (cd_shape_keyindex_offset != -1) &&
+ ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
+ (keyi < currkey->totelem)) {
+ /* Reconstruct keys via vertices original key indices.
+ * WARNING(@campbellbarton): `currkey->data` is known to be unreliable as the edit-mesh
+ * coordinates may be flushed back to the shape-key when exporting or rendering.
+ * This is a last resort! If this branch is running as part of regular usage
+ * it can be considered a bug. */
+ const float(*oldkey)[3] = static_cast<const float(*)[3]>(currkey->data);
+ copy_v3_v3(currkey_data[i], oldkey[keyi]);
+ }
+ else {
+ /* Fail! fill in with dummy value. */
+ copy_v3_v3(currkey_data[i], eve->co);
+ }
+ }
+
+ currkey->totelem = bm->totvert;
+ if (currkey->data) {
+ MEM_freeN(currkey->data);
+ }
+ currkey->data = currkey_data;
+ }
+ }
+
+ if (ofs) {
+ MEM_freeN(ofs);
+ }
+}
+
+/** \} */
+
BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
{
/* This is a cheap way to set the edge draw, its not precise and will
@@ -604,23 +892,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
- MVert *oldverts = nullptr;
const int ototvert = me->totvert;
- if (me->key && (cd_shape_keyindex_offset != -1)) {
- /* Keep the old verts in case we are working on* a key, which is done at the end. */
-
- /* Use the array in-place instead of duplicating the array. */
-#if 0
- oldverts = MEM_dupallocN(me->mvert);
-#else
- oldverts = me->mvert;
- me->mvert = nullptr;
- CustomData_update_typemap(&me->vdata);
- CustomData_set_layer(&me->vdata, CD_MVERT, nullptr);
-#endif
- }
-
/* Free custom data. */
CustomData_free(&me->vdata, me->totvert);
CustomData_free(&me->edata, me->totedge);
@@ -661,9 +934,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
- /* There is no way to tell if BMesh normals are dirty or not. Instead of calculating the normals
- * on the BMesh possibly unnecessarily, just tag them dirty on the resulting mesh. */
- BKE_mesh_normals_tag_dirty(me);
+ /* Clear normals on the mesh completely, since the original vertex and polygon count might be
+ * different than the BMesh's. */
+ BKE_mesh_clear_derived_normals(me);
me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);
@@ -846,152 +1119,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
- /* See comment below, this logic is in twice. */
-
if (me->key) {
- KeyBlock *currkey;
- KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1));
-
- float(*ofs)[3] = nullptr;
-
- /* Go through and find any shape-key custom-data layers
- * that might not have corresponding KeyBlocks, and add them if necessary. */
- for (i = 0; i < bm->vdata.totlayer; i++) {
- if (bm->vdata.layers[i].type != CD_SHAPEKEY) {
- continue;
- }
-
- for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) {
- if (currkey->uid == bm->vdata.layers[i].uid) {
- break;
- }
- }
-
- if (!currkey) {
- currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name);
- currkey->uid = bm->vdata.layers[i].uid;
- }
- }
-
- /* Editing the base key should update others. */
- if (/* Only need offsets for relative shape keys. */
- (me->key->type == KEY_RELATIVE) &&
-
- /* Unlikely, but the active key may not be valid if the
- * BMesh and the mesh are out of sync. */
- (actkey != nullptr) &&
-
- /* Not used here, but 'oldverts' is used later for applying 'ofs'. */
- (oldverts != nullptr) &&
-
- /* Needed for referencing oldverts. */
- (cd_shape_keyindex_offset != -1)) {
-
- const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1);
-
- /* Active key is a base. */
- if (act_is_basis) {
- const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data);
-
- ofs = static_cast<float(*)[3]>(
- MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"));
- mvert = me->mvert;
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
- const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
-
- /* Could use 'eve->co' or 'mvert->co', they're the same at this point. */
- if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) {
- sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]);
- }
- else {
- /* If there are new vertices in the mesh, we can't propagate the offset
- * because it will only work for the existing vertices and not the new
- * ones, creating a mess when doing e.g. subdivide + translate. */
- MEM_freeN(ofs);
- ofs = nullptr;
- break;
- }
-
- mvert++;
- }
- }
- }
-
- LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) {
- int keyi;
- const float(*ofs_pt)[3] = ofs;
- float *newkey, (*oldkey)[3], *fp;
-
- const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
- const int cd_shape_offset = (currkey_uuid == -1) ? -1 :
- CustomData_get_n_offset(&bm->vdata,
- CD_SHAPEKEY,
- currkey_uuid);
- const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) &&
- (currkey != actkey) && (bm->shapenr - 1 == currkey->relative);
-
- fp = newkey = static_cast<float *>(
- MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"));
- oldkey = static_cast<float(*)[3]>(currkey->data);
-
- mvert = me->mvert;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
-
- if (currkey == actkey) {
- copy_v3_v3(fp, eve->co);
-
- if (actkey != me->key->refkey) { /* Important see bug T30771. */
- if (cd_shape_keyindex_offset != -1) {
- if (oldverts) {
- keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
- if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */
- copy_v3_v3(mvert->co, oldverts[keyi].co);
- }
- }
- }
- }
- }
- else if (cd_shape_offset != -1) {
- /* In most cases this runs. */
- copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
- }
- else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) &&
- ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
- (keyi < currkey->totelem)) {
- /* Old method of reconstructing keys via vertices original key indices,
- * currently used if the new method above fails
- * (which is theoretically possible in certain cases of undo). */
- copy_v3_v3(fp, oldkey[keyi]);
- }
- else {
- /* Fail! fill in with dummy value. */
- copy_v3_v3(fp, mvert->co);
- }
-
- /* Propagate edited basis offsets to other shapes. */
- if (apply_offset) {
- add_v3_v3(fp, *ofs_pt++);
- /* Apply back new coordinates shape-keys that have offset into BMesh.
- * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh,
- * we'll apply diff from previous call to #BM_mesh_bm_to_me,
- * to shape-key values from *original creation of the BMesh*. See T50524. */
- copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp);
- }
-
- fp += 3;
- mvert++;
- }
-
- currkey->totelem = bm->totvert;
- if (currkey->data) {
- MEM_freeN(currkey->data);
- }
- currkey->data = newkey;
- }
-
- if (ofs) {
- MEM_freeN(ofs);
- }
+ bm_to_mesh_shape(bm, me->key, me->mvert);
}
/* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */
@@ -1005,10 +1134,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
- if (oldverts != nullptr) {
- MEM_freeN(oldverts);
- }
-
/* Topology could be changed, ensure #CD_MDISPS are ok. */
multires_topology_changed(me);
@@ -1028,10 +1153,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
me->totloop = bm->totloop;
me->totpoly = bm->totface;
- CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totvert);
- CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totedge);
- CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totface);
-
CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert);
CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge);
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop);
@@ -1059,22 +1180,18 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
MEdge *medge = me->medge;
MLoop *mloop = me->mloop;
MPoly *mpoly = me->mpoly;
- int *index, add_orig;
unsigned int i, j;
const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
- BKE_mesh_normals_tag_dirty(me);
+ /* Clear normals on the mesh completely, since the original vertex and polygon count might be
+ * different than the BMesh's. */
+ BKE_mesh_clear_derived_normals(me);
me->runtime.deformed_only = true;
- /* Don't add origindex layer if one already exists. */
- add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX);
-
- index = (int *)CustomData_get_layer(&me->vdata, CD_ORIGINDEX);
-
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
MVert *mv = &mvert[i];
@@ -1088,15 +1205,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset);
}
- if (add_orig) {
- *index++ = i;
- }
-
CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i);
}
bm->elem_index_dirty &= ~BM_VERT;
- index = (int *)CustomData_get_layer(&me->edata, CD_ORIGINDEX);
BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) {
MEdge *med = &medge[i];
@@ -1123,13 +1235,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
}
CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i);
- if (add_orig) {
- *index++ = i;
- }
}
bm->elem_index_dirty &= ~BM_EDGE;
- index = (int *)CustomData_get_layer(&me->pdata, CD_ORIGINDEX);
j = 0;
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
BMLoop *l_iter;
@@ -1156,10 +1264,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
} while ((l_iter = l_iter->next) != l_first);
CustomData_from_bmesh_block(&bm->pdata, &me->pdata, efa->head.data, i);
-
- if (add_orig) {
- *index++ = i;
- }
}
bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c
index 45a42c29d84..1bc5b70f874 100644
--- a/source/blender/bmesh/intern/bmesh_query.c
+++ b/source/blender/bmesh/intern/bmesh_query.c
@@ -2234,7 +2234,9 @@ int BM_mesh_calc_face_groups(BMesh *bm,
MEM_freeN(stack);
/* reduce alloc to required size */
- group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
+ if (group_index_len != group_curr) {
+ group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
+ }
*r_group_index = group_index;
return group_curr;
@@ -2354,7 +2356,9 @@ int BM_mesh_calc_edge_groups(BMesh *bm,
MEM_freeN(stack);
/* reduce alloc to required size */
- group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
+ if (group_index_len != group_curr) {
+ group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
+ }
*r_group_index = group_index;
return group_curr;
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index e2e26c5c52b..10bdc2c7294 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -532,7 +532,7 @@ static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv)
/**
* Find the EdgeHalf representing the other end of e->e.
- * \return Return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have
+ * \return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have
* been constructed yet, in which case return NULL.
*/
static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert **r_bvother)
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index f72e81d82b9..23520364bf0 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -90,11 +90,10 @@ void CompositorOperation::deinit_execution()
re = nullptr;
}
+ Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result");
+ BKE_image_partial_update_mark_full_update(image);
BLI_thread_lock(LOCK_DRAW_IMAGE);
- BKE_image_signal(G.main,
- BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result"),
- nullptr,
- IMA_SIGNAL_FREE);
+ BKE_image_signal(G.main, image, nullptr, IMA_SIGNAL_FREE);
BLI_thread_unlock(LOCK_DRAW_IMAGE);
}
else {
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
index 1b23b0e001c..fa45034b00c 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
@@ -29,8 +29,10 @@ void GaussianBokehBlurOperation::init_data()
const float width = this->get_width();
const float height = this->get_height();
- if (!sizeavailable_) {
- update_size();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ if (!sizeavailable_) {
+ update_size();
+ }
}
radxf_ = size_ * (float)data_.sizex;
@@ -96,6 +98,22 @@ void GaussianBokehBlurOperation::update_gauss()
void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data)
{
+ float result[4];
+ input_size_->read_sampled(result, 0, 0, PixelSampler::Nearest);
+ size_ = result[0];
+
+ const float width = this->get_width();
+ const float height = this->get_height();
+
+ radxf_ = size_ * (float)data_.sizex;
+ CLAMP(radxf_, 0.0f, width / 2.0f);
+
+ radyf_ = size_ * (float)data_.sizey;
+ CLAMP(radyf_, 0.0f, height / 2.0f);
+
+ radx_ = ceil(radxf_);
+ rady_ = ceil(radyf_);
+
float temp_color[4];
temp_color[0] = 0;
temp_color[1] = 0;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 2a0d5ce9116..77597e0db06 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -335,16 +335,10 @@ void DepsgraphNodeBuilder::begin_build()
* same as id_orig. Additionally, such ID might have been removed, which makes the check
* for whether id_cow is expanded to access freed memory. In order to deal with this we
* check whether CoW is needed based on a scalar value which does not lead to access of
- * possibly deleted memory.
- * Additionally, this saves some space in the map by skipping mapping for datablocks which
- * do not need CoW, */
- if (!deg_copy_on_write_is_needed(id_node->id_type)) {
- id_node->id_cow = nullptr;
- continue;
- }
-
+ * possibly deleted memory. */
IDInfo *id_info = (IDInfo *)MEM_mallocN(sizeof(IDInfo), "depsgraph id info");
- if (deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) {
+ if (deg_copy_on_write_is_needed(id_node->id_type) &&
+ deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) {
id_info->id_cow = id_node->id_cow;
}
else {
@@ -580,7 +574,7 @@ void DepsgraphNodeBuilder::build_id(ID *id)
break;
case ID_ME:
case ID_MB:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_LT:
case ID_GD:
case ID_CV:
@@ -872,7 +866,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
/* type-specific data. */
switch (object->type) {
case OB_MESH:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT:
case OB_SURF:
case OB_MBALL:
@@ -1504,7 +1498,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
op_node->set_as_entry();
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
op_node = add_operation_node(obdata,
NodeType::GEOMETRY,
OperationCode::GEOMETRY_EVAL,
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 25d7a0a6ac2..faad053e30c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -221,7 +221,8 @@ OperationCode bone_target_opcode(ID *target,
bool object_have_geometry_component(const Object *object)
{
- return ELEM(object->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL);
+ return ELEM(
+ object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL);
}
} // namespace
@@ -537,7 +538,7 @@ void DepsgraphRelationBuilder::build_id(ID *id)
break;
case ID_ME:
case ID_MB:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_LT:
case ID_CV:
case ID_PT:
@@ -827,7 +828,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
/* type-specific data. */
switch (object->type) {
case OB_MESH:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT:
case OB_SURF:
case OB_MBALL:
@@ -983,7 +984,7 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object)
add_relation(parent_key, object_transform_key, "Lattice Deform Parent");
add_relation(geom_key, object_transform_key, "Lattice Deform Parent Geom");
}
- else if (object->parent->type == OB_CURVE) {
+ else if (object->parent->type == OB_CURVES_LEGACY) {
Curve *cu = (Curve *)object->parent->data;
if (cu->flag & CU_PATH) {
@@ -2040,7 +2041,7 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key)
* Therefore, each user of a piece of shared geometry data ends up evaluating
* its own version of the stuff, complete with whatever modifiers it may use.
*
- * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU, ID_LT.)
+ * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU_LEGACY, ID_LT.)
* are used for
* 1) calculating the bounding boxes of the geometry data,
* 2) aggregating inward links from other objects (e.g. for text on curve)
@@ -2125,7 +2126,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
/* Materials. */
build_materials(object->mat, object->totcol);
/* Geometry collision. */
- if (ELEM(object->type, OB_MESH, OB_CURVE, OB_LATTICE)) {
+ if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) {
// add geometry collider relations
}
/* Make sure uber update is the last in the dependencies. */
@@ -2220,7 +2221,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
case ID_MB:
break;
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)obdata;
if (cu->bevobj != nullptr) {
ComponentKey bevob_geom_key(&cu->bevobj->id, NodeType::GEOMETRY);
@@ -2362,8 +2363,9 @@ void DepsgraphRelationBuilder::build_light(Light *lamp)
/* light's nodetree */
if (lamp->nodetree != nullptr) {
build_nodetree(lamp->nodetree);
- ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::NTREE_OUTPUT);
- add_relation(nodetree_key, shading_key, "NTree->Light Parameters");
+ OperationKey ntree_key(
+ &lamp->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
+ add_relation(ntree_key, shading_key, "NTree->Light Parameters");
build_nested_nodetree(&lamp->id, lamp->nodetree);
}
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index b5968cbaeca..b8c85430f06 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -71,7 +71,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type)
bool is_selectable_data_id_type(const ID_Type id_type)
{
- return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO);
+ return ELEM(id_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO);
}
void depsgraph_select_tag_to_component_opcode(const ID *id,
@@ -332,7 +332,7 @@ void deg_graph_id_tag_legacy_compat(
}
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *curve = (Curve *)id;
if (curve->key != nullptr) {
ID *key_id = &curve->key->id;
@@ -483,6 +483,10 @@ void deg_graph_node_tag_zero(Main *bmain,
if (comp_node->type == NodeType::ANIMATION) {
continue;
}
+ else if (comp_node->type == NodeType::COPY_ON_WRITE) {
+ id_node->is_cow_explicitly_tagged = true;
+ }
+
comp_node->tag_update(graph, update_source);
}
deg_graph_id_tag_legacy_compat(bmain, graph, id, (IDRecalcFlag)0, update_source);
@@ -569,7 +573,7 @@ NodeType geometry_tag_to_component(const ID *id)
const Object *object = (Object *)id;
switch (object->type) {
case OB_MESH:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
case OB_LATTICE:
@@ -586,7 +590,7 @@ NodeType geometry_tag_to_component(const ID *id)
break;
}
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_LT:
case ID_MB:
case ID_CV:
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 646e4d6d6d6..6346bab1fe8 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
@@ -125,7 +125,7 @@ void nested_id_hack_discard_pointers(ID *id_cow)
SPECIAL_CASE(ID_WO, World, nodetree)
SPECIAL_CASE(ID_SIM, Simulation, nodetree)
- SPECIAL_CASE(ID_CU, Curve, key)
+ SPECIAL_CASE(ID_CU_LEGACY, Curve, key)
SPECIAL_CASE(ID_LT, Lattice, key)
SPECIAL_CASE(ID_ME, Mesh, key)
@@ -174,7 +174,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage
SPECIAL_CASE(ID_WO, World, nodetree, world)
SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation)
- SPECIAL_CASE(ID_CU, Curve, key, curve)
+ SPECIAL_CASE(ID_CU_LEGACY, Curve, key, curve)
SPECIAL_CASE(ID_LT, Lattice, key, lattice)
SPECIAL_CASE(ID_ME, Mesh, key, mesh)
@@ -214,7 +214,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id)
SPECIAL_CASE(ID_WO, World, nodetree)
SPECIAL_CASE(ID_SIM, Simulation, nodetree)
- SPECIAL_CASE(ID_CU, Curve, key)
+ SPECIAL_CASE(ID_CU_LEGACY, Curve, key)
SPECIAL_CASE(ID_LT, Lattice, key)
SPECIAL_CASE(ID_ME, Mesh, key)
@@ -252,7 +252,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow)
SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree)
SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree)
- SPECIAL_CASE(ID_CU, Curve, key, Key)
+ SPECIAL_CASE(ID_CU_LEGACY, Curve, key, Key)
SPECIAL_CASE(ID_LT, Lattice, key, Key)
SPECIAL_CASE(ID_ME, Mesh, key, Key)
@@ -578,7 +578,7 @@ void update_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_orig, ID
case ID_ME:
update_mesh_edit_mode_pointers(id_orig, id_cow);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
update_curve_edit_mode_pointers(depsgraph, id_orig, id_cow);
break;
case ID_MB:
@@ -953,7 +953,7 @@ void discard_edit_mode_pointers(ID *id_cow)
case ID_ME:
discard_mesh_edit_mode_pointers(id_cow);
break;
- case ID_CU:
+ case ID_CU_LEGACY:
discard_curve_edit_mode_pointers(id_cow);
break;
case ID_MB:
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
index 0992e242c7a..50012350036 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
@@ -81,7 +81,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
object->runtime = runtime;
object->runtime.data_orig = data_orig;
object->runtime.bb = bb;
- if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVE, OB_FONT) && data_eval != nullptr) {
+ if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT) && data_eval != nullptr) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* If geometry is tagged for update it means, that part of
* evaluated mesh are not valid anymore. In this case we can not
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 4103d9a7087..98f75ad6106 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -221,6 +221,8 @@ set(SRC
engines/image/image_space_image.hh
engines/image/image_space_node.hh
engines/image/image_space.hh
+ engines/image/image_texture_info.hh
+ engines/image/image_usage.hh
engines/image/image_wrappers.hh
engines/workbench/workbench_engine.h
engines/workbench/workbench_private.h
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index 58676caa6f9..253981d321b 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -27,25 +27,12 @@
static void eevee_motion_blur_mesh_data_free(void *val)
{
- EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
- EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val;
- switch (geom_mb->type) {
- case EEVEE_MOTION_DATA_HAIR:
- for (int j = 0; j < hair_mb->psys_len; j++) {
- for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
- GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]);
- }
- for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
- DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]);
- }
- }
- break;
-
- case EEVEE_MOTION_DATA_MESH:
- for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
- GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
- }
- break;
+ EEVEE_ObjectMotionData *mb_data = (EEVEE_ObjectMotionData *)val;
+ if (mb_data->hair_data != NULL) {
+ MEM_freeN(mb_data->hair_data);
+ }
+ if (mb_data->geometry_data != NULL) {
+ MEM_freeN(mb_data->geometry_data);
}
MEM_freeN(val);
}
@@ -84,39 +71,57 @@ static bool eevee_object_key_cmp(const void *a, const void *b)
return false;
}
+void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data)
+{
+ GPU_vertbuf_discard(step_data->hair_pos);
+ DRW_texture_free(step_data->hair_pos_tx);
+ MEM_freeN(step_data);
+}
+
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
{
if (mb->object == NULL) {
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
}
- if (mb->geom == NULL) {
- mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
+ for (int i = 0; i < 2; i++) {
+ if (mb->position_vbo_cache[i] == NULL) {
+ mb->position_vbo_cache[i] = BLI_ghash_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE duplicate vbo cache");
+ }
+ if (mb->hair_motion_step_cache[i] == NULL) {
+ mb->hair_motion_step_cache[i] = BLI_ghash_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE hair motion step cache");
+ }
}
}
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
{
if (mb->object) {
- BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
+ BLI_ghash_free(mb->object, MEM_freeN, eevee_motion_blur_mesh_data_free);
mb->object = NULL;
}
- if (mb->geom) {
- BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
- mb->geom = NULL;
+ for (int i = 0; i < 2; i++) {
+ if (mb->position_vbo_cache[i]) {
+ BLI_ghash_free(mb->position_vbo_cache[i], NULL, (GHashValFreeFP)GPU_vertbuf_discard);
+ }
+ if (mb->hair_motion_step_cache[i]) {
+ BLI_ghash_free(
+ mb->hair_motion_step_cache[i], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free);
+ }
}
}
-EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
- Object *ob,
- bool hair)
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob)
{
if (mb->object == NULL) {
return NULL;
}
EEVEE_ObjectKey key, *key_p;
- /* Small hack to avoid another comparison. */
- key.ob = (Object *)((char *)ob + hair);
+ /* Assumes that all instances have the same object pointer. This is currently the case because
+ * instance objects are temporary objects on the stack. */
+ key.ob = ob;
DupliObject *dup = DRW_object_get_dupli(ob);
if (dup) {
key.parent = DRW_object_get_dupli_parent(ob);
@@ -139,53 +144,28 @@ EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *
return ob_step;
}
-static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair)
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data)
{
- if (mb->geom == NULL) {
- return NULL;
+ if (mb_data->geometry_data == NULL) {
+ EEVEE_GeometryMotionData *geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
+ geom_step->type = EEVEE_MOTION_DATA_MESH;
+ mb_data->geometry_data = geom_step;
}
- DupliObject *dup = DRW_object_get_dupli(ob);
- void *key;
- if (dup) {
- key = dup->ob;
- }
- else {
- key = ob;
- }
- /* Only use data for object that have no modifiers. */
- if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) {
- key = ob->data;
- }
- key = (char *)key + (int)hair;
- EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
- if (geom_step == NULL) {
- if (hair) {
- EEVEE_HairMotionData *hair_step;
- /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
- int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
- hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
- __func__);
- hair_step->psys_len = psys_len;
- geom_step = (EEVEE_GeometryMotionData *)hair_step;
- geom_step->type = EEVEE_MOTION_DATA_HAIR;
- }
- else {
- geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
- geom_step->type = EEVEE_MOTION_DATA_MESH;
- }
- BLI_ghash_insert(mb->geom, key, geom_step);
- }
- return geom_step;
+ return mb_data->geometry_data;
}
-EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
+EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob)
{
- return motion_blur_deform_data_get(mb, ob, false);
-}
-
-EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob)
-{
- return motion_blur_deform_data_get(mb, ob, true);
+ if (mb_data->hair_data == NULL) {
+ /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
+ int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
+ EEVEE_HairMotionData *hair_step = MEM_callocN(
+ sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__);
+ hair_step->psys_len = psys_len;
+ hair_step->type = EEVEE_MOTION_DATA_HAIR;
+ mb_data->hair_data = hair_step;
+ }
+ return mb_data->hair_data;
}
/* View Layer data. */
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
index 39cfbb40318..ef4d88bd521 100644
--- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
@@ -626,11 +626,6 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl,
"dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL);
txl->dof_reduced_coc = GPU_texture_create_2d(
"dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL);
-
- /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation.
- */
- GPU_texture_generate_mipmap(txl->dof_reduced_color);
- GPU_texture_generate_mipmap(txl->dof_reduced_coc);
}
GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb,
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index 93ffa2be9f3..fbc19a01a8b 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -226,15 +226,14 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
/* For now we assume hair objects are always moving. */
- EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
- &effects->motion_blur, ob, true);
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]);
- EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob);
+ EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(mb_data, ob);
int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0;
if (psys_id >= mb_hair->psys_len) {
@@ -252,8 +251,8 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
}
- GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
- GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
+ GPUTexture *tex_prev = mb_hair->psys[psys_id].step_data[MB_PREV].hair_pos_tx;
+ GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx;
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
@@ -265,7 +264,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
else {
/* Store vertex position buffer. */
- mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
+ mb_hair->psys[psys_id].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md);
mb_hair->use_deform = true;
}
}
@@ -304,24 +303,14 @@ void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
return;
}
- const DupliObject *dup = DRW_object_get_dupli(ob);
- if (dup != NULL && dup->ob->data != dup->ob_data) {
- /* Geometry instances do not support motion blur correctly yet. The #key used in
- * #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into
- * account. Otherwise it can't find matching geometry instances at different points in time. */
- return;
- }
-
- EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
- &effects->motion_blur, ob, false);
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
- EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur,
- ob);
+ EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data);
if (mb_step == MB_CURR) {
GPUBatch *batch = DRW_cache_object_surface_get(ob);
@@ -407,86 +396,93 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
DRW_cache_restart();
}
- for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
- EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
- EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
-
- if (!mb_geom->use_deform) {
- continue;
- }
-
- switch (mb_geom->type) {
- case EEVEE_MOTION_DATA_HAIR:
- if (mb_step == MB_CURR) {
- /* TODO(fclem): Check if vertex count mismatch. */
- mb_hair->use_deform = true;
- }
- else {
- for (int i = 0; i < mb_hair->psys_len; i++) {
- if (mb_hair->psys[i].hair_pos[mb_step] == NULL) {
- continue;
- }
- mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate(
- mb_hair->psys[i].hair_pos[mb_step]);
-
+ EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
+ EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
+ EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
+ if (mb_hair != NULL && mb_hair->use_deform) {
+ if (mb_step == MB_CURR) {
+ /* TODO(fclem): Check if vertex count mismatch. */
+ mb_hair->use_deform = true;
+ }
+ else {
+ for (int i = 0; i < mb_hair->psys_len; i++) {
+ GPUVertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos;
+ if (vbo == NULL) {
+ continue;
+ }
+ EEVEE_HairMotionStepData **step_data_cache_ptr;
+ if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step],
+ vbo,
+ (void ***)&step_data_cache_ptr)) {
+ EEVEE_HairMotionStepData *new_step_data = MEM_callocN(sizeof(EEVEE_HairMotionStepData),
+ __func__);
+ /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
+ new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo);
/* Create vbo immediately to bind to texture buffer. */
- GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]);
-
- mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
- "hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]);
+ GPU_vertbuf_use(new_step_data->hair_pos);
+ new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur",
+ new_step_data->hair_pos);
+ *step_data_cache_ptr = new_step_data;
}
+ mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr;
}
- break;
-
- case EEVEE_MOTION_DATA_MESH:
- if (mb_step == MB_CURR) {
- /* Modify batch to have data from adjacent frames. */
- GPUBatch *batch = mb_geom->batch;
- for (int i = 0; i < MB_CURR; i++) {
- GPUVertBuf *vbo = mb_geom->vbo[i];
- if (vbo && batch) {
- if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
- /* Vertex count mismatch, disable deform motion blur. */
- mb_geom->use_deform = false;
- }
-
- if (mb_geom->use_deform == false) {
- motion_blur_remove_vbo_reference_from_batch(
- batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
-
- GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
- GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
- break;
- }
+ }
+ }
+ if (mb_geom != NULL && mb_geom->use_deform) {
+ if (mb_step == MB_CURR) {
+ /* Modify batch to have data from adjacent frames. */
+ GPUBatch *batch = mb_geom->batch;
+ for (int i = 0; i < MB_CURR; i++) {
+ GPUVertBuf *vbo = mb_geom->vbo[i];
+ if (vbo && batch) {
+ if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
+ /* Vertex count mismatch, disable deform motion blur. */
+ mb_geom->use_deform = false;
+ }
+ if (mb_geom->use_deform == false) {
+ motion_blur_remove_vbo_reference_from_batch(
+ batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
+ break;
+ }
+ /* Avoid adding the same vbo more than once when the batch is used by multiple
+ * instances. */
+ if (!GPU_batch_vertbuf_has(batch, vbo)) {
+ /* Currently, the code assumes that all objects that share the same mesh in the
+ * current frame also share the same mesh on other frames. */
GPU_batch_vertbuf_add_ex(batch, vbo, false);
}
}
}
- else {
- GPUVertBuf *vbo = mb_geom->vbo[mb_step];
- if (vbo) {
- /* Use the vbo to perform the copy on the GPU. */
- GPU_vertbuf_use(vbo);
- /* Perform a copy to avoid losing it after RE_engine_frame_set(). */
- mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
+ }
+ else {
+ GPUVertBuf *vbo = mb_geom->vbo[mb_step];
+ if (vbo) {
+ /* Use the vbo to perform the copy on the GPU. */
+ GPU_vertbuf_use(vbo);
+ /* Perform a copy to avoid losing it after RE_engine_frame_set(). */
+ GPUVertBuf **vbo_cache_ptr;
+ if (!BLI_ghash_ensure_p(effects->motion_blur.position_vbo_cache[mb_step],
+ vbo,
+ (void ***)&vbo_cache_ptr)) {
+ /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
+ GPUVertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo);
+ *vbo_cache_ptr = duplicated_vbo;
/* Find and replace "pos" attrib name. */
- GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
+ GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "pos");
GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
}
- else {
- /* This might happen if the object visibility has been animated. */
- mb_geom->use_deform = false;
- }
+ mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr;
}
- break;
-
- default:
- BLI_assert(0);
- break;
+ else {
+ /* This might happen if the object visibility has been animated. */
+ mb_geom->use_deform = false;
+ }
+ }
}
}
}
@@ -503,54 +499,62 @@ void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata)
/* Camera Data. */
effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT];
- /* Object Data. */
- for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
- BLI_ghashIterator_done(&ghi) == false;
- BLI_ghashIterator_step(&ghi)) {
- EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
-
- copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
+ /* Swap #position_vbo_cache pointers. */
+ if (effects->motion_blur.position_vbo_cache[MB_PREV]) {
+ BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV],
+ NULL,
+ (GHashValFreeFP)GPU_vertbuf_discard);
+ }
+ effects->motion_blur.position_vbo_cache[MB_PREV] =
+ effects->motion_blur.position_vbo_cache[MB_NEXT];
+ effects->motion_blur.position_vbo_cache[MB_NEXT] = NULL;
+
+ /* Swap #hair_motion_step_cache pointers. */
+ if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) {
+ BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV],
+ NULL,
+ (GHashValFreeFP)EEVEE_motion_hair_step_free);
}
+ effects->motion_blur.hair_motion_step_cache[MB_PREV] =
+ effects->motion_blur.hair_motion_step_cache[MB_NEXT];
+ effects->motion_blur.hair_motion_step_cache[MB_NEXT] = NULL;
- /* Deformation Data. */
- for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
- BLI_ghashIterator_done(&ghi) == false;
+ /* Rename attributes in #position_vbo_cache. */
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]);
+ !BLI_ghashIterator_done(&ghi);
BLI_ghashIterator_step(&ghi)) {
- EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
- EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
+ GPUVertBuf *vbo = BLI_ghashIterator_getValue(&ghi);
+ GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
+ int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
+ GPU_vertformat_attr_rename(format, attrib_id, "prv");
+ }
- switch (mb_geom->type) {
- case EEVEE_MOTION_DATA_HAIR:
- for (int i = 0; i < mb_hair->psys_len; i++) {
- GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]);
- DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]);
- mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT];
- mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT];
- mb_hair->psys[i].hair_pos[MB_NEXT] = NULL;
- mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL;
- }
- break;
+ /* Object Data. */
+ for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi);
+ BLI_ghashIterator_step(&ghi)) {
+ EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
+ EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
+ EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
- case EEVEE_MOTION_DATA_MESH:
- if (mb_geom->batch != NULL) {
- motion_blur_remove_vbo_reference_from_batch(
- mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
- }
- GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
- mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
- mb_geom->vbo[MB_NEXT] = NULL;
-
- if (mb_geom->vbo[MB_PREV]) {
- GPUVertBuf *vbo = mb_geom->vbo[MB_PREV];
- GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
- int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
- GPU_vertformat_attr_rename(format, attrib_id, "prv");
- }
- break;
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
- default:
- BLI_assert(0);
- break;
+ if (mb_hair != NULL) {
+ for (int i = 0; i < mb_hair->psys_len; i++) {
+ mb_hair->psys[i].step_data[MB_PREV].hair_pos =
+ mb_hair->psys[i].step_data[MB_NEXT].hair_pos;
+ mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx =
+ mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx;
+ mb_hair->psys[i].step_data[MB_NEXT].hair_pos = NULL;
+ mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = NULL;
+ }
+ }
+ if (mb_geom != NULL) {
+ if (mb_geom->batch != NULL) {
+ motion_blur_remove_vbo_reference_from_batch(
+ mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
+ }
+ mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
+ mb_geom->vbo[MB_NEXT] = NULL;
}
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index e8828cc7494..2518ee53da3 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -618,8 +618,23 @@ enum {
#define MB_CURR 2
typedef struct EEVEE_MotionBlurData {
+ /**
+ * Maps #EEVEE_ObjectKey to #EEVEE_ObjectMotionData.
+ */
struct GHash *object;
- struct GHash *geom;
+ /**
+ * Maps original #GPUVertBuf to duplicated #GPUVertBuf.
+ * There are two maps for #MB_PREV and #MB_NEXT.
+ * Only the values are owned.
+ */
+ struct GHash *position_vbo_cache[2];
+ /**
+ * Maps original #GPUVertBuf to #EEVEE_HairMotionStepData.
+ * There are two maps for #MB_PREV and #MB_NEXT.
+ * Only the values are owned.
+ */
+ struct GHash *hair_motion_step_cache[2];
+
struct {
float viewmat[4][4];
float persmat[4][4];
@@ -637,15 +652,16 @@ typedef struct EEVEE_ObjectKey {
int id[8]; /* MAX_DUPLI_RECUR */
} EEVEE_ObjectKey;
-typedef struct EEVEE_ObjectMotionData {
- float obmat[3][4][4];
-} EEVEE_ObjectMotionData;
-
typedef enum eEEVEEMotionData {
EEVEE_MOTION_DATA_MESH = 0,
EEVEE_MOTION_DATA_HAIR,
} eEEVEEMotionData;
+typedef struct EEVEE_HairMotionStepData {
+ struct GPUVertBuf *hair_pos;
+ struct GPUTexture *hair_pos_tx;
+} EEVEE_HairMotionStepData;
+
typedef struct EEVEE_HairMotionData {
/** Needs to be first to ensure casting. */
eEEVEEMotionData type;
@@ -653,8 +669,8 @@ typedef struct EEVEE_HairMotionData {
/** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */
int psys_len;
struct {
- struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */
- struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */
+ /* The vbos and textures are not owned. */
+ EEVEE_HairMotionStepData step_data[2]; /* Data for time = t +/- step. */
} psys[0];
} EEVEE_HairMotionData;
@@ -664,10 +680,18 @@ typedef struct EEVEE_GeometryMotionData {
/** To disable deform mb if vertcount mismatch. */
int use_deform;
+ /* The batch and vbos are not owned. */
struct GPUBatch *batch; /* Batch for time = t. */
struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */
} EEVEE_GeometryMotionData;
+typedef struct EEVEE_ObjectMotionData {
+ float obmat[3][4][4];
+
+ EEVEE_GeometryMotionData *geometry_data;
+ EEVEE_HairMotionData *hair_data;
+} EEVEE_ObjectMotionData;
+
/* ************ EFFECTS DATA ************* */
typedef enum EEVEE_EffectsFlag {
@@ -1062,17 +1086,15 @@ typedef struct EEVEE_PrivateData {
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb);
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb);
void EEVEE_view_layer_data_free(void *storage);
+void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data);
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
-EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
- Object *ob,
- bool hair);
-EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
- Object *ob);
-EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob);
+EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob);
+EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data);
+EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index b5e9a296c16..29d98f6795d 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -273,11 +273,9 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
/* Resize shcasters buffers if too big. */
if (frontbuffer->alloc_count - frontbuffer->count > SH_CASTER_ALLOC_CHUNK) {
- frontbuffer->alloc_count = (frontbuffer->count / SH_CASTER_ALLOC_CHUNK) *
+ frontbuffer->alloc_count = divide_ceil_u(max_ii(1, frontbuffer->count),
+ SH_CASTER_ALLOC_CHUNK) *
SH_CASTER_ALLOC_CHUNK;
- frontbuffer->alloc_count += (frontbuffer->count % SH_CASTER_ALLOC_CHUNK != 0) ?
- SH_CASTER_ALLOC_CHUNK :
- 0;
frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox,
sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count);
BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count);
diff --git a/source/blender/draw/engines/image/image_buffer_cache.hh b/source/blender/draw/engines/image/image_buffer_cache.hh
new file mode 100644
index 00000000000..ef11551c879
--- /dev/null
+++ b/source/blender/draw/engines/image/image_buffer_cache.hh
@@ -0,0 +1,131 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2022, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+#include "BLI_vector.hh"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+struct FloatImageBuffer {
+ ImBuf *source_buffer = nullptr;
+ ImBuf *float_buffer = nullptr;
+ bool is_used = true;
+
+ FloatImageBuffer(ImBuf *source_buffer, ImBuf *float_buffer)
+ : source_buffer(source_buffer), float_buffer(float_buffer)
+ {
+ }
+
+ FloatImageBuffer(FloatImageBuffer &&other) noexcept
+ {
+ source_buffer = other.source_buffer;
+ float_buffer = other.float_buffer;
+ is_used = other.is_used;
+ other.source_buffer = nullptr;
+ other.float_buffer = nullptr;
+ }
+
+ virtual ~FloatImageBuffer()
+ {
+ IMB_freeImBuf(float_buffer);
+ float_buffer = nullptr;
+ source_buffer = nullptr;
+ }
+
+ FloatImageBuffer &operator=(FloatImageBuffer &&other) noexcept
+ {
+ this->source_buffer = other.source_buffer;
+ this->float_buffer = other.float_buffer;
+ is_used = other.is_used;
+ other.source_buffer = nullptr;
+ other.float_buffer = nullptr;
+ return *this;
+ }
+};
+
+struct FloatBufferCache {
+ private:
+ blender::Vector<FloatImageBuffer> cache_;
+
+ public:
+ ImBuf *ensure_float_buffer(ImBuf *image_buffer)
+ {
+ /* Check if we can use the float buffer of the given image_buffer. */
+ if (image_buffer->rect_float != nullptr) {
+ return image_buffer;
+ }
+
+ /* Do we have a cached float buffer. */
+ for (FloatImageBuffer &item : cache_) {
+ if (item.source_buffer == image_buffer) {
+ item.is_used = true;
+ return item.float_buffer;
+ }
+ }
+
+ /* Generate a new float buffer. */
+ IMB_float_from_rect(image_buffer);
+ ImBuf *new_imbuf = IMB_allocImBuf(image_buffer->x, image_buffer->y, image_buffer->planes, 0);
+ new_imbuf->rect_float = image_buffer->rect_float;
+ new_imbuf->flags |= IB_rectfloat;
+ new_imbuf->mall |= IB_rectfloat;
+ image_buffer->rect_float = nullptr;
+ image_buffer->flags &= ~IB_rectfloat;
+ image_buffer->mall &= ~IB_rectfloat;
+
+ cache_.append(FloatImageBuffer(image_buffer, new_imbuf));
+ return new_imbuf;
+ }
+
+ void reset_usage_flags()
+ {
+ for (FloatImageBuffer &buffer : cache_) {
+ buffer.is_used = false;
+ }
+ }
+
+ void mark_used(const ImBuf *image_buffer)
+ {
+ for (FloatImageBuffer &item : cache_) {
+ if (item.source_buffer == image_buffer) {
+ item.is_used = true;
+ return;
+ }
+ }
+ }
+
+ void remove_unused_buffers()
+ {
+ for (int64_t i = cache_.size() - 1; i >= 0; i--) {
+ if (!cache_[i].is_used) {
+ cache_.remove_and_reorder(i);
+ }
+ }
+ }
+
+ void clear()
+ {
+ cache_.clear();
+ }
+};
diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh
index b3d6c3abd18..c091f800d95 100644
--- a/source/blender/draw/engines/image/image_drawing_mode.hh
+++ b/source/blender/draw/engines/image/image_drawing_mode.hh
@@ -157,6 +157,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
if (tile_buffer == nullptr) {
continue;
}
+ instance_data.float_buffers.mark_used(tile_buffer);
BKE_image_release_ibuf(image, tile_buffer, lock);
DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp);
@@ -184,12 +185,14 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
switch (changes.get_result_code()) {
case ePartialUpdateCollectResult::FullUpdateNeeded:
instance_data.mark_all_texture_slots_dirty();
+ instance_data.float_buffers.clear();
break;
case ePartialUpdateCollectResult::NoChangesDetected:
break;
case ePartialUpdateCollectResult::PartialChangesDetected:
/* Partial update when wrap repeat is enabled is not supported. */
if (instance_data.flags.do_tile_drawing) {
+ instance_data.float_buffers.clear();
instance_data.mark_all_texture_slots_dirty();
}
else {
@@ -200,6 +203,34 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
do_full_update_for_dirty_textures(instance_data, image_user);
}
+ /**
+ * Update the float buffer in the region given by the partial update checker.
+ */
+ void do_partial_update_float_buffer(
+ ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const
+ {
+ ImBuf *src = iterator.tile_data.tile_buffer;
+ BLI_assert(float_buffer->rect_float != nullptr);
+ BLI_assert(float_buffer->rect == nullptr);
+ BLI_assert(src->rect_float == nullptr);
+ BLI_assert(src->rect != nullptr);
+
+ /* Calculate the overlap between the updated region and the buffer size. Partial Update Checker
+ * always returns a tile (256x256). Which could lay partially outside the buffer when using
+ * different resolutions.
+ */
+ rcti buffer_rect;
+ BLI_rcti_init(&buffer_rect, 0, float_buffer->x, 0, float_buffer->y);
+ rcti clipped_update_region;
+ const bool has_overlap = BLI_rcti_isect(
+ &buffer_rect, &iterator.changed_region.region, &clipped_update_region);
+ if (!has_overlap) {
+ return;
+ }
+
+ IMB_float_from_rect_ex(float_buffer, src, &clipped_update_region);
+ }
+
void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator,
IMAGE_InstanceData &instance_data) const
{
@@ -208,7 +239,11 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
if (iterator.tile_data.tile_buffer == nullptr) {
continue;
}
- ensure_float_buffer(*iterator.tile_data.tile_buffer);
+ ImBuf *tile_buffer = ensure_float_buffer(instance_data, iterator.tile_data.tile_buffer);
+ if (tile_buffer != iterator.tile_data.tile_buffer) {
+ do_partial_update_float_buffer(tile_buffer, iterator);
+ }
+
const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x);
const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y);
@@ -283,7 +318,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
&extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat);
int offset = 0;
- ImBuf *tile_buffer = iterator.tile_data.tile_buffer;
for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax;
y++) {
float yf = y / (float)texture_height;
@@ -372,16 +406,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
* rect_float as the reference-counter isn't 0. To work around this we destruct any created local
* buffers ourself.
*/
- bool ensure_float_buffer(ImBuf &image_buffer) const
+ ImBuf *ensure_float_buffer(IMAGE_InstanceData &instance_data, ImBuf *image_buffer) const
{
- if (image_buffer.rect_float == nullptr) {
- IMB_float_from_rect(&image_buffer);
- return true;
- }
- return false;
+ return instance_data.float_buffers.ensure_float_buffer(image_buffer);
}
- void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data,
+ void do_full_update_texture_slot(IMAGE_InstanceData &instance_data,
const TextureInfo &texture_info,
ImBuf &texture_buffer,
ImBuf &tile_buffer,
@@ -389,10 +419,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
{
const int texture_width = texture_buffer.x;
const int texture_height = texture_buffer.y;
- const bool float_buffer_created = ensure_float_buffer(tile_buffer);
- /* TODO(jbakker): Find leak when rendering VSE and don't free here. */
- const bool do_free_float_buffer = float_buffer_created &&
- instance_data.image->type == IMA_TYPE_R_RESULT;
+ ImBuf *float_tile_buffer = ensure_float_buffer(instance_data, &tile_buffer);
/* IMB_transform works in a non-consistent space. This should be documented or fixed!.
* Construct a variant of the info_uv_to_texture that adds the texel space
@@ -423,16 +450,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
transform_mode = IMB_TRANSFORM_MODE_CROP_SRC;
}
- IMB_transform(&tile_buffer,
+ IMB_transform(float_tile_buffer,
&texture_buffer,
transform_mode,
IMB_FILTER_NEAREST,
uv_to_texel,
crop_rect_ptr);
-
- if (do_free_float_buffer) {
- imb_freerectfloatImBuf(&tile_buffer);
- }
}
public:
@@ -451,6 +474,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
instance_data->partial_update.ensure_image(image);
instance_data->clear_dirty_flag();
+ instance_data->float_buffers.reset_usage_flags();
/* Step: Find out which screen space textures are needed to draw on the screen. Remove the
* screen space textures that aren't needed. */
@@ -459,7 +483,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
method.update_screen_uv_bounds();
/* Check for changes in the image user compared to the last time. */
- instance_data->update_image_user(iuser);
+ instance_data->update_image_usage(iuser);
/* Step: Update the GPU textures based on the changes in the image. */
instance_data->update_gpu_texture_allocations();
@@ -467,12 +491,16 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
/* Step: Add the GPU textures to the shgroup. */
instance_data->update_batches();
- add_depth_shgroups(*instance_data, image, iuser);
+ if (!instance_data->flags.do_tile_drawing) {
+ add_depth_shgroups(*instance_data, image, iuser);
+ }
add_shgroups(instance_data);
}
- void draw_finish(IMAGE_Data *UNUSED(vedata)) const override
+ void draw_finish(IMAGE_Data *vedata) const override
{
+ IMAGE_InstanceData *instance_data = vedata->instance_data;
+ instance_data->float_buffers.remove_unused_buffers();
}
void draw_scene(IMAGE_Data *vedata) const override
@@ -481,8 +509,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
+
static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0);
+ float clear_depth = instance_data->flags.do_tile_drawing ? 0.75 : 1.0f;
+ GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, clear_depth);
DRW_view_set_active(instance_data->view);
DRW_draw_pass(instance_data->passes.depth_pass);
diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc
index 180e9601cbd..e972d21cda4 100644
--- a/source/blender/draw/engines/image/image_engine.cc
+++ b/source/blender/draw/engines/image/image_engine.cc
@@ -107,6 +107,7 @@ class ImageEngine {
space->release_buffer(instance_data->image, image_buffer, lock);
ImageUser *iuser = space->get_image_user();
+ BKE_image_multiview_index(instance_data->image, iuser);
drawing_mode.cache_image(vedata, instance_data->image, iuser);
}
diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh
index dcc3b7d15cb..682b93a80b3 100644
--- a/source/blender/draw/engines/image/image_instance_data.hh
+++ b/source/blender/draw/engines/image/image_instance_data.hh
@@ -8,10 +8,12 @@
#pragma once
#include "image_batches.hh"
+#include "image_buffer_cache.hh"
#include "image_partial_updater.hh"
#include "image_private.hh"
#include "image_shader_params.hh"
#include "image_texture_info.hh"
+#include "image_usage.hh"
#include "image_wrappers.hh"
#include "DRW_render.h"
@@ -25,8 +27,8 @@ constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 1;
struct IMAGE_InstanceData {
struct Image *image;
- /** Copy of the last image user to detect iuser differences that require a full update. */
- struct ImageUser last_image_user;
+ /** Usage data of the previous time, to identify changes that require a full update. */
+ ImageUsage last_usage;
PartialImageUpdater partial_update;
@@ -47,11 +49,18 @@ struct IMAGE_InstanceData {
DRWPass *depth_pass;
} passes;
+ /**
+ * Cache containing the float buffers when drawing byte images.
+ */
+ FloatBufferCache float_buffers;
+
/** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */
float ss_to_texture[4][4];
TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN];
public:
+ virtual ~IMAGE_InstanceData() = default;
+
void clear_dirty_flag()
{
reset_dirty_flag(false);
@@ -95,24 +104,13 @@ struct IMAGE_InstanceData {
}
}
- void update_image_user(const ImageUser *image_user)
+ void update_image_usage(const ImageUser *image_user)
{
- short requested_pass = image_user ? image_user->pass : 0;
- short requested_layer = image_user ? image_user->layer : 0;
- short requested_view = image_user ? image_user->multi_index : 0;
- /* There is room for 2 multiview textures. When a higher number is requested we should always
- * target the first view slot. This is fine as multi view images aren't used together. */
- if (requested_view > 1) {
- requested_view = 0;
- }
-
- if (last_image_user.pass != requested_pass || last_image_user.layer != requested_layer ||
- last_image_user.multi_index != requested_view) {
-
- last_image_user.pass = requested_pass;
- last_image_user.layer = requested_layer;
- last_image_user.multi_index = requested_view;
+ ImageUsage usage(image, image_user, flags.do_tile_drawing);
+ if (last_usage != usage) {
+ last_usage = usage;
reset_dirty_flag(true);
+ float_buffers.clear();
}
}
diff --git a/source/blender/draw/engines/image/image_usage.hh b/source/blender/draw/engines/image/image_usage.hh
new file mode 100644
index 00000000000..bea5c3853b0
--- /dev/null
+++ b/source/blender/draw/engines/image/image_usage.hh
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#pragma once
+
+/**
+ * ImageUsage contains data of the image and image user to identify changes that require a rebuild
+ * the texture slots.
+ */
+struct ImageUsage {
+ /** Render pass of the image that is used. */
+ short pass = 0;
+ /** Layer of the image that is used.*/
+ short layer = 0;
+ /** View of the image that is used. */
+ short view = 0;
+
+ ColorManagedColorspaceSettings colorspace_settings;
+ /** IMA_ALPHA_* */
+ char alpha_mode;
+ bool last_tile_drawing;
+
+ const void *last_image = nullptr;
+
+ ImageUsage() = default;
+ ImageUsage(const struct Image *image, const struct ImageUser *image_user, bool do_tile_drawing)
+ {
+ pass = image_user ? image_user->pass : 0;
+ layer = image_user ? image_user->layer : 0;
+ view = image_user ? image_user->multi_index : 0;
+ colorspace_settings = image->colorspace_settings;
+ alpha_mode = image->alpha_mode;
+ last_image = static_cast<const void *>(image);
+ last_tile_drawing = do_tile_drawing;
+ }
+
+ bool operator==(const ImageUsage &other) const
+ {
+ return memcmp(this, &other, sizeof(ImageUsage)) == 0;
+ }
+ bool operator!=(const ImageUsage &other) const
+ {
+ return !(*this == other);
+ }
+};
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index b41d9ce69ef..ad0d939e99a 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -182,7 +182,9 @@ static void OVERLAY_cache_init(void *vedata)
case CTX_MODE_WEIGHT_GPENCIL:
OVERLAY_edit_gpencil_cache_init(vedata);
break;
+ case CTX_MODE_SCULPT_CURVES:
case CTX_MODE_OBJECT:
+ case CTX_MODE_EDIT_CURVES:
break;
default:
BLI_assert_msg(0, "Draw mode invalid");
@@ -210,7 +212,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo
{
OVERLAY_DupliData **dupli_data = (OVERLAY_DupliData **)DRW_duplidata_get(vedata);
*do_init = false;
- if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVE, OB_FONT)) {
+ if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT)) {
return NULL;
}
@@ -237,7 +239,7 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj
return pd->ctx_mode == CTX_MODE_EDIT_MESH;
case OB_ARMATURE:
return pd->ctx_mode == CTX_MODE_EDIT_ARMATURE;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return pd->ctx_mode == CTX_MODE_EDIT_CURVE;
case OB_SURF:
return pd->ctx_mode == CTX_MODE_EDIT_SURFACE;
@@ -296,7 +298,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
(ob->sculpt->mode_type == OB_MODE_SCULPT);
const bool has_surface = ELEM(ob->type,
OB_MESH,
- OB_CURVE,
+ OB_CURVES_LEGACY,
OB_SURF,
OB_MBALL,
OB_FONT,
@@ -366,7 +368,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_edit_armature_cache_populate(vedata, ob);
}
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
OVERLAY_edit_curve_cache_populate(vedata, ob);
break;
case OB_SURF:
@@ -661,6 +663,8 @@ static void OVERLAY_draw_scene(void *vedata)
case CTX_MODE_WEIGHT_GPENCIL:
OVERLAY_edit_gpencil_draw(vedata);
break;
+ case CTX_MODE_SCULPT_CURVES:
+ break;
default:
break;
}
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index e370873c234..aae12e5513e 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -456,7 +456,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons
case ID_ME:
BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize);
break;
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)ob_data;
BKE_curve_texspace_ensure(cu);
texcoloc = cu->loc;
@@ -499,7 +499,7 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL);
float *color = DRW_color_background_blend_get(theme_id);
PartDeflect *pd = ob->pd;
- Curve *cu = (ob->type == OB_CURVE) ? ob->data : NULL;
+ Curve *cu = (ob->type == OB_CURVES_LEGACY) ? ob->data : NULL;
union {
float mat[4][4];
diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c
index 58825923f37..aeba721e7ac 100644
--- a/source/blender/draw/engines/overlay/overlay_motion_path.c
+++ b/source/blender/draw/engines/overlay/overlay_motion_path.c
@@ -90,8 +90,8 @@ static void motion_path_get_frame_range_to_draw(bAnimVizSettings *avs,
end = current_frame + avs->path_ac + 1;
}
else {
- start = avs->path_sf;
- end = avs->path_ef;
+ start = mpath->start_frame;
+ end = mpath->end_frame;
}
if (start > end) {
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index 24eceb30441..2636d7876d5 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.c
@@ -196,14 +196,14 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
}
}
- if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) {
+ if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob);
float *color;
DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color);
struct GPUBatch *geom = NULL;
switch (ob->type) {
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
geom = DRW_cache_curve_edge_wire_get(ob);
break;
case OB_FONT:
diff --git a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl
index 4d0692039a4..ebaa898429d 100644
--- a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl
@@ -15,7 +15,7 @@ void main()
if (maskInvertStencil) {
mask.rgb = 1.0 - mask.rgb;
}
- float mask_step = smoothstep(0, 3.0, mask.r + mask.g + mask.b);
+ float mask_step = smoothstep(0.0, 3.0, mask.r + mask.g + mask.b);
mask.rgb *= maskColor;
mask.a = mask_step * opacity;
diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c
index 82812ef98a5..7615b5bb39c 100644
--- a/source/blender/draw/engines/select/select_draw_utils.c
+++ b/source/blender/draw/engines/select/select_draw_utils.c
@@ -225,7 +225,7 @@ void select_id_draw_object(void *vedata,
stl, ob, select_mode, initial_offset, r_vert_offset, r_edge_offset, r_face_offset);
}
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
break;
}
diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c
index 5e12d6a736c..191a2e6d1cc 100644
--- a/source/blender/draw/engines/workbench/workbench_opaque.c
+++ b/source/blender/draw/engines/workbench/workbench_opaque.c
@@ -73,11 +73,13 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_opaque_get(wpd, data);
wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
@@ -85,6 +87,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_opaque_image_get(wpd, data, false);
wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
@@ -92,6 +95,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
sh = workbench_shader_opaque_image_get(wpd, data, true);
wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
+ DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh
index f387d5371b5..bce001659b2 100644
--- a/source/blender/draw/intern/DRW_gpu_wrapper.hh
+++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh
@@ -641,11 +641,6 @@ class Texture : NonCopyable {
}
if (tx_ == nullptr) {
tx_ = create(w, h, d, mips, format, data, layered, cubemap);
- if (mips > 1) {
- /* TODO(@fclem): Remove once we have immutable storage or when mips are
- * generated on creation. */
- GPU_texture_generate_mipmap(tx_);
- }
return true;
}
return false;
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index ce8d3136432..8fc97ddcfc2 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -813,7 +813,7 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return NULL;
case OB_SURF:
return DRW_cache_surf_edge_detection_get(ob, r_is_manifold);
@@ -837,7 +837,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_face_wireframe_get(ob);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return NULL;
case OB_SURF:
return DRW_cache_surf_face_wireframe_get(ob);
@@ -864,7 +864,7 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_loose_edges_get(ob);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return NULL;
case OB_SURF:
return DRW_cache_surf_loose_edges_get(ob);
@@ -888,7 +888,7 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_surface_get(ob);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return NULL;
case OB_SURF:
return DRW_cache_surf_surface_get(ob);
@@ -915,7 +915,7 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob)
switch (type) {
case OB_MESH:
return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
return DRW_curve_batch_cache_pos_vertbuf_get(ob->data);
@@ -947,7 +947,7 @@ int DRW_cache_object_material_count_get(struct Object *ob)
switch (type) {
case OB_MESH:
return DRW_mesh_material_count_get(ob, (me != NULL) ? me : ob->data);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
return DRW_curve_material_count_get(ob->data);
@@ -972,7 +972,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return NULL;
case OB_SURF:
return DRW_cache_surf_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
@@ -2922,21 +2922,21 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob)
GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob)
{
- BLI_assert(ob->type == OB_CURVE);
+ BLI_assert(ob->type == OB_CURVES_LEGACY);
struct Curve *cu = ob->data;
return DRW_curve_batch_cache_get_wire_edge(cu);
}
GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob)
{
- BLI_assert(ob->type == OB_CURVE);
+ BLI_assert(ob->type == OB_CURVES_LEGACY);
struct Curve *cu = ob->data;
return DRW_curve_batch_cache_get_normal_edge(cu);
}
GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob)
{
- BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF));
+ BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF));
struct Curve *cu = ob->data;
return DRW_curve_batch_cache_get_edit_edges(cu);
@@ -2944,7 +2944,7 @@ GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob)
GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob)
{
- BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF));
+ BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF));
struct Curve *cu = ob->data;
return DRW_curve_batch_cache_get_edit_verts(cu);
@@ -3373,7 +3373,7 @@ void drw_batch_cache_validate(Object *ob)
case OB_MESH:
DRW_mesh_batch_cache_validate(ob, (Mesh *)ob->data);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT:
DRW_curve_batch_cache_validate((Curve *)ob->data);
break;
@@ -3423,7 +3423,7 @@ void drw_batch_cache_generate_requested(Object *ob)
DRW_mesh_batch_cache_create_requested(
DST.task_graph, ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT:
DRW_curve_batch_cache_create_requested(ob, scene);
break;
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 738a9029167..49e51d77f7b 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -819,6 +819,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_data);
/* Make sure UVs are computed before edituv stuffs. */
EXTRACT_ADD_REQUESTED(vbo, uv);
+ EXTRACT_ADD_REQUESTED(vbo, tan);
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area);
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
@@ -832,6 +833,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
return;
}
+ mesh_render_data_update_looptris(mr, MR_ITER_LOOPTRI, MR_DATA_LOOPTRI);
mesh_render_data_update_loose_geom(mr, mbc, MR_ITER_LEDGE | MR_ITER_LVERT, MR_DATA_LOOSE_GEOM);
void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__);
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc
index abba3beb893..6a3d3fa5e9e 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -945,7 +945,7 @@ int DRW_curve_material_count_get(Curve *cu)
void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scene)
{
- BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT));
+ BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT));
Curve *cu = (Curve *)ob->data;
CurveBatchCache *cache = curve_batch_cache_get(cu);
diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc
index a779c694cd2..df1ac12605a 100644
--- a/source/blender/draw/intern/draw_cache_impl_curves.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curves.cc
@@ -22,7 +22,7 @@
#include "DNA_curves_types.h"
#include "DNA_object_types.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "GPU_batch.h"
#include "GPU_material.h"
@@ -133,12 +133,12 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves,
{
/* TODO: use hair radius layer if available. */
const int curve_size = curves->geometry.curve_size;
- Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
-
- Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size};
+ const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
+ curves->geometry);
+ Span<float3> positions = geometry.positions();
for (const int i : IndexRange(curve_size)) {
- const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+ const IndexRange curve_range = geometry.range_for_curve(i);
Span<float3> spline_positions = positions.slice(curve_range);
float total_len = 0.0f;
@@ -215,11 +215,11 @@ static void curves_batch_cache_fill_strands_data(Curves *curves,
GPUVertBufRaw *data_step,
GPUVertBufRaw *seg_step)
{
- const int curve_size = curves->geometry.curve_size;
- Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
+ const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
+ curves->geometry);
- for (const int i : IndexRange(curve_size)) {
- const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+ for (const int i : IndexRange(geometry.curves_size())) {
+ const IndexRange curve_range = geometry.range_for_curve(i);
*(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start();
*(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1;
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 8833a354c21..79a080cfccd 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -339,11 +339,7 @@ static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst,
/* Return true if all requests in b are in a. */
static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b)
{
- if (a->num_requests != b->num_requests) {
- return false;
- }
-
- for (int i = 0; i < a->num_requests; i++) {
+ for (int i = 0; i < b->num_requests; i++) {
if (!has_request(a, b->requests[i])) {
return false;
}
@@ -1712,7 +1708,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
const int required_mode = BKE_subsurf_modifier_eval_required_mode(DRW_state_is_scene_render(),
is_editmode);
- const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, required_mode);
+ const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, me, required_mode);
MeshBufferList *mbuflist = &cache->final.buff;
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index ac2e5bbca2e..5d99478476c 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -67,7 +67,6 @@ enum {
SHADER_BUFFER_NORMALS_ACCUMULATE,
SHADER_BUFFER_NORMALS_FINALIZE,
SHADER_PATCH_EVALUATION,
- SHADER_PATCH_EVALUATION_LIMIT_NORMALS,
SHADER_PATCH_EVALUATION_FVAR,
SHADER_PATCH_EVALUATION_FACE_DOTS,
SHADER_COMP_CUSTOM_DATA_INTERP_1D,
@@ -107,7 +106,6 @@ static const char *get_shader_code(int shader_type)
return datatoc_common_subdiv_normals_finalize_comp_glsl;
}
case SHADER_PATCH_EVALUATION:
- case SHADER_PATCH_EVALUATION_LIMIT_NORMALS:
case SHADER_PATCH_EVALUATION_FVAR:
case SHADER_PATCH_EVALUATION_FACE_DOTS: {
return datatoc_common_subdiv_patch_evaluation_comp_glsl;
@@ -159,9 +157,6 @@ static const char *get_shader_name(int shader_type)
case SHADER_PATCH_EVALUATION: {
return "subdiv patch evaluation";
}
- case SHADER_PATCH_EVALUATION_LIMIT_NORMALS: {
- return "subdiv patch evaluation limit normals";
- }
case SHADER_PATCH_EVALUATION_FVAR: {
return "subdiv patch evaluation face-varying";
}
@@ -199,13 +194,7 @@ static GPUShader *get_patch_evaluation_shader(int shader_type)
const char *compute_code = get_shader_code(shader_type);
const char *defines = nullptr;
- if (shader_type == SHADER_PATCH_EVALUATION_LIMIT_NORMALS) {
- defines =
- "#define OSD_PATCH_BASIS_GLSL\n"
- "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n"
- "#define LIMIT_NORMALS\n";
- }
- else if (shader_type == SHADER_PATCH_EVALUATION_FVAR) {
+ if (shader_type == SHADER_PATCH_EVALUATION_FVAR) {
defines =
"#define OSD_PATCH_BASIS_GLSL\n"
"#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n"
@@ -246,7 +235,6 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines)
{
if (ELEM(shader_type,
SHADER_PATCH_EVALUATION,
- SHADER_PATCH_EVALUATION_LIMIT_NORMALS,
SHADER_PATCH_EVALUATION_FVAR,
SHADER_PATCH_EVALUATION_FACE_DOTS)) {
return get_patch_evaluation_shader(shader_type);
@@ -592,6 +580,67 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache)
SUBDIV_COARSE_FACE_FLAG_ACTIVE) \
<< SUBDIV_COARSE_FACE_FLAG_OFFSET)
+static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act)
+{
+ if (f == nullptr) {
+ /* May happen during mapped extraction. */
+ return 0;
+ }
+
+ uint32_t flag = 0;
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
+ }
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
+ }
+ if (f == efa_act) {
+ flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE;
+ }
+ const int loopstart = BM_elem_index_get(f->l_first);
+ return (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
+}
+
+static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm,
+ BMFace *efa_act,
+ uint32_t *flags_data)
+{
+ BMFace *f;
+ BMIter iter;
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ const int index = BM_elem_index_get(f);
+ flags_data[index] = compute_coarse_face_flag(f, efa_act);
+ }
+}
+
+static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data)
+{
+ for (int i = 0; i < mesh->totpoly; i++) {
+ uint32_t flag = 0;
+ if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) {
+ flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH;
+ }
+ flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
+ }
+}
+
+static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh,
+ BMesh *bm,
+ MeshRenderData *mr,
+ uint32_t *flags_data)
+{
+ if (bm == nullptr) {
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
+ return;
+ }
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ BMFace *f = bm_original_face_get(mr, i);
+ flags_data[i] = compute_coarse_face_flag(f, mr->efa_act);
+ }
+}
+
static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cache,
Mesh *mesh,
MeshRenderData *mr)
@@ -611,56 +660,13 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach
uint32_t *flags_data = (uint32_t *)(GPU_vertbuf_get_data(cache->extra_coarse_face_data));
if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMesh *bm = cache->bm;
- BMFace *f;
- BMIter iter;
-
- /* Ensure all current elements follow new customdata layout. */
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- const int index = BM_elem_index_get(f);
- uint32_t flag = 0;
- if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
- flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
- }
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
- }
- if (f == mr->efa_act) {
- flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE;
- }
- const int loopstart = BM_elem_index_get(f->l_first);
- flags_data[index] = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
- }
+ draw_subdiv_cache_extra_coarse_face_data_bm(cache->bm, mr->efa_act, flags_data);
}
else if (mr->extract_type == MR_EXTRACT_MAPPED) {
- for (int i = 0; i < mesh->totpoly; i++) {
- BMFace *f = bm_original_face_get(mr, i);
- uint32_t flag = 0;
-
- if (f) {
- if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
- flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
- }
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
- }
- if (f == mr->efa_act) {
- flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE;
- }
- const int loopstart = BM_elem_index_get(f->l_first);
- flag = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
- }
- flags_data[i] = flag;
- }
+ draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data);
}
else {
- for (int i = 0; i < mesh->totpoly; i++) {
- uint32_t flag = 0;
- if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) {
- flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH;
- }
- flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
- }
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
}
/* Make sure updated data is re-uploaded. */
@@ -1176,9 +1182,7 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache,
GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1);
}
-void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
- GPUVertBuf *pos_nor,
- const bool do_limit_normals)
+void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor)
{
Subdiv *subdiv = cache->subdiv;
OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
@@ -1203,8 +1207,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
get_patch_param_format());
evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface);
- GPUShader *shader = get_patch_evaluation_shader(
- do_limit_normals ? SHADER_PATCH_EVALUATION_LIMIT_NORMALS : SHADER_PATCH_EVALUATION);
+ GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION);
GPU_shader_bind(shader);
GPU_vertbuf_bind_as_ssbo(src_buffer, 0);
@@ -1299,7 +1302,8 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache,
GPUVertBuf *src_data,
GPUVertBuf *dst_data,
int dimensions,
- int dst_offset)
+ int dst_offset,
+ bool compress_to_u16)
{
GPUShader *shader = nullptr;
@@ -1319,10 +1323,17 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache,
"#define DIMENSIONS 3\n");
}
else if (dimensions == 4) {
- shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D,
- "#define SUBDIV_POLYGON_OFFSET\n"
- "#define DIMENSIONS 4\n"
- "#define GPU_FETCH_U16_TO_FLOAT\n");
+ if (compress_to_u16) {
+ shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D,
+ "#define SUBDIV_POLYGON_OFFSET\n"
+ "#define DIMENSIONS 4\n"
+ "#define GPU_FETCH_U16_TO_FLOAT\n");
+ }
+ else {
+ shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D,
+ "#define SUBDIV_POLYGON_OFFSET\n"
+ "#define DIMENSIONS 4\n");
+ }
}
else {
/* Crash if dimensions are not supported. */
@@ -1376,6 +1387,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache,
GPUVertBuf *pos_nor,
GPUVertBuf *face_adjacency_offsets,
GPUVertBuf *face_adjacency_lists,
+ GPUVertBuf *vertex_loop_map,
GPUVertBuf *vertex_normals)
{
GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_NORMALS_ACCUMULATE, nullptr);
@@ -1386,6 +1398,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache,
GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++);
GPU_vertbuf_bind_as_ssbo(face_adjacency_offsets, binding_point++);
GPU_vertbuf_bind_as_ssbo(face_adjacency_lists, binding_point++);
+ GPU_vertbuf_bind_as_ssbo(vertex_loop_map, binding_point++);
GPU_vertbuf_bind_as_ssbo(vertex_normals, binding_point++);
drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_verts);
@@ -1785,9 +1798,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
- const bool UNUSED(use_subsurf_fdots),
+ const bool /*use_subsurf_fdots*/,
const ToolSettings *ts,
- const bool UNUSED(use_hide),
+ const bool /*use_hide*/,
OpenSubdiv_EvaluatorCache *evaluator_cache)
{
SubsurfModifierData *smd = BKE_object_get_last_subsurf_modifier(ob);
@@ -1833,8 +1846,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
draw_cache->subdiv = subdiv;
draw_cache->optimal_display = optimal_display;
draw_cache->num_subdiv_triangles = tris_count_from_number_of_loops(draw_cache->num_subdiv_loops);
- /* We can only evaluate limit normals if the patches are adaptive. */
- draw_cache->do_limit_normals = settings.is_adaptive;
draw_cache->use_custom_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) &&
(mesh_eval->flag & ME_AUTOSMOOTH) &&
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index fcfaf404fc2..2897234f4dc 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -412,7 +412,7 @@ bool DRW_object_is_flat(Object *ob, int *r_axis)
if (!ELEM(ob->type,
OB_MESH,
- OB_CURVE,
+ OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
OB_MBALL,
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 440f74af64b..2886fe53879 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -2774,7 +2774,7 @@ void DRW_draw_depth_object(
GPU_uniformbuf_free(ubo);
} break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
break;
}
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 3b35b8c1f9d..95691a0df68 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -514,7 +514,7 @@ static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4])
case ID_ME:
BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize);
break;
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)ob_data;
BKE_curve_texspace_ensure(cu);
texcoloc = cu->loc;
diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h
index 6714ba571e5..bd02df6d48b 100644
--- a/source/blender/draw/intern/draw_subdivision.h
+++ b/source/blender/draw/intern/draw_subdivision.h
@@ -51,7 +51,6 @@ typedef struct DRWSubdivCache {
struct BMesh *bm;
struct Subdiv *subdiv;
bool optimal_display;
- bool do_limit_normals;
bool use_custom_loop_normals;
/* Coordinates used to evaluate patches for UVs, positions, and normals. */
@@ -165,6 +164,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache,
struct GPUVertBuf *pos_nor,
struct GPUVertBuf *face_adjacency_offsets,
struct GPUVertBuf *face_adjacency_lists,
+ struct GPUVertBuf *vertex_loop_map,
struct GPUVertBuf *vertex_normals);
void draw_subdiv_finalize_normals(const DRWSubdivCache *cache,
@@ -176,15 +176,14 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache,
GPUVertBuf *src_custom_normals,
GPUVertBuf *pos_nor);
-void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
- struct GPUVertBuf *pos_nor,
- bool do_limit_normals);
+void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor);
void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache,
struct GPUVertBuf *src_data,
struct GPUVertBuf *dst_data,
int dimensions,
- int dst_offset);
+ int dst_offset,
+ bool compress_to_u16);
void draw_subdiv_extract_uvs(const DRWSubdivCache *cache,
struct GPUVertBuf *uvs,
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
index d5e34bc082e..4f4aa764fbc 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
@@ -402,7 +402,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
/* Ensure data is uploaded properly. */
GPU_vertbuf_tag_dirty(src_data);
draw_subdiv_interp_custom_data(
- subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0);
+ subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0, false);
GPU_vertbuf_discard(src_data);
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
index bd7f1ba0128..22fda284a74 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
@@ -217,14 +217,12 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
void *UNUSED(data))
{
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer);
- const bool do_limit_normals = subdiv_cache->do_limit_normals &&
- !subdiv_cache->use_custom_loop_normals;
/* Initialize the vertex buffer, it was already allocated. */
GPU_vertbuf_init_build_on_device(
vbo, get_pos_nor_format(), subdiv_cache->num_subdiv_loops + mr->loop_loose_len);
- draw_subdiv_extract_pos_nor(subdiv_cache, vbo, do_limit_normals);
+ draw_subdiv_extract_pos_nor(subdiv_cache, vbo);
if (subdiv_cache->use_custom_loop_normals) {
Mesh *coarse_mesh = subdiv_cache->mesh;
@@ -243,14 +241,15 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_init_build_on_device(
dst_custom_normals, get_custom_normals_format(), subdiv_cache->num_subdiv_loops);
- draw_subdiv_interp_custom_data(subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0);
+ draw_subdiv_interp_custom_data(
+ subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0, false);
draw_subdiv_finalize_custom_normals(subdiv_cache, dst_custom_normals, vbo);
GPU_vertbuf_discard(src_custom_normals);
GPU_vertbuf_discard(dst_custom_normals);
}
- else if (!do_limit_normals) {
+ else {
/* We cannot evaluate vertex normals using the limit surface, so compute them manually. */
GPUVertBuf *subdiv_loop_subdiv_vert_index = draw_subdiv_build_origindex_buffer(
subdiv_cache->subdiv_loop_subdiv_vert_index, subdiv_cache->num_subdiv_loops);
@@ -263,6 +262,7 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
vbo,
subdiv_cache->subdiv_vertex_face_adjacency_offsets,
subdiv_cache->subdiv_vertex_face_adjacency,
+ subdiv_loop_subdiv_vert_index,
vertex_normals);
draw_subdiv_finalize_normals(subdiv_cache, vertex_normals, subdiv_loop_subdiv_vert_index, vbo);
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
index 78c215845e0..96595df9276 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
@@ -151,7 +151,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_init_build_on_device(
subdiv_mask_vbo, &mask_format, subdiv_cache->num_subdiv_loops);
- draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0);
+ draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0, false);
}
/* Then, gather face sets. */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
index 209168750e7..225d1676151 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
@@ -16,22 +16,26 @@
#include "extract_mesh.h"
+#include "draw_subdivision.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Extract Tangent layers
* \{ */
-static void extract_tan_ex_init(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- GPUVertBuf *vbo,
- const bool do_hq)
+static void extract_tan_init_common(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ GPUVertFormat *format,
+ GPUVertCompType comp_type,
+ GPUVertFetchMode fetch_mode,
+ CustomData *r_loop_data,
+ int *r_v_len,
+ int *r_tan_len,
+ char r_tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME],
+ bool *r_use_orco_tan)
{
- GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
- GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
-
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
+ GPU_vertformat_deinterleave(format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
@@ -41,7 +45,6 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
bool use_orco_tan = cache->cd_used.tan_orco != 0;
int tan_len = 0;
- char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
/* FIXME(T91838): This is to avoid a crash when orco tangent was requested but there are valid
* uv layers. It would be better to fix the root cause. */
@@ -57,17 +60,17 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
/* Tangent layer name. */
BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
+ GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode);
/* Active render layer name. */
if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "t");
+ GPU_vertformat_alias_add(format, "t");
}
/* Active display layer name. */
if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "at");
+ GPU_vertformat_alias_add(format, "at");
}
- BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
+ BLI_strncpy(r_tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
}
}
if (use_orco_tan && orco == nullptr) {
@@ -94,20 +97,19 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
}
/* Start Fresh */
- CustomData loop_data;
- CustomData_reset(&loop_data);
+ CustomData_reset(r_loop_data);
if (tan_len != 0 || use_orco_tan) {
short tangent_mask = 0;
bool calc_active_tangent = false;
if (mr->extract_type == MR_EXTRACT_BMESH) {
BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
calc_active_tangent,
- tangent_names,
+ r_tangent_names,
tan_len,
mr->poly_normals,
mr->loop_normals,
orco,
- &loop_data,
+ r_loop_data,
mr->loop_len,
&tangent_mask);
}
@@ -120,13 +122,13 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
mr->tri_len,
cd_ldata,
calc_active_tangent,
- tangent_names,
+ r_tangent_names,
tan_len,
mr->vert_normals,
mr->poly_normals,
mr->loop_normals,
orco,
- &loop_data,
+ r_loop_data,
mr->loop_len,
&tangent_mask);
}
@@ -134,12 +136,12 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
if (use_orco_tan) {
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
+ const char *layer_name = CustomData_get_layer_name(r_loop_data, CD_TANGENT, 0);
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
- GPU_vertformat_alias_add(&format, "t");
- GPU_vertformat_alias_add(&format, "at");
+ GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode);
+ GPU_vertformat_alias_add(format, "t");
+ GPU_vertformat_alias_add(format, "at");
}
if (orco_allocated) {
@@ -147,12 +149,42 @@ static void extract_tan_ex_init(const MeshRenderData *mr,
}
int v_len = mr->loop_len;
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ if (format->attr_len == 0) {
+ GPU_vertformat_attr_add(format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
/* VBO will not be used, only allocate minimum of memory. */
v_len = 1;
}
+ *r_use_orco_tan = use_orco_tan;
+ *r_v_len = v_len;
+ *r_tan_len = tan_len;
+}
+
+static void extract_tan_ex_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ GPUVertBuf *vbo,
+ const bool do_hq)
+{
+ GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
+ GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
+
+ GPUVertFormat format = {0};
+ CustomData loop_data;
+ int v_len = 0;
+ int tan_len = 0;
+ bool use_orco_tan;
+ char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
+ extract_tan_init_common(mr,
+ cache,
+ &format,
+ comp_type,
+ fetch_mode,
+ &loop_data,
+ &v_len,
+ &tan_len,
+ tangent_names,
+ &use_orco_tan);
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, v_len);
@@ -211,10 +243,92 @@ static void extract_tan_init(const MeshRenderData *mr,
extract_tan_ex_init(mr, cache, vbo, false);
}
+static GPUVertFormat *get_coarse_tan_format()
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "tan", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+ return &format;
+}
+
+static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache,
+ const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *UNUSED(data))
+{
+ GPUVertCompType comp_type = GPU_COMP_F32;
+ GPUVertFetchMode fetch_mode = GPU_FETCH_FLOAT;
+ GPUVertFormat format = {0};
+ CustomData loop_data;
+ int coarse_len = 0;
+ int tan_len = 0;
+ bool use_orco_tan;
+ char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
+ extract_tan_init_common(mr,
+ cache,
+ &format,
+ comp_type,
+ fetch_mode,
+ &loop_data,
+ &coarse_len,
+ &tan_len,
+ tangent_names,
+ &use_orco_tan);
+
+ GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
+ GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops);
+
+ GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc();
+ /* Dynamic as we upload and interpolate layers one at a time. */
+ GPU_vertbuf_init_with_format_ex(coarse_vbo, get_coarse_tan_format(), GPU_USAGE_DYNAMIC);
+ GPU_vertbuf_data_alloc(coarse_vbo, coarse_len);
+
+ /* Index of the tangent layer in the compact buffer. Used layers are stored in a single buffer.
+ */
+ int pack_layer_index = 0;
+ for (int i = 0; i < tan_len; i++) {
+ float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo);
+ const char *name = tangent_names[i];
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(&loop_data, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ copy_v3_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f;
+ tan_data++;
+ }
+
+ /* Ensure data is uploaded properly. */
+ GPU_vertbuf_tag_dirty(coarse_vbo);
+ /* Include stride in offset. */
+ const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++;
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false);
+ }
+ if (use_orco_tan) {
+ float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo);
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ copy_v3_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f;
+ tan_data++;
+ }
+
+ /* Ensure data is uploaded properly. */
+ GPU_vertbuf_tag_dirty(coarse_vbo);
+ /* Include stride in offset. */
+ const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++;
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true);
+ }
+
+ CustomData_free(&loop_data, mr->loop_len);
+ GPU_vertbuf_discard(coarse_vbo);
+}
+
constexpr MeshExtract create_extractor_tan()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_tan_init;
+ extractor.init_subdiv = extract_tan_init_subdiv;
extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI;
extractor.data_size = 0;
extractor.use_threading = false;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
index 138ff9fd1ff..7a8f4a9a17e 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
@@ -164,7 +164,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache,
/* Ensure data is uploaded properly. */
GPU_vertbuf_tag_dirty(src_data);
- draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset);
+ draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true);
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
index 2e30d6bdfcf..89aa16ca0c7 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
@@ -153,10 +153,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
}
static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache,
- const MeshRenderData *UNUSED(mr),
+ const MeshRenderData *mr,
struct MeshBatchCache *cache,
void *buffer,
- void *UNUSED(data))
+ void *_data)
{
Mesh *coarse_mesh = subdiv_cache->mesh;
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer);
@@ -168,32 +168,24 @@ static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_init_build_on_device(vbo, &format, subdiv_cache->num_subdiv_loops);
GPUVertBuf *coarse_weights = GPU_vertbuf_calloc();
- GPU_vertbuf_init_with_format(coarse_weights, &format);
- GPU_vertbuf_data_alloc(coarse_weights, coarse_mesh->totloop);
- float *coarse_weights_data = static_cast<float *>(GPU_vertbuf_get_data(coarse_weights));
+ extract_weights_init(mr, cache, coarse_weights, _data);
- const DRW_MeshWeightState *wstate = &cache->weight_state;
- const MDeformVert *dverts = static_cast<const MDeformVert *>(
- CustomData_get_layer(&coarse_mesh->vdata, CD_MDEFORMVERT));
-
- for (int i = 0; i < coarse_mesh->totpoly; i++) {
- const MPoly *mpoly = &coarse_mesh->mpoly[i];
-
- for (int loop_index = mpoly->loopstart; loop_index < mpoly->loopstart + mpoly->totloop;
- loop_index++) {
- const MLoop *ml = &coarse_mesh->mloop[loop_index];
-
- if (dverts != nullptr) {
- const MDeformVert *dvert = &dverts[ml->v];
- coarse_weights_data[loop_index] = evaluate_vertex_weight(dvert, wstate);
- }
- else {
- coarse_weights_data[loop_index] = evaluate_vertex_weight(nullptr, wstate);
- }
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ for (int i = 0; i < coarse_mesh->totpoly; i++) {
+ const MPoly *mpoly = &coarse_mesh->mpoly[i];
+ extract_weights_iter_poly_mesh(mr, mpoly, i, _data);
+ }
+ }
+ else {
+ BMIter f_iter;
+ BMFace *efa;
+ int face_index = 0;
+ BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, face_index) {
+ extract_weights_iter_poly_bm(mr, efa, face_index, _data);
}
}
- draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0);
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0, false);
GPU_vertbuf_discard(coarse_weights);
}
diff --git a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl
index df0016761e2..097ae0b3913 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 1) readonly restrict buffer sourceBuffer
{
diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl
index f11c0f6427e..3cbb9f980f3 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 0) readonly buffer inputEdgeOrigIndex
{
diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl
index 3257ebdae17..3dccc82541e 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
/* Generate triangles from subdivision quads indices. */
diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
index e6538d80111..5d71c5e4bb8 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl
@@ -140,6 +140,13 @@ void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor)
set_vertex_nor(vertex_data, nor, 0);
}
+void add_newell_cross_v3_v3v3(inout vec3 n, vec3 v_prev, vec3 v_curr)
+{
+ n[0] += (v_prev[1] - v_curr[1]) * (v_prev[2] + v_curr[2]);
+ n[1] += (v_prev[2] - v_curr[2]) * (v_prev[0] + v_curr[0]);
+ n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]);
+}
+
#define ORIGINDEX_NONE -1
#ifdef SUBDIV_POLYGON_OFFSET
diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl
index 575090472b1..0665cadfd2d 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 0) readonly buffer inputVertexData
{
@@ -16,11 +16,33 @@ layout(std430, binding = 2) readonly buffer faceAdjacencyLists
uint face_adjacency_lists[];
};
-layout(std430, binding = 3) writeonly buffer vertexNormals
+layout(std430, binding = 3) readonly buffer vertexLoopMap
+{
+ uint vert_loop_map[];
+};
+
+layout(std430, binding = 4) writeonly buffer vertexNormals
{
vec3 normals[];
};
+void find_prev_and_next_vertex_on_face(
+ uint face_index, uint vertex_index, out uint curr, out uint next, out uint prev)
+{
+ uint start_loop_index = face_index * 4;
+
+ for (uint i = 0; i < 4; i++) {
+ uint subdiv_vert_index = vert_loop_map[start_loop_index + i];
+
+ if (subdiv_vert_index == vertex_index) {
+ curr = i;
+ next = (i + 1) % 4;
+ prev = (i + 4 - 1) % 4;
+ break;
+ }
+ }
+}
+
void main()
{
uint vertex_index = get_global_invocation_index();
@@ -39,18 +61,37 @@ void main()
uint adjacent_face = face_adjacency_lists[first_adjacent_face_offset + i];
uint start_loop_index = adjacent_face * 4;
- /* Compute face normal. */
- vec3 adjacent_verts[3];
- for (uint j = 0; j < 3; j++) {
- adjacent_verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]);
+ /* Compute the face normal using Newell's method. */
+ vec3 verts[4];
+ for (uint j = 0; j < 4; j++) {
+ verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]);
}
- vec3 face_normal = normalize(
- cross(adjacent_verts[1] - adjacent_verts[0], adjacent_verts[2] - adjacent_verts[0]));
- accumulated_normal += face_normal;
+ vec3 face_normal = vec3(0.0);
+ add_newell_cross_v3_v3v3(face_normal, verts[0], verts[1]);
+ add_newell_cross_v3_v3v3(face_normal, verts[1], verts[2]);
+ add_newell_cross_v3_v3v3(face_normal, verts[2], verts[3]);
+ add_newell_cross_v3_v3v3(face_normal, verts[3], verts[0]);
+
+ /* Accumulate angle weighted normal. */
+ uint curr_vert = 0;
+ uint next_vert = 0;
+ uint prev_vert = 0;
+ find_prev_and_next_vertex_on_face(
+ adjacent_face, vertex_index, curr_vert, next_vert, prev_vert);
+
+ vec3 curr_co = verts[curr_vert];
+ vec3 prev_co = verts[next_vert];
+ vec3 next_co = verts[prev_vert];
+
+ vec3 edvec_prev = normalize(prev_co - curr_co);
+ vec3 edvec_next = normalize(curr_co - next_co);
+
+ float fac = acos(-dot(edvec_prev, edvec_next));
+
+ accumulated_normal += face_normal * fac;
}
- float weight = 1.0 / float(number_of_adjacent_faces);
vec3 normal = normalize(accumulated_normal);
normals[vertex_index] = normal;
}
diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl
index c2e0e752783..e6a56ff02c7 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
#ifdef CUSTOM_NORMALS
struct CustomNormal {
diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl
index 5dd7decf663..65cf4ebb90f 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
/* Source buffer. */
layout(std430, binding = 0) buffer src_buffer
@@ -394,12 +394,8 @@ void main()
evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, pos, du, dv);
-# if defined(LIMIT_NORMALS)
- vec3 nor = normalize(cross(du, dv));
-# else
/* This will be computed later. */
vec3 nor = vec3(0.0);
-# endif
int origindex = input_vert_origindex[loop_index];
uint flag = 0;
diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl
index 6c76cd41ca4..2161f0b28a9 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 0) readonly buffer inputVertexData
{
diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl
index ea73b9482d3..a8c9b7183eb 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 0) readonly buffer inputVerts
{
diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl
index e897fb3f3c0..230484048b1 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 1) readonly buffer inputCoarseData
{
diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl
index 41a8df3cf82..b7e04e240fb 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
layout(std430, binding = 1) readonly buffer inputVertexData
{
@@ -38,13 +38,18 @@ void main()
}
}
else {
- /* Face is flat shaded, compute flat face normal from an inscribed triangle. */
- vec3 verts[3];
- for (int i = 0; i < 3; i++) {
- verts[i] = get_vertex_pos(pos_nor[start_loop_index + i]);
- }
-
- vec3 face_normal = normalize(cross(verts[1] - verts[0], verts[2] - verts[0]));
+ vec3 v0 = get_vertex_pos(pos_nor[start_loop_index + 0]);
+ vec3 v1 = get_vertex_pos(pos_nor[start_loop_index + 1]);
+ vec3 v2 = get_vertex_pos(pos_nor[start_loop_index + 2]);
+ vec3 v3 = get_vertex_pos(pos_nor[start_loop_index + 3]);
+
+ vec3 face_normal = vec3(0.0);
+ add_newell_cross_v3_v3v3(face_normal, v0, v1);
+ add_newell_cross_v3_v3v3(face_normal, v1, v2);
+ add_newell_cross_v3_v3v3(face_normal, v2, v3);
+ add_newell_cross_v3_v3v3(face_normal, v3, v0);
+
+ face_normal = normalize(face_normal);
for (int i = 0; i < 4; i++) {
output_lnor[start_loop_index + i] = face_normal;
}
diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
index 7182ce57ad3..77b599f6252 100644
--- a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
+++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
@@ -1,5 +1,5 @@
-/* To be compile with common_subdiv_lib.glsl */
+/* To be compiled with common_subdiv_lib.glsl */
struct SculptData {
uint face_set_color;
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 4780352e5dc..edb6d188ab8 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -668,7 +668,7 @@ static int acf_object_icon(bAnimListElem *ale)
return ICON_OUTLINER_OB_MESH;
case OB_CAMERA:
return ICON_OUTLINER_OB_CAMERA;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return ICON_OUTLINER_OB_CURVE;
case OB_MBALL:
return ICON_OUTLINER_OB_META;
@@ -4601,7 +4601,7 @@ void ANIM_channel_draw(
/* Draw slider:
* - Even if we can draw sliders for this view,
* we must also check that the channel-type supports them
- * (only only F-Curves really can support them for now).
+ * (only F-Curves really can support them for now).
* - Slider should start before the toggles (if they're visible)
* to keep a clean line down the side.
*/
@@ -5336,7 +5336,7 @@ void ANIM_channel_draw_widgets(const bContext *C,
/* Draw slider:
* - Even if we can draw sliders for this view, we must also check that the channel-type
- * supports them (only only F-Curves really can support them for now).
+ * supports them (only F-Curves really can support them for now).
* - To make things easier, we use RNA-autobuts for this so that changes are
* reflected immediately, wherever they occurred.
* BUT, we don't use the layout engine, otherwise we'd get wrong alignment,
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index d6163f21c79..0389e57627a 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -2530,9 +2530,9 @@ static size_t animdata_filter_ds_obdata(
expanded = FILTER_LAM_OBJD(la);
break;
}
- case OB_CURVE: /* ------- Curve ---------- */
- case OB_SURF: /* ------- Nurbs Surface ---------- */
- case OB_FONT: /* ------- Text Curve ---------- */
+ case OB_CURVES_LEGACY: /* ------- Curve ---------- */
+ case OB_SURF: /* ------- Nurbs Surface ---------- */
+ case OB_FONT: /* ------- Text Curve ---------- */
{
Curve *cu = (Curve *)ob->data;
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index 9566402ad85..95125516fe8 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -700,7 +700,7 @@ static void MARKER_OT_add(wmOperatorType *ot)
typedef struct MarkerMove {
SpaceLink *slink;
ListBase *markers;
- int event_type; /* store invoke-event, to verify */
+ short event_type, event_val; /* store invoke-event, to verify */
int *oldframe, evtx, firstx;
NumInput num;
} MarkerMove;
@@ -844,6 +844,7 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve
mm->evtx = event->xy[0];
mm->firstx = event->xy[0];
mm->event_type = event->type;
+ mm->event_val = event->val;
/* add temp handler */
WM_event_add_modal_handler(C, op);
@@ -941,7 +942,7 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even
case EVT_PADENTER:
case LEFTMOUSE:
case MIDDLEMOUSE:
- if (WM_event_is_modal_tweak_exit(event, mm->event_type)) {
+ if (WM_event_is_modal_drag_exit(event, mm->event_type, mm->event_val)) {
ed_marker_move_exit(C, op);
WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
@@ -960,7 +961,13 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even
mm->evtx = event->xy[0];
fac = ((float)(event->xy[0] - mm->firstx) * dx);
- apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0);
+ apply_keyb_grid((event->modifier & KM_SHIFT) != 0,
+ (event->modifier & KM_CTRL) != 0,
+ &fac,
+ 0.0,
+ FPS,
+ 0.1 * FPS,
+ 0);
RNA_int_set(op->ptr, "frames", (int)fac);
ed_marker_move_apply(C, op);
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index 539227933cf..2c99cd1cc1f 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -340,6 +340,55 @@ static void motionpath_free_free_tree_data(ListBase *targets)
}
}
+void animviz_motionpath_compute_range(Object *ob, Scene *scene)
+{
+ struct AnimKeylist *keylist = ED_keylist_create();
+ bAnimVizSettings *avs;
+ if (ob->mode == OB_MODE_POSE) {
+ avs = &ob->pose->avs;
+ bArmature *arm = ob->data;
+
+ if (!ELEM(NULL, ob->adt, ob->adt->action, arm->adt)) {
+ /* Loop through all the fcurves and get only the keylists for the bone location fcurves */
+ LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) {
+ if (strstr(fcu->rna_path, "pose.bones[") && strstr(fcu->rna_path, "location")) {
+ fcurve_to_keylist(arm->adt, fcu, keylist, 0);
+ }
+ }
+ }
+ }
+ else {
+ avs = &ob->avs;
+
+ if (!ELEM(NULL, ob->adt, ob->adt->action)) {
+ /* Loop through all the fcurves and get only the keylists for the location fcurves */
+ LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) {
+ if (strcmp(fcu->rna_path, "location") == 0) {
+ fcurve_to_keylist(ob->adt, fcu, keylist, 0);
+ }
+ }
+ }
+ }
+
+ if (ED_keylist_is_empty(keylist) || (avs->path_range == MOTIONPATH_RANGE_SCENE)) {
+ /* Apply scene frame range if no keys where found or if scene range is selected */
+ avs->path_sf = PSFRA;
+ avs->path_ef = PEFRA;
+ }
+ else {
+ /* Compute keys range */
+ Range2f frame_range;
+ const bool only_selected = avs->path_range == MOTIONPATH_RANGE_KEYS_SELECTED;
+ /* Get range for all keys if selected_only is false or if no keys are selected */
+ if (!(only_selected && ED_keylist_selected_keys_frame_range(keylist, &frame_range))) {
+ ED_keylist_all_keys_frame_range(keylist, &frame_range);
+ }
+ avs->path_sf = frame_range.min;
+ avs->path_ef = frame_range.max;
+ }
+ ED_keylist_free(keylist);
+}
+
void animviz_calc_motionpaths(Depsgraph *depsgraph,
Main *bmain,
Scene *scene,
diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc
index 3f9592fb4ae..0b795fea278 100644
--- a/source/blender/editors/animation/keyframes_keylist.cc
+++ b/source/blender/editors/animation/keyframes_keylist.cc
@@ -304,7 +304,21 @@ const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist)
return &keylist->key_columns;
}
-bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
+static void keylist_first_last(const struct AnimKeylist *keylist,
+ const struct ActKeyColumn **first_column,
+ const struct ActKeyColumn **last_column)
+{
+ if (keylist->is_runtime_initialized) {
+ *first_column = &keylist->runtime.key_columns[0];
+ *last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
+ }
+ else {
+ *first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first);
+ *last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last);
+ }
+}
+
+bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
{
BLI_assert(r_frame_range);
@@ -314,13 +328,33 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_
const ActKeyColumn *first_column;
const ActKeyColumn *last_column;
- if (keylist->is_runtime_initialized) {
- first_column = &keylist->runtime.key_columns[0];
- last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
+ keylist_first_last(keylist, &first_column, &last_column);
+ r_frame_range->min = first_column->cfra;
+ r_frame_range->max = last_column->cfra;
+
+ return true;
+}
+
+bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist,
+ Range2f *r_frame_range)
+{
+ BLI_assert(r_frame_range);
+
+ if (ED_keylist_is_empty(keylist)) {
+ return false;
}
- else {
- first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first);
- last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last);
+
+ const ActKeyColumn *first_column;
+ const ActKeyColumn *last_column;
+ keylist_first_last(keylist, &first_column, &last_column);
+ while (first_column && !(first_column->sel & SELECT)) {
+ first_column = first_column->next;
+ }
+ while (last_column && !(last_column->sel & SELECT)) {
+ last_column = last_column->prev;
+ }
+ if (!first_column || !last_column || first_column == last_column) {
+ return false;
}
r_frame_range->min = first_column->cfra;
r_frame_range->max = last_column->cfra;
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index e227e69f9e1..128126e515e 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -220,8 +220,8 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven
bAnimVizSettings *avs = &ob->pose->avs;
PointerRNA avs_ptr;
- RNA_int_set(op->ptr, "start_frame", avs->path_sf);
- RNA_int_set(op->ptr, "end_frame", avs->path_ef);
+ RNA_enum_set(op->ptr, "display_type", avs->path_type);
+ RNA_enum_set(op->ptr, "range", avs->path_range);
RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location"));
@@ -229,7 +229,7 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven
/* show popup dialog to allow editing of range... */
/* FIXME: hard-coded dimensions here are just arbitrary. */
- return WM_operator_props_dialog_popup(C, op, 200);
+ return WM_operator_props_dialog_popup(C, op, 270);
}
/* For the object with pose/action: create path curves for selected bones
@@ -249,8 +249,9 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op)
bAnimVizSettings *avs = &ob->pose->avs;
PointerRNA avs_ptr;
- avs->path_sf = RNA_int_get(op->ptr, "start_frame");
- avs->path_ef = RNA_int_get(op->ptr, "end_frame");
+ avs->path_type = RNA_enum_get(op->ptr, "display_type");
+ avs->path_range = RNA_enum_get(op->ptr, "range");
+ animviz_motionpath_compute_range(ob, scene);
RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location"));
@@ -258,7 +259,6 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op)
/* set up path data for bones being calculated */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
- /* verify makes sure that the selected bone has a bone with the appropriate settings */
animviz_verify_motionpaths(op->reports, scene, ob, pchan);
}
CTX_DATA_END;
@@ -281,6 +281,19 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static bool pose_calculate_paths_poll(bContext *C)
+{
+ if (!ED_operator_posemode_exclusive(C)) {
+ return false;
+ }
+ Object *ob = CTX_data_active_object(C);
+ bArmature *arm = ob->data;
+ if (ELEM(NULL, ob, arm, ob->pose)) {
+ return false;
+ }
+ return true;
+}
+
void POSE_OT_paths_calculate(wmOperatorType *ot)
{
/* identifiers */
@@ -291,30 +304,24 @@ void POSE_OT_paths_calculate(wmOperatorType *ot)
/* api callbacks */
ot->invoke = pose_calculate_paths_invoke;
ot->exec = pose_calculate_paths_exec;
- ot->poll = ED_operator_posemode_exclusive;
+ ot->poll = pose_calculate_paths_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_int(ot->srna,
- "start_frame",
- 1,
- MINAFRAME,
- MAXFRAME,
- "Start",
- "First frame to calculate bone paths on",
- MINFRAME,
- MAXFRAME / 2.0);
- RNA_def_int(ot->srna,
- "end_frame",
- 250,
- MINAFRAME,
- MAXFRAME,
- "End",
- "Last frame to calculate bone paths on",
- MINFRAME,
- MAXFRAME / 2.0);
+ RNA_def_enum(ot->srna,
+ "display_type",
+ rna_enum_motionpath_display_type_items,
+ MOTIONPATH_TYPE_RANGE,
+ "Display type",
+ "");
+ RNA_def_enum(ot->srna,
+ "range",
+ rna_enum_motionpath_range_items,
+ MOTIONPATH_RANGE_SCENE,
+ "Computation Range",
+ "");
RNA_def_enum(ot->srna,
"bake_location",
@@ -336,7 +343,7 @@ static bool pose_update_paths_poll(bContext *C)
return false;
}
-static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op))
+static int pose_update_paths_exec(bContext *C, wmOperator *op)
{
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
Scene *scene = CTX_data_scene(C);
@@ -344,6 +351,13 @@ static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op))
if (ELEM(NULL, ob, scene)) {
return OPERATOR_CANCELLED;
}
+ animviz_motionpath_compute_range(ob, scene);
+
+ /* set up path data for bones being calculated */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
+ animviz_verify_motionpaths(op->reports, scene, ob, pchan);
+ }
+ CTX_DATA_END;
/* Calculate the bones that now have motion-paths. */
/* TODO: only make for the selected bones? */
@@ -427,7 +441,7 @@ static int pose_clear_paths_exec(bContext *C, wmOperator *op)
/* operator callback/wrapper */
static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) {
+ if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) {
RNA_boolean_set(op->ptr, "only_selected", true);
}
return pose_clear_paths_exec(C, op);
diff --git a/source/blender/editors/asset/intern/asset_library_reference.cc b/source/blender/editors/asset/intern/asset_library_reference.cc
index 04f77821114..5096b9d653d 100644
--- a/source/blender/editors/asset/intern/asset_library_reference.cc
+++ b/source/blender/editors/asset/intern/asset_library_reference.cc
@@ -17,9 +17,9 @@ AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryRef
bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b)
{
- return (a.type == b.type) && (a.type == ASSET_LIBRARY_CUSTOM) ?
- (a.custom_library_index == b.custom_library_index) :
- true;
+ return (a.type == b.type) &&
+ ((a.type == ASSET_LIBRARY_CUSTOM) ? (a.custom_library_index == b.custom_library_index) :
+ true);
}
uint64_t AssetLibraryReferenceWrapper::hash() const
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 20a2251b6e1..a33fbb29f85 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -72,7 +72,7 @@ static bool curve_delete_vertices(Object *obedit, View3D *v3d);
ListBase *object_editcurve_get(Object *ob)
{
- if (ob && ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ if (ob && ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = ob->data;
return &cu->editnurb->nurbs;
}
@@ -1238,7 +1238,7 @@ void ED_curve_editnurb_load(Main *bmain, Object *obedit)
return;
}
- if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
ListBase newnurb = {NULL, NULL}, oldnurb = cu->nurb;
@@ -1273,7 +1273,7 @@ void ED_curve_editnurb_make(Object *obedit)
EditNurb *editnurb = cu->editnurb;
KeyBlock *actkey;
- if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
actkey = BKE_keyblock_from_object(obedit);
if (actkey) {
@@ -5637,7 +5637,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
}
/* First test: curve? */
- if (obedit->type != OB_CURVE) {
+ if (obedit->type != OB_CURVES_LEGACY) {
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) {
as_curve = true;
@@ -5646,7 +5646,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- if (obedit->type == OB_CURVE || as_curve) {
+ if (obedit->type == OB_CURVES_LEGACY || as_curve) {
changed = ed_editcurve_extrude(cu, editnurb, v3d);
}
else {
@@ -6715,7 +6715,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
- if (obedit->type != OB_CURVE) {
+ if (obedit->type != OB_CURVES_LEGACY) {
continue;
}
@@ -6874,7 +6874,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
cu = ob_active->data;
BLI_movelisttolist(&cu->nurb, &tempbase);
- if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) {
+ if (ob_active->type == OB_CURVES_LEGACY && CU_IS_2D(cu)) {
/* Account for mixed 2D/3D curves when joining */
BKE_curve_dimension_update(cu);
}
@@ -6984,7 +6984,7 @@ static bool match_texture_space_poll(bContext *C)
{
Object *object = CTX_data_active_object(C);
- return object && ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT);
+ return object && ELEM(object->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT);
}
static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 2aaebf494a6..d7201495f75 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -53,25 +53,25 @@ static const char *get_curve_defname(int type)
if ((type & CU_TYPE) == CU_BEZIER) {
switch (stype) {
case CU_PRIM_CURVE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCurve");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCurve");
case CU_PRIM_CIRCLE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCircle");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCircle");
case CU_PRIM_PATH:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "CurvePath");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "CurvePath");
default:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve");
}
}
else {
switch (stype) {
case CU_PRIM_CURVE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCurve");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCurve");
case CU_PRIM_CIRCLE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCircle");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCircle");
case CU_PRIM_PATH:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsPath");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsPath");
default:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve");
}
}
}
@@ -82,17 +82,17 @@ static const char *get_surf_defname(int type)
switch (stype) {
case CU_PRIM_CURVE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCurve");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCurve");
case CU_PRIM_CIRCLE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCircle");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle");
case CU_PRIM_PATCH:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfPatch");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch");
case CU_PRIM_SPHERE:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfSphere");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere");
case CU_PRIM_DONUT:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfTorus");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfTorus");
default:
- return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Surface");
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Surface");
}
}
@@ -510,11 +510,11 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf)
}
if (!isSurf) { /* adding curve */
- if (obedit == NULL || obedit->type != OB_CURVE) {
+ if (obedit == NULL || obedit->type != OB_CURVES_LEGACY) {
const char *name = get_curve_defname(type);
Curve *cu;
- obedit = ED_object_add_type(C, OB_CURVE, name, loc, rot, true, local_view_bits);
+ obedit = ED_object_add_type(C, OB_CURVES_LEGACY, name, loc, rot, true, local_view_bits);
newob = true;
cu = (Curve *)obedit->data;
diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c
index 7b68c859b43..888bb2169e0 100644
--- a/source/blender/editors/curve/editcurve_undo.c
+++ b/source/blender/editors/curve/editcurve_undo.c
@@ -162,7 +162,7 @@ static Object *editcurve_object_from_context(bContext *C)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
- if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
if (BKE_curve_editNurbs_get(cu) != NULL) {
return obedit;
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index b2af0643e2c..02c7f3856e8 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1640,7 +1640,9 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
EditFont *ef = cu->editfont;
static int accentcode = 0;
uintptr_t ascii = event->ascii;
- int alt = event->alt, shift = event->shift, ctrl = event->ctrl;
+ const bool alt = event->modifier & KM_ALT;
+ const bool shift = event->modifier & KM_SHIFT;
+ const bool ctrl = event->modifier & KM_CTRL;
int event_type = event->type, event_val = event->val;
char32_t inserted_text[2] = {0};
diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt
index d2b7dacbc20..1731d224b3e 100644
--- a/source/blender/editors/curves/CMakeLists.txt
+++ b/source/blender/editors/curves/CMakeLists.txt
@@ -6,6 +6,7 @@ set(INC
../../blenlib
../../blentranslation
../../depsgraph
+ ../../functions
../../makesdna
../../makesrna
../../windowmanager
@@ -13,6 +14,7 @@ set(INC
)
set(SRC
+ intern/curves_add.cc
intern/curves_ops.cc
)
diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc
new file mode 100644
index 00000000000..9cde23451dc
--- /dev/null
+++ b/source/blender/editors/curves/intern/curves_add.cc
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edcurves
+ */
+
+#include "BLI_rand.hh"
+
+#include "BKE_curves.hh"
+
+#include "ED_curves.h"
+
+namespace blender::ed::curves {
+
+bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve)
+{
+ bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size);
+
+ MutableSpan<int> offsets = curves.offsets();
+ MutableSpan<float3> positions = curves.positions();
+
+ float *radius_data = (float *)CustomData_add_layer_named(
+ &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius");
+ MutableSpan<float> radii{radius_data, curves.points_size()};
+
+ for (const int i : offsets.index_range()) {
+ offsets[i] = points_per_curve * i;
+ }
+
+ RandomNumberGenerator rng;
+
+ for (const int i : curves.curves_range()) {
+ const IndexRange curve_range = curves.range_for_curve(i);
+ MutableSpan<float3> curve_positions = positions.slice(curve_range);
+ MutableSpan<float> curve_radii = radii.slice(curve_range);
+
+ const float theta = 2.0f * M_PI * rng.get_float();
+ const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
+
+ float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)};
+ no = math::normalize(no);
+
+ float3 co = no;
+ for (int key = 0; key < points_per_curve; key++) {
+ float t = key / (float)(points_per_curve - 1);
+ curve_positions[key] = co;
+ curve_radii[key] = 0.02f * (1.0f - t);
+
+ float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f;
+ co += (offset + no) / points_per_curve;
+ }
+ }
+
+ return curves;
+}
+
+} // namespace blender::ed::curves
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index fdda8e636f7..52a55ae1760 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -4,67 +4,8 @@
* \ingroup edcurves
*/
-#include "BLI_utildefines.h"
-
#include "ED_curves.h"
-#include "ED_object.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-#include "BKE_context.h"
-
-#include "RNA_access.h"
-#include "RNA_define.h"
-#include "RNA_types.h"
-
-static bool curves_sculptmode_toggle_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr) {
- return false;
- }
- if (ob->type != OB_CURVES) {
- return false;
- }
- return true;
-}
-
-static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_active_object(C);
- const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
-
- if (is_mode_set) {
- if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) {
- return OPERATOR_CANCELLED;
- }
- }
-
- if (is_mode_set) {
- ob->mode = OB_MODE_OBJECT;
- }
- else {
- ob->mode = OB_MODE_SCULPT_CURVES;
- }
-
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
- return OPERATOR_CANCELLED;
-}
-
-static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
-{
- ot->name = "Curve Sculpt Mode Toggle";
- ot->idname = "CURVES_OT_sculptmode_toggle";
- ot->description = "Enter/Exit sculpt mode for curves";
-
- ot->exec = curves_sculptmode_toggle_exec;
- ot->poll = curves_sculptmode_toggle_poll;
-
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
-}
void ED_operatortypes_curves()
{
- WM_operatortype_append(CURVES_OT_sculptmode_toggle);
}
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 aea6d41202e..447fe1005a1 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -147,7 +147,7 @@ static void move3d_get_translate(const wmGizmo *gz,
float co_delta[3])
{
MoveInteraction *inter = gz->interaction_data;
- const float mval_delta[2] = {
+ const float xy_delta[2] = {
event->mval[0] - inter->init.mval[0],
event->mval[1] - inter->init.mval[1],
};
@@ -155,9 +155,9 @@ static void move3d_get_translate(const wmGizmo *gz,
RegionView3D *rv3d = region->regiondata;
float co_ref[3];
mul_v3_mat3_m4v3(co_ref, gz->matrix_space, inter->init.prop_co);
- const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
+ const float zfac = ED_view3d_calc_zfac(rv3d, co_ref);
- ED_view3d_win_to_delta(region, mval_delta, co_delta, zfac);
+ ED_view3d_win_to_delta(region, xy_delta, zfac, co_delta);
float matrix_space_inv[3][3];
copy_m3_m4(matrix_space_inv, gz->matrix_space);
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 163b5657326..5ab4a663efe 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -326,8 +326,7 @@ static void annotation_stroke_convertcoords(tGPsdata *p,
}
else {
float mval_prj[2];
- float rvec[3], dvec[3];
- float zfac;
+ float rvec[3];
/* Current method just converts each point in screen-coordinates to
* 3D-coordinates using the 3D-cursor as reference. In general, this
@@ -339,13 +338,14 @@ static void annotation_stroke_convertcoords(tGPsdata *p,
*/
annotation_get_3d_reference(p, rvec);
- zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec);
if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- float mval_f[2];
- sub_v2_v2v2(mval_f, mval_prj, mval);
- ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, mval);
+ ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec);
sub_v3_v3v3(out, rvec, dvec);
}
else {
@@ -2060,7 +2060,7 @@ static void annotation_draw_apply_event(
p->mval[1] = (float)event->mval[1] - y;
/* Key to toggle stabilization. */
- if (event->shift && p->paintmode == GP_PAINTMODE_DRAW) {
+ if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) {
/* Using permanent stabilization, shift will deactivate the flag. */
if (p->flags & GP_PAINTFLAG_USE_STABILIZER) {
if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
@@ -2075,7 +2075,7 @@ static void annotation_draw_apply_event(
}
}
/* verify key status for straight lines */
- else if (event->ctrl || event->alt) {
+ else if (event->modifier & (KM_CTRL | KM_ALT)) {
if (p->straight[0] == 0) {
int dx = abs((int)(p->mval[0] - p->mvalo[0]));
int dy = abs((int)(p->mval[1] - p->mvalo[1]));
@@ -2299,7 +2299,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev
p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP;
annotation_draw_toggle_stabilizer_cursor(p, true);
}
- else if (event->shift) {
+ else if (event->modifier & KM_SHIFT) {
p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP;
annotation_draw_toggle_stabilizer_cursor(p, true);
}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 63239fd6341..d4518f21586 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1300,8 +1300,8 @@ static void gpencil_layer_to_curve(bContext *C,
/* init the curve object (remove rotation and get curve data from it)
* - must clear transforms set on object, as those skew our results
*/
- ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info);
- cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE);
+ ob = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, gpl->info);
+ cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVES_LEGACY);
BKE_collection_object_add(bmain, collection, ob);
base_new = BKE_view_layer_base_find(view_layer, ob);
DEG_relations_tag_update(bmain); /* added object */
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 680b313c47b..d734fb2678e 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -4356,6 +4356,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
const float length = RNA_float_get(op->ptr, "length");
+ const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold");
/* sanity checks */
if (ELEM(NULL, gpd)) {
@@ -4365,7 +4366,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
/* Go through each editable + selected stroke */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_stroke_sample(gpd, gps, length, true);
+ BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 8be34a35ca9..069493025dc 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1097,7 +1097,7 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
/**
* Naive dilate
*
- * Expand green areas into enclosing red areas.
+ * Expand green areas into enclosing red or transparent areas.
* Using stack prevents creep when replacing colors directly.
* <pre>
* -----------
@@ -1110,8 +1110,8 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
*/
static bool dilate_shape(ImBuf *ibuf)
{
-#define IS_RED (color[0] == 1.0f)
#define IS_GREEN (color[1] == 1.0f)
+#define IS_NOT_GREEN (color[1] != 1.0f)
bool done = false;
@@ -1140,7 +1140,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v - 1 >= 0) {
index = v - 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
lt = index;
}
@@ -1149,7 +1149,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v + 1 <= maxpixel) {
index = v + 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
rt = index;
}
@@ -1158,7 +1158,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v + ibuf->x <= max_size) {
index = v + ibuf->x;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
tp = index;
}
@@ -1167,7 +1167,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v - ibuf->x >= 0) {
index = v - ibuf->x;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
bm = index;
}
@@ -1176,7 +1176,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (tp && lt) {
index = tp - 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@@ -1184,7 +1184,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (tp && rt) {
index = tp + 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@@ -1192,7 +1192,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (bm && lt) {
index = bm - 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@@ -1200,7 +1200,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (bm && rt) {
index = bm + 1;
get_pixel(ibuf, index, color);
- if (IS_RED) {
+ if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@@ -1218,8 +1218,8 @@ static bool dilate_shape(ImBuf *ibuf)
return done;
-#undef IS_RED
#undef IS_GREEN
+#undef IS_NOT_GREEN
}
/**
@@ -1239,7 +1239,7 @@ static bool contract_shape(ImBuf *ibuf)
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
const int max_size = (ibuf->x * ibuf->y) - 1;
- /* Detect if pixel is near of no green pixels and mark green to be cleared. */
+ /* Detect if pixel is near of no green pixels and mark green pixel to be cleared. */
for (int row = 0; row < ibuf->y; row++) {
if (!is_row_filled(ibuf, row)) {
continue;
@@ -2172,7 +2172,8 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
tgpf->on_back = RNA_boolean_get(op->ptr, "on_back");
const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN;
- const bool is_inverted = (is_brush_inv && !event->ctrl) || (!is_brush_inv && event->ctrl);
+ const bool is_inverted = (is_brush_inv && (event->modifier & KM_CTRL) == 0) ||
+ (!is_brush_inv && (event->modifier & KM_CTRL) != 0);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd);
const bool do_extend = (tgpf->fill_extend_fac > 0.0f);
const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) ||
@@ -2313,7 +2314,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EVT_PAGEUPKEY:
case WHEELUPMOUSE:
if (tgpf->oldkey == 1) {
- tgpf->fill_extend_fac -= (event->shift) ? 0.01f : 0.1f;
+ tgpf->fill_extend_fac -= (event->modifier & KM_SHIFT) ? 0.01f : 0.1f;
CLAMP_MIN(tgpf->fill_extend_fac, 0.0f);
gpencil_update_extend(tgpf);
}
@@ -2321,7 +2322,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
case EVT_PAGEDOWNKEY:
case WHEELDOWNMOUSE:
if (tgpf->oldkey == 1) {
- tgpf->fill_extend_fac += (event->shift) ? 0.01f : 0.1f;
+ tgpf->fill_extend_fac += (event->modifier & KM_SHIFT) ? 0.01f : 0.1f;
CLAMP_MAX(tgpf->fill_extend_fac, 100.0f);
gpencil_update_extend(tgpf);
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index a6691a12505..5409cea2a2a 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -235,7 +235,7 @@ typedef struct tGPsdata {
/** key used for invoking the operator */
short keymodifier;
/** shift modifier flag */
- short shift;
+ bool shift;
/** size in pixels for uv calculation */
float totpixlen;
/** Special mode for fill brush. */
@@ -447,21 +447,21 @@ static void gpencil_stroke_convertcoords(tGPsdata *p,
}
float mval_prj[2];
- float rvec[3], dvec[3];
- float mval_f[2];
- float zfac;
+ float rvec[3];
/* Current method just converts each point in screen-coordinates to
* 3D-coordinates using the 3D-cursor as reference. In general, this
* works OK, but it could of course be improved. */
gpencil_get_3d_reference(p, rvec);
- zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec);
if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval);
- ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, mval);
+ ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec);
sub_v3_v3v3(out, rvec, dvec);
}
else {
@@ -2841,11 +2841,11 @@ static void gpencil_draw_apply_event(bContext *C,
* add any x,y override position
*/
copy_v2fl_v2i(p->mval, event->mval);
- p->shift = event->shift;
+ p->shift = (event->modifier & KM_SHIFT) != 0;
/* verify direction for straight lines and guides */
if ((is_speed_guide) ||
- (event->alt && (RNA_boolean_get(op->ptr, "disable_straight") == false))) {
+ ((event->modifier & KM_ALT) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) {
if (p->straight == 0) {
int dx = (int)fabsf(p->mval[0] - p->mvali[0]);
int dy = (int)fabsf(p->mval[1] - p->mvali[1]);
@@ -2886,13 +2886,13 @@ static void gpencil_draw_apply_event(bContext *C,
/* special eraser modes */
if (p->paintmode == GP_PAINTMODE_ERASER) {
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
p->flags |= GP_PAINTFLAG_HARD_ERASER;
}
else {
p->flags &= ~GP_PAINTFLAG_HARD_ERASER;
}
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
p->flags |= GP_PAINTFLAG_STROKE_ERASER;
}
else {
@@ -3116,11 +3116,11 @@ static void gpencil_guide_event_handling(bContext *C,
else if ((event->type == EVT_LKEY) && (event->val == KM_RELEASE)) {
add_notifier = true;
guide->use_guide = true;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
guide->angle = 0.0f;
guide->type = GP_GUIDE_PARALLEL;
}
- else if (event->alt) {
+ else if (event->modifier & KM_ALT) {
guide->type = GP_GUIDE_PARALLEL;
guide->angle = RNA_float_get(op->ptr, "guide_last_angle");
}
@@ -3150,10 +3150,10 @@ static void gpencil_guide_event_handling(bContext *C,
add_notifier = true;
float angle = guide->angle;
float adjust = (float)M_PI / 180.0f;
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
adjust *= 45.0f;
}
- else if (!event->shift) {
+ else if ((event->modifier & KM_SHIFT) == 0) {
adjust *= 15.0f;
}
angle += (event->type == EVT_JKEY) ? adjust : -adjust;
@@ -3633,7 +3633,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
*/
}
else if (event->type == EVT_ZKEY) {
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 2d761dd6c91..57a184b0e8d 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1494,7 +1494,7 @@ static void gpencil_primitive_edit_event_handling(
float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
tgpi->cp1[0] += dx;
tgpi->cp1[1] += dy;
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
copy_v2_v2(tgpi->cp2, tgpi->cp1);
}
}
@@ -1503,7 +1503,7 @@ static void gpencil_primitive_edit_event_handling(
float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
tgpi->cp2[0] += dx;
tgpi->cp2[1] += dy;
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
copy_v2_v2(tgpi->cp1, tgpi->cp2);
}
}
@@ -1692,7 +1692,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
copy_v2_v2(tgpi->end, tgpi->mval);
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
gpencil_primitive_constrain(tgpi, true);
}
@@ -1722,7 +1722,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
case EVT_FKEY: /* brush thickness/ brush strength */
{
if ((event->val == KM_PRESS)) {
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
tgpi->prev_flag = tgpi->flag;
tgpi->flag = IN_BRUSH_STRENGTH;
}
@@ -1900,7 +1900,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
case EVT_FKEY: /* brush thickness/ brush strength */
{
if ((event->val == KM_PRESS)) {
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
tgpi->prev_flag = tgpi->flag;
tgpi->flag = IN_BRUSH_STRENGTH;
}
@@ -1954,12 +1954,12 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
copy_v2_v2(tgpi->origin, tgpi->mval);
}
/* Keep square if shift key */
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
gpencil_primitive_constrain(
tgpi, (ELEM(tgpi->type, GP_STROKE_LINE, GP_STROKE_POLYLINE) || tgpi->curve));
}
/* Center primitive if alt key */
- if (event->alt && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) {
+ if ((event->modifier & KM_ALT) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) {
tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
}
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index e8f097d0018..67325e8a3d1 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -507,7 +507,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso)
/* Convert mouse-movements to movement vector */
RegionView3D *rv3d = gso->region->regiondata;
float *rvec = gso->object->loc;
- float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(rv3d, rvec);
float mval_f[2];
@@ -525,7 +525,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso)
copy_v2_v2(mval_f, r);
}
- ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac);
+ ED_view3d_win_to_delta(gso->region, mval_f, zfac, gso->dvec);
}
/* Apply grab transform to all relevant points of the affected strokes */
@@ -624,17 +624,16 @@ static void gpencil_brush_calc_midpoint(tGP_BrushEditData *gso)
*/
RegionView3D *rv3d = gso->region->regiondata;
const float *rvec = gso->object->loc;
- float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(rv3d, rvec);
- float mval_f[2];
- copy_v2_v2(mval_f, gso->mval);
float mval_prj[2];
- float dvec[3];
if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval_f);
- ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, gso->mval);
+ ED_view3d_win_to_delta(gso->region, xy_delta, zfac, dvec);
sub_v3_v3v3(gso->dvec, rvec, dvec);
}
else {
@@ -830,10 +829,10 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso,
/* 3D: Project to 3D space */
bool flip;
RegionView3D *rv3d = gso->region->regiondata;
- float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
+ const float zfac = ED_view3d_calc_zfac_ex(rv3d, &pt->x, &flip);
if (flip == false) {
float dvec[3];
- ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac);
+ ED_view3d_win_to_delta(gso->gsc.region, svec, zfac, dvec);
add_v3_v3(&pt->x, dvec);
/* compute lock axis */
gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
@@ -1883,7 +1882,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "mouse", mouse);
- RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
+ RNA_boolean_set(&itemptr, "pen_flip", (event->modifier & KM_CTRL) != 0);
RNA_boolean_set(&itemptr, "is_start", gso->first);
/* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */
@@ -1895,7 +1894,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const
}
RNA_float_set(&itemptr, "pressure", pressure);
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
gso->brush_prev = gso->brush;
gso->brush = gpencil_sculpt_get_smooth_brush(gso);
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index da263d44fc6..fca4ff84dc5 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -2659,7 +2659,7 @@ static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *eve
RNA_int_set_array(op->ptr, "location", event->mval);
if (!RNA_struct_property_is_set(op->ptr, "use_shift_extend")) {
- RNA_boolean_set(op->ptr, "use_shift_extend", event->shift);
+ RNA_boolean_set(op->ptr, "use_shift_extend", event->modifier & KM_SHIFT);
}
return gpencil_select_exec(C, op);
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index 10be8c3e91e..735759d40ec 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -314,7 +314,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
if (sample > 0.0f) {
/* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
* the sample function already call that. */
- BKE_gpencil_stroke_sample(gpd, gps, sample, false);
+ BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
}
else {
BKE_gpencil_stroke_geometry_update(gpd, gps);
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index c0777ac3105..9a658b68f21 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -822,17 +822,16 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc,
ED_gpencil_drawing_reference_get(scene, gsc->ob, scene->toolsettings->gpencil_v3d_align, rvec);
- float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
+ float zfac = ED_view3d_calc_zfac(rv3d, rvec);
- float mval_f[2], mval_prj[2];
- float dvec[3];
-
- copy_v2_v2(mval_f, screen_co);
+ float mval_prj[2];
if (ED_view3d_project_float_global(gsc->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval_f);
- ED_view3d_win_to_delta(gsc->region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, screen_co);
+ ED_view3d_win_to_delta(gsc->region, xy_delta, zfac, dvec);
sub_v3_v3v3(r_out, rvec, dvec);
return true;
@@ -863,21 +862,21 @@ void gpencil_stroke_convertcoords_tpoint(Scene *scene,
*/
}
else {
- float mval_f[2] = {UNPACK2(point2D->m_xy)};
float mval_prj[2];
- float rvec[3], dvec[3];
- float zfac;
+ float rvec[3];
/* Current method just converts each point in screen-coordinates to
* 3D-coordinates using the 3D-cursor as reference.
*/
ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, rvec);
- zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec);
if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval_f);
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy);
+ ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
sub_v3_v3v3(r_out, rvec, dvec);
}
else {
@@ -2005,19 +2004,19 @@ static void gpencil_stroke_convertcoords(ARegion *region,
const float origin[3],
float out[3])
{
- float mval_f[2] = {UNPACK2(point2D->m_xy)};
float mval_prj[2];
- float rvec[3], dvec[3];
- float zfac;
+ float rvec[3];
copy_v3_v3(rvec, origin);
- zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL);
+ const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec);
if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval_f);
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ float dvec[3];
+ float xy_delta[2];
+ sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy);
+ ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
sub_v3_v3v3(out, rvec, dvec);
}
else {
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 1d1b00e3f08..244942a87ba 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -1241,7 +1241,7 @@ static void gpencil_vertexpaint_brush_apply_event(bContext *C,
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "mouse", mouse);
- RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
+ RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL);
RNA_boolean_set(&itemptr, "is_start", gso->first);
/* Handle pressure sensitivity (which is supplied by tablets). */
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
index 8fe4cba7021..01e73cd2abd 100644
--- a/source/blender/editors/gpencil/gpencil_weight_paint.c
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -698,7 +698,7 @@ static void gpencil_weightpaint_brush_apply_event(bContext *C,
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "mouse", mouse);
- RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
+ RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL);
RNA_boolean_set(&itemptr, "is_start", gso->first);
/* Handle pressure sensitivity (which is supplied by tablets). */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index e6a68f7fab7..4b6f5e4cac6 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -1090,6 +1090,15 @@ void animviz_calc_motionpaths(struct Depsgraph *depsgraph,
bool restore);
/**
+ * Update motion path computation range (in `ob.avs` or `armature.avs`) from user choice in
+ * `ob.avs.path_range` or `arm.avs.path_range`, depending on active user mode.
+ *
+ * \param ob: Object to compute range for (must be provided).
+ * \param scene: Used when scene range is chosen.
+ */
+void animviz_motionpath_compute_range(struct Object *ob, struct Scene *scene);
+
+/**
* Get list of motion paths to be baked for the given object.
* - assumes the given list is ready to be used.
*/
diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h
index 7316b045646..9233b65b2ce 100644
--- a/source/blender/editors/include/ED_curves.h
+++ b/source/blender/editors/include/ED_curves.h
@@ -15,3 +15,14 @@ void ED_operatortypes_curves(void);
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BKE_curves.hh"
+
+namespace blender::ed::curves {
+
+bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
+
+}
+#endif
diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h
new file mode 100644
index 00000000000..8aab1533e25
--- /dev/null
+++ b/source/blender/editors/include/ED_curves_sculpt.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup editors
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ED_operatortypes_sculpt_curves(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h
index 6a8820d0083..fd3d35e1df7 100644
--- a/source/blender/editors/include/ED_keyframes_keylist.h
+++ b/source/blender/editors/include/ED_keyframes_keylist.h
@@ -131,7 +131,13 @@ const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist
const Range2f frame_range);
bool ED_keylist_is_empty(const struct AnimKeylist *keylist);
const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist);
-bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range);
+bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range);
+/**
+ * Return the selected key-frame's range.
+ * \return False If none are selected and does not affect the frame range.
+ */
+bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist,
+ Range2f *r_frame_range);
const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist);
int64_t ED_keylist_array_len(const struct AnimKeylist *keylist);
@@ -183,10 +189,10 @@ void mask_to_keylist(struct bDopeSheet *ads,
/* ActKeyColumn API ---------------- */
-/* Checks if ActKeyColumn has any block data */
+/** Checks if #ActKeyColumn has any block data. */
bool actkeyblock_is_valid(const ActKeyColumn *ac);
-/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */
+/** Checks if #ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds"). */
int actkeyblock_get_valid_hold(const ActKeyColumn *ac);
#ifdef __cplusplus
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index e1b6a935d6d..ee40cc75e1e 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -24,6 +24,7 @@ struct Scene;
struct ScrArea;
struct bContext;
struct bScreen;
+struct PreviewImage;
struct wmWindow;
struct wmWindowManager;
@@ -87,16 +88,13 @@ void ED_preview_shader_job(const struct bContext *C,
ePreviewRenderMethod method);
void ED_preview_icon_render(const struct bContext *C,
struct Scene *scene,
+ struct PreviewImage *prv_img,
struct ID *id,
- unsigned int *rect,
- int sizex,
- int sizey);
+ enum eIconSizes icon_size);
void ED_preview_icon_job(const struct bContext *C,
- void *owner,
+ struct PreviewImage *prv_img,
struct ID *id,
- unsigned int *rect,
- int sizex,
- int sizey,
+ enum eIconSizes icon_size,
bool delay);
void ED_preview_restart_queue_free(void);
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 5b439cb0978..c89df9c3789 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -250,6 +250,12 @@ void ED_area_offscreen_free(struct wmWindowManager *wm,
struct wmWindow *win,
struct ScrArea *area);
+/**
+ * Search all screens, even non-active or overlapping (multiple windows), return the most-likely
+ * area of interest. xy is relative to active window, like all similar functions.
+ */
+ScrArea *ED_area_find_under_cursor(const struct bContext *C, int spacetype, const int xy[2]);
+
ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen);
ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/**
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index bd3a6bce8e8..f235f696ccc 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -106,7 +106,7 @@ void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value);
* \note Shift/Control are not configurable key-bindings.
*/
void apply_keyb_grid(
- int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert);
+ bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert);
/* where else to go ? */
void unpack_menu(struct bContext *C,
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 46c9a1973eb..b1435e76eb2 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -507,9 +507,18 @@ float ED_view3d_pixel_size(const struct RegionView3D *rv3d, const float co[3]);
float ED_view3d_pixel_size_no_ui_scale(const struct RegionView3D *rv3d, const float co[3]);
/**
- * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta
+ * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta.
+ *
+ * \param r_flip: Set to `zfac < 0.0` before the value is made signed.
+ * Since it's important in some cases to know if the value was flipped.
+ *
+ * \return The unsigned depth component of `co` multiplied by `rv3d->persmat` matrix,
+ * with additional sanitation to ensure the result is never negative
+ * as this isn't useful for tool-code.
*/
-float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3], bool *r_flip);
+float ED_view3d_calc_zfac_ex(const struct RegionView3D *rv3d, const float co[3], bool *r_flip);
+/** See #ED_view3d_calc_zfac_ex doc-string. */
+float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3]);
/**
* Calculate a depth value from `co` (result should only be used for comparison).
*/
@@ -627,16 +636,24 @@ bool ED_view3d_win_to_3d_on_plane_int(const struct ARegion *region,
float r_out[3]);
/**
* Calculate a 3d difference vector from 2d window offset.
- * note that #ED_view3d_calc_zfac() must be called first to determine
+ *
+ * \note that #ED_view3d_calc_zfac() must be called first to determine
* the depth used to calculate the delta.
+ *
+ * When the `zfac` is calculated based on a world-space location directly under the cursor,
+ * the value of `r_out` can be subtracted from #RegionView3D.ofs to pan the view
+ * with the contents following the cursor perfectly (without sliding).
+ *
* \param region: The region (used for the window width and height).
- * \param mval: The area relative 2d difference (such as `event->mval[0] - other_x`).
- * \param out: The resulting world-space delta.
+ * \param xy_delta: 2D difference (in pixels) such as `event->mval[0] - other_x`.
+ * \param zfac: The depth result typically calculated by by #ED_view3d_calc_zfac
+ * (see it's doc-string for details).
+ * \param r_out: The resulting world-space delta.
*/
void ED_view3d_win_to_delta(const struct ARegion *region,
- const float mval[2],
- float out[3],
- float zfac);
+ const float xy_delta[2],
+ float zfac,
+ float r_out[3]);
/**
* Calculate a 3d origin from 2d window coordinates.
* \note Orthographic views have a less obvious origin,
@@ -645,23 +662,23 @@ void ED_view3d_win_to_delta(const struct ARegion *region,
*
* \param region: The region (used for the window width and height).
* \param mval: The area relative 2d location (such as event->mval converted to floats).
- * \param out: The resulting normalized world-space direction vector.
+ * \param r_out: The resulting normalized world-space direction vector.
*/
-void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float out[3]);
+void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float r_out[3]);
/**
* Calculate a 3d direction vector from 2d window coordinates.
* This direction vector starts and the view in the direction of the 2d window coordinates.
* In orthographic view all window coordinates yield the same vector.
*
- * \note doesn't rely on ED_view3d_calc_zfac
+ * \note doesn't rely on #ED_view3d_calc_zfac
* for perspective view, get the vector direction to
* the mouse cursor as a normalized vector.
*
* \param region: The region (used for the window width and height).
* \param mval: The area relative 2d location (such as event->mval converted to floats).
- * \param out: The resulting normalized world-space direction vector.
+ * \param r_out: The resulting normalized world-space direction vector.
*/
-void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float out[3]);
+void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float r_out[3]);
/**
* Calculate a 3d segment from 2d window coordinates.
* This ray_start is located at the viewpoint, ray_end is a far point.
@@ -1078,6 +1095,20 @@ bool ED_view3d_persp_ensure(const struct Depsgraph *depsgraph,
struct View3D *v3d,
struct ARegion *region);
+/* Camera view functions. */
+
+/**
+ * Utility to scale zoom level when in camera-view #RegionView3D.camzoom and apply limits.
+ * \return true a change was made.
+ */
+bool ED_view3d_camera_view_zoom_scale(struct RegionView3D *rv3d, const float scale);
+/**
+ * Utility to pan when in camera view.
+ * \param event_ofs: The offset the pan in screen (pixel) coordinates.
+ * \return true when a change was made.
+ */
+bool ED_view3d_camera_view_pan(struct ARegion *region, const float event_ofs[2]);
+
/* Camera lock functions */
/**
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 7bbc8249a97..060c9dc33d6 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2902,7 +2902,7 @@ uiBut *UI_context_active_but_prop_get(const struct bContext *C,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop,
int *r_index);
-void UI_context_active_but_prop_handle(struct bContext *C);
+void UI_context_active_but_prop_handle(struct bContext *C, bool handle_undo);
void UI_context_active_but_clear(struct bContext *C, struct wmWindow *win, struct ARegion *region);
struct wmOperator *UI_context_active_operator_get(const struct bContext *C);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 4819e8ed82c..3619a7ce317 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1418,7 +1418,7 @@ static bool ui_but_event_property_operator_string(const bContext *C,
}
/* This version is only for finding hotkeys for properties.
- * These are set set via a data-path which is appended to the context,
+ * These are set via a data-path which is appended to the context,
* manipulated using operators (see #ctx_toggle_opnames). */
if (ptr->owner_id) {
@@ -3087,11 +3087,11 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
PointerRNA rptr;
/* This is kind of hackish, in theory think we could only ever use the second member of
- * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when
+ * this if/else, since #ui_searchbox_apply() is supposed to always set that pointer when
* we are storing pointers... But keeping str search first for now,
* to try to break as little as possible existing code. All this is band-aids anyway.
- * Fact remains, using editstr as main 'reference' over whole search button thingy
- * is utterly weak and should be redesigned imho, but that's not a simple task. */
+ * Fact remains, using `editstr` as main 'reference' over whole search button thingy
+ * is utterly weak and should be redesigned IMHO, but that's not a simple task. */
if (search_but && search_but->rnasearchprop &&
RNA_property_collection_lookup_string(
&search_but->rnasearchpoin, search_but->rnasearchprop, str, &rptr)) {
@@ -6529,7 +6529,7 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but)
event.type = EVT_BUT_OPEN;
event.val = KM_PRESS;
- event.is_repeat = false;
+ event.flag = 0;
event.customdata = but;
event.customdata_free = false;
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 34a20b91172..07efcdcbe38 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -201,7 +201,7 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *region, void *arg)
/* XXX this guess_opname can potentially return a different keymap
* than being found on adding later... */
wmKeyMap *km = WM_keymap_guess_opname(C, idname);
- wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0);
+ wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0, KM_ANY);
const int kmi_id = kmi->id;
/* This takes ownership of prop, or prop can be NULL for reset. */
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index e72381821e8..eaec1e249b7 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -147,8 +147,7 @@ void datadropper_win_area_find(
*r_win = CTX_wm_window(C);
*r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval);
if (*r_area == NULL) {
- wmWindowManager *wm = CTX_wm_manager(C);
- *r_win = WM_window_find_under_cursor(wm, NULL, *r_win, mval, r_mval);
+ *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval);
if (*r_win) {
screen = WM_window_get_active_screen(*r_win);
*r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval);
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index d6f529f9f94..f3c70e6a96a 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -222,9 +222,9 @@ static void eyedropper_add_palette_color(bContext *C, const float col_conv[4])
static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
{
- const bool only_stroke = ((!event->ctrl) && (!event->shift));
- const bool only_fill = ((!event->ctrl) && (event->shift));
- const bool both = ((event->ctrl) && (event->shift));
+ const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0;
+ const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT));
+ const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT));
float col_conv[4];
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index bbb6bfabdd1..e0b64dcd4d6 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -275,7 +275,7 @@ static void ui_selectcontext_apply(bContext *C,
const double value,
const double value_orig);
-# define IS_ALLSELECT_EVENT(event) ((event)->alt != 0)
+# define IS_ALLSELECT_EVENT(event) (((event)->modifier & KM_ALT) != 0)
/** just show a tinted color so users know its activated */
# define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE
@@ -708,7 +708,8 @@ enum eSnapType {
static enum eSnapType ui_event_to_snap(const wmEvent *event)
{
- return (event->ctrl) ? (event->shift) ? SNAP_ON_SMALL : SNAP_ON : SNAP_OFF;
+ return (event->modifier & KM_CTRL) ? (event->modifier & KM_SHIFT) ? SNAP_ON_SMALL : SNAP_ON :
+ SNAP_OFF;
}
static bool ui_event_is_snap(const wmEvent *event)
@@ -1937,7 +1938,7 @@ static void ui_selectcontext_apply(bContext *C,
/* could check for 'handle_layer_buttons' */
but->func) {
wmWindow *win = CTX_wm_window(C);
- if (!win->eventstate->shift) {
+ if ((win->eventstate->modifier & KM_SHIFT) == 0) {
const int len = RNA_property_array_length(&but->rnapoin, prop);
bool *tmparray = MEM_callocN(sizeof(bool) * len, __func__);
@@ -3747,11 +3748,11 @@ static void ui_do_but_textedit(
case EVT_XKEY:
case EVT_CKEY:
#if defined(__APPLE__)
- if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) ||
- (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) {
+ if (ELEM(event->modifier, KM_OSKEY, KM_CTRL))
#else
- if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) {
+ if (event->modifier == KM_CTRL)
#endif
+ {
if (event->type == EVT_VKEY) {
changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE);
}
@@ -3769,16 +3770,16 @@ static void ui_do_but_textedit(
ui_textedit_move(but,
data,
STRCUR_DIR_NEXT,
- event->shift != 0,
- event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
+ event->modifier & KM_SHIFT,
+ (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
retval = WM_UI_HANDLER_BREAK;
break;
case EVT_LEFTARROWKEY:
ui_textedit_move(but,
data,
STRCUR_DIR_PREV,
- event->shift != 0,
- event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
+ event->modifier & KM_SHIFT,
+ (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
retval = WM_UI_HANDLER_BREAK;
break;
case WHEELDOWNMOUSE:
@@ -3795,7 +3796,7 @@ static void ui_do_but_textedit(
}
ATTR_FALLTHROUGH;
case EVT_ENDKEY:
- ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->shift != 0, STRCUR_JUMP_ALL);
+ ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL);
retval = WM_UI_HANDLER_BREAK;
break;
case WHEELUPMOUSE:
@@ -3812,7 +3813,7 @@ static void ui_do_but_textedit(
}
ATTR_FALLTHROUGH;
case EVT_HOMEKEY:
- ui_textedit_move(but, data, STRCUR_DIR_PREV, event->shift != 0, STRCUR_JUMP_ALL);
+ ui_textedit_move(but, data, STRCUR_DIR_PREV, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL);
retval = WM_UI_HANDLER_BREAK;
break;
case EVT_PADENTER:
@@ -3822,13 +3823,13 @@ static void ui_do_but_textedit(
break;
case EVT_DELKEY:
changed = ui_textedit_delete(
- but, data, 1, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
+ but, data, 1, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
retval = WM_UI_HANDLER_BREAK;
break;
case EVT_BACKSPACEKEY:
changed = ui_textedit_delete(
- but, data, 0, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
+ but, data, 0, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
retval = WM_UI_HANDLER_BREAK;
break;
@@ -3837,10 +3838,9 @@ static void ui_do_but_textedit(
/* Ctrl-A: Select all. */
#if defined(__APPLE__)
/* OSX uses Command-A system-wide, so add it. */
- if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) ||
- (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)))
+ if (ELEM(event->modifier, KM_OSKEY, KM_CTRL))
#else
- if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))
+ if (event->modifier == KM_CTRL)
#endif
{
ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_ALL);
@@ -3859,9 +3859,9 @@ static void ui_do_but_textedit(
button_activate_state(C, but, BUTTON_STATE_EXIT);
}
}
- else if (!IS_EVENT_MOD(event, ctrl, alt, oskey)) {
+ else if ((event->modifier & (KM_CTRL | KM_ALT | KM_OSKEY)) == 0) {
/* Use standard keys for cycling through buttons Tab, Shift-Tab to reverse. */
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
ui_textedit_prev_but(block, but, data);
}
else {
@@ -3874,12 +3874,12 @@ static void ui_do_but_textedit(
case EVT_ZKEY: {
/* Ctrl-Z or Ctrl-Shift-Z: Undo/Redo (allowing for OS-Key on Apple). */
- const bool is_redo = (event->shift != 0);
+ const bool is_redo = (event->modifier & KM_SHIFT);
if (
#if defined(__APPLE__)
- (event->oskey && !IS_EVENT_MOD(event, alt, ctrl)) ||
+ ((event->modifier & KM_OSKEY) && ((event->modifier & (KM_ALT | KM_CTRL)) == 0)) ||
#endif
- (event->ctrl && !IS_EVENT_MOD(event, alt, oskey))) {
+ ((event->modifier & KM_CTRL) && ((event->modifier & (KM_ALT | KM_OSKEY)) == 0))) {
int undo_pos;
const char *undo_str = ui_textedit_undo(
data->undo_stack_text, is_redo ? 1 : -1, &undo_pos);
@@ -4542,19 +4542,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
}
/* always set */
- but->modifier_key = 0;
- if (event->shift) {
- but->modifier_key |= KM_SHIFT;
- }
- if (event->alt) {
- but->modifier_key |= KM_ALT;
- }
- if (event->ctrl) {
- but->modifier_key |= KM_CTRL;
- }
- if (event->oskey) {
- but->modifier_key |= KM_OSKEY;
- }
+ but->modifier_key = event->modifier;
ui_but_update(but);
ED_region_tag_redraw(data->region);
@@ -4633,7 +4621,8 @@ static int ui_do_but_TAB(
const int rna_type = but->rnaprop ? RNA_property_type(but->rnaprop) : 0;
if (is_property && ELEM(rna_type, PROP_POINTER, PROP_STRING) && (but->custom_data != NULL) &&
- (event->type == LEFTMOUSE) && ((event->val == KM_DBL_CLICK) || event->ctrl)) {
+ (event->type == LEFTMOUSE) &&
+ ((event->val == KM_DBL_CLICK) || (event->modifier & KM_CTRL))) {
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
return WM_UI_HANDLER_BREAK;
}
@@ -4666,7 +4655,8 @@ static int ui_do_but_TEX(
if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) {
/* pass - allow filesel, enter to execute */
}
- else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) && !event->ctrl) {
+ else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) &&
+ ((event->modifier & KM_CTRL) == 0)) {
/* pass */
}
else {
@@ -4735,7 +4725,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
- if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
+ if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
/* Support Ctrl-Wheel to cycle values on expanded enum rows. */
if (but->type == UI_BTYPE_ROW) {
int type = event->type;
@@ -5325,24 +5315,24 @@ static int ui_do_but_NUM(
}
/* XXX hardcoded keymap check.... */
- if (type == MOUSEPAN && event->ctrl) {
+ if (type == MOUSEPAN && (event->modifier & KM_CTRL)) {
/* allow accumulating values, otherwise scrolling gets preference */
retval = WM_UI_HANDLER_BREAK;
}
- else if (type == WHEELDOWNMOUSE && event->ctrl) {
+ else if (type == WHEELDOWNMOUSE && (event->modifier & KM_CTRL)) {
mx = but->rect.xmin;
but->drawflag &= ~UI_BUT_ACTIVE_RIGHT;
but->drawflag |= UI_BUT_ACTIVE_LEFT;
click = 1;
}
- else if (type == WHEELUPMOUSE && event->ctrl) {
+ else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
mx = but->rect.xmax;
but->drawflag &= ~UI_BUT_ACTIVE_LEFT;
but->drawflag |= UI_BUT_ACTIVE_RIGHT;
click = 1;
}
else if (event->val == KM_PRESS) {
- if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) {
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
retval = WM_UI_HANDLER_BREAK;
}
@@ -5402,7 +5392,7 @@ static int ui_do_but_NUM(
#endif
fac = 1.0f;
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
fac /= 10.0f;
}
@@ -5668,27 +5658,27 @@ static int ui_do_but_SLI(
}
/* XXX hardcoded keymap check.... */
- if (type == MOUSEPAN && event->ctrl) {
+ if ((type == MOUSEPAN) && (event->modifier & KM_CTRL)) {
/* allow accumulating values, otherwise scrolling gets preference */
retval = WM_UI_HANDLER_BREAK;
}
- else if (type == WHEELDOWNMOUSE && event->ctrl) {
+ else if ((type == WHEELDOWNMOUSE) && (event->modifier & KM_CTRL)) {
mx = but->rect.xmin;
click = 2;
}
- else if (type == WHEELUPMOUSE && event->ctrl) {
+ else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
mx = but->rect.xmax;
click = 2;
}
else if (event->val == KM_PRESS) {
- if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) {
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
retval = WM_UI_HANDLER_BREAK;
}
#ifndef USE_ALLSELECT
/* alt-click on sides to get "arrows" like in UI_BTYPE_NUM buttons,
* and match wheel usage above */
- else if (event->type == LEFTMOUSE && event->alt) {
+ else if ((event->type == LEFTMOUSE) && (event->modifier & KM_ALT)) {
int halfpos = BLI_rctf_cent_x(&but->rect);
click = 2;
if (mx < halfpos) {
@@ -5754,8 +5744,13 @@ static int ui_do_but_SLI(
data->multi_data.drag_dir[0] += abs(data->draglastx - mx);
data->multi_data.drag_dir[1] += abs(data->draglasty - my);
#endif
- if (ui_numedit_but_SLI(
- but, data, mx, true, is_motion, event->ctrl != 0, event->shift != 0)) {
+ if (ui_numedit_but_SLI(but,
+ data,
+ mx,
+ true,
+ is_motion,
+ event->modifier & KM_CTRL,
+ event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
@@ -5981,8 +5976,8 @@ static int ui_do_but_LISTROW(bContext *C,
/* hack to pass on ctrl+click and double click to overlapping text
* editing field for editing list item names
*/
- if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS &&
- event->ctrl) ||
+ if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_PRESS) &&
+ (event->modifier & KM_CTRL)) ||
(event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) {
uiBut *labelbut = ui_but_list_row_text_activate(
C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING);
@@ -6023,7 +6018,8 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
return WM_UI_HANDLER_BREAK;
}
if (ui_but_supports_cycling(but)) {
- if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
+ if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) &&
+ (event->modifier & KM_CTRL)) {
int type = event->type;
int val = event->val;
@@ -6210,7 +6206,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
return WM_UI_HANDLER_BREAK;
}
- if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
+ if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
ColorPicker *cpicker = but->custom_data;
float hsv_static[3] = {0.0f};
float *hsv = cpicker ? cpicker->hsv_perceptual : hsv_static;
@@ -6269,7 +6265,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
if (color_but->is_pallete_color) {
- if (!event->ctrl) {
+ if ((event->modifier & KM_CTRL) == 0) {
float color[3];
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
@@ -6639,7 +6635,7 @@ static int ui_do_but_HSVCUBE(
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
/* also do drag the first time */
- if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) {
+ if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
@@ -6650,7 +6646,7 @@ static int ui_do_but_HSVCUBE(
const wmNDOFMotionData *ndof = event->customdata;
const enum eSnapType snap = ui_event_to_snap(event);
- ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->shift != 0);
+ ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->modifier & KM_SHIFT);
button_activate_state(C, but, BUTTON_STATE_EXIT);
ui_apply_but(C, but->block, but, data, true);
@@ -6702,7 +6698,7 @@ static int ui_do_but_HSVCUBE(
if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) {
const enum eSnapType snap = ui_event_to_snap(event);
- if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) {
+ if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
}
@@ -6914,7 +6910,7 @@ static int ui_do_but_HSVCIRCLE(
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
/* also do drag the first time */
- if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) {
+ if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
@@ -6925,7 +6921,7 @@ static int ui_do_but_HSVCIRCLE(
const enum eSnapType snap = ui_event_to_snap(event);
const wmNDOFMotionData *ndof = event->customdata;
- ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->shift != 0);
+ ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->modifier & KM_SHIFT);
button_activate_state(C, but, BUTTON_STATE_EXIT);
ui_apply_but(C, but->block, but, data, true);
@@ -6987,7 +6983,7 @@ static int ui_do_but_HSVCIRCLE(
if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) {
const enum eSnapType snap = ui_event_to_snap(event);
- if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) {
+ if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
}
@@ -7037,7 +7033,7 @@ static int ui_do_but_COLORBAND(
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
ColorBand *coba = (ColorBand *)but->poin;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
/* insert new key on mouse location */
const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect);
BKE_colorband_element_add(coba, pos);
@@ -7237,7 +7233,7 @@ static int ui_do_but_CURVE(
float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */
int sel = -1;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
float f_xy[2];
BLI_rctf_transform_pt_v(&cumap->curr, &but->rect, f_xy, m_xy);
@@ -7301,7 +7297,7 @@ static int ui_do_but_CURVE(
if (sel != -1) {
/* ok, we move a point */
/* deselect all if this one is deselect. except if we hold shift */
- if (!event->shift) {
+ if ((event->modifier & KM_SHIFT) == 0) {
for (int a = 0; a < cuma->totpoint; a++) {
cmp[a].flag &= ~CUMA_SELECT;
}
@@ -7336,8 +7332,8 @@ static int ui_do_but_CURVE(
data,
event->xy[0],
event->xy[1],
- event->ctrl != 0,
- event->shift != 0)) {
+ event->modifier & KM_CTRL,
+ event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
}
@@ -7350,7 +7346,7 @@ static int ui_do_but_CURVE(
if (data->dragchange == false) {
/* deselect all, select one */
- if (!event->shift) {
+ if ((event->modifier & KM_SHIFT) == 0) {
for (int a = 0; a < cuma->totpoint; a++) {
cmp[a].flag &= ~CUMA_SELECT;
}
@@ -7539,7 +7535,7 @@ static int ui_do_but_CURVEPROFILE(
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
const float m_xy[2] = {mx, my};
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
float f_xy[2];
BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
@@ -7616,7 +7612,7 @@ static int ui_do_but_CURVEPROFILE(
/* Change the flag for the point(s) if one was selected or added. */
if (i_selected != -1) {
/* Deselect all if this one is deselected, except if we hold shift. */
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
pts[i_selected].flag ^= selection_type;
}
else {
@@ -7647,7 +7643,7 @@ static int ui_do_but_CURVEPROFILE(
if (event->type == MOUSEMOVE) {
if (mx != data->draglastx || my != data->draglasty) {
if (ui_numedit_but_CURVEPROFILE(
- block, but, data, mx, my, event->ctrl != 0, event->shift != 0)) {
+ block, but, data, mx, my, event->modifier & KM_CTRL, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
}
@@ -7871,7 +7867,7 @@ static int ui_do_but_TRACKPREVIEW(
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
/* also do drag the first time */
- if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) {
+ if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
@@ -7888,7 +7884,7 @@ static int ui_do_but_TRACKPREVIEW(
}
else if (event->type == MOUSEMOVE) {
if (mx != data->draglastx || my != data->draglasty) {
- if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) {
+ if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) {
ui_numedit_apply(C, block, but, data);
}
}
@@ -7918,8 +7914,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* handle copy and paste */
- bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) &&
- !event->shift;
+ bool is_press_ctrl_but_no_shift = (event->val == KM_PRESS) &&
+ (event->modifier & (KM_CTRL | KM_OSKEY)) &&
+ (event->modifier & KM_SHIFT) == 0;
const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift;
const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift;
@@ -7934,12 +7931,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
/* do copy first, because it is the only allowed operator when disabled */
if (do_copy) {
- ui_but_copy(C, but, event->alt);
+ ui_but_copy(C, but, event->modifier & KM_ALT);
return WM_UI_HANDLER_BREAK;
}
/* handle menu */
- if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) &&
+
+ if ((event->type == RIGHTMOUSE) &&
+ (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0 &&
(event->val == KM_PRESS)) {
/* For some button types that are typically representing entire sets of data, right-clicking
* to spawn the context menu should also activate the item. This makes it clear which item
@@ -7960,7 +7959,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
}
if (do_paste) {
- ui_but_paste(C, but, data, event->alt);
+ ui_but_paste(C, but, data, event->modifier & KM_ALT);
return WM_UI_HANDLER_BREAK;
}
@@ -8793,7 +8792,7 @@ uiBut *UI_context_active_but_prop_get(const bContext *C,
return activebut;
}
-void UI_context_active_but_prop_handle(bContext *C)
+void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
{
uiBut *activebut = ui_context_rna_button_active(C);
if (activebut) {
@@ -8804,6 +8803,11 @@ void UI_context_active_but_prop_handle(bContext *C)
if (block->handle_func) {
block->handle_func(C, block->handle_func_arg, activebut->retval);
}
+ if (handle_undo) {
+ /* Update the button so the undo text uses the correct value. */
+ ui_but_update(activebut);
+ ui_apply_but_undo(activebut);
+ }
}
}
@@ -8942,7 +8946,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg
if (but) {
button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER);
- if (event->alt && but->active) {
+ if ((event->modifier & KM_ALT) && but->active) {
/* Display tool-tips if holding Alt on mouse-over when tool-tips are disabled in the
* preferences. */
but->active->tooltip_force = true;
@@ -8970,7 +8974,7 @@ void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but)
wm_event_init_from_window(win, &event);
event.type = EVT_BUT_OPEN;
event.val = KM_PRESS;
- event.is_repeat = false;
+ event.flag = 0;
event.customdata = but;
event.customdata_free = false;
@@ -9394,7 +9398,7 @@ static int ui_list_activate_hovered_row(bContext *C,
}
}
- const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy;
+ const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy;
uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy);
if (listrow) {
wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype;
@@ -9421,7 +9425,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C,
const wmEvent *event)
{
/* On a tweak event, uses the coordinates from where tweaking was started. */
- const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy;
+ const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy;
const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL);
if (list->dyn_data->custom_drag_optype) {
@@ -9438,7 +9442,7 @@ static int ui_list_handle_click_drag(bContext *C,
ARegion *region,
const wmEvent *event)
{
- if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ if (event->type != LEFTMOUSE) {
return WM_HANDLER_CONTINUE;
}
@@ -9448,7 +9452,7 @@ static int ui_list_handle_click_drag(bContext *C,
bool activate = false;
bool activate_dragging = false;
- if (event->type == EVT_TWEAK_L) {
+ if (event->val == KM_CLICK_DRAG) {
if (is_draggable) {
activate_dragging = true;
activate = true;
@@ -9458,7 +9462,7 @@ static int ui_list_handle_click_drag(bContext *C,
* regular events (including mouse presses to start dragging) and this part only kicks in if it
* hasn't handled the release event. Note that if there's no overlaid button, the row selects
* on the press event already via regular #UI_BTYPE_LISTROW handling. */
- else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) {
+ else if (event->val == KM_CLICK) {
activate = true;
}
@@ -9534,7 +9538,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
ui_pan_to_scroll(event, &type, &val);
/* 'ui_pan_to_scroll' gives the absolute direction. */
- if (event->is_direction_inverted) {
+ if (event->flag & WM_EVENT_SCROLL_INVERT) {
scroll_dir = -1;
}
@@ -9545,14 +9549,14 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
}
- if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ if (event->type == LEFTMOUSE) {
retval = ui_list_handle_click_drag(C, ui_list, region, event);
}
else if (val == KM_PRESS) {
if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) &&
- !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
- ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl &&
- !IS_EVENT_MOD(event, shift, alt, oskey)))) {
+ (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0) ||
+ ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_CTRL) &&
+ (event->modifier & (KM_SHIFT | KM_ALT | KM_OSKEY)) == 0))) {
const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop);
int value, min, max;
@@ -9609,7 +9613,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
retval = WM_UI_HANDLER_BREAK;
}
- else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) {
+ else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_SHIFT)) {
/* We now have proper grip, but keep this anyway! */
if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) {
ui_list->list_grip = dyn_data->visual_height;
@@ -10263,7 +10267,7 @@ static int ui_handle_menu_event(bContext *C,
/* Smooth scrolling for popovers. */
case MOUSEPAN: {
- if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
+ if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
/* pass */
}
else if (!ui_block_is_menu(block)) {
@@ -10285,7 +10289,7 @@ static int ui_handle_menu_event(bContext *C,
}
case WHEELUPMOUSE:
case WHEELDOWNMOUSE: {
- if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
+ if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
/* pass */
}
else if (!ui_block_is_menu(block)) {
@@ -10308,7 +10312,7 @@ static int ui_handle_menu_event(bContext *C,
case EVT_HOMEKEY:
case EVT_ENDKEY:
/* Arrow-keys: only handle for block_loop blocks. */
- if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
+ if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
/* pass */
}
else if (inside || (block->flag & UI_BLOCK_LOOP)) {
@@ -10455,11 +10459,11 @@ static int ui_handle_menu_event(bContext *C,
/* Only respond to explicit press to avoid the event that opened the menu
* activating an item when the key is held. */
- if (event->is_repeat) {
+ if (event->flag & WM_EVENT_IS_REPEAT) {
break;
}
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
act += 10;
}
@@ -10539,10 +10543,10 @@ static int ui_handle_menu_event(bContext *C,
case EVT_YKEY:
case EVT_ZKEY: {
if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK) &&
- !IS_EVENT_MOD(event, shift, ctrl, oskey) &&
+ ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0) &&
/* Only respond to explicit press to avoid the event that opened the menu
* activating an item when the key is held. */
- !event->is_repeat) {
+ (event->flag & WM_EVENT_IS_REPEAT) == 0) {
if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) {
break;
}
@@ -11066,7 +11070,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle
case EVT_YKEY:
case EVT_ZKEY: {
if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) &&
- !IS_EVENT_MOD(event, shift, ctrl, oskey)) {
+ ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0)) {
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->menu_key == event->type) {
ui_but_pie_button_activate(C, but, menu);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index e277aa2e629..9dfc9be2a30 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -573,18 +573,6 @@ int UI_icon_from_event_type(short event_type, short event_value)
else if (event_type == EVT_RIGHTALTKEY) {
event_type = EVT_LEFTALTKEY;
}
- else if (event_type == EVT_TWEAK_L) {
- event_type = LEFTMOUSE;
- event_value = KM_CLICK_DRAG;
- }
- else if (event_type == EVT_TWEAK_M) {
- event_type = MIDDLEMOUSE;
- event_value = KM_CLICK_DRAG;
- }
- else if (event_type == EVT_TWEAK_R) {
- event_type = RIGHTMOUSE;
- event_value = KM_CLICK_DRAG;
- }
DrawInfo *di = g_di_event_list;
do {
@@ -1399,19 +1387,17 @@ static void icon_set_image(const bContext *C,
const bool delay = prv_img->rect[size] != NULL;
icon_create_rect(prv_img, size);
- prv_img->flag[size] |= PRV_RENDERING;
if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) {
/* Job (background) version */
- ED_preview_icon_job(
- C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay);
+ ED_preview_icon_job(C, prv_img, id, size, delay);
}
else {
if (!scene) {
scene = CTX_data_scene(C);
}
/* Immediate version */
- ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
+ ED_preview_icon_render(C, scene, prv_img, id, size);
}
}
@@ -2271,7 +2257,7 @@ int UI_icon_from_idcode(const int idcode)
return ICON_CAMERA_DATA;
case ID_CF:
return ICON_FILE;
- case ID_CU:
+ case ID_CU_LEGACY:
return ICON_CURVE_DATA;
case ID_GD:
return ICON_OUTLINER_DATA_GREASEPENCIL;
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 54a5b496048..bd55d2d9d81 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -492,7 +492,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
PointerRNA *ptr = &but->rnapoin;
PropertyRNA *prop = but->rnaprop;
const int index = POINTER_AS_INT(arg_index);
- const int shift = win->eventstate->shift;
+ const bool shift = win->eventstate->modifier & KM_SHIFT;
const int len = RNA_property_array_length(ptr, prop);
if (!shift) {
@@ -752,7 +752,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
{
wmWindow *win = CTX_wm_window(C);
- if (!win->eventstate->shift) {
+ if ((win->eventstate->modifier & KM_SHIFT) == 0) {
uiBut *but = (uiBut *)arg1;
const int enum_value = POINTER_AS_INT(arg2);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index a96f14d7435..498c22748ce 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -314,7 +314,7 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert
RNA_property_update(C, ptr, prop);
/* as if we pressed the button */
- UI_context_active_but_prop_handle(C);
+ UI_context_active_but_prop_handle(C, false);
/* Since we don't want to undo _all_ edits to settings, eg window
* edits on the screen or on operator settings.
@@ -326,6 +326,19 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert
return OPERATOR_CANCELLED;
}
+static int operator_button_property_finish_with_undo(bContext *C,
+ PointerRNA *ptr,
+ PropertyRNA *prop)
+{
+ /* Perform updates required for this property. */
+ RNA_property_update(C, ptr, prop);
+
+ /* As if we pressed the button. */
+ UI_context_active_but_prop_handle(C, true);
+
+ return OPERATOR_FINISHED;
+}
+
static bool reset_default_button_poll(bContext *C)
{
PointerRNA ptr;
@@ -350,7 +363,7 @@ static int reset_default_button_exec(bContext *C, wmOperator *op)
/* if there is a valid property that is editable... */
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) {
- return operator_button_property_finish(C, &ptr, prop);
+ return operator_button_property_finish_with_undo(C, &ptr, prop);
}
}
@@ -369,7 +382,9 @@ static void UI_OT_reset_default_button(wmOperatorType *ot)
ot->exec = reset_default_button_exec;
/* flags */
- ot->flag = OPTYPE_UNDO;
+ /* Don't set #OPTYPE_UNDO because #operator_button_property_finish_with_undo
+ * is responsible for the undo push. */
+ ot->flag = 0;
/* properties */
RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 2cb0f256b71..c7f2eb230cb 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -2061,8 +2061,8 @@ static void ui_handle_panel_header(const bContext *C,
const uiBlock *block,
const int mx,
const int event_type,
- const short ctrl,
- const short shift)
+ const bool ctrl,
+ const bool shift)
{
Panel *panel = block->panel;
ARegion *region = CTX_wm_region(C);
@@ -2274,7 +2274,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event,
(event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin));
/* If mouse is inside non-tab region, ctrl key is required. */
- if (is_mousewheel && !event->ctrl && !inside_tabregion) {
+ if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) {
return WM_UI_HANDLER_CONTINUE;
}
@@ -2291,7 +2291,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event,
pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
}
else {
- const bool backwards = event->shift;
+ const bool backwards = event->modifier & KM_SHIFT;
pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
if (!pc_dyn) {
/* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
@@ -2349,7 +2349,7 @@ int ui_handler_panel_region(bContext *C,
retval = WM_UI_HANDLER_BREAK;
}
}
- else if ((event->type == EVT_TABKEY && event->ctrl) ||
+ else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) ||
ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
/* Cycle tabs. */
retval = ui_handle_panel_category_cycling(event, region, active_but);
@@ -2386,9 +2386,11 @@ int ui_handler_panel_region(bContext *C,
/* The panel collapse / expand key "A" is special as it takes priority over
* active button handling. */
- if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
+ if (event->type == EVT_AKEY &&
+ ((event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0)) {
retval = WM_UI_HANDLER_BREAK;
- ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
+ ui_handle_panel_header(
+ C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
break;
}
}
@@ -2402,7 +2404,8 @@ int ui_handler_panel_region(bContext *C,
/* All mouse clicks inside panel headers should return in break. */
if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
retval = WM_UI_HANDLER_BREAK;
- ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
+ ui_handle_panel_header(
+ C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
}
else if (event->type == RIGHTMOUSE) {
retval = WM_UI_HANDLER_BREAK;
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 8a945c8c913..4703367671d 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -310,7 +310,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event)
{
- return ui_but_find_mouse_over_ex(region, event->xy, event->ctrl != 0, NULL, NULL);
+ return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, NULL, NULL);
}
uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 09faf493ce7..29553ff65d1 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -701,7 +701,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* Keymap */
/* This is too handy not to expose somehow, let's be sneaky for now. */
- if ((is_label == false) && CTX_wm_window(C)->eventstate->shift) {
+ if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
const char *expr_imports[] = {"bpy", "bl_ui", NULL};
char expr[256];
SNPRINTF(expr,
diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc
index 40675da71a9..6139ac8e702 100644
--- a/source/blender/editors/interface/interface_template_list.cc
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -598,7 +598,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha
}
/**
- * \note Note that \a layout_type may be null.
+ * \note that \a layout_type may be null.
*/
static uiList *ui_list_ensure(bContext *C,
uiListType *ui_list_type,
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 2b7ca1f8b71..32b3bb5e926 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -609,7 +609,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop);
- if (id && CTX_wm_window(C)->eventstate->shift) {
+ if (id && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
/* only way to force-remove data (on save) */
id_us_clear_real(id);
id_fake_user_clear(id);
@@ -635,7 +635,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
- if (CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
if (ID_IS_OVERRIDABLE_LIBRARY(id)) {
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
@@ -731,7 +731,7 @@ static const char *template_id_browse_tip(const StructRNA *type)
return N_("Browse Object to be linked");
case ID_ME:
return N_("Browse Mesh Data to be linked");
- case ID_CU:
+ case ID_CU_LEGACY:
return N_("Browse Curve Data to be linked");
case ID_MB:
return N_("Browse Metaball Data to be linked");
@@ -844,7 +844,7 @@ static uiBut *template_id_def_new_but(uiBlock *block,
BLT_I18NCONTEXT_ID_SCENE,
BLT_I18NCONTEXT_ID_OBJECT,
BLT_I18NCONTEXT_ID_MESH,
- BLT_I18NCONTEXT_ID_CURVE,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY,
BLT_I18NCONTEXT_ID_METABALL,
BLT_I18NCONTEXT_ID_MATERIAL,
BLT_I18NCONTEXT_ID_TEXTURE,
@@ -5539,7 +5539,7 @@ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2)
uiBut *but = arg1;
const int cur = POINTER_AS_INT(arg2);
wmWindow *win = CTX_wm_window(C);
- const int shift = win->eventstate->shift;
+ const bool shift = win->eventstate->modifier & KM_SHIFT;
if (!shift) {
const int tot = RNA_property_array_length(&but->rnapoin, but->rnaprop);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index fbbf3c6fdf1..d1f3843c643 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -2579,7 +2579,7 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp
*
* A lot of places of the UI like the Node Editor or panels are zoomable. In most cases we can
* get the zoom factor from the aspect, but in some cases like popups we need to fall back to
- * using the the size of the element. The latter method relies on the element always being the same
+ * using the size of the element. The latter method relies on the element always being the same
* size.
* \{ */
@@ -4293,7 +4293,7 @@ static void widget_tab(
const bool is_active = (state & UI_SELECT);
/* Draw shaded outline - Disabled for now,
- * seems incorrect and also looks nicer without it imho ;) */
+ * seems incorrect and also looks nicer without it IMHO ;). */
// #define USE_TAB_SHADED_HIGHLIGHT
uchar theme_col_tab_highlight[3];
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 09d57d3ea99..28a025ee581 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -1449,7 +1449,7 @@ static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* tune these until it feels right */
const float zoom_sensitivity = 0.5f;
const float speed = 10.0f; /* match view3d ortho */
- const bool has_translate = (ndof->tvec[0] && ndof->tvec[1]) && view_pan_poll(C);
+ const bool has_translate = !is_zero_v2(ndof->tvec) && view_pan_poll(C);
const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C);
if (has_translate) {
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 28e14a14f5f..1c821eebdee 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -38,12 +38,12 @@ static const EnumPropertyItem io_obj_transform_axis_forward[] = {
{OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"},
{OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"},
{OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"},
- {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z (Default)", "Negative Z axis"},
+ {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"},
{0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem io_obj_transform_axis_up[] = {
{OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"},
- {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y (Default)", "Positive Y axis"},
+ {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"},
{OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"},
{OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"},
{OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"},
@@ -55,7 +55,7 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
{DAG_EVAL_VIEWPORT,
"DAG_EVAL_VIEWPORT",
0,
- "Viewport (Default)",
+ "Viewport",
"Export objects as they appear in the viewport"},
{0, NULL, 0, NULL, NULL}};
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 429db50f321..e53dda1760e 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -546,7 +546,7 @@ static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
value = value_start[vmode] + value * opdata->scale[vmode];
/* Fake shift-transform... */
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
if (opdata->shift_value[vmode] < 0.0f) {
opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
opdata->segments :
diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c
index 58bd906101c..7b251b77750 100644
--- a/source/blender/editors/mesh/editmesh_bisect.c
+++ b/source/blender/editors/mesh/editmesh_bisect.c
@@ -77,14 +77,14 @@ static void mesh_bisect_interactive_calc(bContext *C,
const float *co_ref = rv3d->ofs;
float co_a_ss[2] = {x_start, y_start}, co_b_ss[2] = {x_end, y_end}, co_delta_ss[2];
float co_a[3], co_b[3];
- const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
+ const float zfac = ED_view3d_calc_zfac(rv3d, co_ref);
/* view vector */
ED_view3d_win_to_vector(region, co_a_ss, co_a);
/* view delta */
sub_v2_v2v2(co_delta_ss, co_a_ss, co_b_ss);
- ED_view3d_win_to_delta(region, co_delta_ss, co_b, zfac);
+ ED_view3d_win_to_delta(region, co_delta_ss, zfac, co_b);
/* cross both to get a normal */
cross_v3_v3v3(plane_no, co_a, co_b);
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index 84bda411d4a..bce46dd7cf7 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -58,7 +58,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C,
}
me_eval_needs_free = false;
}
- else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) {
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
me_eval = BKE_mesh_new_nomain_from_curve(ob_eval);
me_eval_needs_free = true;
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c
index 72844908685..c9fc48c3568 100644
--- a/source/blender/editors/mesh/editmesh_loopcut.c
+++ b/source/blender/editors/mesh/editmesh_loopcut.c
@@ -581,7 +581,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event)
handled = true;
break;
case MOUSEPAN:
- if (event->alt == 0) {
+ if ((event->modifier & KM_ALT) == 0) {
cuts += 0.02f * (event->xy[1] - event->prev_xy[1]);
if (cuts < 1 && lcd->cuts >= 1) {
cuts = 1;
@@ -598,7 +598,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (event->val == KM_RELEASE) {
break;
}
- if (event->alt == 0) {
+ if ((event->modifier & KM_ALT) == 0) {
cuts += 1;
}
else {
@@ -612,7 +612,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (event->val == KM_RELEASE) {
break;
}
- if (event->alt == 0) {
+ if ((event->modifier & KM_ALT) == 0) {
cuts = max_ff(cuts - 1, 1);
}
else {
@@ -755,7 +755,8 @@ void MESH_OT_loopcut(wmOperatorType *ot)
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
RNA_def_property_enum_default(prop, PROP_INVSQUARE);
RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
/* For redo only. */
prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", 0, INT_MAX);
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index fc1d60fc768..d8fc7a4f9d4 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -1367,10 +1367,10 @@ static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *e
/* detecting these options based on shift/ctrl here is weak, but it's done
* to make this work when clicking buttons or menus */
if (!RNA_struct_property_is_set(op->ptr, "use_extend")) {
- RNA_boolean_set(op->ptr, "use_extend", event->shift);
+ RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT);
}
if (!RNA_struct_property_is_set(op->ptr, "use_expand")) {
- RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
+ RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL);
}
return edbm_select_mode_exec(C, op);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index a1e661cf2ac..2577218d6a9 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -269,7 +269,8 @@ static void mesh_operator_edgering_props(wmOperatorType *ot,
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
RNA_def_property_enum_default(prop, PROP_SMOOTH);
RNA_def_property_ui_text(prop, "Profile Shape", "Shape of the profile");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
}
static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSubdProps *op_props)
@@ -9640,13 +9641,13 @@ static int edbm_smooth_normals_exec(bContext *C, wmOperator *op)
float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop,
__func__);
- /* This is weird choice of operation, taking all loops of faces of current vertex.
- * Could lead to some rather far away loops weighting as much as very close ones
+ /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current
+ * vertex. Could lead to some rather far away loops weighting as much as very close ones
* (topologically speaking), with complex polygons.
* Using topological distance here (rather than geometrical one)
- * makes sense imho, but would rather go with a more consistent and flexible code,
- * we could even add max topological distance to take into account, * and a weighting curve.
- * Would do that later though, think for now we can live with that choice. --mont29. */
+ * makes sense IMHO, but would rather go with a more consistent and flexible code,
+ * we could even add max topological distance to take into account, and a weighting curve.
+ * Would do that later though, think for now we can live with that choice. */
BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
l = lnor_ed->loop;
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 92f2f859965..417fdca4988 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -632,7 +632,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
return um;
}
-static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *key)
+static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em)
{
BMEditMesh *em_tmp;
BMesh *bm;
@@ -688,29 +688,6 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL;
- /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
- * if the active is a basis for any other. */
- if (key && (key->type == KEY_RELATIVE)) {
- /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume
- * shapenr from restored bmesh and keyblock indices are in sync. */
- const int kb_act_idx = ob->shapenr - 1;
-
- /* If it is, let's patch the current mesh key block to its restored value.
- * Else, the offsets won't be computed and it won't matter. */
- if (BKE_keyblock_is_basis(key, kb_act_idx)) {
- KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx);
-
- if (kb_act->totelem != um->me.totvert) {
- /* The current mesh has some extra/missing verts compared to the undo, adjust. */
- MEM_SAFE_FREE(kb_act->data);
- kb_act->data = MEM_mallocN((size_t)(key->elemsize) * bm->totvert, __func__);
- kb_act->totelem = um->me.totvert;
- }
-
- BKE_keyblock_update_from_mesh(&um->me, kb_act);
- }
- }
-
ob->shapenr = um->shapenr;
MEM_freeN(em_tmp);
@@ -858,7 +835,7 @@ static void mesh_undosys_step_decode(struct bContext *C,
continue;
}
BMEditMesh *em = me->edit_mesh;
- undomesh_to_editmesh(&elem->data, obedit, em, me->key);
+ undomesh_to_editmesh(&elem->data, obedit, em);
em->needs_flush_to_id = 1;
DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY);
}
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index 49a5345d048..b3f90880388 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -21,6 +21,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
@@ -1110,6 +1111,8 @@ static void mesh_add_verts(Mesh *mesh, int len)
mesh->vdata = vdata;
BKE_mesh_update_customdata_pointers(mesh, false);
+ BKE_mesh_runtime_clear_cache(mesh);
+
/* scan the input list and insert the new vertices */
/* set default flags */
@@ -1146,6 +1149,8 @@ static void mesh_add_edges(Mesh *mesh, int len)
mesh->edata = edata;
BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
+ BKE_mesh_runtime_clear_cache(mesh);
+
/* set default flags */
medge = &mesh->medge[mesh->totedge];
for (i = 0; i < len; i++, medge++) {
@@ -1174,6 +1179,8 @@ static void mesh_add_loops(Mesh *mesh, int len)
CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
}
+ BKE_mesh_runtime_clear_cache(mesh);
+
CustomData_free(&mesh->ldata, mesh->totloop);
mesh->ldata = ldata;
BKE_mesh_update_customdata_pointers(mesh, true);
@@ -1205,6 +1212,8 @@ static void mesh_add_polys(Mesh *mesh, int len)
mesh->pdata = pdata;
BKE_mesh_update_customdata_pointers(mesh, true);
+ BKE_mesh_runtime_clear_cache(mesh);
+
/* set default flags */
mpoly = &mesh->mpoly[mesh->totpoly];
for (i = 0; i < len; i++, mpoly++) {
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 5a8708c84b6..f3782c17845 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -412,6 +412,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
* Even though this mesh wont typically have run-time data, the Python API can for e.g.
* create loop-triangle cache here, which is confusing when left in the mesh, see: T90798. */
BKE_mesh_runtime_clear_geometry(me);
+ BKE_mesh_clear_derived_normals(me);
/* new material indices and material array */
if (totmat) {
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 54db59dc2fa..39ccadd1445 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -8,6 +8,7 @@ set(INC
../../blentranslation
../../bmesh
../../depsgraph
+ ../../functions
../../gpencil_modifiers
../../gpu
../../ikplugin
@@ -28,7 +29,7 @@ set(INC
)
set(SRC
- object_add.c
+ object_add.cc
object_bake.c
object_bake_api.c
object_collection.c
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.cc
index c2d811f56dc..7befad3b8d7 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.cc
@@ -5,9 +5,9 @@
* \ingroup edobj
*/
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cctype>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -93,6 +93,7 @@
#include "ED_armature.h"
#include "ED_curve.h"
+#include "ED_curves.h"
#include "ED_gpencil.h"
#include "ED_mball.h"
#include "ED_mesh.h"
@@ -122,7 +123,7 @@ const EnumPropertyItem rna_enum_light_type_items[] = {
{LA_SUN, "SUN", ICON_LIGHT_SUN, "Sun", "Constant direction parallel ray light source"},
{LA_SPOT, "SPOT", ICON_LIGHT_SPOT, "Spot", "Directional cone light source"},
{LA_AREA, "AREA", ICON_LIGHT_AREA, "Area", "Directional area light source"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/* copy from rna_object_force.c */
@@ -140,7 +141,7 @@ static const EnumPropertyItem field_type_items[] = {
{PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", ""},
{PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", ""},
{PFIELD_FLUIDFLOW, "FLUID", ICON_FORCE_FLUIDFLOW, "Fluid Flow", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem lightprobe_type_items[] = {
@@ -159,7 +160,7 @@ static EnumPropertyItem lightprobe_type_items[] = {
ICON_LIGHTPROBE_GRID,
"Irradiance Volume",
"Irradiance probe to capture diffuse indirect lighting"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
enum {
@@ -172,7 +173,7 @@ static const EnumPropertyItem align_options[] = {
{ALIGN_WORLD, "WORLD", 0, "World", "Align the new object to the world"},
{ALIGN_VIEW, "VIEW", 0, "View", "Align the new object to the view"},
{ALIGN_CURSOR, "CURSOR", 0, "3D Cursor", "Use the 3D cursor orientation for the new object"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/** \} */
@@ -198,7 +199,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot)
"X-coordinate (screen space) to place the new object under",
INT_MIN,
INT_MAX);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
prop = RNA_def_int(ot->srna,
"drop_y",
0,
@@ -208,7 +209,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot)
"Y-coordinate (screen space) to place the new object under",
INT_MIN,
INT_MAX);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
}
static bool object_add_drop_xy_is_set(const wmOperator *op)
@@ -343,13 +344,13 @@ float ED_object_new_primitive_matrix(bContext *C,
invert_m3_m3(imat, mat);
mul_m3_v3(imat, r_primmat[3]);
- if (scale != NULL) {
+ if (scale != nullptr) {
rescale_m4(r_primmat, scale);
}
{
- const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) :
- ED_scene_grid_scale(scene, NULL);
+ const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) :
+ ED_scene_grid_scale(scene, nullptr);
return dia;
}
@@ -393,20 +394,20 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode)
if (do_editmode) {
prop = RNA_def_boolean(ot->srna,
"enter_editmode",
- 0,
+ false,
"Enter Edit Mode",
"Enter edit mode when adding this object");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
}
/* NOTE: this property gets hidden for add-camera operator. */
prop = RNA_def_enum(
ot->srna, "align", align_options, ALIGN_WORLD, "Align", "The alignment of the new object");
- RNA_def_property_update_runtime(prop, view_align_update);
+ RNA_def_property_update_runtime(prop, (void *)view_align_update);
prop = RNA_def_float_vector_xyz(ot->srna,
"location",
3,
- NULL,
+ nullptr,
-OBJECT_ADD_SIZE_MAXF,
OBJECT_ADD_SIZE_MAXF,
"Location",
@@ -417,7 +418,7 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode)
prop = RNA_def_float_rotation(ot->srna,
"rotation",
3,
- NULL,
+ nullptr,
-OBJECT_ADD_SIZE_MAXF,
OBJECT_ADD_SIZE_MAXF,
"Rotation",
@@ -429,14 +430,14 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode)
prop = RNA_def_float_vector_xyz(ot->srna,
"scale",
3,
- NULL,
+ nullptr,
-OBJECT_ADD_SIZE_MAXF,
OBJECT_ADD_SIZE_MAXF,
"Scale",
"Scale for the newly added object",
-1000.0f,
1000.0f);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
}
void ED_object_add_mesh_props(wmOperatorType *ot)
@@ -461,11 +462,11 @@ bool ED_object_add_generic_get_opts(bContext *C,
r_enter_editmode = &_enter_editmode;
}
/* Only to ensure the value is _always_ set.
- * Typically the property will exist when the argument is non-NULL. */
+ * Typically the property will exist when the argument is non-nullptr. */
*r_enter_editmode = false;
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode");
- if (prop != NULL) {
+ if (prop != nullptr) {
if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) {
*r_enter_editmode = RNA_property_boolean_get(op->ptr, prop);
}
@@ -571,7 +572,7 @@ bool ED_object_add_generic_get_opts(bContext *C,
copy_v3_fl(r_scale, 1.0f);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale");
- if (prop != NULL) {
+ if (prop != nullptr) {
if (RNA_property_is_set(op->ptr, prop)) {
RNA_property_float_get_array(op->ptr, prop, r_scale);
}
@@ -600,19 +601,19 @@ Object *ED_object_add_type_with_obdata(bContext *C,
{
Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
- if (obedit != NULL) {
+ if (obedit != nullptr) {
ED_object_editmode_exit_ex(bmain, scene, obedit, EM_FREEDATA);
}
}
/* deselects all, sets active object */
Object *ob;
- if (obdata != NULL) {
+ if (obdata != nullptr) {
BLI_assert(type == BKE_object_obdata_to_type(obdata));
ob = BKE_object_add_for_data(bmain, view_layer, type, name, obdata, true);
const short *materials_len_p = BKE_id_material_len_p(obdata);
if (materials_len_p && *materials_len_p > 0) {
- BKE_object_materials_test(bmain, ob, ob->data);
+ BKE_object_materials_test(bmain, ob, static_cast<ID *>(ob->data));
}
}
else {
@@ -630,7 +631,7 @@ Object *ED_object_add_type_with_obdata(bContext *C,
*/
DEG_id_type_tag(bmain, ID_OB);
DEG_relations_tag_update(bmain);
- if (ob->data != NULL) {
+ if (ob->data != nullptr) {
DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS);
}
@@ -657,7 +658,7 @@ Object *ED_object_add_type(bContext *C,
const ushort local_view_bits)
{
return ED_object_add_type_with_obdata(
- C, type, name, loc, rot, enter_editmode, local_view_bits, NULL);
+ C, type, name, loc, rot, enter_editmode, local_view_bits, nullptr);
}
/* for object add operator */
@@ -668,12 +669,12 @@ static int object_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3], radius;
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
radius = RNA_float_get(op->ptr, "radius");
Object *ob = ED_object_add_type(
- C, RNA_enum_get(op->ptr, "type"), NULL, loc, rot, enter_editmode, local_view_bits);
+ C, RNA_enum_get(op->ptr, "type"), nullptr, loc, rot, enter_editmode, local_view_bits);
if (ob->type == OB_LATTICE) {
/* lattice is a special case!
@@ -737,7 +738,7 @@ static int lightprobe_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3];
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
int type = RNA_enum_get(op->ptr, "type");
@@ -829,26 +830,25 @@ static int effector_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3];
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- int type = RNA_enum_get(op->ptr, "type");
+ const ePFieldType type = static_cast<ePFieldType>(RNA_enum_get(op->ptr, "type"));
float dia = RNA_float_get(op->ptr, "radius");
Object *ob;
if (type == PFIELD_GUIDE) {
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
- Curve *cu;
ob = ED_object_add_type(
- C, OB_CURVE, get_effector_defname(type), loc, rot, false, local_view_bits);
+ C, OB_CURVES_LEGACY, get_effector_defname(type), loc, rot, false, local_view_bits);
- cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
cu->flag |= CU_PATH | CU_3D;
ED_object_editmode_enter_ex(bmain, scene, ob, 0);
float mat[4][4];
- ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat);
+ ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat);
mul_mat3_m4_fl(mat, dia);
BLI_addtail(&cu->editnurb->nurbs,
ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1));
@@ -909,22 +909,23 @@ static int object_camera_add_exec(bContext *C, wmOperator *op)
bool enter_editmode;
float loc[3], rot[3];
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- Object *ob = ED_object_add_type(C, OB_CAMERA, NULL, loc, rot, false, local_view_bits);
+ Object *ob = ED_object_add_type(C, OB_CAMERA, nullptr, loc, rot, false, local_view_bits);
if (v3d) {
- if (v3d->camera == NULL) {
+ if (v3d->camera == nullptr) {
v3d->camera = ob;
}
- if (v3d->scenelock && scene->camera == NULL) {
+ if (v3d->scenelock && scene->camera == nullptr) {
scene->camera = ob;
}
}
- Camera *cam = ob->data;
- cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL);
+ Camera *cam = static_cast<Camera *>(ob->data);
+ cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) :
+ ED_scene_grid_scale(scene, nullptr);
return OPERATOR_FINISHED;
}
@@ -969,14 +970,14 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3];
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
bool newob = false;
Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
- if (obedit == NULL || obedit->type != OB_MBALL) {
- obedit = ED_object_add_type(C, OB_MBALL, NULL, loc, rot, true, local_view_bits);
+ if (obedit == nullptr || obedit->type != OB_MBALL) {
+ obedit = ED_object_add_type(C, OB_MBALL, nullptr, loc, rot, true, local_view_bits);
newob = true;
}
else {
@@ -984,7 +985,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op)
}
float mat[4][4];
- ED_object_new_primitive_matrix(C, obedit, loc, rot, NULL, mat);
+ ED_object_new_primitive_matrix(C, obedit, loc, rot, nullptr, mat);
/* Halving here is done to account for constant values from #BKE_mball_element_add.
* While the default radius of the resulting meta element is 2,
* we want to pass in 1 so other values such as resolution are scaled by 1.0. */
@@ -1040,14 +1041,14 @@ static int object_add_text_exec(bContext *C, wmOperator *op)
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
if (obedit && obedit->type == OB_FONT) {
return OPERATOR_CANCELLED;
}
- obedit = ED_object_add_type(C, OB_FONT, NULL, loc, rot, enter_editmode, local_view_bits);
+ obedit = ED_object_add_type(C, OB_FONT, nullptr, loc, rot, enter_editmode, local_view_bits);
BKE_object_obdata_size_init(obedit, RNA_float_get(op->ptr, "radius"));
return OPERATOR_FINISHED;
@@ -1094,11 +1095,11 @@ static int object_armature_add_exec(bContext *C, wmOperator *op)
WM_operator_view3d_unit_defaults(C, op);
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) {
+ C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- if ((obedit == NULL) || (obedit->type != OB_ARMATURE)) {
- obedit = ED_object_add_type(C, OB_ARMATURE, NULL, loc, rot, true, local_view_bits);
+ if ((obedit == nullptr) || (obedit->type != OB_ARMATURE)) {
+ obedit = ED_object_add_type(C, OB_ARMATURE, nullptr, loc, rot, true, local_view_bits);
ED_object_editmode_enter_ex(bmain, scene, obedit, 0);
newob = true;
}
@@ -1106,7 +1107,7 @@ static int object_armature_add_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY);
}
- if (obedit == NULL) {
+ if (obedit == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Cannot create editmode armature");
return OPERATOR_CANCELLED;
}
@@ -1155,10 +1156,11 @@ static int object_empty_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3];
WM_operator_view3d_unit_defaults(C, op);
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- ob = ED_object_add_type(C, OB_EMPTY, NULL, loc, rot, false, local_view_bits);
+ ob = ED_object_add_type(C, OB_EMPTY, nullptr, loc, rot, false, local_view_bits);
BKE_object_empty_draw_type_set(ob, type);
BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius"));
@@ -1192,7 +1194,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
{
Scene *scene = CTX_data_scene(C);
- Image *ima = NULL;
+ Image *ima = nullptr;
ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
if (!ima) {
@@ -1201,7 +1203,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
/* handled below */
id_us_min(&ima->id);
- Object *ob = NULL;
+ Object *ob = nullptr;
Object *ob_cursor = ED_view3d_give_object_under_cursor(C, event->mval);
/* either change empty under cursor or create a new empty */
@@ -1216,10 +1218,10 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
float rot[3];
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', NULL, rot, NULL, NULL, &local_view_bits, NULL)) {
+ C, op, 'Z', nullptr, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, rot, false, local_view_bits);
+ ob = ED_object_add_type(C, OB_EMPTY, nullptr, nullptr, rot, false, local_view_bits);
ED_object_location_from_view(C, ob->loc);
ED_view3d_cursor3d_position(C, event->mval, false, ob->loc);
@@ -1229,9 +1231,9 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
BKE_object_empty_draw_type_set(ob, OB_EMPTY_IMAGE);
- id_us_min(ob->data);
+ id_us_min(static_cast<ID *>(ob->data));
ob->data = ima;
- id_us_plus(ob->data);
+ id_us_plus(static_cast<ID *>(ob->data));
return OPERATOR_FINISHED;
}
@@ -1253,16 +1255,17 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- prop = RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "Filepath", "Path to image file");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_string(ot->srna, "filepath", nullptr, FILE_MAX, "Filepath", "Path to image file");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
RNA_def_boolean(ot->srna,
"relative_path",
true,
"Relative Path",
"Select the file relative to the blend file");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
- prop = RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Image name to assign");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+ prop = RNA_def_string(
+ ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
ED_object_add_generic_props(ot, false);
}
@@ -1277,7 +1280,7 @@ static bool object_gpencil_add_poll(bContext *C)
Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
- if ((scene == NULL) || (ID_IS_LINKED(scene))) {
+ if ((scene == nullptr) || (ID_IS_LINKED(scene))) {
return false;
}
@@ -1293,7 +1296,7 @@ static bool object_gpencil_add_poll(bContext *C)
static int object_gpencil_add_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C), *ob_orig = ob;
- bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL;
+ bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? static_cast<bGPdata *>(ob->data) : nullptr;
const int type = RNA_enum_get(op->ptr, "type");
const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front");
@@ -1307,12 +1310,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* NOTE: We use 'Y' here (not 'Z'), as. */
WM_operator_view3d_unit_defaults(C, op);
- if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Y', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
/* Add new object if not currently editing a GP object. */
- if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) {
- const char *ob_name = NULL;
+ if ((gpd == nullptr) || (GPENCIL_ANY_MODE(gpd) == false)) {
+ const char *ob_name = nullptr;
switch (type) {
case GP_EMPTY: {
ob_name = "GPencil";
@@ -1338,12 +1342,12 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
}
ob = ED_object_add_type(C, OB_GPENCIL, ob_name, loc, rot, true, local_view_bits);
- gpd = ob->data;
+ gpd = static_cast<bGPdata *>(ob->data);
newob = true;
}
else {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, nullptr);
}
/* create relevant geometry */
@@ -1351,7 +1355,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
case GP_EMPTY: {
float mat[4][4];
- ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat);
+ ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat);
ED_gpencil_create_blank(C, ob, mat);
break;
}
@@ -1389,7 +1393,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ED_gpencil_create_lineart(C, ob);
- gpd = ob->data;
+ gpd = static_cast<bGPdata *>(ob->data);
/* Add Line Art modifier */
LineartGpencilModifierData *md = (LineartGpencilModifierData *)BKE_gpencil_modifier_new(
@@ -1458,21 +1462,21 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "radius", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "align", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "location", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "rotation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "type", 0, nullptr, ICON_NONE);
int type = RNA_enum_get(op->ptr, "type");
if (ELEM(type, GP_LRT_COLLECTION, GP_LRT_OBJECT, GP_LRT_SCENE)) {
- uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_lights", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_in_front", 0, nullptr, ICON_NONE);
bool in_front = RNA_boolean_get(op->ptr, "use_in_front");
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, !in_front);
- uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE);
- uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "stroke_depth_offset", 0, nullptr, ICON_NONE);
+ uiItemR(col, op->ptr, "stroke_depth_order", 0, nullptr, ICON_NONE);
}
}
@@ -1483,7 +1487,7 @@ static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = {
"2D Layers",
"Display strokes using grease pencil layers to define order"},
{GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
@@ -1565,7 +1569,8 @@ static int object_light_add_exec(bContext *C, wmOperator *op)
float loc[3], rot[3];
WM_operator_view3d_unit_defaults(C, op);
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
ob = ED_object_add_type(C, OB_LAMP, get_light_defname(type), loc, rot, false, local_view_bits);
@@ -1648,7 +1653,8 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op)
update_location_if_necessary = true;
}
else {
- collection = BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection"));
+ collection = static_cast<Collection *>(
+ BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")));
}
if (update_location_if_necessary) {
@@ -1660,11 +1666,12 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op)
}
}
- if (collection == NULL) {
+ if (collection == nullptr) {
return OPERATOR_CANCELLED;
}
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1735,7 +1742,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot)
"Session UUID of the collection to add",
INT32_MIN,
INT32_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
object_add_drop_xy_props(ot);
}
@@ -1751,7 +1758,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot)
static int object_data_instance_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- ID *id = NULL;
+ ID *id = nullptr;
ushort local_view_bits;
float loc[3], rot[3];
@@ -1767,7 +1774,7 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op)
char name[MAX_ID_NAME - 2];
RNA_property_string_get(op->ptr, prop_name, name);
id = BKE_libblock_find_name(bmain, id_type, name);
- if (id == NULL) {
+ if (id == nullptr) {
return OPERATOR_CANCELLED;
}
const int object_type = BKE_object_obdata_to_type(id);
@@ -1782,7 +1789,8 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op)
RNA_property_float_set_array(op->ptr, prop_location, loc);
}
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1829,10 +1837,11 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op)
ushort local_view_bits;
float loc[3], rot[3];
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- Object *ob = ED_object_add_type(C, OB_SPEAKER, NULL, loc, rot, false, local_view_bits);
+ Object *ob = ED_object_add_type(C, OB_SPEAKER, nullptr, loc, rot, false, local_view_bits);
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ob);
/* To make it easier to start using this immediately in NLA, a default sound clip is created
@@ -1840,8 +1849,8 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op)
{
/* create new data for NLA hierarchy */
AnimData *adt = BKE_animdata_ensure_id(&ob->id);
- NlaTrack *nlt = BKE_nlatrack_add(adt, NULL, is_liboverride);
- NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, ob->data);
+ NlaTrack *nlt = BKE_nlatrack_add(adt, nullptr, is_liboverride);
+ NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, static_cast<Speaker *>(ob->data));
strip->start = CFRA;
strip->end += strip->start;
@@ -1852,7 +1861,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op)
BLI_strncpy(nlt->name, DATA_("SoundTrack"), sizeof(nlt->name));
BKE_nlastrip_validate_name(adt, strip);
- WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, nullptr);
}
return OPERATOR_FINISHED;
@@ -1891,15 +1900,21 @@ static bool object_hair_curves_add_poll(bContext *C)
static int object_hair_curves_add_exec(bContext *C, wmOperator *op)
{
+ using namespace blender;
+
ushort local_view_bits;
float loc[3], rot[3];
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- Object *object = ED_object_add_type(C, OB_CURVES, NULL, loc, rot, false, local_view_bits);
+ Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits);
object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
+ Curves *curves_id = static_cast<Curves *>(object->data);
+ bke::CurvesGeometry::wrap(curves_id->geometry) = ed::curves::primitive_random_sphere(500, 8);
+
return OPERATOR_FINISHED;
}
@@ -1938,11 +1953,12 @@ static int object_pointcloud_add_exec(bContext *C, wmOperator *op)
{
ushort local_view_bits;
float loc[3], rot[3];
- if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
+ if (!ED_object_add_generic_get_opts(
+ C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
- Object *object = ED_object_add_type(C, OB_POINTCLOUD, NULL, loc, rot, false, local_view_bits);
+ Object *object = ED_object_add_type(C, OB_POINTCLOUD, nullptr, loc, rot, false, local_view_bits);
object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
return OPERATOR_FINISHED;
@@ -2062,11 +2078,11 @@ static int object_delete_exec(bContext *C, wmOperator *op)
/* FIXME: this will also remove parent from grease pencil from other scenes. */
/* Remove from Grease Pencil parent */
- for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
+ LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->parent != NULL) {
+ if (gpl->parent != nullptr) {
if (gpl->parent == ob) {
- gpl->parent = NULL;
+ gpl->parent = nullptr;
}
}
}
@@ -2123,8 +2139,8 @@ void OBJECT_OT_delete(wmOperatorType *ot)
PropertyRNA *prop;
prop = RNA_def_boolean(
- ot->srna, "use_global", 0, "Delete Globally", "Remove object from all scenes");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->srna, "use_global", false, "Delete Globally", "Remove object from all scenes");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
WM_operator_properties_confirm_or_exec(ot);
}
@@ -2188,7 +2204,7 @@ static void copy_object_set_idnew(bContext *C)
*/
static uint dupliobject_hash(const void *ptr)
{
- const DupliObject *dob = ptr;
+ const DupliObject *dob = static_cast<const DupliObject *>(ptr);
uint hash = BLI_ghashutil_ptrhash(dob->ob);
if (dob->type == OB_DUPLICOLLECTION) {
@@ -2210,7 +2226,7 @@ static uint dupliobject_hash(const void *ptr)
*/
static uint dupliobject_instancer_hash(const void *ptr)
{
- const DupliObject *dob = ptr;
+ const DupliObject *dob = static_cast<const DupliObject *>(ptr);
uint hash = BLI_ghashutil_inthash(dob->persistent_id[0]);
for (int i = 1; (i < MAX_DUPLI_RECUR) && dob->persistent_id[i] != INT_MAX; i++) {
hash ^= (dob->persistent_id[i] ^ i);
@@ -2221,8 +2237,8 @@ static uint dupliobject_instancer_hash(const void *ptr)
/* Compare function that matches dupliobject_hash */
static bool dupliobject_cmp(const void *a_, const void *b_)
{
- const DupliObject *a = a_;
- const DupliObject *b = b_;
+ const DupliObject *a = static_cast<const DupliObject *>(a_);
+ const DupliObject *b = static_cast<const DupliObject *>(b_);
if (a->ob != b->ob) {
return true;
@@ -2255,8 +2271,8 @@ static bool dupliobject_cmp(const void *a_, const void *b_)
/* Compare function that matches dupliobject_instancer_hash. */
static bool dupliobject_instancer_cmp(const void *a_, const void *b_)
{
- const DupliObject *a = a_;
- const DupliObject *b = b_;
+ const DupliObject *a = static_cast<const DupliObject *>(a_);
+ const DupliObject *b = static_cast<const DupliObject *>(b_);
for (int i = 0; (i < MAX_DUPLI_RECUR); i++) {
if (a->persistent_id[i] != b->persistent_id[i]) {
@@ -2280,7 +2296,7 @@ static void make_object_duplilist_real(bContext *C,
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- GHash *parent_gh = NULL, *instancer_gh = NULL;
+ GHash *parent_gh = nullptr, *instancer_gh = nullptr;
Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object);
@@ -2308,19 +2324,19 @@ static void make_object_duplilist_real(bContext *C,
LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) {
Object *ob_src = DEG_get_original_object(dob->ob);
- Object *ob_dst = ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id));
+ Object *ob_dst = static_cast<Object *>(ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id)));
id_us_min(&ob_dst->id);
/* font duplis can have a totcol without material, we get them from parent
* should be implemented better...
*/
- if (ob_dst->mat == NULL) {
+ if (ob_dst->mat == nullptr) {
ob_dst->totcol = 0;
}
BKE_collection_object_add_from(bmain, scene, base->object, ob_dst);
Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
- BLI_assert(base_dst != NULL);
+ BLI_assert(base_dst != nullptr);
ED_object_base_select(base_dst, BA_SELECT);
DEG_id_tag_update(&ob_dst->id, ID_RECALC_SELECT);
@@ -2329,17 +2345,17 @@ static void make_object_duplilist_real(bContext *C,
/* make sure apply works */
BKE_animdata_free(&ob_dst->id, true);
- ob_dst->adt = NULL;
+ ob_dst->adt = nullptr;
- ob_dst->parent = NULL;
+ ob_dst->parent = nullptr;
BKE_constraints_free(&ob_dst->constraints);
- ob_dst->runtime.curve_cache = NULL;
+ ob_dst->runtime.curve_cache = nullptr;
const bool is_dupli_instancer = (ob_dst->transflag & OB_DUPLI) != 0;
ob_dst->transflag &= ~OB_DUPLI;
/* Remove instantiated collection, it's annoying to keep it here
* (and get potentially a lot of usages of it then...). */
id_us_min((ID *)ob_dst->instance_collection);
- ob_dst->instance_collection = NULL;
+ ob_dst->instance_collection = nullptr;
copy_m4_m4(ob_dst->obmat, dob->mat);
BKE_object_apply_mat4(ob_dst, ob_dst->obmat, false, false);
@@ -2365,7 +2381,7 @@ static void make_object_duplilist_real(bContext *C,
LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) {
Object *ob_src = dob->ob;
- Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob);
+ Object *ob_dst = static_cast<Object *>(BLI_ghash_lookup(dupli_gh, dob));
/* Remap new object to itself, and clear again newid pointer of orig object. */
BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0);
@@ -2375,7 +2391,7 @@ static void make_object_duplilist_real(bContext *C,
if (use_hierarchy) {
/* original parents */
Object *ob_src_par = ob_src->parent;
- Object *ob_dst_par = NULL;
+ Object *ob_dst_par = nullptr;
/* find parent that was also made real */
if (ob_src_par) {
@@ -2392,7 +2408,7 @@ static void make_object_duplilist_real(bContext *C,
else {
dob_key.persistent_id[0] = dob->persistent_id[0];
}
- ob_dst_par = BLI_ghash_lookup(parent_gh, &dob_key);
+ ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(parent_gh, &dob_key));
}
if (ob_dst_par) {
@@ -2408,10 +2424,10 @@ static void make_object_duplilist_real(bContext *C,
ob_dst->parent = ob_dst_par;
}
}
- if (use_base_parent && ob_dst->parent == NULL) {
- Object *ob_dst_par = NULL;
+ if (use_base_parent && ob_dst->parent == nullptr) {
+ Object *ob_dst_par = nullptr;
- if (instancer_gh != NULL) {
+ if (instancer_gh != nullptr) {
/* OK to keep most of the members uninitialized,
* they won't be read, this is simply for a hash lookup. */
DupliObject dob_key;
@@ -2421,10 +2437,10 @@ static void make_object_duplilist_real(bContext *C,
memcpy(&dob_key.persistent_id[0],
&dob->persistent_id[1],
sizeof(dob_key.persistent_id[0]) * (MAX_DUPLI_RECUR - 1));
- ob_dst_par = BLI_ghash_lookup(instancer_gh, &dob_key);
+ ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(instancer_gh, &dob_key));
}
- if (ob_dst_par == NULL) {
+ if (ob_dst_par == nullptr) {
/* Default to parenting to root object...
* Always the case when use_hierarchy is false. */
ob_dst_par = base->object;
@@ -2445,18 +2461,18 @@ static void make_object_duplilist_real(bContext *C,
}
if (base->object->transflag & OB_DUPLICOLLECTION && base->object->instance_collection) {
- base->object->instance_collection = NULL;
+ base->object->instance_collection = nullptr;
}
ED_object_base_select(base, BA_DESELECT);
DEG_id_tag_update(&base->object->id, ID_RECALC_SELECT);
- BLI_ghash_free(dupli_gh, NULL, NULL);
+ BLI_ghash_free(dupli_gh, nullptr, nullptr);
if (parent_gh) {
- BLI_ghash_free(parent_gh, NULL, NULL);
+ BLI_ghash_free(parent_gh, nullptr, nullptr);
}
if (instancer_gh) {
- BLI_ghash_free(instancer_gh, NULL, NULL);
+ BLI_ghash_free(instancer_gh, nullptr, nullptr);
}
free_object_duplilist(lb_duplis);
@@ -2488,7 +2504,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op)
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_SCENE, scene);
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
@@ -2511,11 +2527,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot)
RNA_def_boolean(ot->srna,
"use_base_parent",
- 0,
+ false,
"Parent",
"Parent newly created objects to the original instancer");
RNA_def_boolean(
- ot->srna, "use_hierarchy", 0, "Keep Hierarchy", "Maintain parent child relationships");
+ ot->srna, "use_hierarchy", false, "Keep Hierarchy", "Maintain parent child relationships");
}
/** \} */
@@ -2525,7 +2541,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot)
* \{ */
static const EnumPropertyItem convert_target_items[] = {
- {OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "Curve from Mesh or Text objects"},
+ {OB_CURVES_LEGACY,
+ "CURVE",
+ ICON_OUTLINER_OB_CURVE,
+ "Curve",
+ "Curve from Mesh or Text objects"},
{OB_MESH,
"MESH",
ICON_OUTLINER_OB_MESH,
@@ -2547,19 +2567,19 @@ static const EnumPropertyItem convert_target_items[] = {
"Point Cloud",
"Point Cloud from Mesh objects"},
#endif
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
- if (ob->runtime.curve_cache == NULL) {
+ if (ob->runtime.curve_cache == nullptr) {
/* Force creation. This is normally not needed but on operator
* redo we might end up with an object which isn't evaluated yet.
* Also happens in case we are working on a copy of the object
* (all its caches have been nuked then).
*/
- if (ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
- /* We need 'for render' ON here, to enable computing bevel dipslist if needed.
+ if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)) {
+ /* We need 'for render' ON here, to enable computing bevel #DispList if needed.
* Also makes sense anyway, we would not want e.g. to lose hidden parts etc. */
BKE_displist_make_curveTypes(depsgraph, scene, ob, true);
}
@@ -2572,10 +2592,10 @@ static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *
static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
- Curve *curve = ob->data;
+ Curve *curve = static_cast<Curve *>(ob->data);
Mesh *mesh = BKE_mesh_new_from_object_to_bmain(bmain, depsgraph, object_eval, true);
- if (mesh == NULL) {
+ if (mesh == nullptr) {
/* Unable to convert the curve to a mesh. */
return;
}
@@ -2606,9 +2626,9 @@ static bool object_convert_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
Base *base_act = CTX_data_active_base(C);
- Object *obact = base_act ? base_act->object : NULL;
+ Object *obact = base_act ? base_act->object : nullptr;
- if (obact == NULL || obact->data == NULL || ID_IS_LINKED(obact) ||
+ if (obact == nullptr || obact->data == nullptr || ID_IS_LINKED(obact) ||
ID_IS_OVERRIDE_LIBRARY(obact) || ID_IS_OVERRIDE_LIBRARY(obact->data)) {
return false;
}
@@ -2621,7 +2641,7 @@ static bool object_convert_poll(bContext *C)
static Base *duplibase_for_convert(
Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, Base *base, Object *ob)
{
- if (ob == NULL) {
+ if (ob == nullptr) {
ob = base->object;
}
@@ -2636,14 +2656,14 @@ static Base *duplibase_for_convert(
/* XXX: An ugly hack needed because if we re-run depsgraph with some new meta-ball objects
* having same 'family name' as orig ones, they will affect end result of meta-ball computation.
- * For until we get rid of that name-based thingy in MBalls, that should do the trick
- * (this is weak, but other solution (to change name of `obn`) is even worse imho).
+ * For until we get rid of that name-based thingy in meta-balls, that should do the trick
+ * (this is weak, but other solution (to change name of `obn`) is even worse IMHO).
* See T65996. */
const bool is_meta_ball = (obn->type == OB_MBALL);
void *obdata = obn->data;
if (is_meta_ball) {
obn->type = OB_EMPTY;
- obn->data = NULL;
+ obn->data = nullptr;
}
/* XXX Doing that here is stupid, it means we update and re-evaluate the whole depsgraph every
@@ -2675,7 +2695,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- Base *basen = NULL, *basact = NULL;
+ Base *basen = nullptr, *basact = nullptr;
Object *ob1, *obact = CTX_data_active_object(C);
const short target = RNA_enum_get(op->ptr, "target");
bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
@@ -2723,7 +2743,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
* on other objects data masks too, see: T50950. */
{
LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) {
- Base *base = link->ptr.data;
+ Base *base = static_cast<Base *>(link->ptr.data);
Object *ob = base->object;
/* The way object type conversion works currently (enforcing conversion of *all* objects
@@ -2750,8 +2770,8 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) {
- Object *newob = NULL;
- Base *base = link->ptr.data;
+ Object *newob = nullptr;
+ Base *base = static_cast<Base *>(link->ptr.data);
Object *ob = base->object;
if (ob->flag & OB_DONE || !IS_TAGGED(ob->data)) {
@@ -2773,15 +2793,15 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
}
}
- else if (ob->type == OB_MESH && target == OB_CURVE) {
+ else if (ob->type == OB_MESH && target == OB_CURVES_LEGACY) {
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original mesh's usage count. */
- Mesh *me = newob->data;
+ Mesh *me = static_cast<Mesh *>(newob->data);
id_us_min(&me->id);
/* Make a new copy of the mesh. */
@@ -2793,9 +2813,9 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_mesh_to_curve(bmain, depsgraph, scene, newob);
- if (newob->type == OB_CURVE) {
+ if (newob->type == OB_CURVES_LEGACY) {
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
- if (newob->rigidbody_object != NULL) {
+ if (newob->rigidbody_object != nullptr) {
ED_rigidbody_object_remove(bmain, scene, newob);
}
}
@@ -2851,11 +2871,11 @@ static int object_convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original mesh's usage count. */
- Mesh *me = newob->data;
+ Mesh *me = static_cast<Mesh *>(newob->data);
id_us_min(&me->id);
/* Make a new copy of the mesh. */
@@ -2876,11 +2896,11 @@ static int object_convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original mesh's usage count. */
- Mesh *me = newob->data;
+ Mesh *me = static_cast<Mesh *>(newob->data);
id_us_min(&me->id);
/* Make a new copy of the mesh. */
@@ -2912,50 +2932,58 @@ static int object_convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original curve's usage count. */
id_us_min(&((Curve *)newob->data)->id);
/* Make a new copy of the curve. */
- newob->data = BKE_id_copy(bmain, ob->data);
+ newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data));
}
else {
newob = ob;
}
- Curve *cu = newob->data;
+ Curve *cu = static_cast<Curve *>(newob->data);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
- BKE_vfont_to_curve_ex(ob_eval, ob_eval->data, FO_EDIT, &cu->nurb, NULL, NULL, NULL, NULL);
-
- newob->type = OB_CURVE;
- cu->type = OB_CURVE;
+ BKE_vfont_to_curve_ex(ob_eval,
+ static_cast<Curve *>(ob_eval->data),
+ FO_EDIT,
+ &cu->nurb,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ newob->type = OB_CURVES_LEGACY;
+ cu->type = OB_CURVES_LEGACY;
if (cu->vfont) {
id_us_min(&cu->vfont->id);
- cu->vfont = NULL;
+ cu->vfont = nullptr;
}
if (cu->vfontb) {
id_us_min(&cu->vfontb->id);
- cu->vfontb = NULL;
+ cu->vfontb = nullptr;
}
if (cu->vfonti) {
id_us_min(&cu->vfonti->id);
- cu->vfonti = NULL;
+ cu->vfonti = nullptr;
}
if (cu->vfontbi) {
id_us_min(&cu->vfontbi->id);
- cu->vfontbi = NULL;
+ cu->vfontbi = nullptr;
}
if (!keep_original) {
/* other users */
if (ID_REAL_USERS(&cu->id) > 1) {
- for (ob1 = bmain->objects.first; ob1; ob1 = ob1->id.next) {
+ for (ob1 = static_cast<Object *>(bmain->objects.first); ob1;
+ ob1 = static_cast<Object *>(ob1->id.next)) {
if (ob1->data == ob->data) {
- ob1->type = OB_CURVE;
+ ob1->type = OB_CURVES_LEGACY;
DEG_id_tag_update(&ob1->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
}
@@ -2985,22 +3013,22 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f);
gpencilConverted = true;
gpencilCurveConverted = true;
- basen = NULL;
+ basen = nullptr;
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
ob->flag |= OB_DONE;
if (target == OB_MESH) {
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original curve's usage count. */
id_us_min(&((Curve *)newob->data)->id);
/* make a new copy of the curve */
- newob->data = BKE_id_copy(bmain, ob->data);
+ newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data));
}
else {
newob = ob;
@@ -3013,7 +3041,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_object_free_curve_cache(newob);
}
else if (target == OB_GPENCIL) {
- if (ob->type != OB_CURVE) {
+ if (ob->type != OB_CURVES_LEGACY) {
ob->flag &= ~OB_DONE;
BKE_report(op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported");
}
@@ -3047,23 +3075,24 @@ static int object_convert_exec(bContext *C, wmOperator *op)
basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, baseob);
newob = basen->object;
- MetaBall *mb = newob->data;
+ MetaBall *mb = static_cast<MetaBall *>(newob->data);
id_us_min(&mb->id);
newob->data = BKE_mesh_add(bmain, "Mesh");
newob->type = OB_MESH;
- Mesh *me = newob->data;
+ Mesh *me = static_cast<Mesh *>(newob->data);
me->totcol = mb->totcol;
if (newob->totcol) {
- me->mat = MEM_dupallocN(mb->mat);
+ me->mat = static_cast<Material **>(MEM_dupallocN(mb->mat));
for (a = 0; a < newob->totcol; a++) {
id_us_plus((ID *)me->mat[a]);
}
}
object_data_convert_ensure_curve_cache(depsgraph, scene, baseob);
- BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data);
+ BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp,
+ static_cast<Mesh *>(newob->data));
if (obact->type == OB_MBALL) {
basact = basen;
@@ -3077,11 +3106,11 @@ static int object_convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL);
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original point cloud's usage count. */
- PointCloud *pointcloud = newob->data;
+ PointCloud *pointcloud = static_cast<PointCloud *>(newob->data);
id_us_min(&pointcloud->id);
/* Make a new copy of the point cloud. */
@@ -3104,7 +3133,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* Ensure new object has consistent material data with its new obdata. */
if (newob) {
- BKE_object_materials_test(bmain, newob, newob->data);
+ BKE_object_materials_test(bmain, newob, static_cast<ID *>(newob->data));
}
/* tag obdata if it was been changed */
@@ -3116,7 +3145,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
basact = basen;
}
- basen = NULL;
+ basen = nullptr;
}
if (!keep_original && (ob->flag & OB_DONE)) {
@@ -3137,7 +3166,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
* their basis happens to be removed first. */
FOREACH_SCENE_OBJECT_BEGIN (scene, ob_mball) {
if (ob_mball->type == OB_MBALL) {
- Object *ob_basis = NULL;
+ Object *ob_basis = nullptr;
if (!BKE_mball_is_basis(ob_mball) &&
((ob_basis = BKE_mball_basis_find(scene, ob_mball)) && (ob_basis->flag & OB_DONE))) {
ED_object_base_free_and_unlink(bmain, scene, ob_mball);
@@ -3159,7 +3188,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* Remove curves and meshes converted to Grease Pencil object. */
if (gpencilConverted) {
FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) {
- if (ELEM(ob_delete->type, OB_CURVE, OB_MESH)) {
+ if (ELEM(ob_delete->type, OB_CURVES_LEGACY, OB_MESH)) {
if (ob_delete->flag & OB_DONE) {
ED_object_base_free_and_unlink(bmain, scene, ob_delete);
}
@@ -3172,7 +3201,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* 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)) {
+ if (ELEM(ob_delete->type, OB_CURVES_LEGACY) && (ob_delete->flag & OB_DONE)) {
ED_object_base_free_and_unlink(bmain, scene, ob_delete);
}
}
@@ -3181,7 +3210,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
// XXX ED_object_editmode_enter(C, 0);
- // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */
+ // XXX exit_editmode(C, EM_FREEDATA|); /* free data, but no undo */
if (basact) {
/* active base was changed */
@@ -3208,15 +3237,15 @@ static void object_convert_ui(bContext *UNUSED(C), wmOperator *op)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE);
- uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "target", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "keep_original", 0, nullptr, 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);
+ uiItemR(layout, op->ptr, "thickness", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "angle", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "seams", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "faces", 0, nullptr, ICON_NONE);
}
}
@@ -3243,14 +3272,14 @@ void OBJECT_OT_convert(wmOperatorType *ot)
ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to");
RNA_def_boolean(ot->srna,
"keep_original",
- 0,
+ false,
"Keep Original",
"Keep original objects instead of replacing them");
prop = RNA_def_float_rotation(ot->srna,
"angle",
0,
- NULL,
+ nullptr,
DEG2RADF(0.0f),
DEG2RADF(180.0f),
"Threshold Angle",
@@ -3260,8 +3289,8 @@ void OBJECT_OT_convert(wmOperatorType *ot)
RNA_def_property_float_default(prop, DEG2RADF(70.0f));
RNA_def_int(ot->srna, "thickness", 5, 1, 100, "Thickness", "", 1, 100);
- RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges");
- RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes");
+ RNA_def_boolean(ot->srna, "seams", false, "Only Seam Edges", "Convert only seam edges");
+ RNA_def_boolean(ot->srna, "faces", true, "Export Faces", "Export faces as filled strokes");
RNA_def_float_distance(ot->srna,
"offset",
0.01f,
@@ -3279,16 +3308,11 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/** \name Duplicate Object Operator
* \{ */
-/*
- * dupflag: a flag made from constants declared in DNA_userdef_types.h
- * The flag tells adduplicate() whether to copy data linked to the object,
- * or to reference the existing data.
- * U.dupflag for default operations or you can construct a flag as python does
- * if the dupflag is 0 then no data will be copied (linked duplicate). */
-
-/* used below, assumes id.new is correct */
-/* leaves selection of base/object unaltered */
-/* Does set ID->newid pointers. */
+/**
+ * - Assumes `id.new` is correct.
+ * - Leaves selection of base/object unaltered.
+ * - Sets #ID.newid pointers.
+ */
static Base *object_add_duplicate_internal(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@@ -3296,18 +3320,19 @@ static Base *object_add_duplicate_internal(Main *bmain,
const eDupli_ID_Flags dupflag,
const eLibIDDuplicateFlags duplicate_options)
{
- Base *base, *basen = NULL;
+ Base *base, *basen = nullptr;
Object *obn;
if (ob->mode & OB_MODE_POSE) {
/* nothing? */
}
else {
- obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options));
+ obn = static_cast<Object *>(
+ ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)));
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
base = BKE_view_layer_base_find(view_layer, ob);
- if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
+ if ((base != nullptr) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
BKE_collection_object_add_from(bmain, scene, ob, obn);
}
else {
@@ -3316,7 +3341,7 @@ static Base *object_add_duplicate_internal(Main *bmain,
}
basen = BKE_view_layer_base_find(view_layer, obn);
- if (base != NULL) {
+ if (base != nullptr) {
basen->local_view_bits = base->local_view_bits;
}
@@ -3325,8 +3350,7 @@ static Base *object_add_duplicate_internal(Main *bmain,
*/
/* XXX: is 2) really a good measure here? */
if (ob->rigidbody_object || ob->rigidbody_constraint) {
- Collection *collection;
- for (collection = bmain->collections.first; collection; collection = collection->id.next) {
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (BKE_collection_has_object(collection, ob)) {
BKE_collection_object_add(bmain, collection, obn);
}
@@ -3349,8 +3373,8 @@ Base *ED_object_add_duplicate(
dupflag,
LIB_ID_DUPLICATE_IS_SUBPROCESS |
LIB_ID_DUPLICATE_IS_ROOT_ID);
- if (basen == NULL) {
- return NULL;
+ if (basen == nullptr) {
+ return nullptr;
}
ob = basen->object;
@@ -3363,7 +3387,7 @@ Base *ED_object_add_duplicate(
/* DAG_relations_tag_update(bmain); */ /* caller must do */
- if (ob->data != NULL) {
+ if (ob->data != nullptr) {
DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS);
}
@@ -3379,7 +3403,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool linked = RNA_boolean_get(op->ptr, "linked");
- const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
+ const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag;
/* We need to handle that here ourselves, because we may duplicate several objects, in which case
* we also want to remap pointers between those... */
@@ -3399,7 +3423,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
ED_object_base_select(base, BA_DESELECT);
ED_object_base_select(basen, BA_SELECT);
- if (basen == NULL) {
+ if (basen == nullptr) {
continue;
}
@@ -3409,7 +3433,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
}
if (basen->object->data) {
- DEG_id_tag_update(basen->object->data, 0);
+ DEG_id_tag_update(static_cast<ID *>(basen->object->data), 0);
}
}
CTX_DATA_END;
@@ -3447,7 +3471,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot)
/* to give to transform */
prop = RNA_def_boolean(ot->srna,
"linked",
- 0,
+ false,
"Linked",
"Duplicate object but not object data, linking to the original data");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -3473,14 +3497,14 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
Base *basen;
Object *ob;
const bool linked = RNA_boolean_get(op->ptr, "linked");
- const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
+ const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag;
char name[MAX_ID_NAME - 2];
/* find object, create fake base */
RNA_string_get(op->ptr, "name", name);
ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
- if (ob == NULL) {
+ if (ob == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Object not found");
return OPERATOR_CANCELLED;
}
@@ -3499,7 +3523,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
*/
LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID);
- if (basen == NULL) {
+ if (basen == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
return OPERATOR_CANCELLED;
}
@@ -3510,7 +3534,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, SEL_DESELECT);
+ ED_object_base_deselect_all(view_layer, nullptr, SEL_DESELECT);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_activate(C, basen);
@@ -3566,11 +3590,11 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
"Linked",
"Duplicate object but not object data, linking to the original data");
- RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add");
+ RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add");
prop = RNA_def_float_matrix(
- ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
object_add_drop_xy_props(ot);
}
@@ -3599,7 +3623,7 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op)
ob = OBACT(view_layer);
}
- if (ob == NULL) {
+ if (ob == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Object not found");
return OPERATOR_CANCELLED;
}
@@ -3615,8 +3639,10 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op)
PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix");
if (RNA_property_is_set(op->ptr, prop_matrix)) {
+ ObjectsInViewLayerParams params = {0};
uint objects_len;
- Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0});
+ Object **objects = BKE_view_layer_array_selected_objects_params(
+ view_layer, nullptr, &objects_len, &params);
float matrix[4][4];
RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]);
@@ -3674,14 +3700,14 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot)
PropertyRNA *prop;
RNA_def_string(ot->srna,
"name",
- NULL,
+ nullptr,
MAX_ID_NAME - 2,
"Name",
"Object name to place (when unset use the active object)");
prop = RNA_def_float_matrix(
- ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
object_add_drop_xy_props(ot);
}
@@ -3696,12 +3722,12 @@ static bool object_join_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
ID_IS_OVERRIDE_LIBRARY(ob->data)) {
return false;
}
- if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) {
+ if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_ARMATURE, OB_GPENCIL)) {
return ED_operator_screenactive(C);
}
return false;
@@ -3740,7 +3766,7 @@ static int object_join_exec(bContext *C, wmOperator *op)
if (ob->type == OB_MESH) {
ret = ED_mesh_join_objects_exec(C, op);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
ret = ED_curve_join_objects_exec(C, op);
}
else if (ob->type == OB_ARMATURE) {
@@ -3802,7 +3828,7 @@ static bool join_shapes_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
+ if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) ||
ID_IS_OVERRIDE_LIBRARY(ob->data)) {
return false;
}
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 30e3f2b0c69..3b40a10eb2a 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -301,6 +301,7 @@ static void bake_targets_refresh(BakeTargets *targets)
Image *ima = targets->images[i].image;
if (ima) {
+ BKE_image_partial_update_mark_full_update(ima);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
@@ -605,7 +606,7 @@ static bool bake_objects_check(Main *bmain,
continue;
}
- if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) {
+ if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL) == false) {
BKE_reportf(reports,
RPT_ERROR,
"Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, "
@@ -1038,19 +1039,18 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
* materials and UVs. */
pixel->seed = v;
- /* Barycentric coordinates, nudged a bit to avoid precision issues that
- * may happen when exactly at the vertex coordinate. */
+ /* Barycentric coordinates. */
if (j == 0) {
- pixel->uv[0] = 1.0f - FLT_EPSILON;
- pixel->uv[1] = FLT_EPSILON / 2.0f;
+ pixel->uv[0] = 1.0f;
+ pixel->uv[1] = 0.0f;
}
else if (j == 1) {
- pixel->uv[0] = FLT_EPSILON / 2.0f;
- pixel->uv[1] = 1.0f - FLT_EPSILON;
+ pixel->uv[0] = 0.0f;
+ pixel->uv[1] = 1.0f;
}
else if (j == 2) {
- pixel->uv[0] = FLT_EPSILON / 2.0f;
- pixel->uv[1] = FLT_EPSILON / 2.0f;
+ pixel->uv[0] = 0.0f;
+ pixel->uv[1] = 0.0f;
}
}
}
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 4ccd35b512b..3f4ed27a175 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -511,7 +511,7 @@ static void test_constraint(
*
* In other cases it should be impossible to have a type mismatch.
*/
- if (ct->tar->type != OB_CURVE) {
+ if (ct->tar->type != OB_CURVES_LEGACY) {
con->flag |= CONSTRAINT_DISABLE;
}
else {
@@ -1443,6 +1443,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
+
+ if (con == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL);
/* Store name temporarily for report. */
@@ -1510,6 +1515,11 @@ static int constraint_apply_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
+
+ if (con == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
bPoseChannel *pchan;
ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
@@ -1602,6 +1612,11 @@ static int constraint_copy_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
+
+ if (con == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
bPoseChannel *pchan;
ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
@@ -1682,6 +1697,11 @@ static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *obact = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, obact, 0);
+
+ if (con == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
bPoseChannel *pchan;
ED_object_constraint_list_from_constraint(obact, con, &pchan);
@@ -2275,7 +2295,8 @@ static bool get_new_constraint_target(
break;
}
- if (((!only_curve) || (ob->type == OB_CURVE)) && ((!only_mesh) || (ob->type == OB_MESH))) {
+ if (((!only_curve) || (ob->type == OB_CURVES_LEGACY)) &&
+ ((!only_mesh) || (ob->type == OB_MESH))) {
/* set target */
*tar_ob = ob;
found = true;
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
index f74556e3639..63513eac965 100644
--- a/source/blender/editors/object/object_data_transform.c
+++ b/source/blender/editors/object/object_data_transform.c
@@ -382,7 +382,7 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)id;
struct Key *key = cu->key;
@@ -505,7 +505,7 @@ void ED_object_data_xform_destroy(struct XFormObjectData *xod_base)
}
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
if (xod->key_data != NULL) {
MEM_freeN(xod->key_data);
@@ -565,7 +565,7 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
BLI_assert(xod_base->is_edit_mode == false); /* Not used currently. */
Curve *cu = (Curve *)xod_base->id;
@@ -670,7 +670,7 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base)
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)xod_base->id;
struct Key *key = cu->key;
@@ -745,7 +745,7 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base)
DEG_id_tag_update(&lt->id, ID_RECALC_GEOMETRY);
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
/* Generic update. */
Curve *cu = (Curve *)xod_base->id;
DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index fd40ac7bc7c..82b14787d9b 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -21,6 +21,7 @@
#include "BLT_translation.h"
+#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
@@ -73,6 +74,7 @@
#include "ED_curve.h"
#include "ED_gpencil.h"
#include "ED_image.h"
+#include "ED_keyframes_keylist.h"
#include "ED_lattice.h"
#include "ED_mball.h"
#include "ED_mesh.h"
@@ -89,7 +91,7 @@
#include "CLG_log.h"
-/* For menu/popup icons etc etc. */
+/* For menu/popup icons etc. */
#include "UI_interface.h"
#include "UI_resources.h"
@@ -340,10 +342,10 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
int index = RNA_int_get(op->ptr, "collection_index");
- const bool extend = (win->eventstate->shift != 0);
+ const bool extend = (win->eventstate->modifier & KM_SHIFT) != 0;
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
- if (win->eventstate->alt != 0) {
+ if (win->eventstate->modifier & KM_ALT) {
index += 10;
}
@@ -568,7 +570,7 @@ static bool ED_object_editmode_load_free_ex(Main *bmain,
*/
DEG_relations_tag_update(bmain);
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
const Curve *cu = obedit->data;
if (cu->editnurb == NULL) {
return false;
@@ -799,12 +801,16 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene);
}
- else if (ELEM(ob->type, OB_SURF, OB_CURVE)) {
+ else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) {
ok = true;
ED_curve_editnurb_make(ob);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene);
}
+ else if (ob->type == OB_CURVES) {
+ ok = true;
+ WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVES, scene);
+ }
if (ok) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -1019,7 +1025,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object)
if (!md) {
if (pd && (pd->shape == PFIELD_SHAPE_SURFACE) &&
!ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) {
- if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) {
+ if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVES_LEGACY)) {
ED_object_modifier_add(NULL, bmain, scene, object, NULL, eModifierType_Surface);
}
}
@@ -1205,30 +1211,32 @@ static int object_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEv
/* set default settings from existing/stored settings */
{
bAnimVizSettings *avs = &ob->avs;
-
- RNA_int_set(op->ptr, "start_frame", avs->path_sf);
- RNA_int_set(op->ptr, "end_frame", avs->path_ef);
+ RNA_enum_set(op->ptr, "display_type", avs->path_type);
+ RNA_enum_set(op->ptr, "range", avs->path_range);
}
/* show popup dialog to allow editing of range... */
/* FIXME: hard-coded dimensions here are just arbitrary. */
- return WM_operator_props_dialog_popup(C, op, 200);
+ return WM_operator_props_dialog_popup(C, op, 270);
}
/* Calculate/recalculate whole paths (avs.path_sf to avs.path_ef) */
static int object_calculate_paths_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- int start = RNA_int_get(op->ptr, "start_frame");
- int end = RNA_int_get(op->ptr, "end_frame");
+ short path_type = RNA_enum_get(op->ptr, "display_type");
+ short path_range = RNA_enum_get(op->ptr, "range");
- /* set up path data for bones being calculated */
+ /* set up path data for objects being calculated */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
- bAnimVizSettings *avs = &ob->avs;
-
- /* grab baking settings from operator settings */
- avs->path_sf = start;
- avs->path_ef = end;
+ /* When operator is not invoked, dismiss the operator settings */
+ if (op->flag & OP_IS_INVOKE) {
+ bAnimVizSettings *avs = &ob->avs;
+ /* grab baking settings from operator settings */
+ avs->path_type = path_type;
+ avs->path_range = path_range;
+ }
+ animviz_motionpath_compute_range(ob, scene);
/* verify that the selected object has the appropriate settings */
animviz_verify_motionpaths(op->reports, scene, ob, NULL);
@@ -1247,9 +1255,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op)
void OBJECT_OT_paths_calculate(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Calculate Object Paths";
+ ot->name = "Calculate Object Motion Paths";
ot->idname = "OBJECT_OT_paths_calculate";
- ot->description = "Calculate motion paths for the selected objects";
+ ot->description = "Generate motion paths for the selected objects";
/* api callbacks */
ot->invoke = object_calculate_paths_invoke;
@@ -1260,24 +1268,18 @@ void OBJECT_OT_paths_calculate(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_int(ot->srna,
- "start_frame",
- 1,
- MINAFRAME,
- MAXFRAME,
- "Start",
- "First frame to calculate object paths on",
- MINFRAME,
- MAXFRAME / 2.0);
- RNA_def_int(ot->srna,
- "end_frame",
- 250,
- MINAFRAME,
- MAXFRAME,
- "End",
- "Last frame to calculate object paths on",
- MINFRAME,
- MAXFRAME / 2.0);
+ RNA_def_enum(ot->srna,
+ "display_type",
+ rna_enum_motionpath_display_type_items,
+ MOTIONPATH_TYPE_RANGE,
+ "Display type",
+ "");
+ RNA_def_enum(ot->srna,
+ "range",
+ rna_enum_motionpath_range_items,
+ MOTIONPATH_RANGE_SCENE,
+ "Computation Range",
+ "");
}
/** \} */
@@ -1296,13 +1298,19 @@ static bool object_update_paths_poll(bContext *C)
return false;
}
-static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op))
+static int object_update_paths_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
if (scene == NULL) {
return OPERATOR_CANCELLED;
}
+ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+ animviz_motionpath_compute_range(ob, scene);
+ /* verify that the selected object has the appropriate settings */
+ animviz_verify_motionpaths(op->reports, scene, ob, NULL);
+ }
+ CTX_DATA_END;
/* calculate the paths for objects that have them (and are tagged to get refreshed) */
ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
@@ -1423,7 +1431,7 @@ static int object_clear_paths_exec(bContext *C, wmOperator *op)
/* operator callback/wrapper */
static int object_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) {
+ if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) {
RNA_boolean_set(op->ptr, "only_selected", true);
}
return object_clear_paths_exec(C, op);
@@ -1453,46 +1461,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Update Motion Paths Range from Scene Operator
- * \{ */
-
-static int object_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Scene *scene = CTX_data_scene(C);
-
- /* Loop over all editable objects in scene. */
- CTX_DATA_BEGIN (C, Object *, ob, editable_objects) {
- /* use Preview Range or Full Frame Range - whichever is in use */
- ob->avs.path_sf = PSFRA;
- ob->avs.path_ef = PEFRA;
-
- /* tag for updates */
- DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
- }
- CTX_DATA_END;
-
- return OPERATOR_FINISHED;
-}
-
-void OBJECT_OT_paths_range_update(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Update Range from Scene";
- ot->idname = "OBJECT_OT_paths_range_update";
- ot->description = "Update frame range for motion paths from the Scene's current frame range";
-
- /* callbacks */
- ot->exec = object_update_paths_range_exec;
- ot->poll = ED_operator_object_active_editable;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Object Shade Smooth/Flat Operator
* \{ */
@@ -1548,7 +1516,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
changed = true;
}
- else if (ELEM(ob->type, OB_SURF, OB_CURVE)) {
+ else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) {
BKE_curve_smooth_flag_set(ob->data, use_smooth);
changed = true;
}
diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c
index 338307dc8ca..dffbb3bedd5 100644
--- a/source/blender/editors/object/object_hook.c
+++ b/source/blender/editors/object/object_hook.c
@@ -342,7 +342,7 @@ static bool object_hook_index_array(Main *bmain,
}
return true;
}
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
ED_curve_editnurb_load(bmain, obedit);
ED_curve_editnurb_make(obedit);
@@ -447,7 +447,7 @@ static void object_hook_select(Object *ob, HookModifierData *hmd)
else if (ob->type == OB_LATTICE) {
select_editlattice_hook(ob, hmd);
}
- else if (ob->type == OB_CURVE) {
+ else if (ob->type == OB_CURVES_LEGACY) {
select_editcurve_hook(ob, hmd);
}
else if (ob->type == OB_SURF) {
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 0aac5957c9d..135c76140c1 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -76,7 +76,6 @@ void OBJECT_OT_shade_flat(struct wmOperatorType *ot);
void OBJECT_OT_paths_calculate(struct wmOperatorType *ot);
void OBJECT_OT_paths_update(struct wmOperatorType *ot);
void OBJECT_OT_paths_clear(struct wmOperatorType *ot);
-void OBJECT_OT_paths_range_update(struct wmOperatorType *ot);
void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot);
void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot);
@@ -97,7 +96,7 @@ void OBJECT_OT_select_more(struct wmOperatorType *ot);
void OBJECT_OT_select_less(struct wmOperatorType *ot);
void OBJECT_OT_select_same_collection(struct wmOperatorType *ot);
-/* object_add.c */
+/* object_add.cc */
void OBJECT_OT_add(struct wmOperatorType *ot);
void OBJECT_OT_add_named(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 509e496c39a..8e9e8558016 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -118,7 +118,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
}
}
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
case OB_MBALL:
@@ -143,7 +143,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
}
break;
case OB_CURVES:
- if (mode & (OB_MODE_SCULPT_CURVES)) {
+ if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) {
return true;
}
break;
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 38a955dca0a..e0d25baec16 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -110,7 +110,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
else if (ob->type == OB_MBALL) {
BKE_displist_make_mball(depsgraph, scene_eval, ob_eval);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false);
}
else if (ob->type == OB_GPENCIL) {
@@ -757,7 +757,7 @@ static bool modifier_apply_obdata(
}
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
Curve *curve = ob->data;
Curve *curve_eval = (Curve *)object_eval->data;
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 35f5ede270d..45ee4daa441 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -45,7 +45,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_paths_calculate);
WM_operatortype_append(OBJECT_OT_paths_update);
WM_operatortype_append(OBJECT_OT_paths_clear);
- WM_operatortype_append(OBJECT_OT_paths_range_update);
WM_operatortype_append(OBJECT_OT_paths_update_visible);
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 8834941d083..3ecf86d14ed 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -169,7 +169,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
}
}
}
- else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) {
+ else if (ELEM(obedit->type, OB_SURF, OB_CURVES_LEGACY)) {
ListBase *editnurb = object_editcurve_get(obedit);
for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) {
@@ -341,7 +341,7 @@ EnumPropertyItem prop_clear_parent_types[] = {
/* Helper for ED_object_parent_clear() - Remove deform-modifiers associated with parent */
static void object_remove_parent_deform_modifiers(Object *ob, const Object *par)
{
- if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVE)) {
+ if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVES_LEGACY)) {
ModifierData *md, *mdn;
/* assume that we only need to remove the first instance of matching deform modifier here */
@@ -363,7 +363,7 @@ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par)
free = true;
}
}
- else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVE)) {
+ else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVES_LEGACY)) {
CurveModifierData *cmd = (CurveModifierData *)md;
if (cmd->object == par) {
free = true;
@@ -531,7 +531,7 @@ bool ED_object_parent_set(ReportList *reports,
switch (partype) {
case PAR_FOLLOW:
case PAR_PATH_CONST: {
- if (par->type != OB_CURVE) {
+ if (par->type != OB_CURVES_LEGACY) {
return false;
}
Curve *cu = par->data;
@@ -626,7 +626,7 @@ bool ED_object_parent_set(ReportList *reports,
*/
/* XXX currently this should only happen for meshes, curves, surfaces,
* 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)) {
+ if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) {
ModifierData *md;
switch (partype) {
@@ -968,7 +968,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE);
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE_RELATIVE);
}
- else if (parent->type == OB_CURVE) {
+ else if (parent->type == OB_CURVES_LEGACY) {
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_CURVE);
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_FOLLOW);
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_PATH_CONST);
@@ -1820,7 +1820,7 @@ static void single_obdata_users(
ob->data,
BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
ob->data = cu = ID_NEW_SET(
@@ -2318,7 +2318,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
const bool success = BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id_root, &obact->id, NULL);
+ bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, NULL);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc
index 44e6fb10528..2e495ac6147 100644
--- a/source/blender/editors/object/object_remesh.cc
+++ b/source/blender/editors/object/object_remesh.cc
@@ -406,7 +406,7 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve
d = cd->slow_mval[0] - mval[0];
}
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
/* Linear mode, enables jumping to any voxel size. */
d = d * 0.0005f;
}
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index f017fea7cdf..9e82abf4d07 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -613,7 +613,7 @@ static int apply_objects_internal(bContext *C,
OB_ARMATURE,
OB_LATTICE,
OB_MBALL,
- OB_CURVE,
+ OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
OB_GPENCIL)) {
@@ -639,13 +639,13 @@ static int apply_objects_internal(bContext *C,
}
}
- if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
ID *obdata = ob->data;
Curve *cu;
cu = ob->data;
- if (((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) {
+ if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) {
BKE_reportf(
reports,
RPT_ERROR,
@@ -811,7 +811,7 @@ static int apply_objects_internal(bContext *C,
MetaBall *mb = ob->data;
BKE_mball_transform(mb, mat, do_props);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = ob->data;
scale = mat3_to_scale(rsmat);
BKE_curve_transform_ex(cu, mat, true, do_props, scale);
@@ -1209,7 +1209,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
do_inverse_offset = true;
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = ob->data;
if (centermode == ORIGIN_TO_CURSOR) {
@@ -1223,7 +1223,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
/* don't allow Z change if curve is 2D */
- if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) {
+ if ((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) {
cent[2] = 0.0;
}
@@ -1861,7 +1861,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
view3d_operator_needs_opengl(C);
- const bool is_translate = (event->ctrl != 0);
+ const bool is_translate = event->modifier & KM_CTRL;
const bool is_translate_init = is_translate && (xfd->is_translate != is_translate);
if (event->type == MOUSEMOVE || is_translate_init) {
diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c
index 19940fbb0fc..cb9c8a92abe 100644
--- a/source/blender/editors/object/object_utils.c
+++ b/source/blender/editors/object/object_utils.c
@@ -64,7 +64,7 @@ bool ED_object_calc_active_center_for_editmode(Object *obedit,
break;
}
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF: {
Curve *cu = obedit->data;
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index af9496263e7..fc815ebe682 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -4684,7 +4684,7 @@ static int brush_edit_init(bContext *C, wmOperator *op)
bedit->ob = ob;
bedit->edit = edit;
- bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min, NULL);
+ bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min);
/* cache view depths and settings for re-use */
PE_set_view3d_data(C, &bedit->data);
@@ -4757,7 +4757,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
switch (pset->brushtype) {
case PE_BRUSH_COMB: {
- const float mval_f[2] = {dx, dy};
+ const float xy_delta[2] = {dx, dy};
data.mval = mval;
data.rad = pe_brush_size_get(scene, brush);
@@ -4771,7 +4771,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
invert_m4_m4(ob->imat, ob->obmat);
- ED_view3d_win_to_delta(region, mval_f, vec, bedit->zfac);
+ ED_view3d_win_to_delta(region, xy_delta, bedit->zfac, vec);
data.dvec = vec;
foreach_mouse_hit_key(&data, brush_comb, selected);
@@ -4963,7 +4963,7 @@ static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *e
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "mouse", mouse);
- RNA_boolean_set(&itemptr, "pen_flip", event->shift != false); /* XXX hardcoded */
+ RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_SHIFT); /* XXX hardcoded */
/* apply */
brush_edit_apply(C, op, &itemptr);
diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc
index 5308d4611e7..03c0f3977b7 100644
--- a/source/blender/editors/render/render_internal.cc
+++ b/source/blender/editors/render/render_internal.cc
@@ -618,6 +618,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
static void current_scene_update(void *rjv, Scene *scene)
{
RenderJob *rj = static_cast<RenderJob *>(rjv);
+
+ if (rj->current_scene != scene) {
+ /* Image must be updated when rendered scene changes. */
+ BKE_image_partial_update_mark_full_update(rj->image);
+ }
+
rj->current_scene = scene;
rj->iuser.scene = scene;
}
@@ -1188,5 +1194,6 @@ void RENDER_OT_shutter_curve_preset(wmOperatorType *ot)
ot->exec = render_shutter_curve_preset_exec;
prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
}
diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc
index 1ca2b01a2cb..be66e87f2e5 100644
--- a/source/blender/editors/render/render_opengl.cc
+++ b/source/blender/editors/render/render_opengl.cc
@@ -599,30 +599,30 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data)
const ID_Type id_type = GS(id->name);
switch (id_type) {
/* Whitelist: */
- case ID_ME: /* Mesh */
- case ID_CU: /* Curve */
- case ID_MB: /* MetaBall */
- case ID_MA: /* Material */
- case ID_TE: /* Tex (Texture) */
- case ID_IM: /* Image */
- case ID_LT: /* Lattice */
- case ID_LA: /* Light */
- case ID_CA: /* Camera */
- case ID_KE: /* Key (shape key) */
- case ID_VF: /* VFont (Vector Font) */
- case ID_TXT: /* Text */
- case ID_SPK: /* Speaker */
- case ID_SO: /* Sound */
- case ID_AR: /* bArmature */
- case ID_NT: /* bNodeTree */
- case ID_PA: /* ParticleSettings */
- case ID_MC: /* MovieClip */
- case ID_MSK: /* Mask */
- case ID_LP: /* LightProbe */
- case ID_CV: /* Curves */
- case ID_PT: /* PointCloud */
- case ID_VO: /* Volume */
- case ID_SIM: /* Simulation */
+ case ID_ME: /* Mesh */
+ case ID_CU_LEGACY: /* Curve */
+ case ID_MB: /* MetaBall */
+ case ID_MA: /* Material */
+ case ID_TE: /* Tex (Texture) */
+ case ID_IM: /* Image */
+ case ID_LT: /* Lattice */
+ case ID_LA: /* Light */
+ case ID_CA: /* Camera */
+ case ID_KE: /* Key (shape key) */
+ case ID_VF: /* VFont (Vector Font) */
+ case ID_TXT: /* Text */
+ case ID_SPK: /* Speaker */
+ case ID_SO: /* Sound */
+ case ID_AR: /* bArmature */
+ case ID_NT: /* bNodeTree */
+ case ID_PA: /* ParticleSettings */
+ case ID_MC: /* MovieClip */
+ case ID_MSK: /* Mask */
+ case ID_LP: /* LightProbe */
+ case ID_CV: /* Curves */
+ case ID_PT: /* PointCloud */
+ case ID_VO: /* Volume */
+ case ID_SIM: /* Simulation */
break;
/* Blacklist: */
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index 6aaf551a88a..eca30a6ac25 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -10,6 +10,7 @@
#include <cmath>
#include <cstdlib>
#include <cstring>
+#include <list>
#ifndef WIN32
# include <unistd.h>
@@ -1370,89 +1371,73 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat
ShaderPreview *sp = static_cast<ShaderPreview *>(customdata);
if (sp->pr_method == PR_ICON_DEFERRED) {
- PreviewImage *prv = static_cast<PreviewImage *>(sp->owner);
- ImBuf *thumb;
- char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(prv));
- ThumbSource source = static_cast<ThumbSource>(deferred_data[0]);
- char *path = &deferred_data[1];
-
- // printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path);
-
- thumb = IMB_thumb_manage(path, THB_LARGE, source);
-
- if (thumb) {
- /* PreviewImage assumes premultiplied alhpa... */
- IMB_premultiply_alpha(thumb);
-
- icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect);
- IMB_freeImBuf(thumb);
- }
+ BLI_assert_unreachable();
+ return;
}
- else {
- ID *id = sp->id;
- short idtype = GS(id->name);
- BLI_assert(id != nullptr);
-
- if (idtype == ID_IM) {
- Image *ima = (Image *)id;
- ImBuf *ibuf = nullptr;
- ImageUser iuser;
- BKE_imageuser_default(&iuser);
+ ID *id = sp->id;
+ short idtype = GS(id->name);
- if (ima == nullptr) {
- return;
- }
+ BLI_assert(id != nullptr);
- /* setup dummy image user */
- iuser.framenr = 1;
- iuser.scene = sp->scene;
-
- /* NOTE(@elubie): this needs to be changed: here image is always loaded if not
- * already there. Very expensive for large images. Need to find a way to
- * only get existing `ibuf`. */
- ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
- if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) {
- BKE_image_release_ibuf(ima, ibuf, nullptr);
- return;
- }
+ if (idtype == ID_IM) {
+ Image *ima = (Image *)id;
+ ImBuf *ibuf = nullptr;
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
- icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect);
+ if (ima == nullptr) {
+ return;
+ }
- *do_update = true;
+ /* setup dummy image user */
+ iuser.framenr = 1;
+ iuser.scene = sp->scene;
+ /* NOTE(@elubie): this needs to be changed: here image is always loaded if not
+ * already there. Very expensive for large images. Need to find a way to
+ * only get existing `ibuf`. */
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
+ if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) {
BKE_image_release_ibuf(ima, ibuf, nullptr);
+ return;
}
- else if (idtype == ID_BR) {
- Brush *br = (Brush *)id;
- br->icon_imbuf = icon_preview_imbuf_from_brush(br);
+ icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect);
- memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint));
+ *do_update = true;
- if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) {
- return;
- }
+ BKE_image_release_ibuf(ima, ibuf, nullptr);
+ }
+ else if (idtype == ID_BR) {
+ Brush *br = (Brush *)id;
- icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect);
+ br->icon_imbuf = icon_preview_imbuf_from_brush(br);
- *do_update = true;
- }
- else if (idtype == ID_SCR) {
- bScreen *screen = (bScreen *)id;
+ memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint));
- ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect);
- *do_update = true;
+ if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) {
+ return;
}
- else {
- /* re-use shader job */
- shader_preview_startjob(customdata, stop, do_update);
- /* world is rendered with alpha=0, so it wasn't displayed
- * this could be render option for sky to, for later */
- if (idtype == ID_WO) {
- set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
- }
+ icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect);
+
+ *do_update = true;
+ }
+ else if (idtype == ID_SCR) {
+ bScreen *screen = (bScreen *)id;
+
+ ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect);
+ *do_update = true;
+ }
+ else {
+ /* re-use shader job */
+ shader_preview_startjob(customdata, stop, do_update);
+
+ /* world is rendered with alpha=0, so it wasn't displayed
+ * this could be render option for sky to, for later */
+ if (idtype == ID_WO) {
+ set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
}
}
}
@@ -1670,6 +1655,197 @@ static void icon_preview_endjob(void *customdata)
}
}
+/**
+ * Background job to manage requests for deferred loading of previews from the hard drive.
+ *
+ * Launches a single job to manage all incoming preview requests. The job is kept running until all
+ * preview requests are done loading (or it's otherwise aborted, e.g. by closing Blender).
+ *
+ * Note that this will use the OS thumbnail cache, i.e. load a preview from there or add it if not
+ * there yet. These two cases may lead to different performance.
+ */
+class PreviewLoadJob {
+ struct RequestedPreview {
+ PreviewImage *preview;
+ /** Requested size. */
+ eIconSizes icon_size;
+ };
+
+ /** The previews that are still to be loaded. */
+ ThreadQueue *todo_queue_; /* RequestedPreview * */
+ /** All unfinished preview requests, #update_fn() calls #finish_preview_request() on loaded
+ * previews and removes them from this list. Only access from the main thread! */
+ std::list<struct RequestedPreview> requested_previews_;
+
+ public:
+ PreviewLoadJob();
+ ~PreviewLoadJob();
+
+ static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *);
+ static void load_jobless(PreviewImage *, eIconSizes);
+
+ void push_load_request(PreviewImage *, eIconSizes);
+
+ private:
+ static void run_fn(void *, short *, short *, float *);
+ static void update_fn(void *);
+ static void end_fn(void *);
+ static void free_fn(void *);
+
+ /** Mark a single requested preview as being done, remove the request. */
+ static void finish_request(RequestedPreview &);
+};
+
+PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init())
+{
+}
+
+PreviewLoadJob::~PreviewLoadJob()
+{
+ BLI_thread_queue_free(todo_queue_);
+}
+
+PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win)
+{
+ wmJob *wm_job = WM_jobs_get(wm, win, nullptr, "Load Previews", 0, WM_JOB_TYPE_LOAD_PREVIEW);
+
+ if (!WM_jobs_is_running(wm_job)) {
+ PreviewLoadJob *job_data = MEM_new<PreviewLoadJob>("PreviewLoadJobData");
+
+ WM_jobs_customdata_set(wm_job, job_data, free_fn);
+ WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW);
+ WM_jobs_callbacks(wm_job, run_fn, nullptr, update_fn, end_fn);
+
+ WM_jobs_start(wm, wm_job);
+ }
+
+ return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job));
+}
+
+void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size)
+{
+ PreviewLoadJob job_data{};
+
+ job_data.push_load_request(preview, icon_size);
+
+ short stop = 0, do_update = 0;
+ float progress = 0;
+ run_fn(&job_data, &stop, &do_update, &progress);
+ update_fn(&job_data);
+ end_fn(&job_data);
+}
+
+void PreviewLoadJob::push_load_request(PreviewImage *preview, const eIconSizes icon_size)
+{
+ BLI_assert(preview->tag & PRV_TAG_DEFFERED);
+ RequestedPreview requested_preview{};
+ requested_preview.preview = preview;
+ requested_preview.icon_size = icon_size;
+
+ preview->flag[icon_size] |= PRV_RENDERING;
+ /* Warn main thread code that this preview is being rendered and cannot be freed. */
+ preview->tag |= PRV_TAG_DEFFERED_RENDERING;
+
+ requested_previews_.push_back(requested_preview);
+ BLI_thread_queue_push(todo_queue_, &requested_previews_.back());
+}
+
+void PreviewLoadJob::run_fn(void *customdata,
+ short *stop,
+ short *do_update,
+ float *UNUSED(progress))
+{
+ PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+
+ IMB_thumb_locks_acquire();
+
+ while (RequestedPreview *request = reinterpret_cast<RequestedPreview *>(
+ BLI_thread_queue_pop_timeout(job_data->todo_queue_, 100))) {
+ if (*stop) {
+ break;
+ }
+
+ PreviewImage *preview = request->preview;
+
+ const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview));
+ const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]);
+ const char *path = &deferred_data[1];
+
+ // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path);
+
+ IMB_thumb_path_lock(path);
+ ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source);
+ IMB_thumb_path_unlock(path);
+
+ if (thumb) {
+ /* PreviewImage assumes premultiplied alpha... */
+ IMB_premultiply_alpha(thumb);
+
+ icon_copy_rect(thumb,
+ preview->w[request->icon_size],
+ preview->h[request->icon_size],
+ preview->rect[request->icon_size]);
+ IMB_freeImBuf(thumb);
+ }
+
+ *do_update = true;
+ }
+
+ IMB_thumb_locks_release();
+}
+
+/* Only execute on the main thread! */
+void PreviewLoadJob::finish_request(RequestedPreview &request)
+{
+ PreviewImage *preview = request.preview;
+
+ preview->tag &= ~PRV_TAG_DEFFERED_RENDERING;
+ BKE_previewimg_finish(preview, request.icon_size);
+
+ BLI_assert_msg(BLI_thread_is_main(),
+ "Deferred releasing of preview images should only run on the main thread");
+ if (preview->tag & PRV_TAG_DEFFERED_DELETE) {
+ BLI_assert(preview->tag & PRV_TAG_DEFFERED);
+ BKE_previewimg_deferred_release(preview);
+ }
+}
+
+void PreviewLoadJob::update_fn(void *customdata)
+{
+ PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+
+ for (auto request_it = job_data->requested_previews_.begin();
+ request_it != job_data->requested_previews_.end();) {
+ RequestedPreview &requested = *request_it;
+ /* Skip items that are not done loading yet. */
+ if (requested.preview->tag & PRV_TAG_DEFFERED_RENDERING) {
+ ++request_it;
+ continue;
+ }
+ finish_request(requested);
+
+ /* Remove properly finished previews from the job data. */
+ auto next_it = job_data->requested_previews_.erase(request_it);
+ request_it = next_it;
+ }
+}
+
+void PreviewLoadJob::end_fn(void *customdata)
+{
+ PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+
+ /* Finish any possibly remaining queued previews. */
+ for (RequestedPreview &request : job_data->requested_previews_) {
+ finish_request(request);
+ }
+ job_data->requested_previews_.clear();
+}
+
+void PreviewLoadJob::free_fn(void *customdata)
+{
+ MEM_delete(reinterpret_cast<PreviewLoadJob *>(customdata));
+}
+
static void icon_preview_free(void *customdata)
{
IconPreview *ip = (IconPreview *)customdata;
@@ -1698,8 +1874,19 @@ bool ED_preview_id_is_supported(const ID *id)
}
void ED_preview_icon_render(
- const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
+ const bContext *C, Scene *scene, PreviewImage *prv_img, ID *id, eIconSizes icon_size)
{
+ /* Deferred loading of previews from the file system. */
+ if (prv_img->tag & PRV_TAG_DEFFERED) {
+ if (prv_img->flag[icon_size] & PRV_RENDERING) {
+ /* Already in the queue, don't add it again. */
+ return;
+ }
+
+ PreviewLoadJob::load_jobless(prv_img, icon_size);
+ return;
+ }
+
IconPreview ip = {nullptr};
short stop = false, update = false;
float progress = 0.0f;
@@ -1716,7 +1903,10 @@ void ED_preview_icon_render(
ip.id_copy = duplicate_ids(id, true);
ip.active_object = CTX_data_active_object(C);
- icon_preview_add_size(&ip, rect, sizex, sizey);
+ prv_img->flag[icon_size] |= PRV_RENDERING;
+
+ icon_preview_add_size(
+ &ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]);
icon_preview_startjob_all_sizes(&ip, &stop, &update, &progress);
@@ -1729,20 +1919,31 @@ void ED_preview_icon_render(
}
void ED_preview_icon_job(
- const bContext *C, void *owner, ID *id, uint *rect, int sizex, int sizey, const bool delay)
+ const bContext *C, PreviewImage *prv_img, ID *id, eIconSizes icon_size, const bool delay)
{
- wmJob *wm_job;
+ /* Deferred loading of previews from the file system. */
+ if (prv_img->tag & PRV_TAG_DEFFERED) {
+ if (prv_img->flag[icon_size] & PRV_RENDERING) {
+ /* Already in the queue, don't add it again. */
+ return;
+ }
+ PreviewLoadJob &load_job = PreviewLoadJob::ensure_job(CTX_wm_manager(C), CTX_wm_window(C));
+ load_job.push_load_request(prv_img, icon_size);
+
+ return;
+ }
+
IconPreview *ip, *old_ip;
ED_preview_ensure_dbase();
/* suspended start means it starts after 1 timer step, see WM_jobs_timer below */
- wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- owner,
- "Icon Preview",
- WM_JOB_EXCL_RENDER,
- WM_JOB_TYPE_RENDER_PREVIEW);
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ prv_img,
+ "Icon Preview",
+ WM_JOB_EXCL_RENDER,
+ WM_JOB_TYPE_RENDER_PREVIEW);
ip = MEM_cnew<IconPreview>("icon preview");
@@ -1757,20 +1958,14 @@ void ED_preview_icon_job(
ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ip->scene = DEG_get_input_scene(ip->depsgraph);
ip->active_object = CTX_data_active_object(C);
- ip->owner = owner;
+ ip->owner = prv_img;
ip->id = id;
ip->id_copy = duplicate_ids(id, false);
- icon_preview_add_size(ip, rect, sizex, sizey);
+ prv_img->flag[icon_size] |= PRV_RENDERING;
- /* Special threading hack:
- * warn main code that this preview is being rendered and cannot be freed... */
- {
- PreviewImage *prv_img = static_cast<PreviewImage *>(owner);
- if (prv_img->tag & PRV_TAG_DEFFERED) {
- prv_img->tag |= PRV_TAG_DEFFERED_RENDERING;
- }
- }
+ icon_preview_add_size(
+ ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]);
/* setup job */
WM_jobs_customdata_set(wm_job, ip, icon_preview_free);
diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc
index bad4208cf19..cd395674177 100644
--- a/source/blender/editors/render/render_shading.cc
+++ b/source/blender/editors/render/render_shading.cc
@@ -310,7 +310,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op))
}
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Nurb *nu;
ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data);
@@ -411,7 +411,7 @@ static int material_slot_de_select(bContext *C, bool select)
changed = EDBM_deselect_by_material(em, mat_nr_active, select);
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data);
Nurb *nu;
BPoint *bp;
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 95534d2a036..af84f6f99a9 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -3438,6 +3438,37 @@ bool ED_area_is_global(const ScrArea *area)
return area->global != NULL;
}
+ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int xy[2])
+{
+ bScreen *screen = CTX_wm_screen(C);
+ wmWindow *win = CTX_wm_window(C);
+
+ ScrArea *area = NULL;
+
+ if (win->parent) {
+ /* If active window is a child, check itself first. */
+ area = BKE_screen_find_area_xy(screen, spacetype, xy);
+ }
+
+ if (!area) {
+ /* Check all windows except the active one. */
+ int scr_pos[2];
+ wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos);
+ if (r_win && r_win != win) {
+ win = r_win;
+ screen = WM_window_get_active_screen(win);
+ area = BKE_screen_find_area_xy(screen, spacetype, scr_pos);
+ }
+ }
+
+ if (!area && !win->parent) {
+ /* If active window is a parent window, check itself last. */
+ area = BKE_screen_find_area_xy(screen, spacetype, xy);
+ }
+
+ return area;
+}
+
ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
{
ScrArea *global_area = win->global_areas.areabase.first;
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index f43bc08151a..8a84f4cf079 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -77,8 +77,9 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state,
* filtering results. Mipmaps can be used to get better results (i.e. #GL_LINEAR_MIPMAP_LINEAR),
* so always use mipmaps when filtering. */
const bool use_mipmap = use_filter && ((draw_width < img_w) || (draw_height < img_h));
+ const int mips = use_mipmap ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, 1, gpu_format, NULL);
+ GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, mips, gpu_format, NULL);
const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F);
eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 7171aa7ac3b..ee3bc3cba76 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -585,7 +585,7 @@ bool ED_operator_uvmap(bContext *C)
bool ED_operator_editsurfcurve(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
- if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
return NULL != ((Curve *)obedit->data)->editnurb;
}
return false;
@@ -604,7 +604,7 @@ bool ED_operator_editsurfcurve_region_view3d(bContext *C)
bool ED_operator_editcurve(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
- if (obedit && obedit->type == OB_CURVE) {
+ if (obedit && obedit->type == OB_CURVES_LEGACY) {
return NULL != ((Curve *)obedit->data)->editnurb;
}
return false;
@@ -613,7 +613,7 @@ bool ED_operator_editcurve(bContext *C)
bool ED_operator_editcurve_3d(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
- if (obedit && obedit->type == OB_CURVE) {
+ if (obedit && obedit->type == OB_CURVES_LEGACY) {
Curve *cu = (Curve *)obedit->data;
return (cu->flag & CU_3D) && (NULL != cu->editnurb);
@@ -1008,9 +1008,6 @@ static void actionzone_exit(wmOperator *op)
static void actionzone_apply(bContext *C, wmOperator *op, int type)
{
wmWindow *win = CTX_wm_window(C);
- sActionzoneData *sad = op->customdata;
-
- sad->modifier = RNA_int_get(op->ptr, "modifier");
wmEvent event;
wm_event_init_from_window(win, &event);
@@ -1026,7 +1023,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type)
}
event.val = KM_NOTHING;
- event.is_repeat = false;
+ event.flag = 0;
event.customdata = op->customdata;
event.customdata_free = true;
op->customdata = NULL;
@@ -1051,6 +1048,7 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
sad->az = az;
sad->x = event->xy[0];
sad->y = event->xy[1];
+ sad->modifier = RNA_int_get(op->ptr, "modifier");
/* region azone directly reacts on mouse clicks */
if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
@@ -1114,7 +1112,17 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* What area are we now in? */
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
- if (area == sad->sa1) {
+ if (sad->modifier == 1) {
+ /* Duplicate area into new window. */
+ WM_cursor_set(win, WM_CURSOR_EDIT);
+ is_gesture = (delta_max > area_threshold);
+ }
+ else if (sad->modifier == 2) {
+ /* Swap areas. */
+ WM_cursor_set(win, WM_CURSOR_SWAP_AREA);
+ is_gesture = true;
+ }
+ else if (area == sad->sa1) {
/* Same area, so possible split. */
WM_cursor_set(win,
SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT :
@@ -1320,8 +1328,9 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case MOUSEMOVE:
- /* second area, for join */
- sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy);
+ /* Second area to swap with. */
+ sad->sa2 = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, event->xy);
+ WM_cursor_set(CTX_wm_window(C), (sad->sa2) ? WM_CURSOR_SWAP_AREA : WM_CURSOR_STOP);
break;
case LEFTMOUSE: /* release LMB */
if (event->val == KM_RELEASE) {
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 55099e2e750..59fbc3a64fb 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -9,6 +9,7 @@ set(INC
../../bmesh
../../depsgraph
../../draw
+ ../../functions
../../gpu
../../imbuf
../../makesdna
@@ -24,11 +25,13 @@ set(INC
)
set(SRC
+ curves_sculpt_ops.cc
paint_cursor.c
paint_curve.c
paint_curve_undo.c
paint_hide.c
- paint_image.c
+ paint_image.cc
+ paint_image_ops_paint.cc
paint_image_2d.c
paint_image_2d_curve_mask.cc
paint_image_proj.c
@@ -66,6 +69,7 @@ set(SRC
sculpt_undo.c
sculpt_uv.c
+ curves_sculpt_intern.h
paint_intern.h
sculpt_intern.h
)
@@ -75,5 +79,16 @@ set(LIB
bf_blenlib
)
+if(WITH_TBB)
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_TBB)
+ if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+ endif()
+endif()
blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.h b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h
new file mode 100644
index 00000000000..0d99e61192f
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+struct bContext;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool CURVES_SCULPT_mode_poll(struct bContext *C);
+bool CURVES_SCULPT_mode_poll_view3d(struct bContext *C);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
new file mode 100644
index 00000000000..936226a03ed
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_utildefines.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_paint.h"
+
+#include "WM_api.h"
+#include "WM_toolsystem.h"
+
+#include "ED_curves_sculpt.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_screen_types.h"
+
+#include "RNA_access.h"
+
+#include "BLI_index_mask_ops.hh"
+#include "BLI_math_vector.hh"
+
+#include "curves_sculpt_intern.h"
+#include "paint_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Poll Functions
+ * \{ */
+
+bool CURVES_SCULPT_mode_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ return ob && ob->mode & OB_MODE_SCULPT_CURVES;
+}
+
+bool CURVES_SCULPT_mode_poll_view3d(bContext *C)
+{
+ if (!CURVES_SCULPT_mode_poll(C)) {
+ return false;
+ }
+ if (CTX_wm_region_view3d(C) == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+/** \} */
+
+namespace blender::ed::sculpt_paint {
+
+using blender::bke::CurvesGeometry;
+
+/* -------------------------------------------------------------------- */
+/** \name * SCULPT_CURVES_OT_brush_stroke
+ * \{ */
+
+struct StrokeExtension {
+ bool is_first;
+ float2 mouse_position;
+};
+
+/**
+ * Base class for stroke based operations in curves sculpt mode.
+ */
+class CurvesSculptStrokeOperation {
+ public:
+ virtual ~CurvesSculptStrokeOperation() = default;
+ virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0;
+};
+
+class DeleteOperation : public CurvesSculptStrokeOperation {
+ private:
+ float2 last_mouse_position_;
+
+ public:
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+ {
+ Scene &scene = *CTX_data_scene(C);
+ Object &object = *CTX_data_active_object(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+
+ CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ const float brush_radius = BKE_brush_size_get(&scene, &brush);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
+
+ Curves &curves_id = *static_cast<Curves *>(object.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ MutableSpan<float3> positions = curves.positions();
+
+ const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position :
+ last_mouse_position_;
+ const float2 mouse_end = stroke_extension.mouse_position;
+
+ /* Find indices of curves that have to be removed. */
+ Vector<int64_t> indices;
+ const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate(
+ curves.curves_range(), 512, indices, [&](const int curve_i) {
+ const IndexRange point_range = curves.range_for_curve(curve_i);
+ for (const int segment_i : IndexRange(point_range.size() - 1)) {
+ const float3 pos1 = positions[point_range[segment_i]];
+ const float3 pos2 = positions[point_range[segment_i + 1]];
+
+ float2 pos1_proj, pos2_proj;
+ ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values);
+ ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values);
+
+ const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end);
+ if (dist <= brush_radius) {
+ return true;
+ }
+ }
+ return false;
+ });
+
+ /* Just reset positions instead of actually removing the curves. This is just a prototype. */
+ threading::parallel_for(curves_to_remove.index_range(), 512, [&](const IndexRange range) {
+ for (const int curve_i : curves_to_remove.slice(range)) {
+ for (const int point_i : curves.range_for_curve(curve_i)) {
+ positions[point_i] = {0.0f, 0.0f, 0.0f};
+ }
+ }
+ });
+
+ curves.tag_positions_changed();
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region);
+
+ last_mouse_position_ = stroke_extension.mouse_position;
+ }
+};
+
+class MoveOperation : public CurvesSculptStrokeOperation {
+ private:
+ Vector<int64_t> points_to_move_indices_;
+ IndexMask points_to_move_;
+ float2 last_mouse_position_;
+
+ public:
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+ {
+ Scene &scene = *CTX_data_scene(C);
+ Object &object = *CTX_data_active_object(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+
+ CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ const float brush_radius = BKE_brush_size_get(&scene, &brush);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
+
+ Curves &curves_id = *static_cast<Curves *>(object.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ MutableSpan<float3> positions = curves.positions();
+
+ if (stroke_extension.is_first) {
+ /* Find point indices to move. */
+ points_to_move_ = index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 512, points_to_move_indices_, [&](const int64_t point_i) {
+ const float3 position = positions[point_i];
+ float2 screen_position;
+ ED_view3d_project_float_v2_m4(region, position, screen_position, projection.values);
+ const float distance = len_v2v2(screen_position, stroke_extension.mouse_position);
+ return distance <= brush_radius;
+ });
+ }
+ else {
+ /* Move points based on mouse movement. */
+ const float2 mouse_diff = stroke_extension.mouse_position - last_mouse_position_;
+ threading::parallel_for(points_to_move_.index_range(), 512, [&](const IndexRange range) {
+ for (const int point_i : points_to_move_.slice(range)) {
+ const float3 old_position = positions[point_i];
+ float2 old_position_screen;
+ ED_view3d_project_float_v2_m4(
+ region, old_position, old_position_screen, projection.values);
+ const float2 new_position_screen = old_position_screen + mouse_diff;
+ float3 new_position;
+ ED_view3d_win_to_3d(v3d, region, old_position, new_position_screen, new_position);
+ positions[point_i] = new_position;
+ }
+ });
+
+ curves.tag_positions_changed();
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region);
+ }
+
+ last_mouse_position_ = stroke_extension.mouse_position;
+ }
+};
+
+static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C,
+ wmOperator *UNUSED(op))
+{
+ Scene &scene = *CTX_data_scene(C);
+ CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ switch (brush.curves_sculpt_tool) {
+ case CURVES_SCULPT_TOOL_TEST1:
+ return std::make_unique<MoveOperation>();
+ case CURVES_SCULPT_TOOL_TEST2:
+ return std::make_unique<DeleteOperation>();
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+struct SculptCurvesBrushStrokeData {
+ std::unique_ptr<CurvesSculptStrokeOperation> operation;
+ PaintStroke *stroke;
+};
+
+static bool stroke_get_location(bContext *C, float out[3], const float mouse[2])
+{
+ out[0] = mouse[0];
+ out[1] = mouse[1];
+ out[2] = 0;
+ UNUSED_VARS(C);
+ return true;
+}
+
+static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
+{
+ UNUSED_VARS(C, op, mouse);
+ return true;
+}
+
+static void stroke_update_step(bContext *C,
+ wmOperator *op,
+ PaintStroke *UNUSED(stroke),
+ PointerRNA *stroke_element)
+{
+ SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>(
+ op->customdata);
+
+ StrokeExtension stroke_extension;
+ RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
+
+ if (!op_data->operation) {
+ stroke_extension.is_first = true;
+ op_data->operation = start_brush_operation(C, op);
+ }
+ else {
+ stroke_extension.is_first = false;
+ }
+
+ op_data->operation->on_stroke_extended(C, stroke_extension);
+}
+
+static void stroke_done(const bContext *C, PaintStroke *stroke)
+{
+ UNUSED_VARS(C, stroke);
+}
+
+static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__);
+ op_data->stroke = paint_stroke_new(C,
+ op,
+ stroke_get_location,
+ stroke_test_start,
+ stroke_update_step,
+ nullptr,
+ stroke_done,
+ event->type);
+ op->customdata = op_data;
+
+ int return_value = op->type->modal(C, op, event);
+ if (return_value == OPERATOR_FINISHED) {
+ paint_stroke_free(C, op, op_data->stroke);
+ MEM_delete(op_data);
+ return OPERATOR_FINISHED;
+ }
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>(
+ op->customdata);
+ int return_value = paint_stroke_modal(C, op, event, op_data->stroke);
+ if (ELEM(return_value, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
+ MEM_delete(op_data);
+ }
+ return return_value;
+}
+
+static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op)
+{
+ SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>(
+ op->customdata);
+ paint_stroke_cancel(C, op, op_data->stroke);
+ MEM_delete(op_data);
+}
+
+static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
+{
+ ot->name = "Stroke Curves Sculpt";
+ ot->idname = "SCULPT_CURVES_OT_brush_stroke";
+ ot->description = "Sculpt curves using a brush";
+
+ ot->invoke = sculpt_curves_stroke_invoke;
+ ot->modal = sculpt_curves_stroke_modal;
+ ot->cancel = sculpt_curves_stroke_cancel;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ paint_stroke_operator_properties(ot);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name * CURVES_OT_sculptmode_toggle
+ * \{ */
+
+static bool curves_sculptmode_toggle_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if (ob == nullptr) {
+ return false;
+ }
+ if (ob->type != OB_CURVES) {
+ return false;
+ }
+ return true;
+}
+
+static void curves_sculptmode_enter(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
+ CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt;
+
+ ob->mode = OB_MODE_SCULPT_CURVES;
+
+ paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d);
+}
+
+static void curves_sculptmode_exit(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ ob->mode = OB_MODE_OBJECT;
+}
+
+static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
+
+ if (is_mode_set) {
+ if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (is_mode_set) {
+ curves_sculptmode_exit(C);
+ }
+ else {
+ curves_sculptmode_enter(C);
+ }
+
+ WM_toolsystem_update_from_context_view3d(C);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
+ return OPERATOR_CANCELLED;
+}
+
+static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
+{
+ ot->name = "Curve Sculpt Mode Toggle";
+ ot->idname = "CURVES_OT_sculptmode_toggle";
+ ot->description = "Enter/Exit sculpt mode for curves";
+
+ ot->exec = curves_sculptmode_toggle_exec;
+ ot->poll = curves_sculptmode_toggle_poll;
+
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+}
+
+} // namespace blender::ed::sculpt_paint
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name * Registration
+ * \{ */
+
+void ED_operatortypes_sculpt_curves()
+{
+ using namespace blender::ed::sculpt_paint;
+ WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke);
+ WM_operatortype_append(CURVES_OT_sculptmode_toggle);
+}
+
+/** \} */
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.cc
index df9e2cf136f..0c73c2e1f43 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.cc
@@ -6,10 +6,10 @@
* \brief Functions to paint images in 2D and 3D.
*/
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -67,6 +67,8 @@
#include "paint_intern.h"
+extern "C" {
+
/**
* This is a static resource for non-global access.
* Maybe it should be exposed as part of the paint operation,
@@ -84,7 +86,7 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr)
imapaintpartial = *ippr;
}
-/* Imagepaint Partial Redraw & Dirty Region */
+/* Image paint Partial Redraw & Dirty Region. */
void ED_imapaint_clear_partial_redraw(void)
{
@@ -96,7 +98,7 @@ void imapaint_region_tiles(
{
int srcx = 0, srcy = 0;
- IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
+ IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
*tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS);
*th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS);
@@ -107,11 +109,11 @@ void imapaint_region_tiles(
void ED_imapaint_dirty_region(
Image *ima, ImBuf *ibuf, ImageUser *iuser, int x, int y, int w, int h, bool find_old)
{
- ImBuf *tmpibuf = NULL;
+ ImBuf *tmpibuf = nullptr;
int tilex, tiley, tilew, tileh, tx, ty;
int srcx = 0, srcy = 0;
- IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
+ IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
if (w == 0 || h == 0) {
return;
@@ -128,7 +130,7 @@ void ED_imapaint_dirty_region(
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
ED_image_paint_tile_push(
- undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, NULL, NULL, false, find_old);
+ undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, nullptr, nullptr, false, find_old);
}
}
@@ -169,17 +171,19 @@ void imapaint_image_update(
BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
{
int i, j;
- BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel");
+ BlurKernel *kernel = MEM_new<BlurKernel>("BlurKernel");
+
float radius;
int side;
- eBlurKernelType type = br->blur_mode;
+ eBlurKernelType type = static_cast<eBlurKernelType>(br->blur_mode);
if (proj) {
radius = 0.5f;
side = kernel->side = 2;
kernel->side_squared = kernel->side * kernel->side;
- kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data");
+ kernel->wdata = static_cast<float *>(
+ MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"));
kernel->pixel_len = radius;
}
else {
@@ -191,7 +195,8 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
side = kernel->side = radius * 2 + 1;
kernel->side_squared = kernel->side * kernel->side;
- kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data");
+ kernel->wdata = static_cast<float *>(
+ MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"));
kernel->pixel_len = br->blur_kernel_radius;
}
@@ -224,9 +229,9 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
default:
printf("unidentified kernel type, aborting\n");
- MEM_freeN(kernel->wdata);
- MEM_freeN(kernel);
- return NULL;
+ paint_delete_blur_kernel(kernel);
+ MEM_delete(kernel);
+ return nullptr;
}
return kernel;
@@ -267,7 +272,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
SpaceImage *sima = CTX_wm_space_image(C);
if (sima) {
- if (sima->image != NULL && ID_IS_LINKED(sima->image)) {
+ if (sima->image != nullptr && ID_IS_LINKED(sima->image)) {
return false;
}
ARegion *region = CTX_wm_region(C);
@@ -281,7 +286,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
return false;
}
-static bool image_paint_poll(bContext *C)
+bool image_paint_poll(bContext *C)
{
return image_paint_poll_ex(C, true);
}
@@ -307,24 +312,6 @@ static bool image_paint_2d_clone_poll(bContext *C)
}
/************************ paint operator ************************/
-typedef enum eTexPaintMode {
- PAINT_MODE_2D,
- PAINT_MODE_3D_PROJECT,
-} eTexPaintMode;
-
-typedef struct PaintOperation {
- eTexPaintMode mode;
-
- void *custom_paint;
-
- float prevmouse[2];
- float startmouse[2];
- double starttime;
-
- void *cursor;
- ViewContext vc;
-} PaintOperation;
-
bool paint_use_opacity_masking(Brush *brush)
{
return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) ||
@@ -369,7 +356,7 @@ void paint_brush_color_get(struct Scene *scene,
break;
}
}
- /* Gradient / Colorband colors are not considered PROP_COLOR_GAMMA.
+ /* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA.
* Brush colors are expected to be in sRGB though. */
IMB_colormanagement_scene_linear_to_srgb_v3(color_gr);
@@ -414,340 +401,11 @@ void paint_brush_exit_tex(Brush *brush)
}
}
-static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
-{
- PaintOperation *pop = (PaintOperation *)customdata;
-
- if (pop) {
- GPU_line_smooth(true);
- GPU_blend(GPU_BLEND_ALPHA);
-
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
-
- ARegion *region = pop->vc.region;
-
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- GPU_line_width(4.0);
- immUniformColor4ub(0, 0, 0, 255);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex2i(pos, x, y);
- immVertex2i(
- pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
- immEnd();
-
- GPU_line_width(2.0);
- immUniformColor4ub(255, 255, 255, 255);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex2i(pos, x, y);
- immVertex2i(
- pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
- immEnd();
-
- immUnbindProgram();
-
- GPU_blend(GPU_BLEND_NONE);
- GPU_line_smooth(false);
- }
-}
-
-static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
-{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
- ToolSettings *settings = scene->toolsettings;
- PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */
- Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
- int mode = RNA_enum_get(op->ptr, "mode");
- ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
-
- copy_v2_v2(pop->prevmouse, mouse);
- copy_v2_v2(pop->startmouse, mouse);
-
- /* initialize from context */
- if (CTX_wm_region_view3d(C)) {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
- bool uvs, mat, tex, stencil;
- if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
- ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
- MEM_freeN(pop);
- WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
- return NULL;
- }
- pop->mode = PAINT_MODE_3D_PROJECT;
- pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode);
- }
- else {
- pop->mode = PAINT_MODE_2D;
- pop->custom_paint = paint_2d_new_stroke(C, op, mode);
- }
-
- if (!pop->custom_paint) {
- MEM_freeN(pop);
- return NULL;
- }
-
- if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
- pop->cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
- }
-
- settings->imapaint.flag |= IMAGEPAINT_DRAWING;
- ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
-
- return pop;
-}
-
-static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
-{
- PaintOperation *pop = paint_stroke_mode_data(stroke);
- Scene *scene = CTX_data_scene(C);
- ToolSettings *toolsettings = CTX_data_tool_settings(C);
- UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
- Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
-
- float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
-
- /* initial brush values. Maybe it should be considered moving these to stroke system */
- float startalpha = BKE_brush_alpha_get(scene, brush);
-
- float mouse[2];
- float pressure;
- float size;
- float distance = paint_stroke_distance_get(stroke);
- int eraser;
-
- RNA_float_get_array(itemptr, "mouse", mouse);
- pressure = RNA_float_get(itemptr, "pressure");
- eraser = RNA_boolean_get(itemptr, "pen_flip");
- size = RNA_float_get(itemptr, "size");
-
- /* stroking with fill tool only acts on stroke end */
- if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
- copy_v2_v2(pop->prevmouse, mouse);
- return;
- }
-
- if (BKE_brush_use_alpha_pressure(brush)) {
- BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
- }
- else {
- BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
- }
-
- if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
- UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
- ED_image_undo_restore(ustack->step_init);
- }
-
- if (pop->mode == PAINT_MODE_3D_PROJECT) {
- paint_proj_stroke(
- C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
- }
- else {
- paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
- }
-
- copy_v2_v2(pop->prevmouse, mouse);
-
- /* restore brush values */
- BKE_brush_alpha_set(scene, brush, startalpha);
-}
-
-static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
-{
- PaintOperation *pop = paint_stroke_mode_data(stroke);
-
- if (pop->mode == PAINT_MODE_3D_PROJECT) {
- paint_proj_redraw(C, pop->custom_paint, final);
- }
- else {
- paint_2d_redraw(C, pop->custom_paint, final);
- }
-}
-
-static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
-{
- Scene *scene = CTX_data_scene(C);
- ToolSettings *toolsettings = scene->toolsettings;
- PaintOperation *pop = paint_stroke_mode_data(stroke);
- Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
-
- toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
-
- if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
- if (brush->flag & BRUSH_USE_GRADIENT) {
- if (pop->mode == PAINT_MODE_2D) {
- paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
- }
- else {
- paint_proj_stroke(C,
- pop->custom_paint,
- pop->startmouse,
- pop->prevmouse,
- paint_stroke_flipped(stroke),
- 1.0,
- 0.0,
- BKE_brush_size_get(scene, brush));
- /* two redraws, one for GPU update, one for notification */
- paint_proj_redraw(C, pop->custom_paint, false);
- paint_proj_redraw(C, pop->custom_paint, true);
- }
- }
- else {
- if (pop->mode == PAINT_MODE_2D) {
- float color[3];
- if (paint_stroke_inverted(stroke)) {
- srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
- }
- else {
- srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
- }
- paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
- }
- else {
- paint_proj_stroke(C,
- pop->custom_paint,
- pop->startmouse,
- pop->prevmouse,
- paint_stroke_flipped(stroke),
- 1.0,
- 0.0,
- BKE_brush_size_get(scene, brush));
- /* two redraws, one for GPU update, one for notification */
- paint_proj_redraw(C, pop->custom_paint, false);
- paint_proj_redraw(C, pop->custom_paint, true);
- }
- }
- }
- if (pop->mode == PAINT_MODE_3D_PROJECT) {
- paint_proj_stroke_done(pop->custom_paint);
- }
- else {
- paint_2d_stroke_done(pop->custom_paint);
- }
-
- if (pop->cursor) {
- WM_paint_cursor_end(pop->cursor);
- }
-
- ED_image_undo_push_end();
-
- /* duplicate warning, see texpaint_init */
-#if 0
- if (pop->s.warnmultifile) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Image requires 4 color channels to paint: %s",
- pop->s.warnmultifile);
- }
- if (pop->s.warnpackedfile) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Packed MultiLayer files cannot be painted: %s",
- pop->s.warnpackedfile);
- }
-#endif
- MEM_freeN(pop);
-}
-
-static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
-{
- PaintOperation *pop;
-
- /* TODO: Should avoid putting this here. Instead, last position should be requested
- * from stroke system. */
-
- if (!(pop = texture_paint_init(C, op, mouse))) {
- return false;
- }
-
- paint_stroke_set_mode_data(op->customdata, pop);
-
- return true;
-}
-
-static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- int retval;
-
- op->customdata = paint_stroke_new(C,
- op,
- NULL,
- paint_stroke_test_start,
- paint_stroke_update_step,
- paint_stroke_redraw,
- paint_stroke_done,
- event->type);
-
- if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
- paint_stroke_free(C, op);
- return OPERATOR_FINISHED;
- }
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- OPERATOR_RETVAL_CHECK(retval);
- BLI_assert(retval == OPERATOR_RUNNING_MODAL);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static int paint_exec(bContext *C, wmOperator *op)
-{
- PropertyRNA *strokeprop;
- PointerRNA firstpoint;
- float mouse[2];
-
- strokeprop = RNA_struct_find_property(op->ptr, "stroke");
-
- if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
- return OPERATOR_CANCELLED;
- }
-
- RNA_float_get_array(&firstpoint, "mouse", mouse);
-
- op->customdata = paint_stroke_new(C,
- op,
- NULL,
- paint_stroke_test_start,
- paint_stroke_update_step,
- paint_stroke_redraw,
- paint_stroke_done,
- 0);
- /* frees op->customdata */
- return paint_stroke_exec(C, op);
-}
-
-void PAINT_OT_image_paint(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Image Paint";
- ot->idname = "PAINT_OT_image_paint";
- ot->description = "Paint a stroke into the image";
-
- /* api callbacks */
- ot->invoke = paint_invoke;
- ot->modal = paint_stroke_modal;
- ot->exec = paint_exec;
- ot->poll = image_paint_poll;
- ot->cancel = paint_stroke_cancel;
-
- /* flags */
- ot->flag = OPTYPE_BLOCKING;
-
- paint_stroke_operator_properties(ot);
-}
-
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
{
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = area->spacedata.first;
+ SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
if (sima->mode == SI_MODE_PAINT) {
ARegion *region = CTX_wm_region(C);
ED_space_image_get_zoom(sima, region, zoomx, zoomy);
@@ -768,8 +426,8 @@ static void toggle_paint_cursor(Scene *scene, bool enable)
Paint *p = &settings->imapaint.paint;
if (p->paint_cursor && !enable) {
- WM_paint_cursor_end(p->paint_cursor);
- p->paint_cursor = NULL;
+ WM_paint_cursor_end(static_cast<wmPaintCursor *>(p->paint_cursor));
+ p->paint_cursor = nullptr;
paint_cursor_delete_textures();
}
else if (enable) {
@@ -807,10 +465,10 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene)
/************************ grab clone operator ************************/
-typedef struct GrabClone {
+struct GrabClone {
float startoffset[2];
int startx, starty;
-} GrabClone;
+};
static void grab_clone_apply(bContext *C, wmOperator *op)
{
@@ -834,7 +492,7 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Brush *brush = image_paint_brush(C);
GrabClone *cmv;
- cmv = MEM_callocN(sizeof(GrabClone), "GrabClone");
+ cmv = MEM_new<GrabClone>("GrabClone");
copy_v2_v2(cmv->startoffset, brush->clone.offset);
cmv->startx = event->xy[0];
cmv->starty = event->xy[1];
@@ -849,7 +507,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Brush *brush = image_paint_brush(C);
ARegion *region = CTX_wm_region(C);
- GrabClone *cmv = op->customdata;
+ GrabClone *cmv = static_cast<GrabClone *>(op->customdata);
float startfx, startfy, fx, fy, delta[2];
int xmin = region->winrct.xmin, ymin = region->winrct.ymin;
@@ -880,7 +538,8 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op)
{
- MEM_freeN(op->customdata);
+ GrabClone *cmv = static_cast<GrabClone *>(op->customdata);
+ MEM_delete(cmv);
}
void PAINT_OT_grab_clone(wmOperatorType *ot)
@@ -904,7 +563,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
RNA_def_float_vector(ot->srna,
"delta",
2,
- NULL,
+ nullptr,
-FLT_MAX,
FLT_MAX,
"Delta",
@@ -914,12 +573,12 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
}
/******************** sample color operator ********************/
-typedef struct {
+struct SampleColorData {
bool show_cursor;
short launch_event;
float initcolor[3];
bool sample_palette;
-} SampleColorData;
+};
static void sample_color_update_header(SampleColorData *data, bContext *C)
{
@@ -973,7 +632,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
- SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data");
+ SampleColorData *data = MEM_new<SampleColorData>("sample color custom data");
ARegion *region = CTX_wm_region(C);
wmWindow *win = CTX_wm_window(C);
@@ -1009,7 +668,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event
static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
- SampleColorData *data = op->customdata;
+ SampleColorData *data = static_cast<SampleColorData *>(op->customdata);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
@@ -1023,8 +682,8 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event)
RNA_boolean_set(op->ptr, "palette", true);
}
WM_cursor_modal_restore(CTX_wm_window(C));
- MEM_freeN(data);
- ED_workspace_status_text(C, NULL);
+ MEM_delete(data);
+ ED_workspace_status_text(C, nullptr);
return OPERATOR_FINISHED;
}
@@ -1083,25 +742,26 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
- prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ prop = RNA_def_int_vector(
+ ot->srna, "location", 2, nullptr, 0, INT_MAX, "Location", "", 0, 16384);
+ RNA_def_property_flag(prop, static_cast<PropertyFlag>(PROP_SKIP_SAVE | PROP_HIDDEN));
- RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color");
- RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", "");
+ RNA_def_boolean(ot->srna, "merged", false, "Sample Merged", "Sample the output display color");
+ RNA_def_boolean(ot->srna, "palette", false, "Add to Palette", "");
}
/******************** texture paint toggle operator ********************/
void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob)
{
- Image *ima = NULL;
+ Image *ima = nullptr;
ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
/* This has to stay here to regenerate the texture paint
* cache in case we are loading a file */
BKE_texpaint_slots_refresh_object(scene, ob);
- ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
+ ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
/* entering paint mode also sets image to editors */
if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) {
@@ -1117,11 +777,11 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
}
if (ima) {
- wmWindowManager *wm = bmain->wm.first;
- for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
- for (ScrArea *area = screen->areabase.first; area; area = area->next) {
- SpaceLink *sl = area->spacedata.first;
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
@@ -1142,12 +802,12 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
if (U.glreslimit != 0) {
BKE_image_free_all_gputextures(bmain);
}
- BKE_image_paint_set_mipmap(bmain, 0);
+ BKE_image_paint_set_mipmap(bmain, false);
toggle_paint_cursor(scene, true);
Mesh *me = BKE_mesh_from_object(ob);
- BLI_assert(me != NULL);
+ BLI_assert(me != nullptr);
DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_MODE, scene);
}
@@ -1167,11 +827,11 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob)
if (U.glreslimit != 0) {
BKE_image_free_all_gputextures(bmain);
}
- BKE_image_paint_set_mipmap(bmain, 1);
+ BKE_image_paint_set_mipmap(bmain, true);
toggle_paint_cursor(scene, false);
Mesh *me = BKE_mesh_from_object(ob);
- BLI_assert(me != NULL);
+ BLI_assert(me != nullptr);
DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_MODE, scene);
}
@@ -1187,7 +847,7 @@ void ED_object_texture_paint_mode_exit(bContext *C)
static bool texture_paint_toggle_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (ob == NULL || ob->type != OB_MESH) {
+ if (ob == nullptr || ob->type != OB_MESH) {
return false;
}
if (!ob->data || ID_IS_LINKED(ob->data)) {
@@ -1207,7 +867,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
const bool is_mode_set = (ob->mode & mode_flag) != 0;
if (!is_mode_set) {
- if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
+ if (!ED_object_mode_compat_set(C, ob, static_cast<eObjectMode>(mode_flag), op->reports)) {
return OPERATOR_CANCELLED;
}
}
@@ -1274,7 +934,7 @@ static bool brush_colors_flip_poll(bContext *C)
}
else {
Object *ob = CTX_data_active_object(C);
- if (ob != NULL) {
+ if (ob != nullptr) {
if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) {
return true;
}
@@ -1310,8 +970,8 @@ void ED_imapaint_bucket_fill(struct bContext *C,
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
- const float mouse_init[2] = {mouse[0], mouse[1]};
- paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL);
+ const float mouse_init[2] = {static_cast<float>(mouse[0]), static_cast<float>(mouse[1])};
+ paint_2d_bucket_fill(C, color, nullptr, mouse_init, nullptr, nullptr);
ED_image_undo_push_end();
@@ -1349,3 +1009,4 @@ bool mask_paint_poll(bContext *C)
{
return BKE_paint_select_elem_test(CTX_data_active_object(C));
}
+}
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc
index 64115f7c6eb..f5657b004e2 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc
+++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc
@@ -53,6 +53,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache,
{
BLI_assert(curve_mask_cache->curve_mask != nullptr);
int offset = (int)floorf(diameter / 2.0f);
+ int clamped_radius = max_ff(radius, 1.0);
unsigned short *m = curve_mask_cache->curve_mask;
@@ -76,7 +77,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache,
pixel_xy[1] = static_cast<float>(y) + aa_offset;
for (int j = 0; j < aa_samples; j++) {
const float len = len_v2v2(pixel_xy, bpos);
- const int sample_index = min_ii((len / radius) * CurveSamplesBaseLen,
+ const int sample_index = min_ii((len / clamped_radius) * CurveSamplesBaseLen,
CurveSamplesLen - 1);
const float sample_weight = curve_mask_cache->sampled_curve[sample_index];
diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
new file mode 100644
index 00000000000..786fcc47526
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
@@ -0,0 +1,531 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
+
+/** \file
+ * \ingroup edsculpt
+ * \brief Painting operator to paint in 2D and 3D.
+ */
+
+#include "DNA_brush_types.h"
+#include "DNA_color_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_paint.h"
+#include "BKE_undo_system.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_paint.h"
+#include "ED_view3d.h"
+
+#include "GPU_immediate.h"
+#include "GPU_state.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "paint_intern.h"
+
+namespace blender::ed::sculpt_paint::image::ops::paint {
+
+/**
+ * Interface to use the same painting operator for 3D and 2D painting. Interface removes the
+ * differences between the actual calls that are being performed.
+ */
+class AbstractPaintMode {
+ public:
+ virtual ~AbstractPaintMode() = default;
+ virtual void *paint_new_stroke(
+ bContext *C, wmOperator *op, Object *ob, const float mouse[2], int mode) = 0;
+ virtual void paint_stroke(bContext *C,
+ void *stroke_handle,
+ float prev_mouse[2],
+ float mouse[2],
+ int eraser,
+ float pressure,
+ float distance,
+ float size) = 0;
+
+ virtual void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) = 0;
+ virtual void paint_stroke_done(void *stroke_handle) = 0;
+ virtual void paint_gradient_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) = 0;
+ virtual void paint_bucket_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) = 0;
+};
+
+class ImagePaintMode : public AbstractPaintMode {
+ public:
+ void *paint_new_stroke(bContext *C,
+ wmOperator *op,
+ Object *UNUSED(ob),
+ const float UNUSED(mouse[2]),
+ int mode) override
+ {
+ return paint_2d_new_stroke(C, op, mode);
+ }
+
+ void paint_stroke(bContext *UNUSED(C),
+ void *stroke_handle,
+ float prev_mouse[2],
+ float mouse[2],
+ int eraser,
+ float pressure,
+ float distance,
+ float size) override
+ {
+ paint_2d_stroke(stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
+ }
+
+ void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
+ {
+ paint_2d_redraw(C, stroke_handle, final);
+ }
+
+ void paint_stroke_done(void *stroke_handle) override
+ {
+ paint_2d_stroke_done(stroke_handle);
+ }
+
+ void paint_gradient_fill(const bContext *C,
+ const Scene *UNUSED(scene),
+ Brush *brush,
+ struct PaintStroke *UNUSED(stroke),
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) override
+ {
+ paint_2d_gradient_fill(C, brush, mouse_start, mouse_end, stroke_handle);
+ }
+
+ void paint_bucket_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) override
+ {
+ float color[3];
+ if (paint_stroke_inverted(stroke)) {
+ srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
+ }
+ else {
+ srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
+ }
+ paint_2d_bucket_fill(C, color, brush, mouse_start, mouse_end, stroke_handle);
+ }
+};
+
+class ProjectionPaintMode : public AbstractPaintMode {
+ public:
+ void *paint_new_stroke(
+ bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override
+ {
+ return paint_proj_new_stroke(C, ob, mouse, mode);
+ }
+
+ void paint_stroke(bContext *C,
+ void *stroke_handle,
+ float prev_mouse[2],
+ float mouse[2],
+ int eraser,
+ float pressure,
+ float distance,
+ float size) override
+ {
+ paint_proj_stroke(C, stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
+ };
+
+ void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
+ {
+ paint_proj_redraw(C, stroke_handle, final);
+ }
+
+ void paint_stroke_done(void *stroke_handle) override
+ {
+ paint_proj_stroke_done(stroke_handle);
+ }
+
+ void paint_gradient_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) override
+ {
+ paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
+ }
+
+ void paint_bucket_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2]) override
+ {
+ paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
+ }
+
+ private:
+ void paint_fill(const bContext *C,
+ const Scene *scene,
+ Brush *brush,
+ struct PaintStroke *stroke,
+ void *stroke_handle,
+ float mouse_start[2],
+ float mouse_end[2])
+ {
+ paint_proj_stroke(C,
+ stroke_handle,
+ mouse_start,
+ mouse_end,
+ paint_stroke_flipped(stroke),
+ 1.0,
+ 0.0,
+ BKE_brush_size_get(scene, brush));
+ /* two redraws, one for GPU update, one for notification */
+ paint_proj_redraw(C, stroke_handle, false);
+ paint_proj_redraw(C, stroke_handle, true);
+ }
+};
+
+struct PaintOperation {
+ AbstractPaintMode *mode = nullptr;
+
+ void *stroke_handle = nullptr;
+
+ float prevmouse[2] = {0.0f, 0.0f};
+ float startmouse[2] = {0.0f, 0.0f};
+ double starttime = 0.0;
+
+ wmPaintCursor *cursor = nullptr;
+ ViewContext vc = {nullptr};
+
+ PaintOperation() = default;
+ ~PaintOperation()
+ {
+ MEM_delete(mode);
+ mode = nullptr;
+
+ if (cursor) {
+ WM_paint_cursor_end(cursor);
+ cursor = nullptr;
+ }
+ }
+};
+
+static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
+{
+ PaintOperation *pop = (PaintOperation *)customdata;
+
+ if (pop) {
+ GPU_line_smooth(true);
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+
+ ARegion *region = pop->vc.region;
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ GPU_line_width(4.0);
+ immUniformColor4ub(0, 0, 0, 255);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2i(pos, x, y);
+ immVertex2i(
+ pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
+ immEnd();
+
+ GPU_line_width(2.0);
+ immUniformColor4ub(255, 255, 255, 255);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2i(pos, x, y);
+ immVertex2i(
+ pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
+ immEnd();
+
+ immUnbindProgram();
+
+ GPU_blend(GPU_BLEND_NONE);
+ GPU_line_smooth(false);
+ }
+}
+
+static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *settings = scene->toolsettings;
+ PaintOperation *pop = MEM_new<PaintOperation>("PaintOperation"); /* caller frees */
+ Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
+ int mode = RNA_enum_get(op->ptr, "mode");
+ ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
+
+ copy_v2_v2(pop->prevmouse, mouse);
+ copy_v2_v2(pop->startmouse, mouse);
+
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob = OBACT(view_layer);
+
+ /* initialize from context */
+ if (CTX_wm_region_view3d(C)) {
+ bool uvs, mat, tex, stencil;
+ if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
+ ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
+ MEM_delete(pop);
+ WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
+ return nullptr;
+ }
+ pop->mode = MEM_new<ProjectionPaintMode>("ProjectionPaintMode");
+ }
+ else {
+ pop->mode = MEM_new<ImagePaintMode>("ImagePaintMode");
+ }
+
+ pop->stroke_handle = pop->mode->paint_new_stroke(C, op, ob, mouse, mode);
+ if (!pop->stroke_handle) {
+ MEM_delete(pop);
+ return nullptr;
+ }
+
+ if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
+ pop->cursor = WM_paint_cursor_activate(
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
+ }
+
+ settings->imapaint.flag |= IMAGEPAINT_DRAWING;
+ ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
+
+ return pop;
+}
+
+static void paint_stroke_update_step(bContext *C,
+ wmOperator *UNUSED(op),
+ struct PaintStroke *stroke,
+ PointerRNA *itemptr)
+{
+ PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *toolsettings = CTX_data_tool_settings(C);
+ UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
+ Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
+
+ float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
+
+ /* initial brush values. Maybe it should be considered moving these to stroke system */
+ float startalpha = BKE_brush_alpha_get(scene, brush);
+
+ float mouse[2];
+ float pressure;
+ float size;
+ float distance = paint_stroke_distance_get(stroke);
+ int eraser;
+
+ RNA_float_get_array(itemptr, "mouse", mouse);
+ pressure = RNA_float_get(itemptr, "pressure");
+ eraser = RNA_boolean_get(itemptr, "pen_flip");
+ size = RNA_float_get(itemptr, "size");
+
+ /* stroking with fill tool only acts on stroke end */
+ if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
+ copy_v2_v2(pop->prevmouse, mouse);
+ return;
+ }
+
+ if (BKE_brush_use_alpha_pressure(brush)) {
+ BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
+ }
+ else {
+ BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
+ }
+
+ if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
+ UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
+ ED_image_undo_restore(ustack->step_init);
+ }
+
+ pop->mode->paint_stroke(
+ C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size);
+
+ copy_v2_v2(pop->prevmouse, mouse);
+
+ /* restore brush values */
+ BKE_brush_alpha_set(scene, brush, startalpha);
+}
+
+static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
+{
+ PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
+ pop->mode->paint_stroke_redraw(C, pop->stroke_handle, final);
+}
+
+static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
+{
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *toolsettings = scene->toolsettings;
+ PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
+ Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
+
+ toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
+
+ if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
+ if (brush->flag & BRUSH_USE_GRADIENT) {
+ pop->mode->paint_gradient_fill(
+ C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
+ }
+ else {
+ pop->mode->paint_bucket_fill(
+ C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
+ }
+ }
+ pop->mode->paint_stroke_done(pop->stroke_handle);
+ pop->stroke_handle = nullptr;
+
+ ED_image_undo_push_end();
+
+/* duplicate warning, see texpaint_init */
+#if 0
+ if (pop->s.warnmultifile) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Image requires 4 color channels to paint: %s",
+ pop->s.warnmultifile);
+ }
+ if (pop->s.warnpackedfile) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Packed MultiLayer files cannot be painted: %s",
+ pop->s.warnpackedfile);
+ }
+#endif
+ MEM_delete(pop);
+}
+
+static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
+{
+ PaintOperation *pop;
+
+ /* TODO: Should avoid putting this here. Instead, last position should be requested
+ * from stroke system. */
+
+ if (!(pop = texture_paint_init(C, op, mouse))) {
+ return false;
+ }
+
+ paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop);
+
+ return true;
+}
+
+static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+
+ op->customdata = paint_stroke_new(C,
+ op,
+ nullptr,
+ paint_stroke_test_start,
+ paint_stroke_update_step,
+ paint_stroke_redraw,
+ paint_stroke_done,
+ event->type);
+
+ if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
+ paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata));
+ return OPERATOR_FINISHED;
+ }
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ OPERATOR_RETVAL_CHECK(retval);
+ BLI_assert(retval == OPERATOR_RUNNING_MODAL);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int paint_exec(bContext *C, wmOperator *op)
+{
+ PropertyRNA *strokeprop;
+ PointerRNA firstpoint;
+ float mouse[2];
+
+ strokeprop = RNA_struct_find_property(op->ptr, "stroke");
+
+ if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ RNA_float_get_array(&firstpoint, "mouse", mouse);
+
+ op->customdata = paint_stroke_new(C,
+ op,
+ nullptr,
+ paint_stroke_test_start,
+ paint_stroke_update_step,
+ paint_stroke_redraw,
+ paint_stroke_done,
+ 0);
+ /* frees op->customdata */
+ return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata));
+}
+
+static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata));
+}
+
+static void paint_cancel(bContext *C, wmOperator *op)
+{
+ paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
+}
+} // namespace blender::ed::sculpt_paint::image::ops::paint
+
+extern "C" {
+void PAINT_OT_image_paint(wmOperatorType *ot)
+{
+ using namespace blender::ed::sculpt_paint::image::ops::paint;
+
+ /* identifiers */
+ ot->name = "Image Paint";
+ ot->idname = "PAINT_OT_image_paint";
+ ot->description = "Paint a stroke into the image";
+
+ /* api callbacks */
+ ot->invoke = paint_invoke;
+ ot->modal = paint_modal;
+ ot->exec = paint_exec;
+ ot->poll = image_paint_poll;
+ ot->cancel = paint_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING;
+
+ paint_stroke_operator_properties(ot);
+}
+}
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index f1e79b98b83..82fdf49c28e 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -49,6 +49,7 @@ typedef struct CoNo {
typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
typedef void (*StrokeUpdateStep)(struct bContext *C,
+ struct wmOperator *op,
struct PaintStroke *stroke,
struct PointerRNA *itemptr);
typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final);
@@ -62,7 +63,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C,
StrokeRedraw redraw,
StrokeDone done,
int event_type);
-void paint_stroke_free(struct bContext *C, struct wmOperator *op);
+void paint_stroke_free(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
/**
* Returns zero if the stroke dots should not be spaced, non-zero otherwise.
@@ -84,9 +85,12 @@ bool paint_supports_jitter(enum ePaintMode mode);
* Called in paint_ops.c, on each regeneration of key-maps.
*/
struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf);
-int paint_stroke_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
-int paint_stroke_exec(struct bContext *C, struct wmOperator *op);
-void paint_stroke_cancel(struct bContext *C, struct wmOperator *op);
+int paint_stroke_modal(struct bContext *C,
+ struct wmOperator *op,
+ const struct wmEvent *event,
+ struct PaintStroke *stroke);
+int paint_stroke_exec(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
+void paint_stroke_cancel(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
bool paint_stroke_flipped(struct PaintStroke *stroke);
bool paint_stroke_inverted(struct PaintStroke *stroke);
struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
@@ -265,6 +269,7 @@ void paint_brush_color_get(struct Scene *scene,
bool paint_use_opacity_masking(struct Brush *brush);
void paint_brush_init_tex(struct Brush *brush);
void paint_brush_exit_tex(struct Brush *brush);
+bool image_paint_poll(struct bContext *C);
void PAINT_OT_grab_clone(struct wmOperatorType *ot);
void PAINT_OT_sample_color(struct wmOperatorType *ot);
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index 1b876235ad0..926a564184a 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -39,6 +39,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "curves_sculpt_intern.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -758,6 +759,7 @@ static const ePaintMode brush_select_paint_modes[] = {
PAINT_MODE_VERTEX_GPENCIL,
PAINT_MODE_SCULPT_GPENCIL,
PAINT_MODE_WEIGHT_GPENCIL,
+ PAINT_MODE_SCULPT_CURVES,
};
static int brush_select_exec(bContext *C, wmOperator *op)
@@ -1388,6 +1390,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
keymap = paint_stroke_modal_keymap(keyconf);
WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke");
+ /* Curves Sculpt mode. */
+ keymap = WM_keymap_ensure(keyconf, "Sculpt Curves", 0, 0);
+ keymap->poll = CURVES_SCULPT_mode_poll;
+
/* sculpt expand. */
sculpt_expand_modal_keymap(keyconf);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 9bbfb81e08e..ae7570d21a1 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -505,16 +505,13 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert)
}
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
-static void paint_brush_stroke_add_step(bContext *C,
- wmOperator *op,
- const float mouse_in[2],
- float pressure)
+static void paint_brush_stroke_add_step(
+ bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure)
{
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
- PaintStroke *stroke = op->customdata;
UnifiedPaintSettings *ups = stroke->ups;
float mouse_out[2];
PointerRNA itemptr;
@@ -614,7 +611,7 @@ static void paint_brush_stroke_add_step(bContext *C,
RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt);
RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt);
- stroke->update_step(C, stroke, &itemptr);
+ stroke->update_step(C, op, stroke, &itemptr);
/* don't record this for now, it takes up a lot of memory when doing long
* strokes with small brush size, and operators have register disabled */
@@ -785,12 +782,12 @@ static float paint_space_stroke_spacing_variable(bContext *C,
* towards the final mouse location. */
static int paint_space_stroke(bContext *C,
wmOperator *op,
+ PaintStroke *stroke,
const float final_mouse[2],
float final_pressure)
{
const Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
- PaintStroke *stroke = op->customdata;
UnifiedPaintSettings *ups = stroke->ups;
Paint *paint = BKE_paint_get_active_from_context(C);
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
@@ -852,7 +849,7 @@ static int paint_space_stroke(bContext *C,
spacing / no_pressure_spacing);
stroke->stroke_distance += spacing / stroke->zoom_2d;
- paint_brush_stroke_add_step(C, op, mouse, pressure);
+ paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
length -= spacing;
pressure = stroke->last_pressure;
@@ -929,7 +926,7 @@ PaintStroke *paint_stroke_new(bContext *C,
return stroke;
}
-void paint_stroke_free(bContext *C, wmOperator *op)
+void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke)
{
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (rv3d) {
@@ -938,7 +935,6 @@ void paint_stroke_free(bContext *C, wmOperator *op)
BKE_paint_set_overlay_override(0);
- PaintStroke *stroke = op->customdata;
if (stroke == NULL) {
return;
}
@@ -961,12 +957,11 @@ void paint_stroke_free(bContext *C, wmOperator *op)
BLI_freelistN(&stroke->line);
- MEM_SAFE_FREE(op->customdata);
+ MEM_SAFE_FREE(stroke);
}
-static void stroke_done(bContext *C, wmOperator *op)
+static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
{
- PaintStroke *stroke = op->customdata;
UnifiedPaintSettings *ups = stroke->ups;
/* reset rotation here to avoid doing so in cursor display */
@@ -988,7 +983,7 @@ static void stroke_done(bContext *C, wmOperator *op)
}
}
- paint_stroke_free(C, op);
+ paint_stroke_free(C, op, stroke);
}
bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
@@ -1230,7 +1225,7 @@ static void paint_line_strokes_spacing(bContext *C,
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
stroke->stroke_distance += spacing / stroke->zoom_2d;
- paint_brush_stroke_add_step(C, op, mouse, 1.0);
+ paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0);
length -= spacing;
spacing_final = spacing;
@@ -1252,8 +1247,8 @@ static void paint_stroke_line_end(bContext *C,
if (stroke->stroke_started && (br->flag & BRUSH_LINE)) {
stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0);
- paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0);
- paint_space_stroke(C, op, mouse, 1.0);
+ paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0);
+ paint_space_stroke(C, op, stroke, mouse, 1.0);
}
}
@@ -1331,7 +1326,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
if (stroke->stroke_started) {
- paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0);
+ paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0);
paint_line_strokes_spacing(
C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
}
@@ -1343,7 +1338,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
}
}
- stroke_done(C, op);
+ stroke_done(C, op, stroke);
#ifdef DEBUG_TIME
TIMEIT_END_AVERAGED(whole_stroke);
@@ -1384,11 +1379,10 @@ static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
}
}
-int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
+int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke *stroke)
{
Paint *p = BKE_paint_get_active_from_context(C);
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
- PaintStroke *stroke = op->customdata;
Brush *br = stroke->brush = BKE_paint_brush(p);
PaintSample sample_average;
float mouse[2];
@@ -1482,7 +1476,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
op->type->cancel(C, op);
}
else {
- paint_stroke_cancel(C, op);
+ paint_stroke_cancel(C, op, stroke);
}
return OPERATOR_CANCELLED;
}
@@ -1492,17 +1486,17 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
paint_stroke_line_constrain(stroke, mouse);
paint_stroke_line_end(C, op, stroke, mouse);
- stroke_done(C, op);
+ stroke_done(C, op, stroke);
return OPERATOR_FINISHED;
}
}
else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) {
paint_stroke_line_end(C, op, stroke, sample_average.mouse);
- stroke_done(C, op);
+ stroke_done(C, op, stroke);
return OPERATOR_FINISHED;
}
else if (br->flag & BRUSH_LINE) {
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
stroke->constrain_line = true;
}
else {
@@ -1530,7 +1524,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) {
if (stroke->stroke_started) {
if (paint_space_stroke_enabled(br, mode)) {
- if (paint_space_stroke(C, op, mouse, pressure)) {
+ if (paint_space_stroke(C, op, stroke, mouse, pressure)) {
redraw = true;
}
}
@@ -1538,7 +1532,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
float dmouse[2];
sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
stroke->stroke_distance += len_v2(dmouse);
- paint_brush_stroke_add_step(C, op, mouse, pressure);
+ paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
redraw = true;
}
}
@@ -1549,7 +1543,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
* instead of waiting till we have moved the space distance */
if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0);
- paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure);
+ paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
redraw = true;
}
@@ -1572,10 +1566,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
-int paint_stroke_exec(bContext *C, wmOperator *op)
+int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
{
- PaintStroke *stroke = op->customdata;
-
/* only when executed for the first time */
if (stroke->stroke_started == 0) {
PropertyRNA *strokeprop;
@@ -1592,21 +1584,21 @@ int paint_stroke_exec(bContext *C, wmOperator *op)
if (stroke->stroke_started) {
RNA_BEGIN (op->ptr, itemptr, "stroke") {
- stroke->update_step(C, stroke, &itemptr);
+ stroke->update_step(C, op, stroke, &itemptr);
}
RNA_END;
}
bool ok = (stroke->stroke_started != 0);
- stroke_done(C, op);
+ stroke_done(C, op, stroke);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
-void paint_stroke_cancel(bContext *C, wmOperator *op)
+void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
{
- stroke_done(C, op);
+ stroke_done(C, op, stroke);
}
ViewContext *paint_stroke_view_context(PaintStroke *stroke)
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index f54b012b8dd..31b965c6a92 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -130,13 +130,12 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3], flo
{
Object *ob = vc->obact;
float delta[3], scale, loc[3];
- const float mval_f[2] = {pixel_radius, 0.0f};
- float zfac;
+ const float xy_delta[2] = {pixel_radius, 0.0f};
mul_v3_m4v3(loc, ob->obmat, center);
- zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL);
- ED_view3d_win_to_delta(vc->region, mval_f, delta, zfac);
+ const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc);
+ ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta);
scale = fabsf(mat4_to_scale(ob->obmat));
scale = (scale == 0.0f) ? 1.0f : scale;
@@ -585,7 +584,8 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot)
ot->poll = brush_curve_preset_poll;
prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
}
/* face-select ops */
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index a82636023f8..e2f8d81fe13 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -2370,7 +2370,10 @@ static void wpaint_do_symmetrical_brush_actions(
cache->is_last_valid = true;
}
-static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
+static void wpaint_stroke_update_step(bContext *C,
+ wmOperator *UNUSED(op),
+ struct PaintStroke *stroke,
+ PointerRNA *itemptr)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -2551,7 +2554,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
event->type);
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
- paint_stroke_free(C, op);
+ paint_stroke_free(C, op, op->customdata);
return OPERATOR_FINISHED;
}
/* add modal handler */
@@ -2575,7 +2578,7 @@ static int wpaint_exec(bContext *C, wmOperator *op)
0);
/* frees op->customdata */
- paint_stroke_exec(C, op);
+ paint_stroke_exec(C, op, op->customdata);
return OPERATOR_FINISHED;
}
@@ -2588,7 +2591,12 @@ static void wpaint_cancel(bContext *C, wmOperator *op)
ob->sculpt->cache = NULL;
}
- paint_stroke_cancel(C, op);
+ paint_stroke_cancel(C, op, op->customdata);
+}
+
+static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return paint_stroke_modal(C, op, event, op->customdata);
}
void PAINT_OT_weight_paint(wmOperatorType *ot)
@@ -2600,7 +2608,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot)
/* api callbacks */
ot->invoke = wpaint_invoke;
- ot->modal = paint_stroke_modal;
+ ot->modal = wpaint_modal;
ot->exec = wpaint_exec;
ot->poll = weight_paint_poll;
ot->cancel = wpaint_cancel;
@@ -3395,7 +3403,10 @@ static void vpaint_do_symmetrical_brush_actions(
cache->is_last_valid = true;
}
-static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
+static void vpaint_stroke_update_step(bContext *C,
+ wmOperator *UNUSED(op),
+ struct PaintStroke *stroke,
+ PointerRNA *itemptr)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
@@ -3497,7 +3508,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
event->type);
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
- paint_stroke_free(C, op);
+ paint_stroke_free(C, op, op->customdata);
return OPERATOR_FINISHED;
}
@@ -3522,7 +3533,7 @@ static int vpaint_exec(bContext *C, wmOperator *op)
0);
/* frees op->customdata */
- paint_stroke_exec(C, op);
+ paint_stroke_exec(C, op, op->customdata);
return OPERATOR_FINISHED;
}
@@ -3535,7 +3546,12 @@ static void vpaint_cancel(bContext *C, wmOperator *op)
ob->sculpt->cache = NULL;
}
- paint_stroke_cancel(C, op);
+ paint_stroke_cancel(C, op, op->customdata);
+}
+
+static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return paint_stroke_modal(C, op, event, op->customdata);
}
void PAINT_OT_vertex_paint(wmOperatorType *ot)
@@ -3547,7 +3563,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
/* api callbacks */
ot->invoke = vpaint_invoke;
- ot->modal = paint_stroke_modal;
+ ot->modal = vpaint_modal;
ot->exec = vpaint_exec;
ot->poll = vertex_paint_poll;
ot->cancel = vpaint_cancel;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 68edf9cd54a..70ff7596d6d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -2648,13 +2648,13 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
static void calc_local_y(ViewContext *vc, const float center[3], float y[3])
{
Object *ob = vc->obact;
- float loc[3], mval_f[2] = {0.0f, 1.0f};
- float zfac;
+ float loc[3];
+ const float xy_delta[2] = {0.0f, 1.0f};
mul_v3_m4v3(loc, ob->imat, center);
- zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL);
+ const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc);
- ED_view3d_win_to_delta(vc->region, mval_f, y, zfac);
+ ED_view3d_win_to_delta(vc->region, xy_delta, zfac, y);
normalize_v3(y);
add_v3_v3(y, ob->loc);
@@ -5186,6 +5186,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
}
static void sculpt_stroke_update_step(bContext *C,
+ wmOperator *UNUSED(op),
struct PaintStroke *UNUSED(stroke),
PointerRNA *itemptr)
{
@@ -5333,12 +5334,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click");
if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) {
- paint_stroke_free(C, op);
+ paint_stroke_free(C, op, op->customdata);
return OPERATOR_PASS_THROUGH;
}
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
- paint_stroke_free(C, op);
+ paint_stroke_free(C, op, op->customdata);
return OPERATOR_FINISHED;
}
/* Add modal handler. */
@@ -5364,7 +5365,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
0);
/* Frees op->customdata. */
- paint_stroke_exec(C, op);
+ paint_stroke_exec(C, op, op->customdata);
return OPERATOR_FINISHED;
}
@@ -5382,7 +5383,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
paint_mesh_restore_co(sd, ob);
}
- paint_stroke_cancel(C, op);
+ paint_stroke_cancel(C, op, op->customdata);
if (ss->cache) {
SCULPT_cache_free(ss->cache);
@@ -5392,6 +5393,11 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
sculpt_brush_exit_tex(sd);
}
+static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return paint_stroke_modal(C, op, event, op->customdata);
+}
+
void SCULPT_OT_brush_stroke(wmOperatorType *ot)
{
/* Identifiers. */
@@ -5401,7 +5407,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot)
/* API callbacks. */
ot->invoke = sculpt_brush_stroke_invoke;
- ot->modal = paint_stroke_modal;
+ ot->modal = sculpt_brush_stroke_modal;
ot->exec = sculpt_brush_stroke_exec;
ot->poll = SCULPT_poll;
ot->cancel = sculpt_brush_stroke_cancel;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index a6b412b2b7e..6f9df4d8252 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -1105,10 +1105,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v);
bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v);
/**
- * Initialize a point-in-brush test with a given falloff shape
+ * Initialize a point-in-brush test with a given falloff shape.
*
- * \param falloff_shape PAINT_FALLOFF_SHAPE_SPHERE or PAINT_FALLOFF_SHAPE_TUBE
- * \return The brush falloff function
+ * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE.
+ * \return The brush falloff function.
*/
SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss,
SculptBrushTest *test,
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index 72640893dea..8fc10061f83 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -245,7 +245,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
/* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill
* mask and face sets by connectivity directly. */
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1;
}
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index e383ada1331..8e8902c2ea7 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -659,7 +659,7 @@ static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *even
{
/* NOTE: this is hardcoded to match the behavior for the unlink button
* (in interface_templates.c). */
- RNA_boolean_set(op->ptr, "force_delete", event->shift != 0);
+ RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT);
return action_unlink_exec(C, op);
}
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 897091731a4..d53fe2efb03 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -29,6 +29,7 @@
#include "ED_clip.h"
#include "ED_curve.h"
#include "ED_curves.h"
+#include "ED_curves_sculpt.h"
#include "ED_fileselect.h"
#include "ED_geometry.h"
#include "ED_gizmo_library.h"
@@ -96,6 +97,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_mesh();
ED_operatortypes_geometry();
ED_operatortypes_sculpt();
+ ED_operatortypes_sculpt_curves();
ED_operatortypes_uvedit();
ED_operatortypes_paint();
ED_operatortypes_physics();
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 287aef178ae..4e80e7ea5c2 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -230,7 +230,7 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
return true;
}
if (RNA_struct_is_a(ptr->type, &RNA_Curve) &&
- (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) {
+ (type == -1 || ELEM(type, OB_CURVES_LEGACY, OB_SURF, OB_FONT))) {
return true;
}
if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (ELEM(type, -1, OB_ARMATURE))) {
@@ -293,7 +293,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path)
if (ELEM(ob->type,
OB_MESH,
- OB_CURVE,
+ OB_CURVES_LEGACY,
OB_FONT,
OB_SURF,
OB_LATTICE,
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index cad6d34af24..e215b7c7992 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -282,11 +282,11 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* Useful yet irritating feature, Shift+Click to open the file
* Alt+Click to browse a folder in the OS's browser. */
- if (event->shift || event->alt) {
+ if (event->modifier & (KM_SHIFT | KM_ALT)) {
wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true);
PointerRNA props_ptr;
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
char *lslash = (char *)BLI_path_slash_rfind(str);
if (lslash) {
*lslash = '\0';
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index ba0b3a59e24..a24eae6a0ce 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -408,7 +408,7 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
* (when input method are used for utf8 inputs, the user may assign key event
* including alt/ctrl/super like ctrl+m to commit utf8 string. in such case,
* the modifiers in the utf8 character event make no sense.) */
- if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
+ if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
@@ -456,7 +456,18 @@ void CONSOLE_OT_insert(wmOperatorType *ot)
static int console_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(op))
{
ConsoleLine *ci = console_history_verify(C);
- bool text_before_cursor = ci->cursor != 0 && !ELEM(ci->line[ci->cursor - 1], ' ', '\t');
+ bool text_before_cursor = false;
+
+ /* Check any text before cursor (not just the previous character) as is done for
+ * #TEXT_OT_indent_or_autocomplete because Python auto-complete operates on import
+ * statements such as completing possible sub-modules: `from bpy import `. */
+ for (int i = 0; i < ci->cursor; i += BLI_str_utf8_size_safe(&ci->line[i])) {
+ if (!ELEM(ci->line[i], ' ', '\t')) {
+ text_before_cursor = true;
+ break;
+ }
+ }
+
if (text_before_cursor) {
WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL);
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 3b3d968aed6..daa4b53803f 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -2245,7 +2245,7 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const
cache->misc_entries_indices[cache->misc_cursor] = index;
cache->misc_cursor = (cache->misc_cursor + 1) % cache_size;
-#if 0 /* Actually no, only block cached entries should have preview imho. */
+#if 0 /* Actually no, only block cached entries should have preview IMHO. */
if (cache->previews_pool) {
filelist_cache_previews_push(filelist, ret, index);
}
diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c
index 1006ba4b3f4..1a3355b0139 100644
--- a/source/blender/editors/space_graph/graph_slider_ops.c
+++ b/source/blender/editors/space_graph/graph_slider_ops.c
@@ -163,6 +163,18 @@ static void reset_bezts(tGraphSliderOp *gso)
ANIM_animdata_freelist(&anim_data);
}
+/**
+ * Get factor value and store it in RNA property.
+ * Custom data of #wmOperator needs to contain #tGraphSliderOp.
+ */
+static float slider_factor_get_and_remember(wmOperator *op)
+{
+ tGraphSliderOp *gso = op->customdata;
+ const float factor = ED_slider_factor_get(gso->slider);
+ RNA_property_float_set(op->ptr, gso->factor_prop, factor);
+ return factor;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -378,8 +390,7 @@ static void decimate_modal_update(bContext *C, wmOperator *op)
reset_bezts(gso);
/* Apply... */
- float factor = ED_slider_factor_get(gso->slider);
- RNA_property_float_set(op->ptr, gso->factor_prop, factor);
+ const float factor = slider_factor_get_and_remember(op);
/* We don't want to limit the decimation to a certain error margin. */
const float error_sq_max = FLT_MAX;
decimate_graph_keys(&gso->ac, factor, error_sq_max);
@@ -598,8 +609,7 @@ static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op)
/* Reset keyframe data to the state at invoke. */
reset_bezts(gso);
- const float factor = ED_slider_factor_get(gso->slider);
- RNA_property_float_set(op->ptr, gso->factor_prop, factor);
+ const float factor = slider_factor_get_and_remember(op);
blend_to_neighbor_graph_keys(&gso->ac, factor);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -725,7 +735,8 @@ static void breakdown_modal_update(bContext *C, wmOperator *op)
/* Reset keyframe data to the state at invoke. */
reset_bezts(gso);
- breakdown_graph_keys(&gso->ac, ED_slider_factor_get(gso->slider));
+ const float factor = slider_factor_get_and_remember(op);
+ breakdown_graph_keys(&gso->ac, factor);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
@@ -739,6 +750,7 @@ static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
tGraphSliderOp *gso = op->customdata;
gso->modal_update = breakdown_modal_update;
+ gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
breakdown_draw_status_header(C, gso);
return invoke_result;
diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c
index 4351186dc6f..9f934e47ebb 100644
--- a/source/blender/editors/space_graph/graph_utils.c
+++ b/source/blender/editors/space_graph/graph_utils.c
@@ -185,6 +185,7 @@ bool graphop_editable_keyframes_poll(bContext *C)
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (items == 0) {
+ CTX_wm_operator_poll_msg_set(C, "There is no animation data to operate on");
return found;
}
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 27ac34ba346..22427675ff3 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -242,7 +242,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
GPU_blend(GPU_BLEND_NONE);
- /* Vertical component of of the cursor. */
+ /* Vertical component of the cursor. */
if (sipo->mode == SIPO_MODE_DRIVERS) {
/* cursor x-value */
float x = sipo->cursorTime;
diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc
index da899ef4c9a..6f0d5c2dbe9 100644
--- a/source/blender/editors/space_info/info_stats.cc
+++ b/source/blender/editors/space_info/info_stats.cc
@@ -152,7 +152,7 @@ static void stats_object(Object *ob,
}
break;
case OB_SURF:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_FONT: {
const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
if ((me_eval != nullptr) && !BLI_gset_add(objects_gset, (void *)me_eval)) {
@@ -260,7 +260,7 @@ static void stats_object_edit(Object *obedit, SceneStats *stats)
stats->totvert += 2;
}
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { /* OB_FONT has no cu->editnurb */
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { /* OB_FONT has no cu->editnurb */
/* Curve Edit */
Curve *cu = static_cast<Curve *>(obedit->data);
BezTriple *bezt;
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 4a507aa3bf2..8b059b33a9a 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -583,7 +583,7 @@ static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *
{
/* NOTE: this is hardcoded to match the behavior for the unlink button
* (in interface_templates.c) */
- RNA_boolean_set(op->ptr, "force_delete", event->shift != 0);
+ RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT);
return nla_action_unlink_exec(C, op);
}
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index eda9f89b51c..d8a0fde6d07 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -110,7 +110,7 @@ static void nla_action_draw_keyframes(
*/
Range2f frame_range;
- ED_keylist_frame_range(keylist, &frame_range);
+ ED_keylist_all_keys_frame_range(keylist, &frame_range);
immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2);
immUnbindProgram();
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 28ac7a34fa8..afb205f9f9e 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -147,7 +147,7 @@ static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerR
} // namespace blender::ed::space_node
#define SAMPLE_FLT_ISNONE FLT_MAX
-/* Bad bad, 2.5 will do better? ... no it won't! */
+/* Bad! 2.5 will do better? ... no it won't! */
static float _sample_col[4] = {SAMPLE_FLT_ISNONE};
void ED_node_sample_set(const float col[4])
{
@@ -1967,9 +1967,10 @@ void node_draw_link_bezier(const bContext &C,
const bNodeLink &link,
const int th_col1,
const int th_col2,
- const int th_col3)
+ const int th_col3,
+ const bool selected)
{
- const float dim_factor = node_link_dim_factor(v2d, link);
+ const float dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link);
float thickness = 1.5f;
float dash_factor = 1.0f;
@@ -2025,19 +2026,17 @@ void node_draw_link_bezier(const bContext &C,
}
/* Highlight links connected to selected nodes. */
- const bool is_fromnode_selected = link.fromnode && link.fromnode->flag & SELECT;
- const bool is_tonode_selected = link.tonode && link.tonode->flag & SELECT;
- if (is_fromnode_selected || is_tonode_selected) {
+ if (selected) {
float color_selected[4];
UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected);
const float alpha = color_selected[3];
/* Interpolate color if highlight color is not fully transparent. */
if (alpha != 0.0) {
- if (is_fromnode_selected) {
+ if (link.fromsock) {
interp_v3_v3v3(colors[1], colors[1], color_selected, alpha);
}
- if (is_tonode_selected) {
+ if (link.tosock) {
interp_v3_v3v3(colors[2], colors[2], color_selected, alpha);
}
}
@@ -2102,7 +2101,8 @@ void node_draw_link_bezier(const bContext &C,
void node_draw_link(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
- const bNodeLink &link)
+ const bNodeLink &link,
+ const bool selected)
{
int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE;
@@ -2146,7 +2146,7 @@ void node_draw_link(const bContext &C,
}
}
- node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3);
+ node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
}
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc
index a76988a60d0..349fa92d06d 100644
--- a/source/blender/editors/space_node/node_context_path.cc
+++ b/source/blender/editors/space_node/node_context_path.cc
@@ -47,7 +47,7 @@ static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Obje
Light *light = (Light *)object.data;
ui::context_path_add_generic(path, RNA_Light, light);
}
- if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) {
+ if (ELEM(object.type, OB_CURVES_LEGACY, OB_FONT, OB_SURF) && object.data) {
Curve *curve = (Curve *)object.data;
ui::context_path_add_generic(path, RNA_Curve, curve);
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 455eceed293..1286f6a818c 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -660,7 +660,7 @@ static void node_draw_mute_line(const bContext &C,
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (const bNodeLink *, link, &node.internal_links) {
- node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE);
+ node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
}
GPU_blend(GPU_BLEND_NONE);
@@ -718,7 +718,12 @@ static void node_socket_draw_multi_input(const float color[4],
const int locx,
const int locy)
{
- const float outline_width = 1.0f;
+ /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness
+ * that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac`
+ * has the the same effect here. It scales the outline correctly across different screen DPIs
+ * and UI scales without being affected by the 'line-width'. */
+ const float outline_width = NODE_SOCK_OUTLINE_SCALE * U.dpi_fac;
+
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
const rctf rect = {
locx - width + outline_width * 0.5f,
@@ -1049,7 +1054,7 @@ static void node_socket_draw_nested(const bContext &C,
},
data,
MEM_freeN);
- /* Disable the button so that clicks on it are ignored the the link operator still works. */
+ /* Disable the button so that clicks on it are ignored the link operator still works. */
UI_but_flag_enable(but, UI_BUT_DISABLED);
UI_block_emboss_set(&block, old_emboss);
}
@@ -1060,7 +1065,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
{
using namespace blender::ed::space_node;
- const float size = 2.25f * NODE_SOCKSIZE * scale;
+ const float size = NODE_SOCKSIZE_DRAW_MULIPLIER * NODE_SOCKSIZE * scale;
rcti draw_rect = *rect;
float outline_color[4] = {0};
@@ -1081,7 +1086,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
- immUniform1f("outline_scale", 1.0f);
+ immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Single point. */
@@ -1232,13 +1237,14 @@ static void node_draw_sockets(const View2D &v2d,
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
- immUniform1f("outline_scale", 1.0f);
+ immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Set handle size. */
+ const float socket_draw_size = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER;
float scale;
UI_view2d_scale_get(&v2d, &scale, nullptr);
- scale *= 2.25f * NODE_SOCKSIZE;
+ scale *= socket_draw_size;
if (!select_all) {
immBeginAtMost(GPU_PRIM_POINTS, total_input_len + total_output_len);
@@ -1251,7 +1257,10 @@ static void node_draw_sockets(const View2D &v2d,
continue;
}
if (select_all || (sock->flag & SELECT)) {
- selected_input_len++;
+ if (!(sock->flag & SOCK_MULTI_INPUT)) {
+ /* Don't add multi-input sockets here since they are drawn in a different batch. */
+ selected_input_len++;
+ }
continue;
}
/* Don't draw multi-input sockets here since they are drawn in a different batch. */
@@ -1318,6 +1327,10 @@ static void node_draw_sockets(const View2D &v2d,
if (nodeSocketIsHidden(sock)) {
continue;
}
+ /* Don't draw multi-input sockets here since they are drawn in a different batch. */
+ if (sock->flag & SOCK_MULTI_INPUT) {
+ continue;
+ }
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
@@ -1383,13 +1396,13 @@ static void node_draw_sockets(const View2D &v2d,
}
const bool is_node_hidden = (node.flag & NODE_HIDDEN);
- const float width = NODE_SOCKSIZE;
+ const float width = 0.5f * socket_draw_size;
float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width;
float color[4];
float outline_color[4];
node_socket_color_get(C, ntree, node_ptr, *socket, color);
- node_socket_outline_color_get(selected, socket->type, outline_color);
+ node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
}
@@ -2650,10 +2663,18 @@ static void node_draw_nodetree(const bContext &C,
nodelink_batch_start(snode);
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
- if (!nodeLinkIsHidden(link)) {
- node_draw_link(C, region.v2d, snode, *link);
+ if (!nodeLinkIsHidden(link) && !nodeLinkIsSelected(link)) {
+ node_draw_link(C, region.v2d, snode, *link, false);
}
}
+
+ /* Draw selected node links after the unselected ones, so they are shown on top. */
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
+ if (!nodeLinkIsHidden(link) && nodeLinkIsSelected(link)) {
+ node_draw_link(C, region.v2d, snode, *link, true);
+ }
+ }
+
nodelink_batch_end(snode);
GPU_blend(GPU_BLEND_NONE);
@@ -2838,7 +2859,7 @@ void node_draw_space(const bContext &C, ARegion &region)
GPU_line_smooth(true);
if (snode.runtime->linkdrag) {
for (const bNodeLink *link : snode.runtime->linkdrag->links) {
- node_draw_link(C, v2d, snode, *link);
+ node_draw_link(C, v2d, snode, *link, true);
}
}
GPU_line_smooth(false);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 7b7aaef518b..b30be6ae0af 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -89,7 +89,7 @@ struct CompoJob {
float node_socket_calculate_height(const bNodeSocket &socket)
{
- float sock_height = NODE_SOCKSIZE * 2.0f;
+ float sock_height = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER;
if (socket.flag & SOCK_MULTI_INPUT) {
sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket.total_inputs, NODE_SOCKSIZE);
}
@@ -1160,12 +1160,16 @@ bool node_find_indicated_socket(SpaceNode &snode,
{
rctf rect;
+ const float size_sock_padded = NODE_SOCKSIZE + 4;
+
*nodep = nullptr;
*sockp = nullptr;
/* check if we click in a socket */
LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) {
- BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4);
+ BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded);
+ rctf node_visible;
+ BLI_rctf_init_pt_radius(&node_visible, cursor, size_sock_padded);
if (!(node->flag & NODE_HIDDEN)) {
/* extra padding inside and out - allow dragging on the text areas too */
@@ -1184,7 +1188,7 @@ bool node_find_indicated_socket(SpaceNode &snode,
if (!nodeSocketIsHidden(sock)) {
if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
- if (node == visible_node(snode, rect)) {
+ if (node == visible_node(snode, node_visible)) {
*nodep = node;
*sockp = sock;
return true;
@@ -1192,7 +1196,7 @@ bool node_find_indicated_socket(SpaceNode &snode,
}
}
else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
- if (node == visible_node(snode, rect)) {
+ if (node == visible_node(snode, node_visible)) {
*nodep = node;
*sockp = sock;
return true;
@@ -1205,7 +1209,7 @@ bool node_find_indicated_socket(SpaceNode &snode,
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
- if (node == visible_node(snode, rect)) {
+ if (node == visible_node(snode, node_visible)) {
*nodep = node;
*sockp = sock;
return true;
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 592db3f7877..319f97e57f5 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -104,6 +104,8 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
#define NODE_HEIGHT(node) (node.height * UI_DPI_FAC)
#define NODE_MARGIN_X (1.2f * U.widget_unit)
#define NODE_SOCKSIZE (0.25f * U.widget_unit)
+#define NODE_SOCKSIZE_DRAW_MULIPLIER 2.25f
+#define NODE_SOCK_OUTLINE_SCALE 1.0f
#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
#define NODE_LINK_RESOL 12
@@ -196,7 +198,8 @@ void nodelink_batch_end(SpaceNode &snode);
void node_draw_link(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
- const bNodeLink &link);
+ const bNodeLink &link,
+ bool selected);
/**
* Don't do shadows if th_col3 is -1.
*/
@@ -206,7 +209,8 @@ void node_draw_link_bezier(const bContext &C,
const bNodeLink &link,
int th_col1,
int th_col2,
- int th_col3);
+ int th_col3,
+ bool selected);
/** If v2d not nullptr, it clips and returns 0 if not visible. */
bool node_link_bezier_points(const View2D *v2d,
const SpaceNode *snode,
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index 1cfa932356b..299e7e5658c 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -2436,16 +2436,18 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
- /* Ignore main sockets when the types don't match. */
- if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
- !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
- static_cast<eNodeSocketDatatype>(best_input->type))) {
- best_input = nullptr;
- }
- if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
- !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
- static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
- best_output = nullptr;
+ if (node_to_insert->type != NODE_REROUTE) {
+ /* Ignore main sockets when the types don't match. */
+ if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
+ static_cast<eNodeSocketDatatype>(best_input->type))) {
+ best_input = nullptr;
+ }
+ if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
+ static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
+ best_output = nullptr;
+ }
}
bNode *from_node = old_link->fromnode;
diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc
index 765661aa9d5..f38f6c2855d 100644
--- a/source/blender/editors/space_outliner/outliner_collections.cc
+++ b/source/blender/editors/space_outliner/outliner_collections.cc
@@ -1071,7 +1071,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op)
static int collection_isolate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "extend");
- if (!RNA_property_is_set(op->ptr, prop) && (event->shift)) {
+ if (!RNA_property_is_set(op->ptr, prop) && (event->modifier & KM_SHIFT)) {
RNA_property_boolean_set(op->ptr, prop, true);
}
return collection_isolate_exec(C, op);
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc
index 0d8ee76d2f0..edd2e5f304f 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.cc
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc
@@ -319,7 +319,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
}
if (!allow_parenting_without_modifier_key(space_outliner)) {
- if (!event->shift) {
+ if ((event->modifier & KM_SHIFT) == 0) {
return false;
}
}
@@ -417,8 +417,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ListBase *lb = reinterpret_cast<ListBase *>(event->customdata);
wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
- parent_drop_set_parents(
- C, op->reports, reinterpret_cast<wmDragID *>(drag->ids.first), par, PAR_OBJECT, event->alt);
+ parent_drop_set_parents(C,
+ op->reports,
+ reinterpret_cast<wmDragID *>(drag->ids.first),
+ par,
+ PAR_OBJECT,
+ event->modifier & KM_ALT);
return OPERATOR_FINISHED;
}
@@ -446,7 +450,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
if (!allow_parenting_without_modifier_key(space_outliner)) {
- if (!event->shift) {
+ if ((event->modifier & KM_SHIFT) == 0) {
return false;
}
}
@@ -471,7 +475,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
case ID_OB:
return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE);
case ID_GR:
- return event->shift || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE);
+ return (event->modifier & KM_SHIFT) || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE);
default:
return true;
}
@@ -496,7 +500,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven
if (GS(drag_id->id->name) == ID_OB) {
Object *object = (Object *)drag_id->id;
- ED_object_parent_clear(object, event->alt ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL);
+ ED_object_parent_clear(
+ object, (event->modifier & KM_ALT) ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL);
}
}
@@ -1166,10 +1171,11 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
CollectionDrop data;
- if (!event->shift && collection_drop_init(C, drag, event->xy, event->ctrl, &data)) {
+ if (((event->modifier & KM_SHIFT) == 0) &&
+ collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) {
TreeElement *te = data.te;
TreeStoreElem *tselem = TREESTORE(te);
- if (!data.from || event->ctrl) {
+ if (!data.from || event->modifier & KM_CTRL) {
tselem->flag |= TSE_DRAG_INTO;
changed = true;
}
@@ -1210,9 +1216,10 @@ static char *collection_drop_tooltip(bContext *C,
const wmEvent *event = win ? win->eventstate : nullptr;
CollectionDrop data;
- if (event && !event->shift && collection_drop_init(C, drag, xy, event->ctrl, &data)) {
+ if (event && ((event->modifier & KM_SHIFT) == 0) &&
+ collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) {
TreeElement *te = data.te;
- if (!data.from || event->ctrl) {
+ if (!data.from || event->modifier & KM_CTRL) {
return BLI_strdup(TIP_("Link inside Collection"));
}
switch (data.insert_type) {
@@ -1263,7 +1270,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
CollectionDrop data;
- if (!collection_drop_init(C, drag, event->xy, event->ctrl, &data)) {
+ if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) {
return OPERATOR_CANCELLED;
}
@@ -1291,7 +1298,9 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
/* Ctrl enables linking, so we don't need a from collection then. */
- Collection *from = (event->ctrl) ? nullptr : collection_parent_from_ID(drag_id->from_parent);
+ Collection *from = (event->modifier & KM_CTRL) ?
+ nullptr :
+ collection_parent_from_ID(drag_id->from_parent);
if (GS(drag_id->id->name) == ID_OB) {
/* Move/link object into collection. */
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index dd7ca128282..2da416c8671 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -166,7 +166,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU
{
Bone *bone = (Bone *)poin;
- if (CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0);
}
}
@@ -178,7 +178,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void
bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0);
}
@@ -194,7 +194,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *poin, void *poin2)
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
restrictbutton_recursive_ebone(
arm, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0);
}
@@ -210,7 +210,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *poin, void *po
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
- if (CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
restrictbutton_recursive_ebone(arm, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0);
}
@@ -250,7 +250,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
PointerRNA ptr;
- bool extend = (win->eventstate->shift != 0);
+ bool extend = (win->eventstate->modifier & KM_SHIFT);
if (!extend) {
return;
@@ -571,8 +571,8 @@ static void outliner_collection_set_flag_recursive_fn(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
PointerRNA ptr;
- bool do_isolate = (win->eventstate->ctrl != 0);
- bool extend = (win->eventstate->shift != 0);
+ bool do_isolate = (win->eventstate->modifier & KM_CTRL);
+ bool extend = (win->eventstate->modifier & KM_SHIFT);
if (!ELEM(true, do_isolate, extend)) {
return;
@@ -2043,7 +2043,7 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED
const bool object_data_shared = (ob->data == tvc.obact->data);
wmWindow *win = CTX_wm_window(C);
- const bool do_extend = win->eventstate->ctrl != 0 && !object_data_shared;
+ const bool do_extend = (win->eventstate->modifier & KM_CTRL) && !object_data_shared;
outliner_item_mode_toggle(C, &tvc, te, do_extend);
}
@@ -2592,7 +2592,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case OB_CAMERA:
data.icon = ICON_OUTLINER_OB_CAMERA;
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
data.icon = ICON_OUTLINER_OB_CURVE;
break;
case OB_MBALL:
@@ -2655,7 +2655,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case ID_ME:
data.icon = ICON_OUTLINER_DATA_MESH;
break;
- case ID_CU:
+ case ID_CU_LEGACY:
data.icon = ICON_OUTLINER_DATA_CURVE;
break;
case ID_MB:
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index a6ac2a5a1f3..6916f5fe502 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -240,7 +240,7 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE
outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
/* Only toggle once for single click toggling */
- if (event->type == LEFTMOUSE) {
+ if ((event->type == LEFTMOUSE) && (event->val != KM_CLICK_DRAG)) {
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index bba4bac0e37..0516758e887 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -108,7 +108,7 @@ typedef struct TreeElementIcon {
ID_LI, \
ID_OB, \
ID_ME, \
- ID_CU, \
+ ID_CU_LEGACY, \
ID_MB, \
ID_NT, \
ID_MA, \
diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc
index df10ce002c3..c6b9d9577b5 100644
--- a/source/blender/editors/space_outliner/outliner_select.cc
+++ b/source/blender/editors/space_outliner/outliner_select.cc
@@ -1171,7 +1171,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
context = BCONTEXT_OBJECT;
break;
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_MB:
case ID_IM:
case ID_LT:
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 18c37e08eff..8fcf967bce8 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -117,7 +117,7 @@ static void get_element_operation_type(
break;
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_MB:
case ID_LT:
case ID_LA:
@@ -236,7 +236,7 @@ static void unlink_material_fn(bContext *UNUSED(C),
totcol = me->totcol;
matar = me->mat;
}
- else if (GS(tsep->id->name) == ID_CU) {
+ else if (GS(tsep->id->name) == ID_CU_LEGACY) {
Curve *cu = (Curve *)tsep->id;
totcol = cu->totcol;
matar = cu->mat;
@@ -766,29 +766,37 @@ static void id_override_library_create_fn(bContext *C,
void *user_data)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
- ID *id_root = tselem->id;
+
+ /* We can only safely apply this operation on one item at a time, so only do it on the active
+ * one. */
+ if ((tselem->flag & TSE_ACTIVE) == 0) {
+ return;
+ }
+
+ ID *id_root_reference = tselem->id;
OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
bool success = false;
- ID *id_reference = nullptr;
+ ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr &&
GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id)) {
Object *ob = (Object *)tsep->id;
- if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root) {
- BLI_assert(GS(id_root->name) == ID_GR);
+ if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) {
+ BLI_assert(GS(id_root_reference->name) == ID_GR);
/* Empty instantiating the collection we override, we need to pass it to BKE overriding code
* for proper handling. */
- id_reference = tsep->id;
+ id_instance_hint = tsep->id;
is_override_instancing_object = true;
}
}
- if (ID_IS_OVERRIDABLE_LIBRARY(id_root) || (ID_IS_LINKED(id_root) && do_hierarchy)) {
+ if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
+ (ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
Main *bmain = CTX_data_main(C);
- id_root->tag |= LIB_TAG_DOIT;
+ id_root_reference->tag |= LIB_TAG_DOIT;
/* For now, remap all local usages of linked ID to local override one here. */
ID *id_iter;
@@ -804,38 +812,100 @@ static void id_override_library_create_fn(bContext *C,
if (do_hierarchy) {
/* Tag all linked parents in tree hierarchy to be also overridden. */
+ ID *id_hierarchy_root_reference = id_root_reference;
while ((te = te->parent) != nullptr) {
if (!TSE_IS_REAL_ID(te->store_elem)) {
continue;
}
- if (!ID_IS_LINKED(te->store_elem->id)) {
+
+ /* Tentative hierarchy root. */
+ ID *id_current_hierarchy_root = te->store_elem->id;
+
+ /* If the parent ID is from a different library than the reference root one, we are done
+ * with upwards tree processing in any case. */
+ if (id_current_hierarchy_root->lib != id_root_reference->lib) {
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
+ /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
+ * get an actual real override. */
+ continue;
+ }
+
+ /* If the parent ID is already an override, and is valid (i.e. local override), we can
+ * access its hierarchy root directly. */
+ if (!ID_IS_LINKED(id_current_hierarchy_root) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
+ id_current_hierarchy_root->override_library->reference->lib ==
+ id_root_reference->lib) {
+ id_hierarchy_root_reference =
+ id_current_hierarchy_root->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+ break;
+ }
+
+ if (ID_IS_LINKED(id_current_hierarchy_root)) {
+ /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
+ * would most likely generate invisible/confusing/hard to use and manage overrides. */
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid anchor ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_current_hierarchy_root->name,
+ id_root_reference->name);
+ return;
+ }
+
+ /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
+ * current `id_hierarchy_root_reference` is our best candidate. */
+
break;
}
+
/* If some element in the tree needs to be overridden, but its ID is not overridable,
* abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(te->store_elem->id)) {
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from data-block '%s', one of its parents "
"is not overridable ('%s')",
- id_root->name,
- te->store_elem->id->name);
+ id_root_reference->name,
+ id_current_hierarchy_root->name);
return;
}
- te->store_elem->id->tag |= LIB_TAG_DOIT;
+ id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
+ id_hierarchy_root_reference = id_current_hierarchy_root;
+ }
+
+ /* That case can happen when linked data is a complex mix involving several libraries and/or
+ * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
+ * from another library. Do not try to support such cases for now. */
+ if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
+ (!ID_IS_LINKED(id_hierarchy_root_reference) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
+ id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib))) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid hierarchy root ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_hierarchy_root_reference->name,
+ id_root_reference->name);
+ return;
}
success = BKE_lib_override_library_create(bmain,
CTX_data_scene(C),
CTX_data_view_layer(C),
nullptr,
- id_root,
- id_reference,
+ id_root_reference,
+ id_hierarchy_root_reference,
+ id_instance_hint,
nullptr);
}
- else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
- success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != nullptr;
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
+ success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
/* Cleanup. */
BKE_main_id_newptr_and_tag_clear(bmain);
@@ -845,14 +915,14 @@ static void id_override_library_create_fn(bContext *C,
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, (Object *)id_reference);
+ ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
}
}
if (!success) {
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from data-block '%s'",
- id_root->name);
+ id_root_reference->name);
}
}
@@ -1770,13 +1840,15 @@ static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
"OVERRIDE_LIBRARY_CREATE",
0,
- "Make Library Override",
- "Make a local override of this linked data-block"},
+ "Make Library Override Single",
+ "Make a single, out-of-hierarchy local override of this linked data-block - only applies to "
+ "active Outliner item"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
"OVERRIDE_LIBRARY_CREATE_HIERARCHY",
0,
"Make Library Override Hierarchy",
- "Make a local override of this linked data-block, and its hierarchy of dependencies"},
+ "Make a local override of this linked data-block, and its hierarchy of dependencies - only "
+ "applies to active Outliner item"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
"OVERRIDE_LIBRARY_RESET",
0,
diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc
index 1605d5874ae..06a5918f25c 100644
--- a/source/blender/editors/space_outliner/outliner_tree.cc
+++ b/source/blender/editors/space_outliner/outliner_tree.cc
@@ -579,7 +579,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
* would require going over all tfaces, sort images in use. etc... */
break;
}
- case ID_CU: {
+ case ID_CU_LEGACY: {
Curve *cu = (Curve *)id;
if (outliner_animdata_test(cu->adt)) {
diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc
index 6b68f1ee4a4..f9141dffd6a 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display.cc
@@ -7,6 +7,8 @@
#include "DNA_listBase.h"
#include "DNA_space_types.h"
+#include "BLI_utildefines.h"
+
#include "tree_display.hh"
using namespace blender::ed::outliner;
@@ -30,11 +32,11 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode(
case SO_OVERRIDES_LIBRARY:
return std::make_unique<TreeDisplayOverrideLibrary>(space_outliner);
case SO_VIEW_LAYER:
- /* FIXME(Julian): this should not be the default! Return nullptr and handle that as valid
- * case. */
- default:
return std::make_unique<TreeDisplayViewLayer>(space_outliner);
}
+
+ BLI_assert_unreachable();
+ return nullptr;
}
bool AbstractTreeDisplay::hasWarnings() const
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
index 43d67ee106d..f94727ba356 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
@@ -23,11 +23,6 @@
namespace blender::ed::outliner {
/* Convenience/readability. */
-/* Convenience/readability. */
-/* Convenience/readability. */
-/* Convenience/readability. */
-/* Convenience/readability. */
-/* Convenience/readability. */
template<typename T> using List = ListBaseWrapper<T>;
TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner)
diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh
index 996f51eee82..2fbc86705b9 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element.hh
@@ -89,8 +89,8 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner
/**
* Get actual warning data of a tree element, if any.
*
- * \param r_icon The icon to display as warning.
- * \param r_message The message to display as warning.
+ * \param r_icon: The icon to display as warning.
+ * \param r_message: The message to display as warning.
* \return true if there is a warning, false otherwise.
*/
bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message);
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc
index e126b024d52..64c73f57107 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc
@@ -34,7 +34,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id);
case ID_OB:
case ID_ME:
- case ID_CU:
+ case ID_CU_LEGACY:
case ID_MB:
case ID_MA:
case ID_TE:
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 8c12193fb88..5ac4363e63d 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -1636,7 +1636,9 @@ static void sequencer_draw_gpencil_overlay(const bContext *C)
ED_annotation_draw_view2d(C, 0);
}
-/* Draw content and safety borders borders. */
+/**
+ * Draw content and safety borders.
+ */
static void sequencer_draw_borders_overlay(const SpaceSeq *sseq,
const View2D *v2d,
const Scene *scene)
@@ -1926,7 +1928,6 @@ static void sequencer_draw_display_buffer(const bContext *C,
if (!glsl_used) {
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
- immUniform1i("image", 0);
}
immBegin(GPU_PRIM_TRI_FAN, 4);
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 3be890bfcc5..fbdc451cf06 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -264,7 +264,7 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
return nullptr;
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE, OB_FONT)) {
+ if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVES_LEGACY, OB_FONT)) {
return nullptr;
}
diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c
index 496f500ef04..55873740491 100644
--- a/source/blender/editors/space_text/text_autocomplete.c
+++ b/source/blender/editors/space_text/text_autocomplete.c
@@ -408,7 +408,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
case EVT_BACKSPACEKEY:
if (event->val == KM_PRESS) {
if (tools & TOOL_SUGG_LIST) {
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
texttool_suggest_clear();
retval = OPERATOR_CANCELLED;
draw = 1;
@@ -445,7 +445,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
case EVT_RIGHTARROWKEY:
if (event->val == KM_PRESS) {
if (tools & TOOL_SUGG_LIST) {
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
texttool_suggest_clear();
retval = OPERATOR_CANCELLED;
draw = 1;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index ddba6803f61..3c29b32c2fa 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -3495,7 +3495,7 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
* (when input method are used for utf8 inputs, the user may assign key event
* including alt/ctrl/super like ctrl+m to commit utf8 string. in such case,
* the modifiers in the utf8 character event make no sense.) */
- if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
+ if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 7addda0f8c3..4656540c19b 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -413,6 +413,9 @@ static void view3d_main_region_init(wmWindowManager *wm, ARegion *region)
keymap = WM_keymap_ensure(wm->defaultconf, "Particle", 0, 0);
WM_event_add_keymap_handler(&region->handlers, keymap);
+ keymap = WM_keymap_ensure(wm->defaultconf, "Sculpt Curves", 0, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+
/* editfont keymap swallows all... */
keymap = WM_keymap_ensure(wm->defaultconf, "Font", 0, 0);
WM_event_add_keymap_handler(&region->handlers, keymap);
@@ -1475,6 +1478,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_EDIT_CURVE:
ARRAY_SET_ITEMS(contexts, ".curve_edit");
break;
+ case CTX_MODE_EDIT_CURVES:
+ ARRAY_SET_ITEMS(contexts, ".curves_edit");
+ break;
case CTX_MODE_EDIT_SURFACE:
ARRAY_SET_ITEMS(contexts, ".curve_edit");
break;
@@ -1523,6 +1529,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_VERTEX_GPENCIL:
ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex");
break;
+ case CTX_MODE_SCULPT_CURVES:
+ ARRAY_SET_ITEMS(contexts, ".curves_sculpt");
+ break;
default:
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index b77994e28cb..cf52134f5ab 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -355,7 +355,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
has_meshdata = (tot || totedgedata);
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
TransformMedian_Curve *median = &median_basis.curve;
Curve *cu = ob->data;
BPoint *bp;
@@ -1089,7 +1089,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
}
}
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF) &&
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF) &&
(apply_vcos || median_basis.curve.b_weight || median_basis.curve.weight ||
median_basis.curve.radius || median_basis.curve.tilt)) {
const TransformMedian_Curve *median = &median_basis.curve,
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index 785c5ab28c8..53f7b3d5871 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -63,7 +63,7 @@ typedef struct SnapCursorDataIntern {
int x;
int y;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- short shift, ctrl, alt, oskey;
+ uint8_t modifier;
#endif
} last_eventstate;
@@ -478,10 +478,7 @@ static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *data_intern,
}
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
if (!(state && (state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) {
- if ((event->ctrl != data_intern->last_eventstate.ctrl) ||
- (event->shift != data_intern->last_eventstate.shift) ||
- (event->alt != data_intern->last_eventstate.alt) ||
- (event->oskey != data_intern->last_eventstate.oskey)) {
+ if (event->modifier != data_intern->last_eventstate.modifier) {
return true;
}
}
@@ -507,19 +504,13 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w
}
const wmEvent *event = wm->winactive->eventstate;
- if ((event->ctrl == data_intern->last_eventstate.ctrl) &&
- (event->shift == data_intern->last_eventstate.shift) &&
- (event->alt == data_intern->last_eventstate.alt) &&
- (event->oskey == data_intern->last_eventstate.oskey)) {
+ if (event->modifier == data_intern->last_eventstate.modifier) {
/* Nothing has changed. */
return data_intern->snap_data.is_snap_invert;
}
/* Save new eventstate. */
- data_intern->last_eventstate.ctrl = event->ctrl;
- data_intern->last_eventstate.shift = event->shift;
- data_intern->last_eventstate.alt = event->alt;
- data_intern->last_eventstate.oskey = event->oskey;
+ data_intern->last_eventstate.modifier = event->modifier;
const int snap_on = data_intern->snap_on;
@@ -530,10 +521,10 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w
}
if (kmi->propvalue == snap_on) {
- if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) ||
- (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) ||
- (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) ||
- ((kmi->type == EVT_OSKEY) && event->oskey)) {
+ if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && (event->modifier & KM_CTRL)) ||
+ (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && (event->modifier & KM_SHIFT)) ||
+ (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) ||
+ ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) {
return true;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index c4078c4a690..593c4f6e755 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1351,7 +1351,7 @@ static void draw_selected_name(
}
}
}
- else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) {
+ else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY)) {
/* try to display active bone and active shapekey too (if they exist) */
if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) {
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index d6bc7ded92e..5adce170e06 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -832,13 +832,13 @@ void ED_view3d_cursor3d_position(bContext *C,
return;
}
- ED_view3d_calc_zfac(rv3d, cursor_co, &flip);
+ ED_view3d_calc_zfac_ex(rv3d, cursor_co, &flip);
/* Reset the depth based on the view offset (we _know_ the offset is in front of us). */
if (flip) {
negate_v3_v3(cursor_co, rv3d->ofs);
/* re initialize, no need to check flip again */
- ED_view3d_calc_zfac(rv3d, cursor_co, NULL /* &flip */);
+ ED_view3d_calc_zfac(rv3d, cursor_co);
}
if (use_depth) { /* maybe this should be accessed some other way */
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
index 6cc197c8a43..a0c010a6813 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
@@ -105,8 +105,8 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
/* Hack: Switch action mode based on key input */
- const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL;
- const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT;
+ const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0;
+ const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0;
EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM);
if (is_ctrl_pressed && !is_shift_pressed) {
EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE);
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index e1fd96ca1d4..055aac041f1 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -25,6 +25,7 @@
#include "BKE_editmesh.h"
#include "BKE_mesh_iterators.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "DEG_depsgraph.h"
@@ -334,6 +335,7 @@ void mesh_foreachScreenVert(
Mesh *me = editbmesh_get_eval_cage_from_orig(
vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
+ me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me);
ED_view3d_check_mats_rv3d(vc->rv3d);
@@ -396,6 +398,7 @@ void mesh_foreachScreenEdge(ViewContext *vc,
Mesh *me = editbmesh_get_eval_cage_from_orig(
vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
+ me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me);
ED_view3d_check_mats_rv3d(vc->rv3d);
@@ -483,6 +486,7 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc,
Mesh *me = editbmesh_get_eval_cage_from_orig(
vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
+ me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me);
ED_view3d_check_mats_rv3d(vc->rv3d);
@@ -554,6 +558,7 @@ void mesh_foreachScreenFace(
Mesh *me = editbmesh_get_eval_cage_from_orig(
vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
+ me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me);
ED_view3d_check_mats_rv3d(vc->rv3d);
data.vc = *vc;
diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c
index 0305989d142..d1e7f6ffb12 100644
--- a/source/blender/editors/space_view3d/view3d_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate.c
@@ -396,7 +396,7 @@ ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOp
{
float tvec[3];
negate_v3_v3(tvec, rv3d->ofs);
- vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
+ vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec);
}
vod->reverse = 1.0f;
@@ -544,26 +544,24 @@ static void axis_set_view(bContext *C,
void viewmove_apply(ViewOpsData *vod, int x, int y)
{
- if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
- vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx;
- vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy;
+ const float event_ofs[2] = {
+ vod->prev.event_xy[0] - x,
+ vod->prev.event_xy[1] - y,
+ };
+
+ if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
+ ED_view3d_camera_view_pan(vod->region, event_ofs);
}
- else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
- const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
- vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac);
- vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac);
- CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
- CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
+ else if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
+ vod->rv3d->ofs_lock[0] -= (event_ofs[0] * 2.0f) / (float)vod->region->winx;
+ vod->rv3d->ofs_lock[1] -= (event_ofs[1] * 2.0f) / (float)vod->region->winy;
}
else {
float dvec[3];
- float mval_f[2];
- mval_f[0] = x - vod->prev.event_xy[0];
- mval_f[1] = y - vod->prev.event_xy[1];
- ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac);
+ ED_view3d_win_to_delta(vod->region, event_ofs, vod->init.zfac, dvec);
- add_v3_v3(vod->rv3d->ofs, dvec);
+ sub_v3_v3(vod->rv3d->ofs, dvec);
if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
view3d_boxview_sync(vod->area, vod->region);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
index 06b616e71da..7b6b119294d 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
@@ -50,9 +50,12 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf)
/* disabled mode switching for now, can re-implement better, later on */
#if 0
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
- WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
+ WM_modalkeymap_add_item(
+ keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(
+ keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(
+ keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_MOVE);
#endif
/* assign map to operators */
diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c
index d2fd505a703..071643e9314 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_move.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_move.c
@@ -43,8 +43,8 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf)
keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items);
/* items for modal map */
- WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
- WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM);
/* disabled mode switching for now, can re-implement better, later on */
#if 0
diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
index ced8eca710b..1ce9bdcb211 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_ndof.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
@@ -48,7 +48,7 @@ static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth
float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND;
if (rv3d->is_persp) {
- speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL);
+ speed *= ED_view3d_calc_zfac(rv3d, depth_pt);
}
return speed;
@@ -347,6 +347,70 @@ void view3d_ndof_fly(const wmNDOFMotionData *ndof,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name NDOF Camera View Support
+ * \{ */
+
+/**
+ * 2D orthographic style NDOF navigation within the camera view.
+ * Support navigating the camera view instead of leaving the camera-view and navigating in 3D.
+ */
+static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event)
+{
+ const wmNDOFMotionData *ndof = event->customdata;
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+
+ ED_view3d_smooth_view_force_finish(C, v3d, region);
+
+ if ((v3d->camera && (rv3d->persp == RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA) == 0)) {
+ /* pass */
+ }
+ else {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ const bool has_translate = !is_zero_v2(ndof->tvec);
+ const bool has_zoom = ndof->tvec[2] != 0.0f;
+
+ /* NOTE(@campbellbarton): In principle rotating could pass through to regular
+ * non-camera NDOF behavior (exiting the camera-view and rotating).
+ * Disabled this block since in practice it's difficult to control NDOF devices
+ * to perform some rotation with absolutely no translation. Causing rotation to
+ * randomly exit from the user perspective. Adjusting the dead-zone could avoid
+ * the motion feeling *glitchy* although in my own tests even then it didn't work reliably.
+ * Leave rotating out of camera-view disabled unless it can be made to work reliably. */
+ if (!(has_translate || has_zoom)) {
+ // return OPERATOR_PASS_THROUGH;
+ }
+
+ bool changed = false;
+
+ if (has_translate) {
+ const float speed = ndof->dt * NDOF_PIXELS_PER_SECOND;
+ float event_ofs[2] = {ndof->tvec[0] * speed, ndof->tvec[1] * speed};
+ if (ED_view3d_camera_view_pan(region, event_ofs)) {
+ changed = true;
+ }
+ }
+
+ if (has_zoom) {
+ const float scale = 1.0f + (ndof->dt * ndof->tvec[2]);
+ if (ED_view3d_camera_view_zoom_scale(rv3d, scale)) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ ED_region_tag_redraw(region);
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name NDOF Orbit/Translate Operator
* \{ */
@@ -436,6 +500,13 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev
return OPERATOR_CANCELLED;
}
+ if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) {
+ const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event);
+ if (camera_retval != OPERATOR_PASS_THROUGH) {
+ return camera_retval;
+ }
+ }
+
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewOpsData *vod;
View3D *v3d;
@@ -550,6 +621,13 @@ static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *e
return OPERATOR_CANCELLED;
}
+ if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) {
+ const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event);
+ if (camera_retval != OPERATOR_PASS_THROUGH) {
+ return camera_retval;
+ }
+ }
+
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c
index 56bd9c93216..9c070fb0341 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_roll.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c
@@ -24,8 +24,17 @@
/** \name View Roll Operator
* \{ */
-static void view_roll_angle(
- ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle)
+/**
+ * \param use_axis_view: When true, keep axis-aligned orthographic views
+ * (when rotating in 90 degree increments). While this may seem obscure some NDOF
+ * devices have key shortcuts to do this (see #NDOF_BUTTON_ROLL_CW & #NDOF_BUTTON_ROLL_CCW).
+ */
+static void view_roll_angle(ARegion *region,
+ float quat[4],
+ const float orig_quat[4],
+ const float dvec[3],
+ float angle,
+ bool use_axis_view)
{
RegionView3D *rv3d = region->regiondata;
float quat_mul[4];
@@ -38,7 +47,16 @@ static void view_roll_angle(
/* avoid precision loss over time */
normalize_qt(quat);
- rv3d->view = RV3D_VIEW_USER;
+ if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) {
+ if (ED_view3d_quat_to_axis_view(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll)) {
+ if (rv3d->view != RV3D_VIEW_USER) {
+ ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_mul);
+ }
+ }
+ }
+ else {
+ rv3d->view = RV3D_VIEW_USER;
+ }
}
static void viewroll_apply(ViewOpsData *vod, int x, int y)
@@ -46,7 +64,8 @@ static void viewroll_apply(ViewOpsData *vod, int x, int y)
float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y});
if (angle != 0.0f) {
- view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle);
+ view_roll_angle(
+ vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle, false);
}
if (vod->use_dyn_ofs) {
@@ -169,7 +188,7 @@ static int viewroll_exec(bContext *C, wmOperator *op)
normalize_v3_v3(mousevec, rv3d->viewinv[2]);
negate_v3(mousevec);
- view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle);
+ view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle, true);
const float *dyn_ofs_pt = NULL;
float dyn_ofs[3];
diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
index 774a8983c67..11de5463cdb 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
@@ -383,7 +383,7 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int event_xy[2];
if (event->type == MOUSEPAN) {
- if (event->is_direction_inverted) {
+ if (event->flag & WM_EVENT_SCROLL_INVERT) {
event_xy[0] = 2 * event->xy[0] - event->prev_xy[0];
event_xy[1] = 2 * event->xy[1] - event->prev_xy[1];
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
index 8eb8fffaa31..5f6f9fde324 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
@@ -125,18 +125,18 @@ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoo
float dvec[3];
float tvec[3];
float tpos[3];
- float mval_f[2];
+ float xy_delta[2];
float zfac;
negate_v3_v3(tpos, rv3d->ofs);
- mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f;
- mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f;
+ xy_delta[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f;
+ xy_delta[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f;
/* Project cursor position into 3D space */
- zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ zfac = ED_view3d_calc_zfac(rv3d, tpos);
+ ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
/* Calculate view target position for dolly */
add_v3_v3v3(tvec, tpos, dvec);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
index 4e909151ce4..f834efe4a7b 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
@@ -125,7 +125,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
negate_v3_v3(new_ofs, p);
}
else {
- float mval_f[2];
+ float xy_delta[2];
float zfac;
/* We can't use the depth, fallback to the old way that doesn't set the center depth */
@@ -134,12 +134,12 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
{
float tvec[3];
negate_v3_v3(tvec, new_ofs);
- zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
+ zfac = ED_view3d_calc_zfac(rv3d, tvec);
}
- mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f;
- mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f;
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ xy_delta[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f;
+ xy_delta[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f;
+ ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
/* center the view to the center of the rectangle */
sub_v3_v3(new_ofs, dvec);
}
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 06b848571d8..98fb914cda9 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -727,6 +727,17 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active();
if (snap_state_new) {
ipd->snap_state = snap_state = snap_state_new;
+
+ /* For drag events, update the location since it will be set from the drag-start.
+ * This is needed as cursor-drawing doesn't deal with drag events and will use
+ * the current cursor location instead of the drag-start. */
+ if (event->val == KM_CLICK_DRAG) {
+ /* Set this flag so snapping always updated. */
+ int flag_orig = snap_state_new->flag;
+ snap_state_new->flag |= V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE;
+ ED_view3d_cursor_snap_data_get(snap_state_new, C, event->mval[0], event->mval[1]);
+ snap_state_new->flag = flag_orig;
+ }
}
snap_state->draw_point = true;
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 2cf9ac0a52e..85d239507ce 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -276,7 +276,7 @@ float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[
return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize;
}
-float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip)
+float ED_view3d_calc_zfac_ex(const RegionView3D *rv3d, const float co[3], bool *r_flip)
{
float zfac = mul_project_m4_v3_zfac(rv3d->persmat, co);
@@ -299,10 +299,15 @@ float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_f
return zfac;
}
+float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
+{
+ return ED_view3d_calc_zfac_ex(rv3d, co, NULL);
+}
+
float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float co[3])
{
if (rv3d->is_persp) {
- return ED_view3d_calc_zfac(rv3d, co, NULL);
+ return ED_view3d_calc_zfac(rv3d, co);
}
return -dot_v3v3(rv3d->viewinv[2], co);
}
@@ -436,8 +441,8 @@ bool view3d_get_view_aligned_coordinate(ARegion *region,
if (ret == V3D_PROJ_RET_OK) {
const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]), (float)(mval_cpy[1] - mval[1])};
- const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL);
- ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ const float zfac = ED_view3d_calc_zfac(rv3d, fp);
+ ED_view3d_win_to_delta(region, mval_f, zfac, dvec);
sub_v3_v3(fp, dvec);
return true;
@@ -584,57 +589,57 @@ bool ED_view3d_win_to_3d_on_plane_with_fallback(const ARegion *region,
}
void ED_view3d_win_to_delta(const ARegion *region,
- const float mval[2],
- float out[3],
- const float zfac)
+ const float xy_delta[2],
+ const float zfac,
+ float r_out[3])
{
RegionView3D *rv3d = region->regiondata;
float dx, dy;
- dx = 2.0f * mval[0] * zfac / region->winx;
- dy = 2.0f * mval[1] * zfac / region->winy;
+ dx = 2.0f * xy_delta[0] * zfac / region->winx;
+ dy = 2.0f * xy_delta[1] * zfac / region->winy;
- out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy);
- out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy);
- out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy);
+ r_out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy);
+ r_out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy);
+ r_out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy);
}
-void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float out[3])
+void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float r_out[3])
{
RegionView3D *rv3d = region->regiondata;
if (rv3d->is_persp) {
- copy_v3_v3(out, rv3d->viewinv[3]);
+ copy_v3_v3(r_out, rv3d->viewinv[3]);
}
else {
- out[0] = 2.0f * mval[0] / region->winx - 1.0f;
- out[1] = 2.0f * mval[1] / region->winy - 1.0f;
+ r_out[0] = 2.0f * mval[0] / region->winx - 1.0f;
+ r_out[1] = 2.0f * mval[1] / region->winy - 1.0f;
if (rv3d->persp == RV3D_CAMOB) {
- out[2] = -1.0f;
+ r_out[2] = -1.0f;
}
else {
- out[2] = 0.0f;
+ r_out[2] = 0.0f;
}
- mul_project_m4_v3(rv3d->persinv, out);
+ mul_project_m4_v3(rv3d->persinv, r_out);
}
}
-void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float out[3])
+void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3])
{
RegionView3D *rv3d = region->regiondata;
if (rv3d->is_persp) {
- out[0] = 2.0f * (mval[0] / region->winx) - 1.0f;
- out[1] = 2.0f * (mval[1] / region->winy) - 1.0f;
- out[2] = -0.5f;
- mul_project_m4_v3(rv3d->persinv, out);
- sub_v3_v3(out, rv3d->viewinv[3]);
+ r_out[0] = 2.0f * (mval[0] / region->winx) - 1.0f;
+ r_out[1] = 2.0f * (mval[1] / region->winy) - 1.0f;
+ r_out[2] = -0.5f;
+ mul_project_m4_v3(rv3d->persinv, r_out);
+ sub_v3_v3(r_out, rv3d->viewinv[3]);
}
else {
- negate_v3_v3(out, rv3d->viewinv[2]);
+ negate_v3_v3(r_out, rv3d->viewinv[2]);
}
- normalize_v3(out);
+ normalize_v3(r_out);
}
bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph,
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index f08c53fff47..e380a08dcc7 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -1313,7 +1313,7 @@ static bool view3d_lasso_select(bContext *C,
case OB_MESH:
changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
changed = do_lasso_select_curve(vc, mcoords, mcoords_len, sel_op);
break;
@@ -2695,7 +2695,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
retval = ED_lattice_deselect_all_multi(C);
}
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle);
if (!retval && deselect_all) {
retval = ED_curve_deselect_all_multi(C);
@@ -3586,7 +3586,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
}
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
changed = do_nurbs_box_select(&vc, &rect, sel_op);
if (changed) {
@@ -4342,7 +4342,7 @@ static bool obedit_circle_select(bContext *C,
case OB_MESH:
changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
changed = nurbscurve_circle_select(vc, sel_op, mval, rad);
break;
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 8a219cd96d1..3e788f2d643 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -510,6 +510,39 @@ bool ED_view3d_persp_ensure(const Depsgraph *depsgraph, View3D *v3d, ARegion *re
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Camera View Utilities
+ *
+ * Utilities for manipulating the camera-view.
+ * \{ */
+
+bool ED_view3d_camera_view_zoom_scale(RegionView3D *rv3d, const float scale)
+{
+ const float camzoom_init = rv3d->camzoom;
+ float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
+ /* Clamp both before and after conversion to prevent NAN on negative values. */
+
+ zoomfac = zoomfac * scale;
+ CLAMP(zoomfac, RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
+ rv3d->camzoom = BKE_screen_view3d_zoom_from_fac(zoomfac);
+ CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+ return (rv3d->camzoom != camzoom_init);
+}
+
+bool ED_view3d_camera_view_pan(ARegion *region, const float event_ofs[2])
+{
+ RegionView3D *rv3d = region->regiondata;
+ const float camdxy_init[2] = {rv3d->camdx, rv3d->camdy};
+ const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
+ rv3d->camdx += event_ofs[0] / (region->winx * zoomfac);
+ rv3d->camdy += event_ofs[1] / (region->winy * zoomfac);
+ CLAMP(rv3d->camdx, -1.0f, 1.0f);
+ CLAMP(rv3d->camdy, -1.0f, 1.0f);
+ return (camdxy_init[0] != rv3d->camdx) || (camdxy_init[1] != rv3d->camdy);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Camera Lock API
*
* Lock the camera to the 3D Viewport, allowing view manipulation to transform the camera.
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 3bcc1b2968e..fd01f708ed2 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -177,8 +177,8 @@ void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
r_vec[1] = dy;
}
else {
- const float mval_f[2] = {(float)dx, (float)dy};
- ED_view3d_win_to_delta(t->region, mval_f, r_vec, t->zfac);
+ const float xy_delta[2] = {(float)dx, (float)dy};
+ ED_view3d_win_to_delta(t->region, xy_delta, t->zfac, r_vec);
}
}
else if (t->spacetype == SPACE_IMAGE) {
@@ -1150,10 +1150,10 @@ int transformEvent(TransInfo *t, const wmEvent *event)
else if (event->val == KM_PRESS) {
switch (event->type) {
case EVT_CKEY:
- if (event->is_repeat) {
+ if (event->flag & WM_EVENT_IS_REPEAT) {
break;
}
- if (event->alt) {
+ if (event->modifier & KM_ALT) {
if (!(t->options & CTX_NO_PET)) {
t->flag ^= T_PROP_CONNECTED;
sort_trans_data_dist(t);
@@ -1164,10 +1164,10 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
break;
case EVT_OKEY:
- if (event->is_repeat) {
+ if (event->flag & WM_EVENT_IS_REPEAT) {
break;
}
- if (t->flag & T_PROP_EDIT && event->shift) {
+ if ((t->flag & T_PROP_EDIT) && (event->modifier & KM_SHIFT)) {
t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
calculatePropRatio(t);
t->redraw |= TREDRAW_HARD;
@@ -1175,7 +1175,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
break;
case EVT_PADPLUSKEY:
- if (event->alt && t->flag & T_PROP_EDIT) {
+ if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) {
t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
@@ -1186,7 +1186,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
break;
case EVT_PADMINUS:
- if (event->alt && t->flag & T_PROP_EDIT) {
+ if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) {
t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
calculatePropRatio(t);
t->redraw = TREDRAW_HARD;
@@ -1202,7 +1202,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
break;
case EVT_NKEY:
- if (event->is_repeat) {
+ if (event->flag & WM_EVENT_IS_REPEAT) {
break;
}
if (ELEM(t->mode, TFM_ROTATION)) {
@@ -1697,7 +1697,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
/* Needed to translate tweak events to mouse buttons. */
t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
- t->is_launch_event_tweak = event ? ISTWEAK(event->type) : false;
+ t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false;
/* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate
* (which can have type = 0) */
@@ -1780,10 +1780,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
- if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) ||
- (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) ||
- (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) ||
- ((kmi->type == EVT_OSKEY) && event->oskey)) {
+ if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) &&
+ (event->modifier & KM_CTRL)) ||
+ (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) &&
+ (event->modifier & KM_SHIFT)) ||
+ (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) ||
+ ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) {
t->modifiers |= MOD_SNAP_INVERT;
}
break;
@@ -1967,7 +1969,7 @@ bool checkUseAxisMatrix(TransInfo *t)
/* currently only checks for editmode */
if (t->flag & T_EDIT) {
if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
- (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) {
+ (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE))) {
/* not all editmode supports axis-matrix */
return true;
}
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 757c11f1179..3ee5868d5be 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -592,9 +592,11 @@ typedef struct TransInfo {
/*************** NEW STUFF *********************/
/** event type used to launch transform. */
short launch_event;
- /** Is the actual launch event a tweak event? (launch_event above is set to the corresponding
- * mouse button then.) */
- bool is_launch_event_tweak;
+ /**
+ * Is the actual launch event a drag event?
+ * (`launch_event` is set to the corresponding mouse button then.)
+ */
+ bool is_launch_event_drag;
bool is_orient_default_overwrite;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 64ef170a13f..81b35e4539b 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -1041,8 +1041,7 @@ static void setNearestAxis3d(TransInfo *t)
* and to overflow the short integers.
* The formula used is a bit stupid, just a simplification of the subtraction
* of two 2D points 30 pixels apart (that's the last factor in the formula) after
- * projecting them with ED_view3d_win_to_delta and then get the length of that vector.
- */
+ * projecting them with #ED_view3d_win_to_delta and then get the length of that vector. */
zfac = mul_project_m4_v3_zfac(t->persmat, t->center_global);
zfac = len_v3(t->persinv[0]) * 2.0f / t->region->winx * zfac * 30.0f;
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 95b810daeaf..4a2169b381e 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -1040,7 +1040,7 @@ static void init_proportional_edit(TransInfo *t)
/* Already calculated by uv_set_connectivity_distance. */
}
else if (convert_type == TC_CURVE_VERTS) {
- BLI_assert(t->obedit_type == OB_CURVE);
+ BLI_assert(t->obedit_type == OB_CURVES_LEGACY);
set_prop_dist(t, false);
}
else {
@@ -1049,7 +1049,7 @@ static void init_proportional_edit(TransInfo *t)
sort_trans_data_dist(t);
}
- else if (ELEM(t->obedit_type, OB_CURVE)) {
+ else if (ELEM(t->obedit_type, OB_CURVES_LEGACY)) {
/* Needed because bezier handles can be partially selected
* and are still added into transform data. */
sort_trans_data_selected_first(t);
@@ -1286,7 +1286,7 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur
convert_type = TC_MESH_VERTS;
}
}
- else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) {
convert_type = TC_CURVE_VERTS;
}
else if (t->obedit_type == OB_LATTICE) {
diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c
index 608fd59c8b7..54222fbb117 100644
--- a/source/blender/editors/transform/transform_convert_graph.c
+++ b/source/blender/editors/transform/transform_convert_graph.c
@@ -161,7 +161,7 @@ static void graph_bezt_get_transform_selection(const TransInfo *t,
bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key;
bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key;
- if (use_handle && t->is_launch_event_tweak) {
+ if (use_handle && t->is_launch_event_drag) {
if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) {
key = right = false;
}
diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c
index e12d0db8758..763af1f3384 100644
--- a/source/blender/editors/transform/transform_convert_object_texspace.c
+++ b/source/blender/editors/transform/transform_convert_object_texspace.c
@@ -44,7 +44,7 @@ void createTransTexspace(TransInfo *t)
}
id = ob->data;
- if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) {
+ if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU_LEGACY, ID_MB)) {
BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform");
return;
}
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 1d86a8f9413..8987325145c 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -731,7 +731,8 @@ void postTrans(bContext *C, TransInfo *t)
if (t->data_len_all != 0) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
/* free data malloced per trans-data */
- if (ELEM(t->obedit_type, OB_CURVE, OB_SURF, OB_GPENCIL) || (t->spacetype == SPACE_GRAPH)) {
+ if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF, OB_GPENCIL) ||
+ (t->spacetype == SPACE_GRAPH)) {
TransData *td = tc->data;
for (int a = 0; a < tc->data_len; a++, td++) {
if (td->flag & TD_BEZTRIPLE) {
@@ -1145,7 +1146,7 @@ void calculateCenter(TransInfo *t)
projectFloatView(t, axis, t->center2d);
- /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */
+ /* Rotate only needs correct 2d center, grab needs #ED_view3d_calc_zfac() value. */
if (t->mode == TFM_TRANSLATION) {
copy_v3_v3(t->center_global, axis);
}
@@ -1154,17 +1155,16 @@ void calculateCenter(TransInfo *t)
}
if (t->spacetype == SPACE_VIEW3D) {
- /* ED_view3d_calc_zfac() defines a factor for perspective depth correction,
- * used in ED_view3d_win_to_delta() */
+ /* #ED_view3d_calc_zfac() defines a factor for perspective depth correction,
+ * used in #ED_view3d_win_to_delta(). */
- /* zfac is only used convertViewVec only in cases operator was invoked in RGN_TYPE_WINDOW
- * and never used in other cases.
+ /* NOTE: `t->zfac` is only used #convertViewVec only in cases operator was invoked in
+ * #RGN_TYPE_WINDOW and never used in other cases.
*
- * We need special case here as well, since ED_view3d_calc_zfac will crash when called
- * for a region different from RGN_TYPE_WINDOW.
- */
+ * We need special case here as well, since #ED_view3d_calc_zfac will crash when called
+ * for a region different from #RGN_TYPE_WINDOW. */
if (t->region->regiontype == RGN_TYPE_WINDOW) {
- t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global, NULL);
+ t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global);
}
else {
t->zfac = 0.0f;
diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c
index d2b2d2f116e..da601328192 100644
--- a/source/blender/editors/transform/transform_gizmo_2d.c
+++ b/source/blender/editors/transform/transform_gizmo_2d.c
@@ -669,6 +669,7 @@ static void gizmo2d_xform_invoke_prepare(const bContext *C,
float c[3] = {mid[0], mid[1], 0.0f};
float orient_matrix[3][3];
+ unit_m3(orient_matrix);
ScrArea *area = CTX_wm_area(C);
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 7e121a717aa..f07fadb7fc1 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -816,7 +816,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
}
FOREACH_EDIT_OBJECT_END();
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
Curve *cu = ob_iter->data;
Nurb *nu;
@@ -1869,7 +1869,7 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C,
if (axis != -1) {
wmWindow *win = CTX_wm_window(C);
/* Swap single axis for two-axis constraint. */
- bool flip = win->eventstate->shift;
+ bool flip = (win->eventstate->modifier & KM_SHIFT) != 0;
BLI_assert(axis_idx != -1);
const short axis_type = gizmo_get_axis_type(axis_idx);
if (axis_type != MAN_AXES_ROTATE) {
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index 6873ea862ce..f6f43f867ae 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -153,7 +153,7 @@ static void gizmo_mesh_extrude_setup(const bContext *C, wmGizmoGroup *gzgroup)
op_idname = "ARMATURE_OT_extrude_move";
ggd->normal_axis = 1;
}
- else if (obact->type == OB_CURVE) {
+ else if (obact->type == OB_CURVES_LEGACY) {
op_idname = "CURVE_OT_extrude_move";
ggd->normal_axis = 2;
}
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index b6c4002b1c7..6162dfc9bb5 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -57,7 +57,7 @@ bool transdata_check_local_center(const TransInfo *t, short around)
return ((around == V3D_AROUND_LOCAL_ORIGINS) &&
((t->options & (CTX_OBJECT | CTX_POSE_BONE)) ||
/* implicit: (t->flag & T_EDIT) */
- (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) ||
+ (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) ||
(t->spacetype == SPACE_GRAPH) ||
(t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE))));
}
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 0afc527d4a8..77c5707d814 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -146,9 +146,9 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2]
* by finding the closest edge in local-space.
* However this skews the outcome with non-uniform-scale. */
- /* first get the direction of the original mouse position */
+ /* First get the direction of the original mouse position. */
sub_v2_v2v2(dir, imval_fl, mval_fl);
- ED_view3d_win_to_delta(t->region, dir, dir, t->zfac);
+ ED_view3d_win_to_delta(t->region, dir, t->zfac, dir);
normalize_v3(dir);
for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) {
@@ -425,18 +425,18 @@ void drawVertSlide(TransInfo *t)
/* direction from active vertex! */
if ((t->mval[0] != t->mouse.imval[0]) || (t->mval[1] != t->mouse.imval[1])) {
float zfac;
- float mval_ofs[2];
+ float xy_delta[2];
float co_orig_3d[3];
float co_dest_3d[3];
- mval_ofs[0] = t->mval[0] - t->mouse.imval[0];
- mval_ofs[1] = t->mval[1] - t->mouse.imval[1];
+ xy_delta[0] = t->mval[0] - t->mouse.imval[0];
+ xy_delta[1] = t->mval[1] - t->mouse.imval[1];
mul_v3_m4v3(
co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d);
- zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d, NULL);
+ zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d);
- ED_view3d_win_to_delta(t->region, mval_ofs, co_dest_3d, zfac);
+ ED_view3d_win_to_delta(t->region, xy_delta, zfac, co_dest_3d);
invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat,
TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 3722e83e451..936aca7d2e0 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -628,7 +628,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
"Proportional Falloff",
"Falloff type for proportional editing mode");
/* Abusing id_curve :/ */
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE);
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY);
RNA_def_float(ot->srna,
"proportional_size",
1,
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 6ca61d415ea..c0d943e17ee 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -350,7 +350,7 @@ bool BIF_createTransformOrientation(bContext *C,
else if (obedit->type == OB_ARMATURE) {
ts = createBoneSpace(C, reports, name, overwrite);
}
- else if (obedit->type == OB_CURVE) {
+ else if (obedit->type == OB_CURVES_LEGACY) {
ts = createCurveSpace(C, reports, name, overwrite);
}
}
@@ -984,7 +984,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer,
negate_v3(plane);
} /* end editmesh */
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
Nurb *nu = NULL;
int a;
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index ce0cf3aa6cb..2f3c021ba38 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -86,7 +86,7 @@ int BIF_snappingSupported(Object *obedit)
int status = 0;
/* only support object mesh, armature, curves */
- if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
+ if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) {
status = 1;
}
@@ -308,7 +308,8 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
eRedrawFlag status = TREDRAW_NOTHING;
#if 0 /* XXX need a proper selector for all snap mode */
- if (BIF_snappingSupported(t->obedit) && event->type == TABKEY && event->shift) {
+ if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) &&
+ (event->modifier & KM_SHIFT)) {
/* toggle snap and reinit */
t->settings->snap_flag ^= SCE_SNAP;
initSnapping(t, NULL);
@@ -583,7 +584,7 @@ static short snap_mode_from_scene(TransInfo *t)
/* All obedit types will match. */
const int obedit_type = t->obedit_type;
if ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) ||
- ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL, -1)) {
+ ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL, -1)) {
r_snap_mode = ts->snap_mode;
if ((r_snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) &&
(t->mode == TFM_TRANSLATION)) {
@@ -617,7 +618,7 @@ static short snap_select_type_get(TransInfo *t)
* When we're moving the origins, allow snapping onto our own geometry (see T69132). */
}
else if ((obedit_type != -1) &&
- ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
+ ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) {
/* Edit mode */
/* Temporary limited to edit mode meshes, armature, curves, metaballs. */
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index ca6940040b2..8b7133892ff 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -477,7 +477,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
}
}
else if (snap_select == SNAP_NOT_SELECTED) {
- if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) {
+ if (is_object_active && base->object->mode != OB_MODE_OBJECT) {
/* Pass. Consider the selection of elements being edited. */
}
else if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) {
@@ -1054,7 +1054,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
dt->r_hit_list);
break;
}
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT: {
if (!is_object_active) {
@@ -2743,7 +2743,7 @@ static void snap_obj_fn(SnapObjectContext *sctx,
dt->r_no,
dt->r_index);
break;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
retval = snapCurve(
sctx, params, ob_eval, obmat, 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. */
@@ -3117,7 +3117,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
sctx->runtime.has_occlusion_plane = false;
/* By convention we only snap to the original elements of a curve. */
- if (has_hit && ob_eval->type != OB_CURVE) {
+ if (has_hit && ob_eval->type != OB_CURVES_LEGACY) {
/* Compute the new clip_pane but do not add it yet. */
float new_clipplane[4];
BLI_ASSERT_UNIT_V3(no);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 2300e664dfa..7b4551fdb0c 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -40,6 +40,7 @@ set(SRC
../include/ED_clip.h
../include/ED_curve.h
../include/ED_curves.h
+ ../include/ED_curves_sculpt.h
../include/ED_datafiles.h
../include/ED_file_indexer.h
../include/ED_fileselect.h
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index 0523a58825e..c1e093d5555 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -45,7 +45,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BM_mesh_normals_update(em->bm);
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
Nurb *nu = nurbs->first;
@@ -181,7 +181,8 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f
bool ED_transverts_check_obedit(const Object *obedit)
{
- return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL));
+ return (
+ ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_MBALL));
}
void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode)
@@ -351,7 +352,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit,
}
}
}
- else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
int totmalloc = 0;
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 15fe944be49..32d405df841 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -306,7 +306,7 @@ bool ED_editors_flush_edits(Main *bmain)
/* ***** XXX: functions are using old blender names, cleanup later ***** */
void apply_keyb_grid(
- int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert)
+ bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert)
{
/* fac1 is for 'nothing', fac2 for CTRL, fac3 for SHIFT */
if (invert) {
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index 014944da916..25deacbcdd1 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -226,7 +226,7 @@ static int lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op)
ID *id = (ID *)idptr.data;
- if ((id->lib != nullptr) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
+ if (ID_IS_LINKED(id) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 3bf226f6ba1..be6ac6e13e6 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -321,7 +321,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
if (U.flag & USER_FLAG_NUMINPUT_ADVANCED)
#endif
{
- if ((event->ctrl == 0) && (event->alt == 0) && (event->ascii != '\0') &&
+ if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') &&
strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) {
if (!(n->flag & NUM_EDIT_FULL)) {
n->flag |= NUM_EDITED;
@@ -339,7 +339,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
n->val_flag[idx] |= NUM_EDITED;
return true;
}
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
n->flag &= ~NUM_EDIT_FULL;
return true;
}
@@ -375,7 +375,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
updated = true;
break;
}
- else if (event->shift || !n->str[0]) {
+ else if ((event->modifier & KM_SHIFT) || !n->str[0]) {
n->val[idx] = n->val_org[idx];
n->val_flag[idx] &= ~NUM_EDITED;
n->str[0] = '\0';
@@ -390,7 +390,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
case EVT_DELKEY:
if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
int t_cur = cur = n->str_cur;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
mode = STRCUR_JUMP_DELIM;
}
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
@@ -416,7 +416,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
ATTR_FALLTHROUGH;
case EVT_RIGHTARROWKEY:
cur = n->str_cur;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
mode = STRCUR_JUMP_DELIM;
}
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
@@ -442,7 +442,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);
#endif
- idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
+ idx = (idx + idx_max + ((event->modifier & KM_CTRL) ? 0 : 2)) % (idx_max + 1);
n->idx = idx;
if (n->val_flag[idx] & NUM_EDITED) {
value_to_editstr(n, idx);
@@ -470,7 +470,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
n->val_flag[idx] |= NUM_EDITED;
return true;
}
- else if (event->ctrl) {
+ else if (event->modifier & KM_CTRL) {
n->flag &= ~NUM_EDIT_FULL;
return true;
}
@@ -480,28 +480,28 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
#ifdef USE_FAKE_EDIT
case EVT_PADMINUS:
case EVT_MINUSKEY:
- if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
+ if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) {
n->val_flag[idx] ^= NUM_NEGATE;
updated = true;
}
break;
case EVT_PADSLASHKEY:
case EVT_SLASHKEY:
- if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
+ if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) {
n->val_flag[idx] ^= NUM_INVERSE;
updated = true;
}
break;
#endif
case EVT_CKEY:
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
/* Copy current `str` to the copy/paste buffer. */
WM_clipboard_text_set(n->str, 0);
updated = true;
}
break;
case EVT_VKEY:
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
/* extract the first line from the clipboard */
int pbuf_len;
char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);
@@ -531,7 +531,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
/* Up to this point, if we have a ctrl modifier, skip.
* This allows to still access most of modals' shortcuts even in numinput mode.
*/
- if (!updated && event->ctrl) {
+ if (!updated && (event->modifier & KM_CTRL)) {
return false;
}
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 91b29049ecf..c0d0fe95c8c 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -2598,7 +2598,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Increase limit */
case EVT_PADPLUSKEY:
case WHEELUPMOUSE:
- if (event->val == KM_PRESS && event->alt) {
+ if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) {
ssc->limit_dist += 0.01f;
if (!stitch_process_data(ssc, active_state, scene, false)) {
stitch_cancel(C, op);
@@ -2612,7 +2612,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Decrease limit */
case EVT_PADMINUS:
case WHEELDOWNMOUSE:
- if (event->val == KM_PRESS && event->alt) {
+ if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) {
ssc->limit_dist -= 0.01f;
ssc->limit_dist = MAX2(0.01f, ssc->limit_dist);
if (!stitch_process_data(ssc, active_state, scene, false)) {
@@ -2673,7 +2673,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Select geometry */
case RIGHTMOUSE:
- if (!event->shift) {
+ if ((event->modifier & KM_SHIFT) == 0) {
stitch_cancel(C, op);
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index 2655cd26bfe..ced0c2b9546 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -139,7 +139,7 @@ class GVArrayCommon {
*/
bool is_span() const;
/**
- * Returns the internally used span of the virtual array. This invokes undefined behavior is the
+ * Returns the internally used span of the virtual array. This invokes undefined behavior if the
* virtual array is not stored as a span internally.
*/
GSpan get_internal_span() const;
diff --git a/source/blender/functions/FN_multi_function_procedure_optimization.hh b/source/blender/functions/FN_multi_function_procedure_optimization.hh
index 6e1e804c804..f5a02d84d42 100644
--- a/source/blender/functions/FN_multi_function_procedure_optimization.hh
+++ b/source/blender/functions/FN_multi_function_procedure_optimization.hh
@@ -38,9 +38,9 @@ namespace blender::fn::procedure_optimization {
* For simplicity, and because this is the most common use case, this optimization currently only
* works on a single chain of instructions. Destruct instructions are not moved across branches.
*
- * \param procedure The procedure that should be optimized.
- * \param block_end_instr The instruction that points to the last instruction within a linear chain
- * of instructions. The algorithm moves instructions backward starting at this instruction.
+ * \param procedure: The procedure that should be optimized.
+ * \param block_end_instr: The instruction that points to the last instruction within a linear
+ * chain of instructions. The algorithm moves instructions backward starting at this instruction.
*/
void move_destructs_up(MFProcedure &procedure, MFInstruction &block_end_instr);
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 054a6b51adf..7b514b6a49b 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -26,7 +26,7 @@ struct FieldTreeInfo {
*/
MultiValueMap<GFieldRef, GFieldRef> field_users;
/**
- * The same field input may exist in the field tree as as separate nodes due to the way
+ * The same field input may exist in the field tree as separate nodes due to the way
* the tree is constructed. This set contains every different input only once.
*/
VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs;
@@ -137,7 +137,7 @@ static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info,
}
/**
- * Builds the #procedure so that it computes the the fields.
+ * Builds the #procedure so that it computes the fields.
*/
static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
ResourceScope &scope,
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc
index c98bd9d6b74..4f7024eea82 100644
--- a/source/blender/geometry/intern/realize_instances.cc
+++ b/source/blender/geometry/intern/realize_instances.cc
@@ -13,6 +13,7 @@
#include "BLI_task.hh"
#include "BKE_collection.h"
+#include "BKE_curves.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -118,7 +119,7 @@ struct RealizeMeshTask {
};
struct RealizeCurveInfo {
- const CurveEval *curve = nullptr;
+ const Curves *curves;
/**
* Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional`
* will be empty.
@@ -163,7 +164,7 @@ struct AllCurvesInfo {
/** Ordering of all attributes that are propagated to the output curve generically. */
OrderedAttributes attributes;
/** Ordering of the original curves that are joined. */
- VectorSet<const CurveEval *> order;
+ VectorSet<const Curves *> order;
/** Preprocessed data about every original curve. This is ordered by #order. */
Array<RealizeCurveInfo> realize_info;
bool create_id_attribute = false;
@@ -443,16 +444,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
}
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component);
- const CurveEval *curve = curve_component.get_for_read();
- if (curve != nullptr && !curve->splines().is_empty()) {
- const int curve_index = gather_info.curves.order.index_of(curve);
+ const Curves *curves = curve_component.get_for_read();
+ if (curves != nullptr && curves->geometry.curve_size > 0) {
+ const int curve_index = gather_info.curves.order.index_of(curves);
const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index];
gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset,
&curve_info,
base_transform,
base_instance_context.curves,
base_instance_context.id});
- gather_info.r_offsets.spline_offset += curve->splines().size();
+ gather_info.r_offsets.spline_offset += curves->geometry.curve_size;
}
break;
}
@@ -1038,11 +1039,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
}
static void gather_curves_to_realize(const GeometrySet &geometry_set,
- VectorSet<const CurveEval *> &r_curves)
+ VectorSet<const Curves *> &r_curves)
{
- if (const CurveEval *curve = geometry_set.get_curve_for_read()) {
- if (!curve->splines().is_empty()) {
- r_curves.add(curve);
+ if (const Curves *curves = geometry_set.get_curves_for_read()) {
+ if (curves->geometry.curve_size != 0) {
+ r_curves.add(curves);
}
}
if (const InstancesComponent *instances =
@@ -1064,12 +1065,12 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set,
info.realize_info.reinitialize(info.order.size());
for (const int curve_index : info.realize_info.index_range()) {
RealizeCurveInfo &curve_info = info.realize_info[curve_index];
- const CurveEval *curve = info.order[curve_index];
- curve_info.curve = curve;
+ const Curves *curves = info.order[curve_index];
+ curve_info.curves = curves;
/* Access attributes. */
CurveComponent component;
- component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
+ component.replace(const_cast<Curves *>(curves), GeometryOwnershipType::ReadOnly);
curve_info.spline_attributes.reinitialize(info.attributes.size());
for (const int attribute_index : info.attributes.index_range()) {
const AttributeDomain domain = info.attributes.kinds[attribute_index].domain;
@@ -1095,9 +1096,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
MutableSpan<GMutableSpan> dst_spline_attributes)
{
const RealizeCurveInfo &curve_info = *task.curve_info;
- const CurveEval &curve = *curve_info.curve;
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_info.curves);
- const Span<SplinePtr> src_splines = curve.splines();
+ const Span<SplinePtr> src_splines = curve->splines();
/* Initialize point attributes. */
threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) {
@@ -1206,12 +1207,12 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
}
const RealizeCurveTask &last_task = tasks.last();
- const CurveEval &last_curve = *last_task.curve_info->curve;
- const int tot_splines = last_task.start_spline_index + last_curve.splines().size();
+ const Curves &last_curves = *last_task.curve_info->curves;
+ const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size;
Array<SplinePtr> dst_splines(tot_splines);
- CurveEval *dst_curve = new CurveEval();
+ std::unique_ptr<CurveEval> dst_curve = std::make_unique<CurveEval>();
dst_curve->attributes.reallocate(tot_splines);
CustomDataAttributes &spline_attributes = dst_curve->attributes;
@@ -1242,7 +1243,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
dst_curve->add_splines(dst_splines);
CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>();
- dst_component.replace(dst_curve);
+ dst_component.replace(curve_eval_to_curves(*dst_curve));
}
/** \} */
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index e036b814916..fba83579ab1 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -104,7 +104,9 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob,
float obinv[4][4];
unit_m4(mat_offset);
- add_v3_v3(mat_offset[3], mmd->offset);
+ if (mmd->flag & GP_ARRAY_USE_OFFSET) {
+ add_v3_v3(mat_offset[3], mmd->offset);
+ }
invert_m4_m4(obinv, ob->obmat);
mul_m4_series(r_offset, mat_offset, obinv, mmd->object->obmat);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index ba61ea148ca..1058f861be3 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -219,7 +219,7 @@ static void add_this_collection(Collection *c,
return;
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) {
- if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) {
+ if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) {
DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
@@ -393,7 +393,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE);
uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
- uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_back_face_culling", 0, IFACE_("Force Backface Culling"), ICON_NONE);
}
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
index 3b7b82e3085..365b9afe348 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
@@ -88,7 +88,7 @@ static void deformStroke(GpencilModifierData *md,
break;
}
case GP_SIMPLIFY_SAMPLE: {
- BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false);
+ BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false, mmd->sharp_threshold);
break;
}
case GP_SIMPLIFY_MERGE: {
@@ -143,6 +143,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
}
else if (mode == GP_SIMPLIFY_SAMPLE) {
uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "sharp_threshold", 0, NULL, ICON_NONE);
}
else if (mode == GP_SIMPLIFY_MERGE) {
uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
index 5e916a13f2c..dcec2865f29 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
@@ -79,11 +79,6 @@ static void deformStroke(GpencilModifierData *md,
short type = gps->totpoints < 3 ? GP_SUBDIV_SIMPLE : mmd->type;
BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, type);
-
- /* If the stroke is cyclic, must generate the closing geometry. */
- if (gps->flag & GP_STROKE_CYCLIC) {
- BKE_gpencil_stroke_close(gps);
- }
}
static void bakeModifier(struct Main *UNUSED(bmain),
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 2c7999699c7..5d952991cf7 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -343,6 +343,7 @@ typedef enum eLineartTriangleFlags {
LRT_CULL_GENERATED = (1 << 2),
LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
+ LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
} eLineartTriangleFlags;
/**
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 6177b9ac366..2f648d7ed4b 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -1388,21 +1388,20 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb)
LineartVert *vt;
int i;
- if (!rb->cam_is_persp) {
- return;
- }
-
LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) {
vt = eln->pointer;
for (i = 0; i < eln->element_count; i++) {
- /* Do not divide Z, we use Z to back transform cut points in later chaining process. */
- vt[i].fbcoord[0] /= vt[i].fbcoord[3];
- vt[i].fbcoord[1] /= vt[i].fbcoord[3];
- /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates)
- * at the moment.
- * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in
- * the future, the line below correctly transforms it to view space coordinates. */
- // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
+ if (rb->cam_is_persp) {
+ /* Do not divide Z, we use Z to back transform cut points in later chaining process. */
+ vt[i].fbcoord[0] /= vt[i].fbcoord[3];
+ vt[i].fbcoord[1] /= vt[i].fbcoord[3];
+ /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates)
+ * at the moment.
+ * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed
+ * in the future, the line below correctly transforms it to view space coordinates. */
+ // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
+ }
+ /* Shifting is always needed. */
vt[i].fbcoord[0] -= rb->shift_x * 2;
vt[i].fbcoord[1] -= rb->shift_y * 2;
}
@@ -1529,8 +1528,9 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
double *view_vector = vv;
double dot_1 = 0, dot_2 = 0;
double result;
+ bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING);
- if (rb->use_contour || rb->use_back_face_culling) {
+ if (rb->use_contour || rb->use_back_face_culling || material_back_face) {
if (rb->cam_is_persp) {
sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc);
@@ -1555,14 +1555,19 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
tri2->flags |= LRT_CULL_DISCARD;
}
}
+ if (material_back_face) {
+ if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) {
+ tri1->flags |= LRT_CULL_DISCARD;
+ }
+ if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) {
+ tri2->flags |= LRT_CULL_DISCARD;
+ }
+ }
}
else {
view_vector = rb->view_vector;
}
- dot_1 = dot_v3v3_db(view_vector, tri1->gn);
- dot_2 = dot_v3v3_db(view_vector, tri2->gn);
-
if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) {
edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
}
@@ -1573,6 +1578,16 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
return edge_flag_result;
}
+ /* Do not show lines other than contour on back face (because contour has one adjacent face that
+ * isn't a back face).
+ * TODO(Yiming): Do we need separate option for this? */
+ if (rb->use_back_face_culling ||
+ ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) {
+ if (dot_1 < 0 && dot_2 < 0) {
+ return edge_flag_result;
+ }
+ }
+
if (rb->use_crease) {
if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) {
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
@@ -1859,6 +1874,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
mat->lineart.material_mask_bits :
0);
tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
+ tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
+ LRT_TRIANGLE_MAT_BACK_FACE_CULLING :
+ 0;
tri->intersection_mask = obi->override_intersection_mask;
@@ -2193,7 +2211,7 @@ static void lineart_main_load_geometries(
mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat);
mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat);
- if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) {
+ if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
continue;
}
@@ -2205,7 +2223,7 @@ static void lineart_main_load_geometries(
}
if (use_ob->type == OB_MESH) {
- use_mesh = use_ob->data;
+ use_mesh = BKE_object_get_evaluated_mesh(use_ob);
}
else {
/* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh
@@ -2502,19 +2520,16 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut);
mul_v4_m4v3_db(trans, vp, gloc);
mul_v3db_db(trans, (1 / trans[3]));
- }
- else {
- interp_v3_v3v3_db(trans, e->v1->fbcoord, e->v2->fbcoord, cut);
- }
- trans[0] -= cam_shift_x * 2;
- trans[1] -= cam_shift_y * 2;
-
- /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */
- if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) {
- cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]);
- }
- else {
- cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]);
+ trans[0] -= cam_shift_x * 2;
+ trans[1] -= cam_shift_y * 2;
+ /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */
+ if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) >
+ fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) {
+ cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]);
+ }
+ else {
+ cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]);
+ }
}
#define LRT_GUARD_NOT_FOUND \
@@ -2940,9 +2955,10 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
* them as well. */
mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc);
mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc);
- mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
- mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
-
+ if (rb->cam_is_persp) {
+ mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
+ mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
+ }
v1->fbcoord[0] -= rb->shift_x * 2;
v1->fbcoord[1] -= rb->shift_y * 2;
v2->fbcoord[0] -= rb->shift_x * 2;
@@ -4453,7 +4469,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) {
if (eval_ob && eval_ob->type == OB_MESH) {
int dindex = 0;
- Mesh *me = (Mesh *)eval_ob->data;
+ Mesh *me = BKE_object_get_evaluated_mesh(eval_ob);
if (me->dvert) {
LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) {
if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) {
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index e883a12a5b2..3ad43ef05af 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -147,6 +147,7 @@ set(SRC
intern/gpu_select_private.h
intern/gpu_shader_create_info.hh
intern/gpu_shader_create_info_private.hh
+ intern/gpu_shader_dependency_private.h
intern/gpu_shader_interface.hh
intern/gpu_shader_private.hh
intern/gpu_state_private.hh
@@ -384,7 +385,7 @@ file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}")
list(APPEND SRC ${glsl_source_list_file})
list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
-set(SHADER_CREATE_INFOS
+set(SRC_SHADER_CREATE_INFOS
../draw/engines/workbench/shaders/infos/workbench_composite_info.hh
../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh
../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh
@@ -435,7 +436,7 @@ set(SHADER_CREATE_INFOS
)
set(SHADER_CREATE_INFOS_CONTENT "")
-foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS})
+foreach(DESCRIPTOR_FILE ${SRC_SHADER_CREATE_INFOS})
string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n")
endforeach()
@@ -452,12 +453,21 @@ if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
+if(WITH_OPENCOLORIO)
+ add_definitions(-DWITH_OCIO)
+endif()
+
blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
target_link_libraries(bf_gpu PUBLIC
bf_draw_shaders
bf_gpu_shaders
)
+if(WITH_OPENCOLORIO)
+ target_link_libraries(bf_gpu PUBLIC bf_ocio_shaders)
+endif()
+
+
if(CXX_WARN_NO_SUGGEST_OVERRIDE)
target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>)
endif()
@@ -477,18 +487,22 @@ if(WITH_GPU_SHADER_BUILDER)
)
target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR})
- set(BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh)
+ set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh)
add_custom_command(
OUTPUT
- ${BAKED_CREATE_INFOS_FILE}
+ ${SRC_BAKED_CREATE_INFOS_FILE}
COMMAND
- "$<TARGET_FILE:shader_builder>" ${BAKED_CREATE_INFOS_FILE}
+ "$<TARGET_FILE:shader_builder>" ${SRC_BAKED_CREATE_INFOS_FILE}
DEPENDS shader_builder
)
set(GPU_SHADER_INFO_SRC
intern/gpu_shader_info_baked.cc
- ${BAKED_CREATE_INFOS_FILE}
+ ${SRC_BAKED_CREATE_INFOS_FILE}
+
+ # For project files to be aware of these headers.
+ ${SRC_SHADER_CREATE_INFOS}
+ shaders/infos/gpu_interface_info.hh
)
blender_add_lib(bf_gpu_shader_infos "${GPU_SHADER_INFO_SRC}" "" "" "")
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 803a5825c94..7fad8dd23be 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -119,6 +119,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
* Returns the index of verts in the batch.
*/
int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
+bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *);
#define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false)
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index d689fbe14b5..734d407d011 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -226,6 +226,19 @@ GPUTexture *GPU_texture_create_compressed_2d(
* Create an error texture that will bind an invalid texture (pink) at draw time.
*/
GPUTexture *GPU_texture_create_error(int dimension, bool array);
+/**
+ * Create an alias of the source texture data.
+ * If \a src is freed, the texture view will continue to be valid.
+ * If \a mip_start or \a mip_len is bigger than available mips they will be clamped.
+ * TODO(@fclem): Target conversion is not implemented yet.
+ */
+GPUTexture *GPU_texture_create_view(const char *name,
+ const GPUTexture *src,
+ eGPUTextureFormat format,
+ int mip_start,
+ int mip_len,
+ int layer_start,
+ int layer_len);
void GPU_texture_update_mipmap(GPUTexture *tex,
int miplvl,
diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc
index 44e95b1f5f5..32b117dac12 100644
--- a/source/blender/gpu/intern/gpu_batch.cc
+++ b/source/blender/gpu/intern/gpu_batch.cc
@@ -191,6 +191,16 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
return -1;
}
+bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts)
+{
+ for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
+ if (batch->verts[v] == verts) {
+ return true;
+ }
+ }
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index da4e49f0257..57e415a8183 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -165,7 +165,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type)
/**
* Link stack uniform buffer.
- * This is called for the input/output sockets that are note connected.
+ * This is called for the input/output sockets that are not connected.
*/
static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat,
bNode *node,
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index bd0187e2dc5..bf74d44d9a7 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -255,7 +255,7 @@ struct StageInterfaceInfo {
};
/**
- * @brief Describe inputs & outputs, stage interfaces, resources and sources of a shader.
+ * \brief Describe inputs & outputs, stage interfaces, resources and sources of a shader.
* If all data is correctly provided, this is all that is needed to create and compile
* a GPUShader.
*
@@ -497,9 +497,9 @@ struct ShaderCreateInfo {
/**
* IMPORTANT: invocations count is only used if GL_ARB_gpu_shader5 is supported. On
- * implementations that do not supports it, the max_vertices will be be multiplied by
- * invocations. Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5`
- * and make a code path that does not rely on gl_InvocationID.
+ * implementations that do not supports it, the max_vertices will be multiplied by invocations.
+ * Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` and make a code
+ * path that does not rely on #gl_InvocationID.
*/
Self &geometry_layout(PrimitiveIn prim_in,
PrimitiveOut prim_out,
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index 8a842ef4d7c..5b7df035acd 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -21,6 +21,9 @@ extern "C" {
#define SHADER_SOURCE(datatoc, filename, filepath) extern char datatoc[];
#include "glsl_draw_source_list.h"
#include "glsl_gpu_source_list.h"
+#ifdef WITH_OCIO
+# include "glsl_ocio_source_list.h"
+#endif
#undef SHADER_SOURCE
}
@@ -348,6 +351,9 @@ void gpu_shader_dependency_init()
g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc));
#include "glsl_draw_source_list.h"
#include "glsl_gpu_source_list.h"
+#ifdef WITH_OCIO
+# include "glsl_ocio_source_list.h"
+#endif
#undef SHADER_SOURCE
int errors = 0;
diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h
index 29ee70962d1..7b9ecef5835 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency_private.h
+++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h
@@ -38,7 +38,7 @@ StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name);
/**
* \brief Find the name of the file from which the given string was generated.
- * \return Return filename or empty string.
+ * \return filename or empty string.
* \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source()
*/
StringRefNull gpu_shader_dependency_get_filename_from_source_string(
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index da5f08f003e..9e6a6f75391 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -52,11 +52,13 @@ Texture::~Texture()
#endif
}
-bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
+bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format)
{
w_ = w;
h_ = layers;
d_ = 0;
+ int mips_max = 1 + floorf(log2f(w));
+ mipmaps_ = min_ii(mips, mips_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_1D_ARRAY : GPU_TEXTURE_1D;
@@ -66,11 +68,13 @@ bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
return this->init_internal();
}
-bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format)
+bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format)
{
w_ = w;
h_ = h;
d_ = layers;
+ int mips_max = 1 + floorf(log2f(max_ii(w, h)));
+ mipmaps_ = min_ii(mips, mips_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_2D_ARRAY : GPU_TEXTURE_2D;
@@ -80,11 +84,13 @@ bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format)
return this->init_internal();
}
-bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format)
+bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format)
{
w_ = w;
h_ = h;
d_ = d;
+ int mips_max = 1 + floorf(log2f(max_iii(w, h, d)));
+ mipmaps_ = min_ii(mips, mips_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = GPU_TEXTURE_3D;
@@ -94,11 +100,13 @@ bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format)
return this->init_internal();
}
-bool Texture::init_cubemap(int w, int layers, eGPUTextureFormat format)
+bool Texture::init_cubemap(int w, int layers, int mips, eGPUTextureFormat format)
{
w_ = w;
h_ = w;
d_ = max_ii(1, layers) * 6;
+ int mips_max = 1 + floorf(log2f(w));
+ mipmaps_ = min_ii(mips, mips_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_CUBE_ARRAY : GPU_TEXTURE_CUBE;
@@ -123,6 +131,42 @@ bool Texture::init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format)
return this->init_internal(vbo);
}
+bool Texture::init_view(const GPUTexture *src_,
+ eGPUTextureFormat format,
+ int mip_start,
+ int mip_len,
+ int layer_start,
+ int layer_len)
+{
+ const Texture *src = unwrap(src_);
+ w_ = src->w_;
+ h_ = src->h_;
+ d_ = src->d_;
+ switch (type_) {
+ case GPU_TEXTURE_1D_ARRAY:
+ h_ = layer_len;
+ break;
+ case GPU_TEXTURE_CUBE_ARRAY:
+ BLI_assert(layer_len % 6 == 0);
+ ATTR_FALLTHROUGH;
+ case GPU_TEXTURE_2D_ARRAY:
+ d_ = layer_len;
+ break;
+ default:
+ BLI_assert(layer_len == 1 && layer_start == 0);
+ break;
+ }
+ mip_start = min_ii(mip_start, src->mipmaps_ - 1);
+ mip_len = min_ii(mip_len, (src->mipmaps_ - mip_start));
+ mipmaps_ = mip_len;
+ format_ = format;
+ format_flag_ = to_format_flag(format);
+ /* For now always copy the target. Target aliasing could be exposed later. */
+ type_ = src->type_;
+ sampler_state = src->sampler_state;
+ return this->init_internal(src_, mip_start, layer_start);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -187,28 +231,29 @@ static inline GPUTexture *gpu_texture_create(const char *name,
const int h,
const int d,
const eGPUTextureType type,
- int UNUSED(mips),
+ int mips,
eGPUTextureFormat tex_format,
eGPUDataFormat data_format,
const void *pixels)
{
+ BLI_assert(mips > 0);
Texture *tex = GPUBackend::get()->texture_alloc(name);
bool success = false;
switch (type) {
case GPU_TEXTURE_1D:
case GPU_TEXTURE_1D_ARRAY:
- success = tex->init_1D(w, h, tex_format);
+ success = tex->init_1D(w, h, mips, tex_format);
break;
case GPU_TEXTURE_2D:
case GPU_TEXTURE_2D_ARRAY:
- success = tex->init_2D(w, h, d, tex_format);
+ success = tex->init_2D(w, h, d, mips, tex_format);
break;
case GPU_TEXTURE_3D:
- success = tex->init_3D(w, h, d, tex_format);
+ success = tex->init_3D(w, h, d, mips, tex_format);
break;
case GPU_TEXTURE_CUBE:
case GPU_TEXTURE_CUBE_ARRAY:
- success = tex->init_cubemap(w, d, tex_format);
+ success = tex->init_cubemap(w, d, mips, tex_format);
break;
default:
break;
@@ -286,7 +331,7 @@ GPUTexture *GPU_texture_create_compressed_2d(
const char *name, int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data)
{
Texture *tex = GPUBackend::get()->texture_alloc(name);
- bool success = tex->init_2D(w, h, 0, tex_format);
+ bool success = tex->init_2D(w, h, 0, miplen, tex_format);
if (!success) {
delete tex;
@@ -334,6 +379,21 @@ GPUTexture *GPU_texture_create_error(int dimension, bool is_array)
return gpu_texture_create("invalid_tex", w, h, d, type, 1, GPU_RGBA8, GPU_DATA_FLOAT, pixel);
}
+GPUTexture *GPU_texture_create_view(const char *name,
+ const GPUTexture *src,
+ eGPUTextureFormat format,
+ int mip_start,
+ int mip_len,
+ int layer_start,
+ int layer_len)
+{
+ BLI_assert(mip_len > 0);
+ BLI_assert(layer_len > 0);
+ Texture *view = GPUBackend::get()->texture_alloc(name);
+ view->init_view(src, format, mip_start, mip_len, layer_start, layer_len);
+ return wrap(view);
+}
+
/* ------ Update ------ */
void GPU_texture_update_mipmap(GPUTexture *tex_,
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 2c57c467bcf..ec11fab421c 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -101,11 +101,17 @@ class Texture {
virtual ~Texture();
/* Return true on success. */
- bool init_1D(int w, int layers, eGPUTextureFormat format);
- bool init_2D(int w, int h, int layers, eGPUTextureFormat format);
- bool init_3D(int w, int h, int d, eGPUTextureFormat format);
- bool init_cubemap(int w, int layers, eGPUTextureFormat format);
+ bool init_1D(int w, int layers, int mips, eGPUTextureFormat format);
+ bool init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format);
+ bool init_3D(int w, int h, int d, int mips, eGPUTextureFormat format);
+ bool init_cubemap(int w, int layers, int mips, eGPUTextureFormat format);
bool init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format);
+ bool init_view(const GPUTexture *src,
+ eGPUTextureFormat format,
+ int mip_start,
+ int mip_len,
+ int layer_start,
+ int layer_len);
virtual void generate_mipmap() = 0;
virtual void copy_to(Texture *tex) = 0;
@@ -234,6 +240,7 @@ class Texture {
protected:
virtual bool init_internal() = 0;
virtual bool init_internal(GPUVertBuf *vbo) = 0;
+ virtual bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) = 0;
};
/* Syntactic sugar. */
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index fd4a87bc544..b5a572bccbe 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -277,8 +277,6 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo
GPU_matrix_identity_set();
GPU_matrix_identity_projection_set();
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE);
- immUniform1i("overlayTexture", 0);
- immUniform1i("imageTexture", 1);
int settings = stereo_format->display_mode;
if (settings == S3D_DISPLAY_ANAGLYPH) {
switch (stereo_format->anaglyph_type) {
@@ -432,8 +430,6 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport,
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE);
GPU_batch_uniform_1i(batch, "overlay", do_overlay_merge);
GPU_batch_uniform_1i(batch, "display_transform", display_colorspace);
- GPU_batch_uniform_1i(batch, "image_texture", 0);
- GPU_batch_uniform_1i(batch, "overlays_texture", 1);
}
GPU_texture_bind(color, 0);
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 7a1674a5f01..302d8249914 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -240,6 +240,7 @@ static void detect_workarounds()
GLContext::texture_cube_map_array_support = false;
GLContext::texture_filter_anisotropic_support = false;
GLContext::texture_gather_support = false;
+ GLContext::texture_storage_support = false;
GLContext::vertex_attrib_binding_support = false;
return;
}
@@ -439,6 +440,7 @@ bool GLContext::shader_draw_parameters_support = false;
bool GLContext::texture_cube_map_array_support = false;
bool GLContext::texture_filter_anisotropic_support = false;
bool GLContext::texture_gather_support = false;
+bool GLContext::texture_storage_support = false;
bool GLContext::vertex_attrib_binding_support = false;
/** Workarounds. */
@@ -501,6 +503,7 @@ void GLBackend::capabilities_init()
GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array;
GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic;
GLContext::texture_gather_support = GLEW_ARB_texture_gather;
+ GLContext::texture_storage_support = GLEW_VERSION_4_3;
GLContext::vertex_attrib_binding_support = GLEW_ARB_vertex_attrib_binding;
detect_workarounds();
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index fe2ad0a4747..369b667cbcc 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -64,6 +64,7 @@ class GLContext : public Context {
static bool texture_cube_map_array_support;
static bool texture_filter_anisotropic_support;
static bool texture_gather_support;
+ static bool texture_storage_support;
static bool vertex_attrib_binding_support;
/** Workarounds. */
diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc
index cfedfe348f1..c32a6afd8cf 100644
--- a/source/blender/gpu/opengl/gl_immediate.cc
+++ b/source/blender/gpu/opengl/gl_immediate.cc
@@ -139,7 +139,7 @@ void GLImmediate::end()
GLContext::get()->state_manager->apply_state();
/* We convert the offset in vertex offset from the buffer's start.
- * This works because we added some padding to align the first vertex vertex. */
+ * This works because we added some padding to align the first vertex. */
uint v_first = buffer_offset() / vertex_format.stride;
GLVertArray::update_bindings(
vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface);
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index 22af2dd463f..0a5c7f8e79e 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -69,9 +69,79 @@ bool GLTexture::init_internal()
return false;
}
- this->ensure_mipmaps(0);
+ GLenum internal_format = to_gl_internal_format(format_);
+ const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE);
+ const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY);
+ const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY);
+ const int dimensions = (is_cubemap) ? 2 : this->dimensions_count();
+ GLenum gl_format = to_gl_data_format(format_);
+ GLenum gl_type = to_gl(to_data_format(format_));
+
+ auto mip_size = [&](int h, int w = 1, int d = 1) -> size_t {
+ return divide_ceil_u(w, 4) * divide_ceil_u(h, 4) * divide_ceil_u(d, 4) *
+ to_block_size(format_);
+ };
+ switch (dimensions) {
+ default:
+ case 1:
+ if (GLContext::texture_storage_support) {
+ glTexStorage1D(target_, mipmaps_, internal_format, w_);
+ }
+ else {
+ for (int i = 0, w = w_; i < mipmaps_; i++) {
+ if (is_compressed) {
+ glCompressedTexImage1D(target_, i, internal_format, w, 0, mip_size(w), nullptr);
+ }
+ else {
+ glTexImage1D(target_, i, internal_format, w, 0, gl_format, gl_type, nullptr);
+ }
+ w = max_ii(1, (w / 2));
+ }
+ }
+ break;
+ case 2:
+ if (GLContext::texture_storage_support) {
+ glTexStorage2D(target_, mipmaps_, internal_format, w_, h_);
+ }
+ else {
+ for (int i = 0, w = w_, h = h_; i < mipmaps_; i++) {
+ for (int f = 0; f < (is_cubemap ? 6 : 1); f++) {
+ GLenum target = (is_cubemap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + f : target_;
+ if (is_compressed) {
+ glCompressedTexImage2D(target, i, internal_format, w, h, 0, mip_size(w, h), nullptr);
+ }
+ else {
+ glTexImage2D(target, i, internal_format, w, h, 0, gl_format, gl_type, nullptr);
+ }
+ }
+ w = max_ii(1, (w / 2));
+ h = is_layered ? h_ : max_ii(1, (h / 2));
+ }
+ }
+ break;
+ case 3:
+ if (GLContext::texture_storage_support) {
+ glTexStorage3D(target_, mipmaps_, internal_format, w_, h_, d_);
+ }
+ else {
+ for (int i = 0, w = w_, h = h_, d = d_; i < mipmaps_; i++) {
+ if (is_compressed) {
+ glCompressedTexImage3D(
+ target_, i, internal_format, w, h, d, 0, mip_size(w, h, d), nullptr);
+ }
+ else {
+ glTexImage3D(target_, i, internal_format, w, h, d, 0, gl_format, gl_type, nullptr);
+ }
+ w = max_ii(1, (w / 2));
+ h = max_ii(1, (h / 2));
+ d = is_layered ? d_ : max_ii(1, (d / 2));
+ }
+ }
+ break;
+ }
+ this->mip_range_set(0, mipmaps_ - 1);
- /* Avoid issue with incomplete textures. */
+ /* Avoid issue with formats not supporting filtering. Nearest by default. */
if (GLContext::direct_state_access_support) {
glTextureParameteri(tex_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
@@ -105,65 +175,26 @@ bool GLTexture::init_internal(GPUVertBuf *vbo)
return true;
}
-void GLTexture::ensure_mipmaps(int miplvl)
+bool GLTexture::init_internal(const GPUTexture *src, int mip_offset, int layer_offset)
{
- int effective_h = (type_ == GPU_TEXTURE_1D_ARRAY) ? 0 : h_;
- int effective_d = (type_ != GPU_TEXTURE_3D) ? 0 : d_;
- int max_dimension = max_iii(w_, effective_h, effective_d);
- int max_miplvl = floor(log2(max_dimension));
- miplvl = min_ii(miplvl, max_miplvl);
-
- while (mipmaps_ < miplvl) {
- int mip = ++mipmaps_;
- const int dimensions = this->dimensions_count();
-
- int w = mip_width_get(mip);
- int h = mip_height_get(mip);
- int d = mip_depth_get(mip);
- GLenum internal_format = to_gl_internal_format(format_);
- GLenum gl_format = to_gl_data_format(format_);
- GLenum gl_type = to_gl(to_data_format(format_));
+ BLI_assert(GLContext::texture_storage_support);
- GLContext::state_manager_active_get()->texture_bind_temp(this);
+ const GLTexture *gl_src = static_cast<const GLTexture *>(unwrap(src));
+ GLenum internal_format = to_gl_internal_format(format_);
+ target_ = to_gl_target(type_);
- if (type_ == GPU_TEXTURE_CUBE) {
- for (int i = 0; i < d; i++) {
- GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
- glTexImage2D(target, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr);
- }
- }
- else if (format_flag_ & GPU_FORMAT_COMPRESSED) {
- size_t size = ((w + 3) / 4) * ((h + 3) / 4) * to_block_size(format_);
- switch (dimensions) {
- default:
- case 1:
- glCompressedTexImage1D(target_, mip, internal_format, w, 0, size, nullptr);
- break;
- case 2:
- glCompressedTexImage2D(target_, mip, internal_format, w, h, 0, size, nullptr);
- break;
- case 3:
- glCompressedTexImage3D(target_, mip, internal_format, w, h, d, 0, size, nullptr);
- break;
- }
- }
- else {
- switch (dimensions) {
- default:
- case 1:
- glTexImage1D(target_, mip, internal_format, w, 0, gl_format, gl_type, nullptr);
- break;
- case 2:
- glTexImage2D(target_, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr);
- break;
- case 3:
- glTexImage3D(target_, mip, internal_format, w, h, d, 0, gl_format, gl_type, nullptr);
- break;
- }
- }
- }
+ glTextureView(tex_id_,
+ target_,
+ gl_src->tex_id_,
+ internal_format,
+ mip_offset,
+ mipmaps_,
+ layer_offset,
+ this->layer_count());
- this->mip_range_set(0, mipmaps_);
+ debug::object_label(GL_TEXTURE, tex_id_, name_);
+
+ return true;
}
/** \} */
@@ -216,9 +247,7 @@ void GLTexture::update_sub(
BLI_assert(validate_data_format(format_, type));
BLI_assert(data != nullptr);
- this->ensure_mipmaps(mip);
-
- if (mip > mipmaps_) {
+ if (mip >= mipmaps_) {
debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels.");
return;
}
@@ -283,7 +312,6 @@ void GLTexture::update_sub(
*/
void GLTexture::generate_mipmap()
{
- this->ensure_mipmaps(9999);
/* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789).
* In this case we just create a complete texture with mipmaps manually without
* down-sampling. You must initialize the texture levels using other methods like
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index 07d3bb8c946..d4d024f5e3e 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -74,11 +74,11 @@ class GLTexture : public Texture {
bool init_internal() override;
/** Return true on success. */
bool init_internal(GPUVertBuf *vbo) override;
+ /** Return true on success. */
+ bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) override;
private:
bool proxy_check(int mip);
- /** Will create enough mipmaps up to get to the given level. */
- void ensure_mipmaps(int mip);
void update_sub_direct_state_access(
int mip, int offset[3], int extent[3], GLenum gl_format, GLenum gl_type, const void *data);
GPUFrameBuffer *framebuffer_get();
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index 88b69eb5b62..04f60f10d41 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -54,7 +54,7 @@ static uint16_t vbo_bind(const ShaderInterface *interface,
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
const ShaderInput *input = interface->attr_get(name);
- if (input == nullptr) {
+ if (input == nullptr || input->location == -1) {
continue;
}
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
index d3b70cb67f9..2798846b310 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh
@@ -16,6 +16,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge)
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.push_constant(Type::BOOL, "display_transform")
.push_constant(Type::BOOL, "overlay")
+ /* Sampler slots should match OCIO's. */
.sampler(0, ImageType::FLOAT_2D, "image_texture")
.sampler(1, ImageType::FLOAT_2D, "overlays_texture")
.vertex_source("gpu_shader_2D_image_vert.glsl")
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index 6dcfd3d014e..470dfb01bdd 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -244,9 +244,9 @@ static int initialize_chain(Object *ob, bPoseChannel *pchan_tip, bConstraint *co
}
if (BLI_listbase_is_empty(&curchan->iktree) == false) {
- /* Oh oh, there is already a chain starting from this channel and our chain is longer...
+ /* Oh, there is already a chain starting from this channel and our chain is longer.
* Should handle this by moving the previous chain up to the beginning of our chain
- * For now we just stop here */
+ * For now we just stop here. */
break;
}
}
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 23b9c85bd5b..8929a467670 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -560,6 +560,9 @@ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf);
* Create char buffer, color corrected if necessary, for ImBufs that lack one.
*/
void IMB_rect_from_float(struct ImBuf *ibuf);
+void IMB_float_from_rect_ex(struct ImBuf *dst,
+ const struct ImBuf *src,
+ const struct rcti *region_to_update);
void IMB_float_from_rect(struct ImBuf *ibuf);
/**
* No profile conversion.
@@ -897,16 +900,16 @@ typedef enum eIMBTransformMode {
/**
* \brief Transform source image buffer onto destination image buffer using a transform matrix.
*
- * \param src Image buffer to read from.
- * \param dst Image buffer to write to. rect or rect_float must already be initialized.
+ * \param src: Image buffer to read from.
+ * \param dst: Image buffer to write to. rect or rect_float must already be initialized.
* - dst buffer must be a 4 channel buffers.
* - Only one data type buffer will be used (rect_float has priority over rect)
- * \param mode Cropping/Wrap repeat effect to apply during transformation.
- * \param filter Interpolation to use during sampling.
- * \param transform_matrix Transformation matrix to use.
+ * \param mode: Cropping/Wrap repeat effect to apply during transformation.
+ * \param filter: Interpolation to use during sampling.
+ * \param transform_matrix: Transformation matrix to use.
* The given matrix should transform between dst pixel space to src pixel space.
* One unit is one pixel.
- * \param src_crop cropping region how to crop the source buffer. Should only be passed when mode
+ * \param src_crop: Cropping region how to crop the source buffer. Should only be passed when mode
* is set to #IMB_TRANSFORM_MODE_CROP_SRC. For any other mode this should be empty.
*
* During transformation no data/color conversion will happens.
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index bad2081448f..e99572adbb0 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -108,7 +108,7 @@ struct anim {
#ifdef WITH_FFMPEG
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
- AVCodec *pCodec;
+ const AVCodec *pCodec;
AVFrame *pFrame;
int pFrameComplete;
AVFrame *pFrameRGB;
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 4125662d35f..f97a50ecf47 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -492,7 +492,7 @@ static int startffmpeg(struct anim *anim)
{
int i, video_stream_index;
- AVCodec *pCodec;
+ const AVCodec *pCodec;
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx;
AVRational frame_rate;
@@ -867,6 +867,17 @@ static void ffmpeg_decode_store_frame_pts(struct anim *anim)
(int64_t)anim->cur_pts);
}
+static int ffmpeg_read_video_frame(struct anim *anim, AVPacket *packet)
+{
+ int ret = 0;
+ while (ret = av_read_frame(anim->pFormatCtx, packet) >= 0) {
+ if (packet->stream_index == anim->videoStream) {
+ break;
+ }
+ }
+ return ret;
+}
+
/* decode one video frame also considering the packet read into cur_packet */
static int ffmpeg_decode_video_frame(struct anim *anim)
{
@@ -887,7 +898,7 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
anim->cur_packet->stream_index = -1;
}
- while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) {
+ while ((rval = ffmpeg_read_video_frame(anim, anim->cur_packet)) >= 0) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
@@ -897,14 +908,13 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
(anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts,
(anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts,
(anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
- if (anim->cur_packet->stream_index == anim->videoStream) {
- avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
- anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
- if (anim->pFrameComplete) {
- ffmpeg_decode_store_frame_pts(anim);
- break;
- }
+ avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
+
+ if (anim->pFrameComplete) {
+ ffmpeg_decode_store_frame_pts(anim);
+ break;
}
av_packet_unref(anim->cur_packet);
anim->cur_packet->stream_index = -1;
@@ -1159,13 +1169,59 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim,
return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD);
}
+/* Read packet until timestamp matches `anim->cur_packet`, thus recovering internal `anim` stream
+ * position state. */
+static void ffmpeg_seek_recover_stream_position(struct anim *anim)
+{
+ AVPacket *temp_packet = av_packet_alloc();
+ while (ffmpeg_read_video_frame(anim, temp_packet)) {
+ int64_t current_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts);
+ int64_t temp_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
+ av_packet_unref(temp_packet);
+
+ if (current_pts == temp_pts) {
+ break;
+ }
+ }
+ av_packet_free(&temp_packet);
+}
+
+/* Check if seeking and mainly flushing codec buffers is needed. */
+static bool ffmpeg_seek_buffers_need_flushing(struct anim *anim, int position, int64_t seek_pos)
+{
+ /* Get timestamp of packet read after seeking. */
+ AVPacket *temp_packet = av_packet_alloc();
+ ffmpeg_read_video_frame(anim, temp_packet);
+ int64_t gop_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
+ av_packet_unref(temp_packet);
+ av_packet_free(&temp_packet);
+
+ /* Seeking gives packet, that is currently read. No seeking was necessary, so buffers don't have
+ * to be flushed. */
+ if (gop_pts == timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts)) {
+ return false;
+ }
+
+ /* Packet after seeking is same key frame as current, and further in time. No seeking was
+ * necessary, so buffers don't have to be flushed. But stream position has to be recovered. */
+ if (gop_pts == anim->cur_key_frame_pts && position > anim->cur_position) {
+ ffmpeg_seek_recover_stream_position(anim);
+ return false;
+ }
+
+ /* Seeking was necessary, but we have read packets. Therefore we must seek again. */
+ av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
+ anim->cur_key_frame_pts = gop_pts;
+ return true;
+}
+
/* Seek to last necessary key frame. */
static int ffmpeg_seek_to_key_frame(struct anim *anim,
int position,
struct anim_index *tc_index,
int64_t pts_to_search)
{
- int64_t pos;
+ int64_t seek_pos;
int ret;
if (tc_index) {
@@ -1180,23 +1236,23 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
uint64_t pts;
uint64_t dts;
- pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
+ seek_pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index);
dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek seek_pos = %" PRId64 "\n", seek_pos);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE seek_pos\n");
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
+ ret = av_seek_frame(anim->pFormatCtx, -1, seek_pos, AVSEEK_FLAG_BYTE);
}
else {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n");
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS seek_pos\n");
ret = av_seek_frame(
anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD);
}
@@ -1204,58 +1260,25 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
else {
/* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
*/
- pos = ffmpeg_get_seek_pts(anim, pts_to_search);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+ seek_pos = ffmpeg_get_seek_pts(anim, pts_to_search);
+ av_log(
+ anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek seek_pos = %" PRId64 "\n", seek_pos);
AVFormatContext *format_ctx = anim->pFormatCtx;
if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) {
- ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
+ ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
}
else {
- ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos);
+ ret = ffmpeg_generic_seek_workaround(anim, &seek_pos, pts_to_search);
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "Adjusted final seek seek_pos = %" PRId64 "\n",
+ seek_pos);
}
- if (ret >= 0) {
- /* Double check if we need to seek and decode all packets. */
- AVPacket *current_gop_start_packet = av_packet_alloc();
- while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) {
- if (current_gop_start_packet->stream_index == anim->videoStream) {
- break;
- }
- av_packet_unref(current_gop_start_packet);
- }
- int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts,
- current_gop_start_packet->dts);
-
- av_packet_free(&current_gop_start_packet);
- bool same_gop = gop_pts == anim->cur_key_frame_pts;
-
- if (same_gop && position > anim->cur_position) {
- /* Change back to our old frame position so we can simply continue decoding from there. */
- int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts);
-
- if (cur_pts == gop_pts) {
- /* We are already at the correct position. */
- return 0;
- }
- AVPacket *temp = av_packet_alloc();
-
- while (av_read_frame(anim->pFormatCtx, temp) >= 0) {
- int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts);
- if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) {
- break;
- }
- av_packet_unref(temp);
- }
- av_packet_free(&temp);
- return 0;
- }
-
- anim->cur_key_frame_pts = gop_pts;
- /* Seek back so we are at the correct position after we decoded a frame. */
- av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
+ if (ret <= 0 && !ffmpeg_seek_buffers_need_flushing(anim, position, seek_pos)) {
+ return 0;
}
}
@@ -1265,7 +1288,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
"FETCH: "
"error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
"): errcode = %d\n",
- pos,
+ seek_pos,
position,
pts_to_search,
ret);
@@ -1290,7 +1313,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
return NULL;
}
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: seek_pos=%d\n", position);
struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index 0bf50937674..588c92d748d 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -6,6 +6,7 @@
*/
#include "BLI_math.h"
+#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "IMB_filter.h"
@@ -752,6 +753,61 @@ void IMB_rect_from_float(ImBuf *ibuf)
ibuf->userflags &= ~IB_RECT_INVALID;
}
+void IMB_float_from_rect_ex(struct ImBuf *dst,
+ const struct ImBuf *src,
+ const rcti *region_to_update)
+{
+ BLI_assert_msg(dst->rect_float != NULL,
+ "Destination buffer should have a float buffer assigned.");
+ BLI_assert_msg(src->rect != NULL, "Source buffer should have a byte buffer assigned.");
+ BLI_assert_msg(dst->x == src->x, "Source and destination buffer should have the same dimension");
+ BLI_assert_msg(dst->y == src->y, "Source and destination buffer should have the same dimension");
+ BLI_assert_msg(dst->channels = 4, "Destination buffer should have 4 channels.");
+ BLI_assert_msg(region_to_update->xmin >= 0,
+ "Region to update should be clipped to the given buffers.");
+ BLI_assert_msg(region_to_update->ymin >= 0,
+ "Region to update should be clipped to the given buffers.");
+ BLI_assert_msg(region_to_update->xmax <= dst->x,
+ "Region to update should be clipped to the given buffers.");
+ BLI_assert_msg(region_to_update->ymax <= dst->y,
+ "Region to update should be clipped to the given buffers.");
+
+ float *rect_float = dst->rect_float;
+ rect_float += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4;
+ unsigned char *rect = (unsigned char *)src->rect;
+ rect += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4;
+ const int region_width = BLI_rcti_size_x(region_to_update);
+ const int region_height = BLI_rcti_size_y(region_to_update);
+
+ /* Convert byte buffer to float buffer without color or alpha conversion. */
+ IMB_buffer_float_from_byte(rect_float,
+ rect,
+ IB_PROFILE_SRGB,
+ IB_PROFILE_SRGB,
+ false,
+ region_width,
+ region_height,
+ src->x,
+ dst->x);
+
+ /* Perform color space conversion from rect color space to linear. */
+ float *float_ptr = rect_float;
+ for (int i = 0; i < region_height; i++) {
+ IMB_colormanagement_colorspace_to_scene_linear(
+ float_ptr, region_width, 1, dst->channels, src->rect_colorspace, false);
+ float_ptr += 4 * dst->x;
+ }
+
+ /* Perform alpha conversion. */
+ if (IMB_alpha_affects_rgb(src)) {
+ float_ptr = rect_float;
+ for (int i = 0; i < region_height; i++) {
+ IMB_premultiply_rect_float(float_ptr, dst->channels, region_width, 1);
+ float_ptr += 4 * dst->x;
+ }
+ }
+}
+
void IMB_float_from_rect(ImBuf *ibuf)
{
float *rect_float;
@@ -775,33 +831,14 @@ void IMB_float_from_rect(ImBuf *ibuf)
}
ibuf->channels = 4;
- }
-
- /* first, create float buffer in non-linear space */
- IMB_buffer_float_from_byte(rect_float,
- (unsigned char *)ibuf->rect,
- IB_PROFILE_SRGB,
- IB_PROFILE_SRGB,
- false,
- ibuf->x,
- ibuf->y,
- ibuf->x,
- ibuf->x);
-
- /* then make float be in linear space */
- IMB_colormanagement_colorspace_to_scene_linear(
- rect_float, ibuf->x, ibuf->y, ibuf->channels, ibuf->rect_colorspace, false);
-
- /* byte buffer is straight alpha, float should always be premul */
- if (IMB_alpha_affects_rgb(ibuf)) {
- IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y);
- }
-
- if (ibuf->rect_float == NULL) {
ibuf->rect_float = rect_float;
ibuf->mall |= IB_rectfloat;
ibuf->flags |= IB_rectfloat;
}
+
+ rcti region_to_update;
+ BLI_rcti_init(&region_to_update, 0, ibuf->x, 0, ibuf->y);
+ IMB_float_from_rect_ex(ibuf, ibuf, &region_to_update);
}
/** \} */
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 2cf82ca5c48..c1e00642a6d 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -478,7 +478,7 @@ struct proxy_output_ctx {
AVFormatContext *of;
AVStream *st;
AVCodecContext *c;
- AVCodec *codec;
+ const AVCodec *codec;
struct SwsContext *sws_ctx;
AVFrame *frame;
int cfra;
@@ -510,12 +510,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->st = avformat_new_stream(rv->of, NULL);
rv->st->id = 0;
- rv->c = avcodec_alloc_context3(NULL);
- rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
- rv->c->codec_id = AV_CODEC_ID_H264;
+ rv->codec = avcodec_find_encoder(AV_CODEC_ID_H264);
- rv->of->oformat->video_codec = rv->c->codec_id;
- rv->codec = avcodec_find_encoder(rv->c->codec_id);
+ rv->c = avcodec_alloc_context3(rv->codec);
if (!rv->codec) {
fprintf(stderr,
@@ -527,8 +524,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
return NULL;
}
- avcodec_get_context_defaults3(rv->c, rv->codec);
-
rv->c->width = width;
rv->c->height = height;
rv->c->gop_size = 10;
@@ -779,7 +774,7 @@ typedef struct FFmpegIndexBuilderContext {
AVFormatContext *iFormatCtx;
AVCodecContext *iCodecCtx;
- AVCodec *iCodec;
+ const AVCodec *iCodec;
AVStream *iStream;
int videoStream;
@@ -1167,7 +1162,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte
}
/* Assess scrubbing performance of provided file. This function is not meant to be very exact.
- * It compares number number of frames decoded in reasonable time with largest detected GOP size.
+ * It compares number of frames decoded in reasonable time with largest detected GOP size.
* Because seeking happens in single GOP, it means, that maximum seek time can be detected this
* way.
* Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index a47009e3abd..418a4724c00 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -16,30 +16,46 @@
#include <stdexcept>
#include <string>
-#include <Iex.h>
-#include <ImathBox.h>
-#include <ImfArray.h>
-#include <ImfChannelList.h>
-#include <ImfCompression.h>
-#include <ImfCompressionAttribute.h>
-#include <ImfIO.h>
-#include <ImfInputFile.h>
-#include <ImfOutputFile.h>
-#include <ImfPixelType.h>
-#include <ImfStandardAttributes.h>
-#include <ImfStringAttribute.h>
-#include <ImfVersion.h>
-#include <half.h>
+/* The OpenEXR version can reliably be found in this header file from OpenEXR,
+ * for both 2.x and 3.x:
+ */
+#include <OpenEXR/OpenEXRConfig.h>
+#define COMBINED_OPENEXR_VERSION \
+ ((10000 * OPENEXR_VERSION_MAJOR) + (100 * OPENEXR_VERSION_MINOR) + OPENEXR_VERSION_PATCH)
+
+#if COMBINED_OPENEXR_VERSION >= 20599
+/* >=2.5.99 -> OpenEXR >=3.0 */
+# include <Imath/half.h>
+# include <OpenEXR/ImfFrameBuffer.h>
+# define exr_file_offset_t uint64_t
+#else
+/* OpenEXR 2.x, use the old locations. */
+# include <OpenEXR/half.h>
+# define exr_file_offset_t Int64
+#endif
+
+#include <OpenEXR/Iex.h>
+#include <OpenEXR/ImfArray.h>
+#include <OpenEXR/ImfChannelList.h>
+#include <OpenEXR/ImfCompression.h>
+#include <OpenEXR/ImfCompressionAttribute.h>
+#include <OpenEXR/ImfIO.h>
+#include <OpenEXR/ImfInputFile.h>
+#include <OpenEXR/ImfOutputFile.h>
+#include <OpenEXR/ImfPixelType.h>
+#include <OpenEXR/ImfStandardAttributes.h>
+#include <OpenEXR/ImfStringAttribute.h>
+#include <OpenEXR/ImfVersion.h>
/* multiview/multipart */
-#include <ImfInputPart.h>
-#include <ImfMultiPartInputFile.h>
-#include <ImfMultiPartOutputFile.h>
-#include <ImfMultiView.h>
-#include <ImfOutputPart.h>
-#include <ImfPartHelper.h>
-#include <ImfPartType.h>
-#include <ImfTiledOutputPart.h>
+#include <OpenEXR/ImfInputPart.h>
+#include <OpenEXR/ImfMultiPartInputFile.h>
+#include <OpenEXR/ImfMultiPartOutputFile.h>
+#include <OpenEXR/ImfMultiView.h>
+#include <OpenEXR/ImfOutputPart.h>
+#include <OpenEXR/ImfPartHelper.h>
+#include <OpenEXR/ImfPartType.h>
+#include <OpenEXR/ImfTiledOutputPart.h>
#include "DNA_scene_types.h" /* For OpenEXR compression constants */
@@ -115,12 +131,12 @@ class IMemStream : public Imf::IStream {
return false;
}
- Int64 tellg() override
+ exr_file_offset_t tellg() override
{
return _exrpos;
}
- void seekg(Int64 pos) override
+ void seekg(exr_file_offset_t pos) override
{
_exrpos = pos;
}
@@ -130,8 +146,8 @@ class IMemStream : public Imf::IStream {
}
private:
- Int64 _exrpos;
- Int64 _exrsize;
+ exr_file_offset_t _exrpos;
+ exr_file_offset_t _exrsize;
unsigned char *_exrbuf;
};
@@ -166,12 +182,12 @@ class IFileStream : public Imf::IStream {
return check_error();
}
- Int64 tellg() override
+ exr_file_offset_t tellg() override
{
return std::streamoff(ifs.tellg());
}
- void seekg(Int64 pos) override
+ void seekg(exr_file_offset_t pos) override
{
ifs.seekg(pos);
check_error();
@@ -215,19 +231,19 @@ class OMemStream : public OStream {
ibuf->encodedsize += n;
}
- Int64 tellp() override
+ exr_file_offset_t tellp() override
{
return offset;
}
- void seekp(Int64 pos) override
+ void seekp(exr_file_offset_t pos) override
{
offset = pos;
ensure_size(offset);
}
private:
- void ensure_size(Int64 size)
+ void ensure_size(exr_file_offset_t size)
{
/* if buffer is too small increase it. */
while (size > ibuf->encodedbuffersize) {
@@ -238,7 +254,7 @@ class OMemStream : public OStream {
}
ImBuf *ibuf;
- Int64 offset;
+ exr_file_offset_t offset;
};
/* File Output Stream */
@@ -268,12 +284,12 @@ class OFileStream : public OStream {
check_error();
}
- Int64 tellp() override
+ exr_file_offset_t tellp() override
{
return std::streamoff(ofs.tellp());
}
- void seekp(Int64 pos) override
+ void seekp(exr_file_offset_t pos) override
{
ofs.seekp(pos);
check_error();
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index cabd6070400..241f1a736f4 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -250,7 +250,7 @@ static int isffmpeg(const char *filepath)
AVFormatContext *pFormatCtx = NULL;
unsigned int i;
int videoStream;
- AVCodec *pCodec;
+ const AVCodec *pCodec;
if (BLI_path_extension_check_n(filepath,
".swf",
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
index 4723b89de08..d33adfdb4b9 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -190,7 +190,7 @@ ABCAbstractWriter *ABCHierarchyIterator::create_data_writer_for_object_type(
return new ABCMeshWriter(writer_args);
case OB_CAMERA:
return new ABCCameraWriter(writer_args);
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
if (params_.curves_as_mesh) {
return new ABCCurveMeshWriter(writer_args);
}
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 67e9846030a..11693eeb4de 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -309,7 +309,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
}
if (args_.export_params->orcos) {
- write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ write_generated_coordinates(abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config);
}
if (!edge_crease_indices.empty()) {
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index 5e307d59ed4..89ed7c5ce06 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -84,7 +84,7 @@ bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const
bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const
{
- return ELEM(context->object->type, OB_SURF, OB_CURVE);
+ return ELEM(context->object->type, OB_SURF, OB_CURVES_LEGACY);
}
static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc
index a6400dc6f6c..d8859acdf5f 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.cc
+++ b/source/blender/io/alembic/intern/abc_reader_curves.cc
@@ -66,7 +66,7 @@ bool AbcCurveReader::accepts_object_type(
return false;
}
- if (ob->type != OB_CURVE) {
+ if (ob->type != OB_CURVES_LEGACY) {
*err_str = "Object type mismatch, Alembic object path points to Curves.";
return false;
}
@@ -76,7 +76,7 @@ bool AbcCurveReader::accepts_object_type(
void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
{
- Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
+ Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVES_LEGACY);
cu->flag |= CU_3D;
cu->actvert = CU_ACT_NONE;
@@ -91,7 +91,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele
}
}
- m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
+ m_object = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, m_object_name.c_str());
m_object->data = cu;
read_curve_sample(cu, m_curves_schema, sample_sel);
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
index b744106ff95..78f9c0a85b4 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
@@ -69,7 +69,7 @@ bool AbcNurbsReader::accepts_object_type(
return false;
}
- if (ob->type != OB_CURVE) {
+ if (ob->type != OB_CURVES_LEGACY) {
*err_str = "Object type mismatch, Alembic object path points to NURBS.";
return false;
}
diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt
index 34949da622a..3289a7c6e66 100644
--- a/source/blender/io/collada/CMakeLists.txt
+++ b/source/blender/io/collada/CMakeLists.txt
@@ -14,6 +14,10 @@ if(OPENCOLLADA_ANIMATION_CLIP)
add_definitions(-DWITH_OPENCOLLADA_ANIMATION_CLIP)
endif()
+# In cmake version 3.21 and up, we can instead use the NO_CACHE option for
+# find_file so we don't need to clear it from the cache here.
+unset(OPENCOLLADA_ANIMATION_CLIP CACHE)
+
set(INC
.
../../blenkernel
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index 9d4311bd693..9379e72bdd9 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -158,7 +158,7 @@ void GpencilIO::create_object_list()
float zdepth = 0;
if (rv3d_) {
if (rv3d_->is_persp) {
- zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3], nullptr);
+ zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3]);
}
else {
zdepth = -dot_v3v3(rv3d_->viewinv[2], object->obmat[3]);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
index 3c4676beb75..cc3eab02e07 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -196,7 +196,7 @@ void GpencilExporterPDF::export_gpencil_layers()
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
- BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false);
+ BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0);
}
export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
index d21af4c09eb..f8d30546e39 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
@@ -221,7 +221,7 @@ void GpencilExporterSVG::export_gpencil_layers()
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
- BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false);
+ BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0);
}
export_stroke_to_path(gpl, gps_perimeter, node_gpl, false);
diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc
index 1f384bdf127..6186e2bb61f 100644
--- a/source/blender/io/usd/intern/usd_capi_import.cc
+++ b/source/blender/io/usd/intern/usd_capi_import.cc
@@ -502,6 +502,9 @@ CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/,
const char *filename,
ListBase *object_paths)
{
+ /* Must call this so that USD file format plugins are loaded. */
+ ensure_usd_plugin_path_registered();
+
pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename);
if (!stage) {
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
index a86deb7c9f0..7a0d896fb3e 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -95,7 +95,7 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch
break;
case OB_EMPTY:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
case OB_SPEAKER:
diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc
index e331030164e..0d3c2feb8f3 100644
--- a/source/blender/io/usd/intern/usd_reader_curve.cc
+++ b/source/blender/io/usd/intern/usd_reader_curve.cc
@@ -26,13 +26,13 @@ namespace blender::io::usd {
void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */)
{
- curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE);
+ curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY);
curve_->flag |= CU_3D;
curve_->actvert = CU_ACT_NONE;
curve_->resolu = 2;
- object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str());
+ object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str());
object_->data = curve_;
}
diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc
index c7fd788e072..d0a5dc1c4b4 100644
--- a/source/blender/io/usd/intern/usd_reader_nurbs.cc
+++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc
@@ -43,13 +43,13 @@ namespace blender::io::usd {
void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */)
{
- curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE);
+ curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY);
curve_->flag |= CU_3D;
curve_->actvert = CU_ACT_NONE;
curve_->resolu = 2;
- object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str());
+ object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str());
object_->data = curve_;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
index 7016dff2a29..3bee93b36a5 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
@@ -310,7 +310,7 @@ class FormatHandler : NonCopyable, NonMovable {
/**
* Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`.
*
- * \param key Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for
+ * \param key: Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for
* `eFileType::MTL`.
*/
template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index 9b9c8483464..5b11c85b7a4 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -189,7 +189,7 @@ class OBJMesh : NonCopyable {
}
/**
* Calculate a polygon's polygon/loop normal indices.
- * \param poly_index Index of the polygon to calculate indices for.
+ * \param poly_index: Index of the polygon to calculate indices for.
* \return Vector of normal indices, aligned with vertices of polygon.
*/
Vector<int> calc_poly_normal_indices(int poly_index) const;
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 9ea75e6eb4c..2cef5192337 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -93,7 +93,7 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par
case OB_MESH:
r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object));
break;
- case OB_CURVE: {
+ case OB_CURVES_LEGACY: {
Curve *curve = static_cast<Curve *>(object->data);
Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)};
if (!nurb) {
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh
index 9f9ec5a6083..676b1f3598c 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh
@@ -64,11 +64,12 @@ void export_frame(Depsgraph *depsgraph,
* Find the objects to be exported in the `view_layer` of the dependency graph`depsgraph`,
* and return them in vectors `unique_ptr`s of `OBJMesh` and `OBJCurve`.
* If `export_params.export_selected_objects` is set, then only selected objects are to be
- * exported, else all objects are to be exported. But only objects of type `OB_MESH`, `OB_CURVE`,
- * and `OB_SURF` are supported; the rest will be ignored. If `export_params.export_curves_as_nurbs`
- * is set, then curves of type `CU_NURBS` are exported in curve form in the .obj file, otherwise
- * they are converted to mesh and returned in the `OBJMesh` vector. All other exportable types are
- * always converted to mesh and returned in the `OBJMesh` vector.
+ * exported, else all objects are to be exported. But only objects of type `OB_MESH`,
+ * `OB_CURVES_LEGACY`, and `OB_SURF` are supported; the rest will be ignored. If
+ * `export_params.export_curves_as_nurbs` is set, then curves of type `CU_NURBS` are exported in
+ * curve form in the .obj file, otherwise they are converted to mesh and returned in the `OBJMesh`
+ * vector. All other exportable types are always converted to mesh and returned in the `OBJMesh`
+ * vector.
*/
std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params);
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index c045082d569..011f3618e15 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -330,7 +330,7 @@ enum {
/* 2 characters for ID code and 64 for actual name */
#define MAX_ID_NAME 66
-/* ID_Runtime.remapping_status */
+/* ID_Runtime_Remap.status */
enum {
/** new_id is directly linked in current .blend. */
ID_REMAP_IS_LINKED_DIRECT = 1 << 0,
@@ -879,7 +879,7 @@ typedef enum IDRecalcFlag {
#define FILTER_ID_AR (1ULL << 1)
#define FILTER_ID_BR (1ULL << 2)
#define FILTER_ID_CA (1ULL << 3)
-#define FILTER_ID_CU (1ULL << 4)
+#define FILTER_ID_CU_LEGACY (1ULL << 4)
#define FILTER_ID_GD (1ULL << 5)
#define FILTER_ID_GR (1ULL << 6)
#define FILTER_ID_IM (1ULL << 7)
@@ -912,12 +912,12 @@ typedef enum IDRecalcFlag {
#define FILTER_ID_SIM (1ULL << 35)
#define FILTER_ID_ALL \
- (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD | \
- FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | \
- FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \
- FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \
- FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \
- FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM)
+ (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \
+ FILTER_ID_GD | FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | \
+ FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \
+ FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \
+ FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \
+ FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM)
/**
* This enum defines the index assigned to each type of IDs in the array returned by
@@ -998,7 +998,7 @@ enum {
/* Object data types. */
INDEX_ID_AR,
INDEX_ID_ME,
- INDEX_ID_CU,
+ INDEX_ID_CU_LEGACY,
INDEX_ID_MB,
INDEX_ID_CV,
INDEX_ID_PT,
diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h
index a6f950517eb..b0ca13615b8 100644
--- a/source/blender/makesdna/DNA_ID_enums.h
+++ b/source/blender/makesdna/DNA_ID_enums.h
@@ -40,46 +40,46 @@ enum eIconSizes {
* and the first 2 bytes of #ID.name (for runtime checks, see #GS macro).
*/
typedef enum ID_Type {
- ID_SCE = MAKE_ID2('S', 'C'), /* Scene */
- ID_LI = MAKE_ID2('L', 'I'), /* Library */
- ID_OB = MAKE_ID2('O', 'B'), /* Object */
- ID_ME = MAKE_ID2('M', 'E'), /* Mesh */
- ID_CU = MAKE_ID2('C', 'U'), /* Curve */
- ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */
- ID_MA = MAKE_ID2('M', 'A'), /* Material */
- ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */
- ID_IM = MAKE_ID2('I', 'M'), /* Image */
- ID_LT = MAKE_ID2('L', 'T'), /* Lattice */
- ID_LA = MAKE_ID2('L', 'A'), /* Light */
- ID_CA = MAKE_ID2('C', 'A'), /* Camera */
- ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */
- ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */
- ID_WO = MAKE_ID2('W', 'O'), /* World */
- ID_SCR = MAKE_ID2('S', 'R'), /* Screen */
- ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */
- ID_TXT = MAKE_ID2('T', 'X'), /* Text */
- ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */
- ID_SO = MAKE_ID2('S', 'O'), /* Sound */
- ID_GR = MAKE_ID2('G', 'R'), /* Collection */
- ID_AR = MAKE_ID2('A', 'R'), /* bArmature */
- ID_AC = MAKE_ID2('A', 'C'), /* bAction */
- ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */
- ID_BR = MAKE_ID2('B', 'R'), /* Brush */
- ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */
- ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */
- ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */
- ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */
- ID_MSK = MAKE_ID2('M', 'S'), /* Mask */
- ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */
- ID_PAL = MAKE_ID2('P', 'L'), /* Palette */
- ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */
- ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
- ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */
- ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */
- ID_CV = MAKE_ID2('C', 'V'), /* Curves */
- ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
- ID_VO = MAKE_ID2('V', 'O'), /* Volume */
- ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */
+ ID_SCE = MAKE_ID2('S', 'C'), /* Scene */
+ ID_LI = MAKE_ID2('L', 'I'), /* Library */
+ ID_OB = MAKE_ID2('O', 'B'), /* Object */
+ ID_ME = MAKE_ID2('M', 'E'), /* Mesh */
+ ID_CU_LEGACY = MAKE_ID2('C', 'U'), /* Curve. ID_CV should be used in the future (see T95355). */
+ ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */
+ ID_MA = MAKE_ID2('M', 'A'), /* Material */
+ ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */
+ ID_IM = MAKE_ID2('I', 'M'), /* Image */
+ ID_LT = MAKE_ID2('L', 'T'), /* Lattice */
+ ID_LA = MAKE_ID2('L', 'A'), /* Light */
+ ID_CA = MAKE_ID2('C', 'A'), /* Camera */
+ ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */
+ ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */
+ ID_WO = MAKE_ID2('W', 'O'), /* World */
+ ID_SCR = MAKE_ID2('S', 'R'), /* Screen */
+ ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */
+ ID_TXT = MAKE_ID2('T', 'X'), /* Text */
+ ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */
+ ID_SO = MAKE_ID2('S', 'O'), /* Sound */
+ ID_GR = MAKE_ID2('G', 'R'), /* Collection */
+ ID_AR = MAKE_ID2('A', 'R'), /* bArmature */
+ ID_AC = MAKE_ID2('A', 'C'), /* bAction */
+ ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */
+ ID_BR = MAKE_ID2('B', 'R'), /* Brush */
+ ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */
+ ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */
+ ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */
+ ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */
+ ID_MSK = MAKE_ID2('M', 'S'), /* Mask */
+ ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */
+ ID_PAL = MAKE_ID2('P', 'L'), /* Palette */
+ ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */
+ ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
+ ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */
+ ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */
+ ID_CV = MAKE_ID2('C', 'V'), /* Curves */
+ ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
+ ID_VO = MAKE_ID2('V', 'O'), /* Volume */
+ ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */
} ID_Type;
/* Only used as 'placeholder' in .blend files for directly linked data-blocks. */
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 2a6b86711ab..fa0898e6ea5 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -104,12 +104,14 @@ typedef struct bAnimVizSettings {
short path_type;
/** Number of frames between points indicated on the paths. */
short path_step;
+ /** #eMotionPath_Ranges. */
+ short path_range;
/** #eMotionPaths_ViewFlag. */
short path_viewflag;
/** #eMotionPaths_BakeFlag. */
short path_bakeflag;
- char _pad[6];
+ char _pad[4];
/** Start and end frames of path-calculation range. */
int path_sf, path_ef;
@@ -131,6 +133,14 @@ typedef enum eMotionPaths_Types {
MOTIONPATH_TYPE_ACFRA = 1,
} eMotionPath_Types;
+/* bAnimVizSettings->path_range */
+typedef enum eMotionPath_Ranges {
+ /* Default is scene */
+ MOTIONPATH_RANGE_SCENE = 0,
+ MOTIONPATH_RANGE_KEYS_SELECTED = 1,
+ MOTIONPATH_RANGE_KEYS_ALL = 2,
+} eMotionPath_Ranges;
+
/* bAnimVizSettings->path_viewflag */
typedef enum eMotionPaths_ViewFlag {
/* show frames on path */
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
index 0667e508a82..bca177f6d7a 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -455,6 +455,12 @@ typedef enum eBrushUVSculptTool {
UV_SCULPT_TOOL_PINCH = 2,
} eBrushUVSculptTool;
+/* Brush.curves_sculpt_tool. */
+typedef enum eBrushCurvesSculptTool {
+ CURVES_SCULPT_TOOL_TEST1 = 0,
+ CURVES_SCULPT_TOOL_TEST2 = 1,
+} eBrushCurvesSculptTool;
+
/** When #BRUSH_ACCUMULATE is used */
#define SCULPT_TOOL_HAS_ACCUMULATE(t) \
ELEM(t, \
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 77c49393029..fe80220b1dd 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -256,7 +256,9 @@ typedef struct Brush {
char gpencil_sculpt_tool;
/** Active grease pencil weight tool. */
char gpencil_weight_tool;
- char _pad1[6];
+ /** Active curves sculpt tool. */
+ char curves_sculpt_tool;
+ char _pad1[5];
float autosmooth_factor;
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 14a06f197bf..66206bcfddd 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -306,14 +306,6 @@ enum {
CU_AUTOSPACE_EVALUATED = 2,
};
-#if 0 /* Moved to overlay options in 2.8 */
-/* Curve.drawflag */
-enum {
- CU_HIDE_HANDLES = 1 << 0,
- CU_HIDE_NORMALS = 1 << 1,
-};
-#endif
-
/* Curve.flag */
enum {
CU_3D = 1 << 0,
diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h
index 03a587c896b..98d2aa4b295 100644
--- a/source/blender/makesdna/DNA_curves_types.h
+++ b/source/blender/makesdna/DNA_curves_types.h
@@ -13,6 +13,33 @@
extern "C" {
#endif
+#ifdef __cplusplus
+namespace blender::bke {
+class CurvesGeometryRuntime;
+} // namespace blender::bke
+using CurvesGeometryRuntimeHandle = blender::bke::CurvesGeometryRuntime;
+#else
+typedef struct CurvesGeometryRuntimeHandle CurvesGeometryRuntimeHandle;
+#endif
+
+typedef enum CurveType {
+ CURVE_TYPE_CATMULL_ROM = 0,
+ CURVE_TYPE_POLY = 1,
+ CURVE_TYPE_BEZIER = 2,
+ CURVE_TYPE_NURBS = 3,
+} CurveType;
+
+typedef enum HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ BEZIER_HANDLE_FREE = 0,
+ /** The location is automatically calculated to be smooth. */
+ BEZIER_HANDLE_AUTO = 1,
+ /** The location is calculated to point to the next/previous control point. */
+ BEZIER_HANDLE_VECTOR = 2,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ BEZIER_HANDLE_ALIGN = 3,
+} HandleType;
+
/**
* A reusable data structure for geometry consisting of many curves. All control point data is
* stored contiguously for better efficiency. Data for each curve is stored as a slice of the
@@ -34,13 +61,19 @@ typedef struct CurvesGeometry {
float *radius;
/**
+ * The type of each curve. #CurveType.
+ * \note This data is owned by #curve_data.
+ */
+ int8_t *curve_type;
+
+ /**
* The start index of each curve in the point data. The size of each curve can be calculated by
* subtracting the offset from the next offset. That is valid even for the last curve because
* this array is allocated with a length one larger than the number of splines.
*
* \note This is *not* stored in #CustomData because its size is one larger than #curve_data.
*/
- int *offsets;
+ int *curve_offsets;
/**
* All attributes stored on control points (#ATTR_DOMAIN_POINT).
@@ -60,6 +93,11 @@ typedef struct CurvesGeometry {
* The number of curves in the data-block.
*/
int curve_size;
+
+ /**
+ * Runtime data for curves, stored as a pointer to allow defining this as a C++ class.
+ */
+ CurvesGeometryRuntimeHandle *runtime;
} CurvesGeometry;
typedef struct Curves {
@@ -77,6 +115,15 @@ typedef struct Curves {
short totcol;
short _pad2[3];
+ /**
+ * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes.
+ * When set, the curves will have attributes that indicate a position on this surface. This is
+ * used for deforming the curves when the surface is deformed dynamically.
+ *
+ * This is expected to be a mesh object.
+ */
+ struct Object *surface;
+
/* Draw Cache. */
void *batch_cache;
} Curves;
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 23406033126..2f9f63cb966 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -98,6 +98,10 @@ typedef enum CustomDataType {
CD_MTFACE = 5,
CD_MCOL = 6,
CD_ORIGINDEX = 7,
+ /**
+ * Used for derived face corner normals on mesh `ldata`, since currently they are not computed
+ * lazily. Derived vertex and polygon normals are stored in #Mesh_Runtime.
+ */
CD_NORMAL = 8,
CD_FACEMAP = 9, /* exclusive face group, each face can only be part of one */
CD_PROP_FLOAT = 10,
diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h
index 953a15f7502..11780d99af8 100644
--- a/source/blender/makesdna/DNA_fluid_types.h
+++ b/source/blender/makesdna/DNA_fluid_types.h
@@ -611,7 +611,7 @@ typedef struct FluidDomainSettings {
/* Fluid guiding options. */
float guide_alpha; /* Guiding weight scalar (determines strength). */
- int guide_beta; /* Guiding blur radius (affects size of vortices vortices). */
+ int guide_beta; /* Guiding blur radius (affects size of vortices). */
float guide_vel_factor; /* Multiply guiding velocity by this factor. */
int guide_res[3]; /* Res for velocity guide grids - independent from base res. */
short guide_source;
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 64099de5a5e..e30dd4e1c8f 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -649,9 +649,10 @@ typedef struct SimplifyGpencilModifierData {
int layer_pass;
/** Sample length */
float length;
+ /** Sample sharp threshold */
+ float sharp_threshold;
/** Merge distance */
float distance;
- char _pad[4];
} SimplifyGpencilModifierData;
typedef enum eSimplifyGpencil_Flag {
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index f40fb55e72e..70ee7c99d01 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -272,7 +272,7 @@ typedef struct Ipo {
#define SEQ_FAC_SPEED 2
#define SEQ_FAC_OPACITY 3
-/* ********* Curve (ID_CU) *********** */
+/* ********* Curve (ID_CU_LEGACY) *********** */
#define CU_TOTIPO 1
#define CU_TOTNAM 1
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index c566ad93335..d8a853681fd 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -116,27 +116,25 @@ typedef struct Mesh_Runtime {
*/
char wrapper_type_finalize;
+ int subsurf_resolution;
/**
* Settings for lazily evaluating the subdivision on the CPU if needed. These are
* set in the modifier when GPU subdivision can be performed.
*/
char subsurf_apply_render;
char subsurf_use_optimal_display;
- char _pad[2];
- int subsurf_resolution;
-
- void *_pad2;
/**
- * Used to mark when derived data needs to be recalculated for a certain layer.
- * Currently only normals.
+ * Caches for lazily computed vertex and polygon normals. These are stored here rather than in
+ * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a
+ * const mesh is not thread-safe.
*/
+ char vert_normals_dirty;
+ char poly_normals_dirty;
+ float (*vert_normals)[3];
+ float (*poly_normals)[3];
- int64_t cd_dirty_vert;
- int64_t cd_dirty_edge;
- int64_t cd_dirty_loop;
- int64_t cd_dirty_poly;
-
+ void *_pad2;
} Mesh_Runtime;
typedef struct Mesh {
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index db5a3d69e4d..807a615f7f9 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1604,6 +1604,10 @@ enum {
MOD_WVG_MIX_DIF = 6,
/** Average of both weights. */
MOD_WVG_MIX_AVG = 7,
+ /** Minimum of both weights. */
+ MOD_WVG_MIX_MIN = 8,
+ /** Maximum of both weights. */
+ MOD_WVG_MIX_MAX = 9,
};
/** #WeightVGMixModifierData.mix_set (what vertices to affect). */
@@ -1694,7 +1698,7 @@ enum {
MOD_WVG_PROXIMITY_GEOM_FACES = (1 << 2),
MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK = (1 << 3),
MOD_WVG_PROXIMITY_INVERT_FALLOFF = (1 << 4),
- MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 3),
+ MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 5),
};
/* Defines common to all WeightVG modifiers. */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 963a34aa645..18b79a6fc25 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1610,6 +1610,11 @@ typedef struct NodeGeometryDeleteGeometry {
int8_t mode;
} NodeGeometryDeleteGeometry;
+typedef struct NodeGeometryDuplicateElements {
+ /* AttributeDomain. */
+ int8_t domain;
+} NodeGeometryDuplicateElements;
+
typedef struct NodeGeometrySeparateGeometry {
/* AttributeDomain. */
int8_t domain;
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 96c60fcac97..9e0bf7dcc5a 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -471,7 +471,8 @@ typedef struct ObHook {
enum {
OB_EMPTY = 0,
OB_MESH = 1,
- OB_CURVE = 2,
+ /** Curve object is still used but replaced by "Curves" for the future (see T95355). */
+ OB_CURVES_LEGACY = 2,
OB_SURF = 3,
OB_FONT = 4,
OB_MBALL = 5,
@@ -515,17 +516,26 @@ enum {
OB_VOLUME))
#define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL))
#define OB_TYPE_SUPPORT_EDITMODE(_type) \
- (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE))
-#define OB_TYPE_SUPPORT_PARVERT(_type) (ELEM(_type, OB_MESH, OB_SURF, OB_CURVE, OB_LATTICE))
+ (ELEM(_type, \
+ OB_MESH, \
+ OB_FONT, \
+ OB_CURVES_LEGACY, \
+ OB_SURF, \
+ OB_MBALL, \
+ OB_LATTICE, \
+ OB_ARMATURE, \
+ OB_CURVES))
+#define OB_TYPE_SUPPORT_PARVERT(_type) \
+ (ELEM(_type, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_LATTICE))
/** Matches #OB_TYPE_SUPPORT_EDITMODE. */
-#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR))
+#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR))
/* is this ID type used as object data */
#define OB_DATA_SUPPORT_ID(_id_type) \
(ELEM(_id_type, \
ID_ME, \
- ID_CU, \
+ ID_CU_LEGACY, \
ID_MB, \
ID_LA, \
ID_SPK, \
@@ -540,7 +550,7 @@ enum {
#define OB_DATA_SUPPORT_ID_CASE \
ID_ME: \
- case ID_CU: \
+ case ID_CU_LEGACY: \
case ID_MB: \
case ID_LA: \
case ID_SPK: \
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 0d42abdb363..daecb3d7a31 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -141,7 +141,6 @@ typedef struct FFMpegCodecData {
int audio_bitrate;
int audio_mixrate;
int audio_channels;
- char _pad0[4];
float audio_volume;
int gop_size;
/** Only used if FFMPEG_USE_MAX_B_FRAMES flag is set. */
@@ -156,9 +155,7 @@ typedef struct FFMpegCodecData {
int rc_buffer_size;
int mux_packet_size;
int mux_rate;
- char _pad1[4];
-
- IDProperty *properties;
+ void *_pad1;
} FFMpegCodecData;
/* ************************************************************* */
@@ -995,6 +992,10 @@ typedef struct Sculpt {
struct Object *gravity_object;
} Sculpt;
+typedef struct CurvesSculpt {
+ Paint paint;
+} CurvesSculpt;
+
typedef struct UvSculpt {
Paint paint;
} UvSculpt;
@@ -1380,6 +1381,8 @@ typedef struct ToolSettings {
GpSculptPaint *gp_sculptpaint;
/** Gpencil weight paint. */
GpWeightPaint *gp_weightpaint;
+ /** Curves sculpt. */
+ CurvesSculpt *curves_sculpt;
/* Vertex group weight - used only for editmode, not weight
* paint */
@@ -1988,7 +1991,7 @@ extern const char *RE_engine_id_CYCLES;
((v3d == NULL) || (((1 << (base)->object->type) & (v3d)->object_type_exclude_select) == 0)) && \
(((base)->flag & BASE_SELECTABLE) != 0))
#define BASE_SELECTED(v3d, base) (BASE_VISIBLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0))
-#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && ((base)->object->id.lib == NULL))
+#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && !ID_IS_LINKED((base)->object))
#define BASE_SELECTED_EDITABLE(v3d, base) \
(BASE_EDITABLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0))
diff --git a/source/blender/makesdna/DNA_userdef_enums.h b/source/blender/makesdna/DNA_userdef_enums.h
index bb061e73c9c..e90aa0e0f07 100644
--- a/source/blender/makesdna/DNA_userdef_enums.h
+++ b/source/blender/makesdna/DNA_userdef_enums.h
@@ -10,7 +10,14 @@
extern "C" {
#endif
-/** #UserDef.dupflag */
+/**
+ * #UserDef.dupflag
+ *
+ * The flag tells #BKE_object_duplicate() whether to copy data linked to the object,
+ * or to reference the existing data.
+ * #U.dupflag should be used for default operations or you can construct a flag as Python does.
+ * If #eDupli_ID_Flags is 0 then no data will be copied (linked duplicate).
+ */
typedef enum eDupli_ID_Flags {
USER_DUP_MESH = (1 << 0),
USER_DUP_CURVE = (1 << 1),
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index e081be73a1c..80a107e4bae 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -1318,6 +1318,7 @@ typedef enum eNdof_Flag {
NDOF_PANY_INVERT_AXIS = (1 << 13),
NDOF_PANZ_INVERT_AXIS = (1 << 14),
NDOF_TURNTABLE = (1 << 15),
+ NDOF_CAMERA_PAN_ZOOM = (1 << 16),
} eNdof_Flag;
#define NDOF_PIXELS_PER_SECOND 600.0f
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 626c2b2a81f..dabef04583b 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -295,9 +295,6 @@ typedef struct wmWindow {
/** Storage for event system. */
struct wmEvent *eventstate;
- /** Internal for wm_operators.c. */
- struct wmGesture *tweak;
-
/* Input Method Editor data - complex character input (especially for Asian character input)
* Currently WIN32 and APPLE, runtime-only data. */
struct wmIMEData *ime_data;
@@ -364,7 +361,9 @@ typedef struct wmKeyMapItem {
/** Event code itself. */
short type;
/** KM_ANY, KM_PRESS, KM_NOTHING etc. */
- short val;
+ int8_t val;
+ /** Use when `val == WM_CLICK_DRAG`, */
+ int8_t direction;
/** `oskey` also known as apple, windows-key or super, value denotes order of pressed. */
short shift, ctrl, alt, oskey;
/** Raw-key modifier. */
@@ -422,7 +421,7 @@ enum {
enum {
KMI_TYPE_KEYBOARD = 0,
KMI_TYPE_MOUSE = 1,
- KMI_TYPE_TWEAK = 2,
+ /* 2 is deprecated, was tweak. */
KMI_TYPE_TEXTINPUT = 3,
KMI_TYPE_TIMER = 4,
KMI_TYPE_NDOF = 5,
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index bf77339a494..09eab0d7bf7 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -106,8 +106,23 @@ typedef enum eXrPoseFlag {
XR_POSE_AIM = (1 << 1),
} eXrPoseFlag;
+/**
+ * The following user and component path lengths are dependent on OpenXR's XR_MAX_PATH_LENGTH
+ * (256). A user path will be combined with a component path to identify an action binding, and
+ * that combined path should also have a max of XR_MAX_PATH_LENGTH (e.g. user_path =
+ * /user/hand/left, component_path = /input/trigger/value, full_path =
+ * /user/hand/left/input/trigger/value).
+ */
+#define XR_MAX_USER_PATH_LENGTH 64
+#define XR_MAX_COMPONENT_PATH_LENGTH 192
+
/* -------------------------------------------------------------------- */
+typedef struct XrComponentPath {
+ struct XrComponentPath *next, *prev;
+ char path[192]; /* XR_MAX_COMPONENT_PATH_LENGTH */
+} XrComponentPath;
+
typedef struct XrActionMapBinding {
struct XrActionMapBinding *next, *prev;
@@ -117,8 +132,7 @@ typedef struct XrActionMapBinding {
/** OpenXR interaction profile path. */
char profile[256];
/** OpenXR component paths. */
- char component_path0[192];
- char component_path1[192];
+ ListBase component_paths; /* XrComponentPath */
/** Input threshold/region. */
float float_threshold;
@@ -132,6 +146,11 @@ typedef struct XrActionMapBinding {
/* -------------------------------------------------------------------- */
+typedef struct XrUserPath {
+ struct XrUserPath *next, *prev;
+ char path[64]; /* XR_MAX_USER_PATH_LENGTH */
+} XrUserPath;
+
typedef struct XrActionMapItem {
struct XrActionMapItem *next, *prev;
@@ -142,8 +161,7 @@ typedef struct XrActionMapItem {
char _pad[7];
/** OpenXR user paths. */
- char user_path0[64];
- char user_path1[64];
+ ListBase user_paths; /* XrUserPath */
/** Operator to be called on XR events. */
char op[64]; /* OP_MAX_TYPENAME */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index eb25733a88a..bc4e7314512 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -997,7 +997,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char *
/**
* Same as RNA_property_editable(), except this checks individual items in an array.
*/
-bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index);
+bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index);
/**
* Without lib check, only checks the flag.
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index 4b68416f5d7..31db8e39e6f 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -87,10 +87,11 @@ DEF_ENUM(rna_enum_keying_flag_items_api)
DEF_ENUM(rna_enum_fmodifier_type_items)
DEF_ENUM(rna_enum_motionpath_bake_location_items)
+DEF_ENUM(rna_enum_motionpath_display_type_items)
+DEF_ENUM(rna_enum_motionpath_range_items)
-DEF_ENUM(rna_enum_event_value_all_items)
-DEF_ENUM(rna_enum_event_value_keymouse_items)
-DEF_ENUM(rna_enum_event_value_tweak_items)
+DEF_ENUM(rna_enum_event_value_items)
+DEF_ENUM(rna_enum_event_direction_items)
DEF_ENUM(rna_enum_event_type_items)
DEF_ENUM(rna_enum_event_type_mask_items)
@@ -107,6 +108,7 @@ DEF_ENUM(rna_enum_brush_gpencil_types_items)
DEF_ENUM(rna_enum_brush_gpencil_vertex_types_items)
DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items)
DEF_ENUM(rna_enum_brush_gpencil_weight_types_items)
+DEF_ENUM(rna_enum_brush_curves_sculpt_tool_items);
DEF_ENUM(rna_enum_brush_image_tool_items)
DEF_ENUM(rna_enum_axis_xy_items)
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 90e979e9fbe..94ffa330064 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -35,7 +35,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""},
{ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""},
{ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""},
- {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""},
+ {ID_CU_LEGACY, "CURVE", ICON_CURVE_DATA, "Curve", ""},
{ID_VF, "FONT", ICON_FONT_DATA, "Font", ""},
{ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""},
{ID_GR, "COLLECTION", ICON_OUTLINER_COLLECTION, "Collection", ""},
@@ -126,7 +126,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
{FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"},
{FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"},
{FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"},
- {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
+ {FILTER_ID_CU_LEGACY, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
{FILTER_ID_GD,
"filter_grease_pencil",
ICON_GREASEPENCIL,
@@ -348,7 +348,7 @@ short RNA_type_to_ID_code(const StructRNA *type)
return ID_CA;
}
if (base_type == &RNA_Curve) {
- return ID_CU;
+ return ID_CU_LEGACY;
}
if (base_type == &RNA_GreasePencil) {
return ID_GD;
@@ -472,7 +472,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
return &RNA_Camera;
case ID_CF:
return &RNA_CacheFile;
- case ID_CU:
+ case ID_CU_LEGACY:
return &RNA_Curve;
case ID_GD:
return &RNA_GreasePencil;
@@ -696,7 +696,7 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
}
static ID *rna_ID_override_hierarchy_create(
- ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference)
+ ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_instance_hint)
{
if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
return NULL;
@@ -706,7 +706,7 @@ static ID *rna_ID_override_hierarchy_create(
ID *id_root_override = NULL;
BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id, id_reference, &id_root_override);
+ bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override);
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
@@ -2057,7 +2057,7 @@ static void rna_def_ID(BlenderRNA *brna)
"reference",
"ID",
"",
- "Another ID (usually an Object or Collection) used to decide where to "
+ "Another ID (usually an Object or Collection) used as a hint to decide where to "
"instantiate the new overrides");
func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create");
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 6c3b46c4408..7ccb8181b51 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1898,59 +1898,63 @@ int RNA_property_ui_icon(const PropertyRNA *prop)
return rna_ensure_property((PropertyRNA *)prop)->icon;
}
-bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig)
+static bool rna_property_editable_do(PointerRNA *ptr,
+ PropertyRNA *prop_orig,
+ const int index,
+ const char **r_info)
{
ID *id = ptr->owner_id;
- int flag;
- const char *dummy_info;
PropertyRNA *prop = rna_ensure_property(prop_orig);
- flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
-
- return (
- (flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 &&
- (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
- (!ID_IS_OVERRIDE_LIBRARY(id) || RNA_property_overridable_get(ptr, prop_orig)))));
-}
-
-bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
-{
- ID *id = ptr->owner_id;
- int flag;
-
- PropertyRNA *prop_type = rna_ensure_property(prop);
- *r_info = "";
- /* get flag */
- if (prop_type->editable) {
- flag = prop_type->editable(ptr, r_info);
+ const char *info = "";
+ const int flag = (prop->itemeditable != NULL && index >= 0) ?
+ prop->itemeditable(ptr, index) :
+ (prop->editable != NULL ? prop->editable(ptr, &info) : prop->flag);
+ if (r_info != NULL) {
+ *r_info = info;
}
- else {
- flag = prop_type->flag;
- if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) {
+
+ /* Early return if the property itself is not editable. */
+ if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER) != 0) {
+ if (r_info != NULL && (*r_info)[0] == '\0') {
*r_info = N_("This property is for internal use only and can't be edited");
}
+ return false;
}
- /* property from linked data-block */
- if (id) {
- if (ID_IS_LINKED(id) && (prop_type->flag & PROP_LIB_EXCEPTION) == 0) {
- if (!(*r_info)[0]) {
- *r_info = N_("Can't edit this property from a linked data-block");
- }
- return false;
+ /* If there is no owning ID, the property is editable at this point. */
+ if (id == NULL) {
+ return true;
+ }
+
+ /* Handle linked or liboverride ID cases. */
+ const bool is_linked_prop_exception = (prop->flag & PROP_LIB_EXCEPTION) != 0;
+ if (ID_IS_LINKED(id) && !is_linked_prop_exception) {
+ if (r_info != NULL && (*r_info)[0] == '\0') {
+ *r_info = N_("Can't edit this property from a linked data-block");
}
- if (ID_IS_OVERRIDE_LIBRARY(id)) {
- if (!RNA_property_overridable_get(ptr, prop)) {
- if (!(*r_info)[0]) {
- *r_info = N_("Can't edit this property from an override data-block");
- }
- return false;
- }
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id) && !RNA_property_overridable_get(ptr, prop_orig)) {
+ if (r_info != NULL && (*r_info)[0] == '\0') {
+ *r_info = N_("Can't edit this property from an override data-block");
}
+ return false;
}
- return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0);
+ /* At this point, property is owned by a local ID and therefore fully editable. */
+ return true;
+}
+
+bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop)
+{
+ return rna_property_editable_do(ptr, prop, -1, NULL);
+}
+
+bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
+{
+ return rna_property_editable_do(ptr, prop, -1, r_info);
}
bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
@@ -1963,29 +1967,11 @@ bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
return (flag & PROP_EDITABLE) != 0;
}
-bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index)
+bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index)
{
- ID *id;
- int flag;
-
BLI_assert(index >= 0);
- prop = rna_ensure_property(prop);
-
- flag = prop->flag;
-
- if (prop->editable) {
- const char *dummy_info;
- flag &= prop->editable(ptr, &dummy_info);
- }
-
- if (prop->itemeditable) {
- flag &= prop->itemeditable(ptr, index);
- }
-
- id = ptr->owner_id;
-
- return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION));
+ return rna_property_editable_do(ptr, prop, index, NULL);
}
bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop)
@@ -4918,7 +4904,7 @@ static char *rna_path_token_in_brackets(const char **path,
}
/**
- * \return true when when the key in the path is correctly parsed and found in the collection
+ * \return true when the key in the path is correctly parsed and found in the collection
* or when the path is empty.
*/
static bool rna_path_parse_collection_key(const char **path,
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 1aa51c93ee8..fd65cc464f2 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -218,7 +218,7 @@ bool RNA_property_copy(
/* IDprops: destination may not exist, if source does and is set, try to create it. */
/* NOTE: this is sort of quick hack/bandage to fix the issue,
- * we need to rethink how IDProps are handled in 'diff' RNA code completely, imho... */
+ * we need to rethink how IDProps are handled in 'diff' RNA code completely, IMHO. */
if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) {
BLI_assert(prop_src->magic != RNA_MAGIC);
IDProperty *idp_dst = RNA_struct_idprops(ptr, true);
diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c
index cb993931296..0453b327b45 100644
--- a/source/blender/makesrna/intern/rna_animviz.c
+++ b/source/blender/makesrna/intern/rna_animviz.c
@@ -36,6 +36,27 @@ const EnumPropertyItem rna_enum_motionpath_bake_location_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_motionpath_display_type_items[] = {
+ {MOTIONPATH_TYPE_ACFRA,
+ "CURRENT_FRAME",
+ 0,
+ "Around Frame",
+ "Display Paths of poses within a fixed number of frames around the current frame"},
+ {MOTIONPATH_TYPE_RANGE,
+ "RANGE",
+ 0,
+ "In Range",
+ "Display Paths of poses within specified range"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+const EnumPropertyItem rna_enum_motionpath_range_items[] = {
+ {MOTIONPATH_RANGE_KEYS_ALL, "KEYS_ALL", 0, "All keys range", ""},
+ {MOTIONPATH_RANGE_KEYS_SELECTED, "KEYS_SELECTED", 0, "Selected keys range", ""},
+ {MOTIONPATH_RANGE_SCENE, "SCENE", 0, "Scene frame range", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
static PointerRNA rna_AnimViz_motion_paths_get(PointerRNA *ptr)
@@ -173,20 +194,6 @@ static void rna_def_animviz_paths(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- static const EnumPropertyItem prop_type_items[] = {
- {MOTIONPATH_TYPE_ACFRA,
- "CURRENT_FRAME",
- 0,
- "Around Frame",
- "Display Paths of poses within a fixed number of frames around the current frame"},
- {MOTIONPATH_TYPE_RANGE,
- "RANGE",
- 0,
- "In Range",
- "Display Paths of poses within specified range"},
- {0, NULL, 0, NULL, NULL},
- };
-
srna = RNA_def_struct(brna, "AnimVizMotionPaths", NULL);
RNA_def_struct_sdna(srna, "bAnimVizSettings");
RNA_def_struct_nested(brna, srna, "AnimViz");
@@ -198,10 +205,16 @@ static void rna_def_animviz_paths(BlenderRNA *brna)
/* Enums */
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "path_type");
- RNA_def_property_enum_items(prop, prop_type_items);
+ RNA_def_property_enum_items(prop, rna_enum_motionpath_display_type_items);
RNA_def_property_ui_text(prop, "Paths Type", "Type of range to show for Motion Paths");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL);
+ prop = RNA_def_property(srna, "range", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "path_range");
+ RNA_def_property_enum_items(prop, rna_enum_motionpath_range_items);
+ RNA_def_property_ui_text(prop, "Paths Range", "Type of range to calculate for Motion Paths");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL);
+
prop = RNA_def_property(srna, "bake_location", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "path_bakeflag");
RNA_def_property_enum_items(prop, rna_enum_motionpath_bake_location_items);
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index f3c7d2747ac..667114a3598 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -243,6 +243,12 @@ const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = {
+ {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""},
+ {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = {
{GP_BRUSH_ERASER_SOFT,
@@ -2312,6 +2318,11 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Grease Pencil Weight Paint Tool", "");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ prop = RNA_def_property(srna, "curves_sculpt_tool", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_brush_curves_sculpt_tool_items);
+ RNA_def_property_ui_text(prop, "Curves Sculpt Tool", "");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
/** End per mode tool properties. */
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index edcf121a8c4..114fe30acf7 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -2969,7 +2969,7 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna)
PropertyRNA *prop;
static const EnumPropertyItem splineik_xz_scale_mode[] = {
- {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes (Default)"},
+ {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes"},
{CONSTRAINT_SPLINEIK_XZS_ORIGINAL,
"BONE_ORIGINAL",
0,
diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c
index bc4bd9ad3d6..e723be2ab71 100644
--- a/source/blender/makesrna/intern/rna_context.c
+++ b/source/blender/makesrna/intern/rna_context.c
@@ -21,6 +21,7 @@
const EnumPropertyItem rna_enum_context_mode_items[] = {
{CTX_MODE_EDIT_MESH, "EDIT_MESH", 0, "Mesh Edit", ""},
{CTX_MODE_EDIT_CURVE, "EDIT_CURVE", 0, "Curve Edit", ""},
+ {CTX_MODE_EDIT_CURVES, "EDIT_CURVES", 0, "Curves Edit", ""},
{CTX_MODE_EDIT_SURFACE, "EDIT_SURFACE", 0, "Surface Edit", ""},
{CTX_MODE_EDIT_TEXT, "EDIT_TEXT", 0, "Text Edit", ""},
/* PARSKEL reuse will give issues */
diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c
index b37a6e5fd0e..891de95a2a2 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -441,7 +441,7 @@ static void rna_Curve_bevelObject_set(PointerRNA *ptr,
if (ob) {
/* If bevel object has got the save curve, as object, for which it's set as bevobj,
* there could be infinity loop in #DispList calculation. */
- if (ob->type == OB_CURVE && ob->data != cu) {
+ if (ob->type == OB_CURVES_LEGACY && ob->data != cu) {
cu->bevobj = ob;
id_lib_extern((ID *)ob);
}
@@ -486,7 +486,7 @@ static bool rna_Curve_otherObject_poll(PointerRNA *ptr, PointerRNA value)
Object *ob = (Object *)value.data;
if (ob) {
- if (ob->type == OB_CURVE && ob->data != cu) {
+ if (ob->type == OB_CURVES_LEGACY && ob->data != cu) {
return 1;
}
}
@@ -516,7 +516,7 @@ static void rna_Curve_taperObject_set(PointerRNA *ptr,
if (ob) {
/* If taper object has got the save curve, as object, for which it's set as bevobj,
* there could be infinity loop in #DispList calculation. */
- if (ob->type == OB_CURVE && ob->data != cu) {
+ if (ob->type == OB_CURVES_LEGACY && ob->data != cu) {
cu->taperobj = ob;
id_lib_extern((ID *)ob);
}
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
index 8e7fb415c7d..7a1a368551f 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -16,6 +16,8 @@
#include "BLI_math_base.h"
#include "BLI_string.h"
+#include "WM_types.h"
+
#ifdef RNA_RUNTIME
# include "BLI_math_vector.h"
@@ -43,7 +45,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter,
{
const Curves *curves = rna_curves(ptr);
rna_iterator_array_begin(iter,
- (void *)curves->geometry.offsets,
+ (void *)curves->geometry.curve_offsets,
sizeof(int),
curves->geometry.curve_size + 1,
false,
@@ -95,7 +97,7 @@ static char *rna_CurvePoint_path(PointerRNA *ptr)
static int rna_CurveSlice_index_get(PointerRNA *ptr)
{
Curves *curves = rna_curves(ptr);
- return (int)((int *)ptr->data - curves->geometry.offsets);
+ return (int)((int *)ptr->data - curves->geometry.curve_offsets);
}
static char *rna_CurveSlice_path(PointerRNA *ptr)
@@ -220,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna)
/* Point and Curve RNA API helpers. */
prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size");
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size");
RNA_def_property_struct_type(prop, "CurveSlice");
RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block");
@@ -243,7 +245,7 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_define_verify_sdna(1);
prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", NULL);
RNA_def_property_struct_type(prop, "IntAttributeValue");
RNA_def_property_collection_funcs(prop,
"rna_Curves_curve_offset_data_begin",
@@ -265,6 +267,13 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_def_property_collection_funcs(
prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
+ prop = RNA_def_property(srna, "surface", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Mesh_object_poll");
+ RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
/* attributes */
rna_def_attributes_common(srna);
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 850accd0b24..5127b418b7f 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -4391,24 +4391,21 @@ void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropert
if (tot == 0) {
*items = MEM_callocN(sizeof(EnumPropertyItem[8]), __func__);
+ /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */
+#ifdef DEBUG
+ memset(*items, 0xff, sizeof(EnumPropertyItem[8]));
+#endif
}
else if (tot >= 8 && (tot & (tot - 1)) == 0) {
/* power of two > 8 */
*items = MEM_recallocN_id(*items, sizeof(EnumPropertyItem) * tot * 2, __func__);
+#ifdef DEBUG
+ memset((*items) + tot, 0xff, sizeof(EnumPropertyItem) * tot);
+#endif
}
(*items)[tot] = *item;
*totitem = tot + 1;
-
- /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */
-#ifdef DEBUG
- static const EnumPropertyItem item_error = {
- -1, POINTER_FROM_INT(-1), -1, POINTER_FROM_INT(-1), POINTER_FROM_INT(-1)};
- if (item != &item_error) {
- RNA_enum_item_add(items, totitem, &item_error);
- *totitem -= 1;
- }
-#endif
}
void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem)
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index f24803af654..8f418fcc7c7 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -1880,7 +1880,7 @@ static void rna_def_drivervar(BlenderRNA *brna)
"SINGLE_PROP",
ICON_RNA,
"Single Property",
- "Use the value from some RNA property (Default)"},
+ "Use the value from some RNA property"},
{DVAR_TYPE_TRANSFORM_CHAN,
"TRANSFORMS",
ICON_DRIVER_TRANSFORM,
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 6a0e5156234..a619d179a33 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -1223,6 +1223,14 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Length", "Length of each segment");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "sharp_threshold");
+ RNA_def_property_range(prop, 0, M_PI);
+ RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1);
+ RNA_def_property_ui_text(
+ prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
/* Merge */
prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "distance");
@@ -2482,7 +2490,8 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_gphook_falloff_items); /* share the enum */
RNA_def_property_ui_text(prop, "Falloff Type", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE);
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index e40fafd2069..56e23278176 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -103,6 +103,7 @@ static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), Pointe
{
Image *ima = (Image *)ptr->owner_id;
BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_FREE);
+ BKE_image_partial_update_mark_full_update(ima);
}
static void rna_Image_colormanage_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
@@ -141,6 +142,7 @@ static void rna_Image_views_format_update(Main *bmain, Scene *scene, PointerRNA
}
BKE_image_release_ibuf(ima, ibuf, lock);
+ BKE_image_partial_update_mark_full_update(ima);
}
static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr)
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 2c5f264f7e9..44bf51d9770 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -649,7 +649,7 @@ const char *rna_translate_ui_text(const char *text,
struct PropertyRNA *prop,
bool translate);
-/* Internal functions that cycles uses so we need to declare (tsk tsk) */
+/* Internal functions that cycles uses so we need to declare (tsk!). */
void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
#ifdef RNA_RUNTIME
diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c
index 51dcfd13657..7687dcbb11f 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -47,7 +47,7 @@
static Key *rna_ShapeKey_find_key(ID *id)
{
switch (GS(id->name)) {
- case ID_CU:
+ case ID_CU_LEGACY:
return ((Curve *)id)->key;
case ID_KE:
return (Key *)id;
@@ -556,7 +556,7 @@ static void rna_ShapeKey_data_begin(CollectionPropertyIterator *iter, PointerRNA
KeyBlock *kb = (KeyBlock *)ptr->data;
int tot = kb->totelem, size = key->elemsize;
- if (GS(key->from->name) == ID_CU && tot > 0) {
+ if (GS(key->from->name) == ID_CU_LEGACY && tot > 0) {
Curve *cu = (Curve *)key->from;
StructRNA *type = NULL;
NurbInfo info = {0};
@@ -593,7 +593,7 @@ static int rna_ShapeKey_data_length(PointerRNA *ptr)
KeyBlock *kb = (KeyBlock *)ptr->data;
int tot = kb->totelem;
- if (GS(key->from->name) == ID_CU) {
+ if (GS(key->from->name) == ID_CU_LEGACY) {
tot = rna_ShapeKey_curve_find_index(key, tot);
}
@@ -613,7 +613,7 @@ static PointerRNA rna_ShapeKey_data_get(CollectionPropertyIterator *iter)
return rna_pointer_inherit_refine(&iter->parent, point->type, point->data);
}
- if (GS(key->from->name) == ID_CU) {
+ if (GS(key->from->name) == ID_CU_LEGACY) {
Curve *cu = (Curve *)key->from;
type = rna_ShapeKey_curve_point_type(cu->nurb.first);
@@ -635,7 +635,7 @@ int rna_ShapeKey_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
return false;
}
- if (GS(key->from->name) == ID_CU) {
+ if (GS(key->from->name) == ID_CU_LEGACY) {
NurbInfo info;
rna_ShapeKey_NurbInfo_find_index(key, index, false, &info);
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index e9d39d708a9..b239260c8eb 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -322,7 +322,7 @@ static Mesh *rna_Main_meshes_new_from_object(Main *bmain,
{
switch (object->type) {
case OB_FONT:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_MBALL:
case OB_MESH:
@@ -822,7 +822,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(screens, screens, ID_SCR)
RNA_MAIN_ID_TAG_FUNCS_DEF(window_managers, wm, ID_WM)
RNA_MAIN_ID_TAG_FUNCS_DEF(images, images, ID_IM)
RNA_MAIN_ID_TAG_FUNCS_DEF(lattices, lattices, ID_LT)
-RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU)
+RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU_LEGACY)
RNA_MAIN_ID_TAG_FUNCS_DEF(metaballs, metaballs, ID_MB)
RNA_MAIN_ID_TAG_FUNCS_DEF(fonts, fonts, ID_VF)
RNA_MAIN_ID_TAG_FUNCS_DEF(textures, textures, ID_TE)
diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c
index e8b8c6e3274..2247a16a7a0 100644
--- a/source/blender/makesrna/intern/rna_mask.c
+++ b/source/blender/makesrna/intern/rna_mask.c
@@ -1025,7 +1025,8 @@ static void rna_def_mask_layer(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "falloff");
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL);
/* filling options */
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 6b3ae373295..87459587a9e 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -456,6 +456,7 @@ static void rna_MeshPolygon_flip(ID *id, MPoly *mp)
BKE_mesh_polygon_flip(mp, me->mloop, &me->ldata);
BKE_mesh_tessface_clear(me);
BKE_mesh_runtime_clear_geometry(me);
+ BKE_mesh_normals_tag_dirty(me);
}
static void rna_MeshLoopTriangle_verts_get(PointerRNA *ptr, int *values)
diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c
index ebacced8b3f..8447074a3ef 100644
--- a/source/blender/makesrna/intern/rna_mesh_api.c
+++ b/source/blender/makesrna/intern/rna_mesh_api.c
@@ -167,7 +167,7 @@ static void rna_Mesh_flip_normals(Mesh *mesh)
{
BKE_mesh_polygons_flip(mesh->mpoly, mesh->mloop, &mesh->ldata, mesh->totpoly);
BKE_mesh_tessface_clear(mesh);
- BKE_mesh_calc_normals(mesh);
+ BKE_mesh_normals_tag_dirty(mesh);
BKE_mesh_runtime_clear_geometry(mesh);
DEG_id_tag_update(&mesh->id, 0);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 45d22981205..b12b33c67af 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -869,10 +869,10 @@ static void modifier_object_set(Object *self, Object **ob_p, int type, PointerRN
RNA_MOD_OBJECT_SET(Armature, object, OB_ARMATURE);
RNA_MOD_OBJECT_SET(Array, start_cap, OB_MESH);
RNA_MOD_OBJECT_SET(Array, end_cap, OB_MESH);
-RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVE);
+RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVES_LEGACY);
RNA_MOD_OBJECT_SET(Boolean, object, OB_MESH);
RNA_MOD_OBJECT_SET(Cast, object, OB_EMPTY);
-RNA_MOD_OBJECT_SET(Curve, object, OB_CURVE);
+RNA_MOD_OBJECT_SET(Curve, object, OB_CURVES_LEGACY);
RNA_MOD_OBJECT_SET(DataTransfer, ob_source, OB_MESH);
RNA_MOD_OBJECT_SET(Lattice, object, OB_LATTICE);
RNA_MOD_OBJECT_SET(Mask, ob_arm, OB_ARMATURE);
@@ -1849,7 +1849,8 @@ static void rna_def_modifier_warp(BlenderRNA *brna)
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_warp_falloff_items);
RNA_def_property_ui_text(prop, "Falloff Type", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE);
@@ -2567,7 +2568,8 @@ static void rna_def_modifier_hook(BlenderRNA *brna)
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_warp_falloff_items); /* share the enum */
RNA_def_property_ui_text(prop, "Falloff Type", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE);
@@ -4947,6 +4949,7 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna)
static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna),
StructRNA *srna,
const char *mask_flags,
+ const int invert_vgroup_mask_flag,
const char *mask_vgroup_setter,
const char *mask_uvlayer_setter)
{
@@ -4992,7 +4995,7 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna),
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_mask_vertex_group", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, mask_flags, MOD_WVG_EDIT_INVERT_VGROUP_MASK);
+ RNA_def_property_boolean_sdna(prop, NULL, mask_flags, invert_vgroup_mask_flag);
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group mask influence");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -5076,7 +5079,8 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, weightvg_edit_falloff_type_items);
RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE);
@@ -5148,6 +5152,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
rna_def_modifier_weightvg_mask(brna,
srna,
"edit_flags",
+ MOD_WVG_EDIT_INVERT_VGROUP_MASK,
"rna_WeightVGEditModifier_mask_defgrp_name_set",
"rna_WeightVGEditModifier_mask_tex_uvlayer_name_set");
}
@@ -5166,6 +5171,8 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna)
"Difference",
"Difference between VGroup A's and VGroup B's weights"},
{MOD_WVG_MIX_AVG, "AVG", 0, "Average", "Average value of VGroup A's and VGroup B's weights"},
+ {MOD_WVG_MIX_MIN, "MIN", 0, "Minimum", "Minimum of VGroup A's and VGroup B's weights"},
+ {MOD_WVG_MIX_MAX, "MAX", 0, "Maximum", "Maximum of VGroup A's and VGroup B's weights"},
{0, NULL, 0, NULL, NULL},
};
@@ -5263,6 +5270,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna)
rna_def_modifier_weightvg_mask(brna,
srna,
"flag",
+ MOD_WVG_MIX_INVERT_VGROUP_MASK,
"rna_WeightVGMixModifier_mask_defgrp_name_set",
"rna_WeightVGMixModifier_mask_tex_uvlayer_name_set");
}
@@ -5364,7 +5372,8 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna)
prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, weightvg_proximity_falloff_type_items);
RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE);
@@ -5392,6 +5401,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna)
rna_def_modifier_weightvg_mask(brna,
srna,
"proximity_flags",
+ MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK,
"rna_WeightVGProximityModifier_mask_defgrp_name_set",
"rna_WeightVGProximityModifier_mask_tex_uvlayer_name_set");
}
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 3ae9ab3cec2..387166e77b4 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -7196,7 +7196,8 @@ static void def_cmp_dilate_erode(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "falloff");
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@@ -8974,7 +8975,8 @@ static void def_cmp_keying(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "feather_falloff");
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
RNA_def_property_ui_text(prop, "Feather Falloff", "Falloff type the feather");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
+ RNA_def_property_translation_context(prop,
+ BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "feather_distance", PROP_INT, PROP_NONE);
@@ -11285,6 +11287,27 @@ static void def_geo_delete_geometry(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_duplicate_elements(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem domain_items[] = {
+ {ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""},
+ {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", ""},
+ {ATTR_DOMAIN_FACE, "FACE", 0, "Face", ""},
+ {ATTR_DOMAIN_CURVE, "SPLINE", 0, "Spline", ""},
+ {ATTR_DOMAIN_INSTANCE, "INSTANCE", 0, "Instance", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+ RNA_def_struct_sdna_from(srna, "NodeGeometryDuplicateElements", "storage");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, domain_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
+ RNA_def_property_ui_text(prop, "Domain", "Which domain to duplicate");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_string_to_curves(StructRNA *srna)
{
static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = {
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 9c4ebf79a08..c598e63a32a 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -241,7 +241,7 @@ const EnumPropertyItem rna_enum_lightprobes_type_items[] = {
/* used for 2 enums */
#define OBTYPE_CU_CURVE \
{ \
- OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \
+ OB_CURVES_LEGACY, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \
}
#define OBTYPE_CU_SURF \
{ \
@@ -479,7 +479,7 @@ static void rna_Object_active_shape_update(Main *bmain, Scene *UNUSED(scene), Po
BKE_editmesh_looptri_and_normals_calc(em);
break;
}
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
ED_curve_editnurb_load(bmain, ob);
ED_curve_editnurb_make(ob);
@@ -571,7 +571,7 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, struct Report
ob->data = id;
BKE_object_materials_test(G_MAIN, ob, id);
- if (GS(id->name) == ID_CU) {
+ if (GS(id->name) == ID_CU_LEGACY) {
BKE_curve_type_test(ob);
}
else if (ob->type == OB_ARMATURE) {
@@ -590,7 +590,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
return &RNA_Image;
case OB_MESH:
return &RNA_Mesh;
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
return &RNA_Curve;
case OB_SURF:
return &RNA_Curve;
@@ -2182,7 +2182,7 @@ bool rna_Lattice_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
bool rna_Curve_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
{
- return ((Object *)value.owner_id)->type == OB_CURVE;
+ return ((Object *)value.owner_id)->type == OB_CURVES_LEGACY;
}
bool rna_Armature_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 9676be69d05..ece1c5e5815 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -407,7 +407,7 @@ static Mesh *rna_Object_to_mesh(Object *object,
* rna_Main_meshes_new_from_object. */
switch (object->type) {
case OB_FONT:
- case OB_CURVE:
+ case OB_CURVES_LEGACY:
case OB_SURF:
case OB_MBALL:
case OB_MESH:
@@ -430,7 +430,7 @@ static Curve *rna_Object_to_curve(Object *object,
Depsgraph *depsgraph,
bool apply_modifiers)
{
- if (!ELEM(object->type, OB_FONT, OB_CURVE)) {
+ if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) {
BKE_report(reports, RPT_ERROR, "Object is not a curve or a text");
return NULL;
}
@@ -785,7 +785,7 @@ bool rna_Object_generate_gpencil_strokes(Object *ob,
float scale_thickness,
float sample)
{
- if (ob->type != OB_CURVE) {
+ if (ob->type != OB_CURVES_LEGACY) {
BKE_reportf(reports,
RPT_ERROR,
"Object '%s' is not valid for this operation! Only curves are supported",
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 9ca78033199..8579f188428 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -691,7 +691,7 @@ static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, Point
rna_FieldSettings_shape_update(bmain, scene, ptr);
- if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE) {
+ if (ob->type == OB_CURVES_LEGACY && ob->pd->forcefield == PFIELD_GUIDE) {
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
}
else {
@@ -899,7 +899,7 @@ static const EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C),
ob = (Object *)ptr->owner_id;
- if (ob->type == OB_CURVE) {
+ if (ob->type == OB_CURVES_LEGACY) {
if (ob->pd->forcefield == PFIELD_VORTEX) {
return curve_vortex_shape_items;
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 178029ef340..29b06060256 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1466,18 +1466,6 @@ static void rna_FFmpegSettings_lossless_output_set(PointerRNA *ptr, bool value)
else {
rd->ffcodecdata.flags &= ~FFMPEG_LOSSLESS_OUTPUT;
}
-
- BKE_ffmpeg_codec_settings_verify(rd);
-}
-
-static void rna_FFmpegSettings_codec_settings_update(Main *UNUSED(bmain),
- Scene *UNUSED(scene_unused),
- PointerRNA *ptr)
-{
- Scene *scene = (Scene *)ptr->owner_id;
- RenderData *rd = &scene->r;
-
- BKE_ffmpeg_codec_settings_verify(rd);
}
# endif
@@ -1659,7 +1647,7 @@ static void rna_Scene_mesh_quality_update(Main *bmain, Scene *UNUSED(scene), Poi
Scene *scene = (Scene *)ptr->owner_id;
FOREACH_SCENE_OBJECT_BEGIN (scene, ob) {
- if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_VOLUME, OB_MBALL)) {
+ if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_VOLUME, OB_MBALL)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
}
@@ -2910,6 +2898,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "Sculpt");
RNA_def_property_ui_text(prop, "Sculpt", "");
+ prop = RNA_def_property(srna, "curves_sculpt", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurvesSculpt");
+ RNA_def_property_ui_text(prop, "Curves Sculpt", "");
+
prop = RNA_def_property(srna, "use_auto_normalize", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1);
@@ -3072,7 +3064,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Proportional Editing Falloff", "Falloff type for proportional editing mode");
/* Abusing id_curve :/ */
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE);
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "proportional_size", PROP_FLOAT, PROP_DISTANCE);
@@ -5727,8 +5719,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
RNA_def_property_enum_items(prop, ffmpeg_format_items);
RNA_def_property_enum_default(prop, FFMPEG_MKV);
RNA_def_property_ui_text(prop, "Container", "Output file container");
- RNA_def_property_update(
- prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update");
prop = RNA_def_property(srna, "codec", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "codec");
@@ -5736,8 +5726,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
RNA_def_property_enum_items(prop, ffmpeg_codec_items);
RNA_def_property_enum_default(prop, AV_CODEC_ID_H264);
RNA_def_property_ui_text(prop, "Video Codec", "FFmpeg codec to use for video output");
- RNA_def_property_update(
- prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update");
prop = RNA_def_property(srna, "video_bitrate", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "video_bitrate");
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 1e7c67ef95e..473711fb74b 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -352,6 +352,12 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value)
}
mode = OB_MODE_WEIGHT_GPENCIL;
}
+ else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) {
+ if (slot_index != brush->curves_sculpt_tool) {
+ return false;
+ }
+ mode = OB_MODE_SCULPT_CURVES;
+ }
return brush->ob_mode & mode;
}
@@ -417,6 +423,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.uv_sculpt");
}
+static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.curves_sculpt");
+}
+
static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("tool_settings.gpencil_paint");
@@ -1492,6 +1503,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
}
+static void rna_def_curves_sculpt(BlenderRNA *brna)
+{
+ StructRNA *srna;
+
+ srna = RNA_def_struct(brna, "CurvesSculpt", "Paint");
+ RNA_def_struct_path_func(srna, "rna_CurvesSculpt_path");
+ RNA_def_struct_ui_text(srna, "Curves Sculpt Paint", "");
+}
+
void RNA_def_sculpt_paint(BlenderRNA *brna)
{
/* *** Non-Animated *** */
@@ -1510,6 +1530,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
rna_def_particle_edit(brna);
rna_def_gpencil_guides(brna);
rna_def_gpencil_sculpt(brna);
+ rna_def_curves_sculpt(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index c51f0c00498..2344aa42838 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -3263,8 +3263,8 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[
ICON_OUTLINER_COLLECTION,
"Objects & Collections",
"Show objects and collections"},
- {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV |
- FILTER_ID_PT | FILTER_ID_VO,
+ {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME |
+ FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO,
"category_geometry",
ICON_NODETREE,
"Geometry",
@@ -4959,7 +4959,9 @@ static void rna_def_space_view3d(BlenderRNA *brna)
const char *identifier[2];
} info[] = {
{"Mesh", (1 << OB_MESH), {"show_object_viewport_mesh", "show_object_select_mesh"}},
- {"Curve", (1 << OB_CURVE), {"show_object_viewport_curve", "show_object_select_curve"}},
+ {"Curve",
+ (1 << OB_CURVES_LEGACY),
+ {"show_object_viewport_curve", "show_object_select_curve"}},
{"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}},
{"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}},
{"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}},
@@ -5467,7 +5469,7 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_cursor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR);
- RNA_def_property_ui_text(prop, "2D cursor", "");
+ RNA_def_property_ui_text(prop, "2D Cursor", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
}
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index e32bedf3ed0..2a759dde39a 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6040,6 +6040,13 @@ static void rna_def_userdef_input(BlenderRNA *brna)
"Helicopter Mode",
"Device up/down directly controls the Z position of the 3D viewport");
+ prop = RNA_def_property(srna, "ndof_lock_camera_pan_zoom", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_CAMERA_PAN_ZOOM);
+ RNA_def_property_ui_text(
+ prop,
+ "Lock Camera Pan/Zoom",
+ "Pan/zoom the camera view instead of leaving the camera view when orbiting");
+
/* let Python know whether NDOF is enabled */
prop = RNA_def_boolean(srna, "use_ndof", true, "", "");
# else
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index ca3b09c61c5..5b323629a80 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -26,7 +26,6 @@ const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = {
{VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"},
{VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"},
{VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"},
- {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"},
{VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"},
{VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"},
{VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"},
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index b0021bd0f3d..8835591f303 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -28,13 +28,6 @@
#ifdef RNA_RUNTIME
-static const EnumPropertyItem event_tweak_type_items[] = {
- {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Left", ""},
- {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Middle", ""},
- {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Right", ""},
- {0, NULL, 0, NULL, NULL},
-};
-
static const EnumPropertyItem event_mouse_type_items[] = {
{LEFTMOUSE, "LEFTMOUSE", 0, "Left", ""},
{MIDDLEMOUSE, "MIDDLEMOUSE", 0, "Middle", ""},
@@ -156,10 +149,6 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
{WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", "WhIn"},
{WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, "Wheel Out", "WhOut"},
{0, "", 0, NULL, NULL},
- {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Tweak Left", "TwkL"},
- {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Tweak Middle", "TwkM"},
- {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Tweak Right", "TwkR"},
- {0, "", 0, NULL, NULL},
{EVT_AKEY, "A", 0, "A", ""},
{EVT_BKEY, "B", 0, "B", ""},
{EVT_CKEY, "C", 0, "C", ""},
@@ -362,26 +351,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
* This is needed for `km.keymap_items.new` value argument,
* to accept values from different types.
*/
-const EnumPropertyItem rna_enum_event_value_all_items[] = {
- {KM_ANY, "ANY", 0, "Any", ""},
- {KM_PRESS, "PRESS", 0, "Press", ""},
- {KM_RELEASE, "RELEASE", 0, "Release", ""},
- {KM_CLICK, "CLICK", 0, "Click", ""},
- {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""},
- {KM_CLICK_DRAG, "CLICK_DRAG", 0, "Click Drag", ""},
- {EVT_GESTURE_N, "NORTH", 0, "North", ""},
- {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""},
- {EVT_GESTURE_E, "EAST", 0, "East", ""},
- {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""},
- {EVT_GESTURE_S, "SOUTH", 0, "South", ""},
- {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""},
- {EVT_GESTURE_W, "WEST", 0, "West", ""},
- {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""},
- {KM_NOTHING, "NOTHING", 0, "Nothing", ""},
- {0, NULL, 0, NULL, NULL},
-};
-
-const EnumPropertyItem rna_enum_event_value_keymouse_items[] = {
+const EnumPropertyItem rna_enum_event_value_items[] = {
{KM_ANY, "ANY", 0, "Any", ""},
{KM_PRESS, "PRESS", 0, "Press", ""},
{KM_RELEASE, "RELEASE", 0, "Release", ""},
@@ -393,16 +363,16 @@ const EnumPropertyItem rna_enum_event_value_keymouse_items[] = {
{0, NULL, 0, NULL, NULL},
};
-const EnumPropertyItem rna_enum_event_value_tweak_items[] = {
+const EnumPropertyItem rna_enum_event_direction_items[] = {
{KM_ANY, "ANY", 0, "Any", ""},
- {EVT_GESTURE_N, "NORTH", 0, "North", ""},
- {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""},
- {EVT_GESTURE_E, "EAST", 0, "East", ""},
- {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""},
- {EVT_GESTURE_S, "SOUTH", 0, "South", ""},
- {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""},
- {EVT_GESTURE_W, "WEST", 0, "West", ""},
- {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""},
+ {KM_DIRECTION_N, "NORTH", 0, "North", ""},
+ {KM_DIRECTION_NE, "NORTH_EAST", 0, "North-East", ""},
+ {KM_DIRECTION_E, "EAST", 0, "East", ""},
+ {KM_DIRECTION_SE, "SOUTH_EAST", 0, "South-East", ""},
+ {KM_DIRECTION_S, "SOUTH", 0, "South", ""},
+ {KM_DIRECTION_SW, "SOUTH_WEST", 0, "South-West", ""},
+ {KM_DIRECTION_W, "WEST", 0, "West", ""},
+ {KM_DIRECTION_NW, "NORTH_WEST", 0, "North-West", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -420,7 +390,6 @@ const EnumPropertyItem rna_enum_event_type_mask_items[] = {
{EVT_TYPE_MASK_MOUSE_BUTTON, "MOUSE_BUTTON", 0, "Mouse Button", ""},
{EVT_TYPE_MASK_MOUSE, "MOUSE", 0, "Mouse", ""},
{EVT_TYPE_MASK_NDOF, "NDOF", 0, "NDOF", ""},
- {EVT_TYPE_MASK_TWEAK, "TWEAK", 0, "Tweak", ""},
{EVT_TYPE_MASK_ACTIONZONE, "ACTIONZONE", 0, "Action Zone", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -612,18 +581,6 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr)
return result;
}
-static const EnumPropertyItem *rna_Event_value_itemf(bContext *UNUSED(C),
- PointerRNA *ptr,
- PropertyRNA *UNUSED(prop),
- bool *UNUSED(r_free))
-{
- const wmEvent *event = ptr->data;
- if (ISTWEAK(event->type)) {
- return rna_enum_event_value_tweak_items;
- }
- return rna_enum_event_value_all_items;
-}
-
static void rna_Event_ascii_get(PointerRNA *ptr, char *value)
{
const wmEvent *event = ptr->data;
@@ -668,7 +625,7 @@ static int rna_Event_unicode_length(PointerRNA *ptr)
static bool rna_Event_is_repeat_get(PointerRNA *ptr)
{
const wmEvent *event = ptr->data;
- return event->is_repeat;
+ return (event->flag & WM_EVENT_IS_REPEAT) != 0;
}
static float rna_Event_pressure_get(PointerRNA *ptr)
@@ -915,10 +872,6 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value)
kmi->type = EVT_AKEY;
kmi->val = KM_PRESS;
break;
- case KMI_TYPE_TWEAK:
- kmi->type = EVT_TWEAK_L;
- kmi->val = KM_ANY;
- break;
case KMI_TYPE_MOUSE:
kmi->type = LEFTMOUSE;
kmi->val = KM_PRESS;
@@ -969,9 +922,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C),
if (map_type == KMI_TYPE_MOUSE) {
return event_mouse_type_items;
}
- if (map_type == KMI_TYPE_TWEAK) {
- return event_tweak_type_items;
- }
if (map_type == KMI_TYPE_TIMER) {
return event_timer_type_items;
}
@@ -986,24 +936,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C),
}
}
-static const EnumPropertyItem *rna_KeyMapItem_value_itemf(bContext *UNUSED(C),
- PointerRNA *ptr,
- PropertyRNA *UNUSED(prop),
- bool *UNUSED(r_free))
-{
- int map_type = rna_wmKeyMapItem_map_type_get(ptr);
-
- if (map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD || map_type == KMI_TYPE_NDOF) {
- return rna_enum_event_value_keymouse_items;
- }
- if (map_type == KMI_TYPE_TWEAK) {
- return rna_enum_event_value_tweak_items;
- }
- else {
- return rna_enum_event_value_all_items;
- }
-}
-
static const EnumPropertyItem *rna_KeyMapItem_propvalue_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
@@ -2106,8 +2038,7 @@ static void rna_def_event(BlenderRNA *brna)
/* enums */
prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "val");
- RNA_def_property_enum_items(prop, rna_enum_event_value_all_items);
- RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Event_value_itemf");
+ RNA_def_property_enum_items(prop, rna_enum_event_value_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Value", "The type of event, only applies to some");
@@ -2118,6 +2049,12 @@ static void rna_def_event(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Type", "");
+ prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "direction");
+ RNA_def_property_enum_items(prop, rna_enum_event_direction_items);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)");
+
/* keyboard */
prop = RNA_def_property(srna, "is_repeat", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -2194,23 +2131,23 @@ static void rna_def_event(BlenderRNA *brna)
/* modifiers */
prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "shift", 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_SHIFT);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Shift", "True when the Shift key is held");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER);
prop = RNA_def_property(srna, "ctrl", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "ctrl", 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_CTRL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Ctrl", "True when the Ctrl key is held");
prop = RNA_def_property(srna, "alt", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "alt", 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_ALT);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Alt", "True when the Alt/Option key is held");
prop = RNA_def_property(srna, "oskey", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "oskey", 1);
+ RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_OSKEY);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "OS Key", "True when the Cmd key is held");
@@ -2538,7 +2475,6 @@ static void rna_def_keyconfig(BlenderRNA *brna)
static const EnumPropertyItem map_type_items[] = {
{KMI_TYPE_KEYBOARD, "KEYBOARD", 0, "Keyboard", ""},
- {KMI_TYPE_TWEAK, "TWEAK", 0, "Tweak", ""},
{KMI_TYPE_MOUSE, "MOUSE", 0, "Mouse", ""},
{KMI_TYPE_NDOF, "NDOF", 0, "NDOF", ""},
{KMI_TYPE_TEXTINPUT, "TEXTINPUT", 0, "Text Input", ""},
@@ -2679,11 +2615,16 @@ static void rna_def_keyconfig(BlenderRNA *brna)
prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "val");
- RNA_def_property_enum_items(prop, rna_enum_event_value_all_items);
- RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_KeyMapItem_value_itemf");
+ RNA_def_property_enum_items(prop, rna_enum_event_value_items);
RNA_def_property_ui_text(prop, "Value", "");
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
+ prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "direction");
+ RNA_def_property_enum_items(prop, rna_enum_event_direction_items);
+ RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)");
+ RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
+
prop = RNA_def_property(srna, "id", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "id");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index 81cc72088a6..0589fa7a96e 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -258,6 +258,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km,
int alt,
int oskey,
int keymodifier,
+ int direction,
bool repeat,
bool head)
{
@@ -275,7 +276,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km,
WM_operator_bl_idname(idname_bl, idname);
/* create keymap item */
- kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier);
+ kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier, direction);
if (!repeat) {
kmi->flag |= KMI_REPEAT_IGNORE;
@@ -324,6 +325,7 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km,
int alt,
int oskey,
int keymodifier,
+ int direction,
bool repeat)
{
/* only modal maps */
@@ -338,13 +340,14 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km,
/* not initialized yet, do delayed lookup */
if (!km->modal_items) {
- kmi = WM_modalkeymap_add_item_str(km, type, value, modifier, keymodifier, propvalue_str);
+ kmi = WM_modalkeymap_add_item_str(
+ km, type, value, modifier, keymodifier, direction, propvalue_str);
}
else {
if (RNA_enum_value_from_id(km->modal_items, propvalue_str, &propvalue) == 0) {
BKE_report(reports, RPT_WARNING, "Property value not in enumeration");
}
- kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, propvalue);
+ kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, direction, propvalue);
}
if (!repeat) {
@@ -635,14 +638,23 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
wmEvent e = *win->eventstate;
e.type = type;
e.val = value;
- e.is_repeat = false;
+ e.flag = 0;
e.xy[0] = x;
e.xy[1] = y;
- e.shift = shift;
- e.ctrl = ctrl;
- e.alt = alt;
- e.oskey = oskey;
+ e.modifier = 0;
+ if (shift) {
+ e.modifier |= KM_SHIFT;
+ }
+ if (ctrl) {
+ e.modifier |= KM_CTRL;
+ }
+ if (alt) {
+ e.modifier |= KM_ALT;
+ }
+ if (oskey) {
+ e.modifier |= KM_OSKEY;
+ }
e.ascii = '\0';
e.utf8_buf[0] = '\0';
@@ -720,7 +732,7 @@ void RNA_api_window(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
- parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", "");
+ parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_string(func, "unicode", NULL, 0, "", "");
RNA_def_parameter_clear_flags(parm, PROP_NEVER_NULL, 0);
@@ -1125,7 +1137,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
- parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", "");
+ parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func, "any", 0, "Any", "");
RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD);
@@ -1133,6 +1145,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD);
RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", "");
+ RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", "");
RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events");
RNA_def_boolean(func,
"head",
@@ -1149,7 +1162,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
- parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", "");
+ parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func, "any", 0, "Any", "");
RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD);
@@ -1157,6 +1170,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD);
RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", "");
+ RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", "");
RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events");
parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", "Added key map item");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index 87d8bc8d844..9fe3153eb1e 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -46,6 +46,43 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
/** \name XR Action Map
* \{ */
+static XrComponentPath *rna_XrComponentPath_new(XrActionMapBinding *amb, const char *path_str)
+{
+# ifdef WITH_XR_OPENXR
+ XrComponentPath *component_path = MEM_callocN(sizeof(XrComponentPath), __func__);
+ BLI_strncpy(component_path->path, path_str, sizeof(component_path->path));
+ BLI_addtail(&amb->component_paths, component_path);
+ return component_path;
+# else
+ UNUSED_VARS(amb, path_str);
+ return NULL;
+# endif
+}
+
+static void rna_XrComponentPath_remove(XrActionMapBinding *amb, PointerRNA *component_path_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrComponentPath *component_path = component_path_ptr->data;
+ int idx = BLI_findindex(&amb->component_paths, component_path);
+ if (idx != -1) {
+ BLI_freelinkN(&amb->component_paths, component_path);
+ }
+ RNA_POINTER_INVALIDATE(component_path_ptr);
+# else
+ UNUSED_VARS(amb, component_path_ptr);
+# endif
+}
+
+static XrComponentPath *rna_XrComponentPath_find(XrActionMapBinding *amb, const char *path_str)
+{
+# ifdef WITH_XR_OPENXR
+ return BLI_findstring(&amb->component_paths, path_str, offsetof(XrComponentPath, path));
+# else
+ UNUSED_VARS(amb, path_str);
+ return NULL;
+# endif
+}
+
static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami,
const char *name,
bool replace_existing)
@@ -99,6 +136,28 @@ static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, con
# endif
}
+static void rna_XrActionMapBinding_component_paths_begin(CollectionPropertyIterator *iter,
+ PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data;
+ rna_iterator_listbase_begin(iter, &amb->component_paths, NULL);
+# else
+ UNUSED_VARS(iter, ptr);
+# endif
+}
+
+static int rna_XrActionMapBinding_component_paths_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data;
+ return BLI_listbase_count(&amb->component_paths);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -174,6 +233,43 @@ static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene)
# endif
}
+static XrUserPath *rna_XrUserPath_new(XrActionMapItem *ami, const char *path_str)
+{
+# ifdef WITH_XR_OPENXR
+ XrUserPath *user_path = MEM_callocN(sizeof(XrUserPath), __func__);
+ BLI_strncpy(user_path->path, path_str, sizeof(user_path->path));
+ BLI_addtail(&ami->user_paths, user_path);
+ return user_path;
+# else
+ UNUSED_VARS(ami, path_str);
+ return NULL;
+# endif
+}
+
+static void rna_XrUserPath_remove(XrActionMapItem *ami, PointerRNA *user_path_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrUserPath *user_path = user_path_ptr->data;
+ int idx = BLI_findindex(&ami->user_paths, user_path);
+ if (idx != -1) {
+ BLI_freelinkN(&ami->user_paths, user_path);
+ }
+ RNA_POINTER_INVALIDATE(user_path_ptr);
+# else
+ UNUSED_VARS(ami, user_path_ptr);
+# endif
+}
+
+static XrUserPath *rna_XrUserPath_find(XrActionMapItem *ami, const char *path_str)
+{
+# ifdef WITH_XR_OPENXR
+ return BLI_findstring(&ami->user_paths, path_str, offsetof(XrUserPath, path));
+# else
+ UNUSED_VARS(ami, path_str);
+ return NULL;
+# endif
+}
+
static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am,
const char *name,
bool replace_existing)
@@ -222,6 +318,27 @@ static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *na
# endif
}
+static void rna_XrActionMapItem_user_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = (XrActionMapItem *)ptr->data;
+ rna_iterator_listbase_begin(iter, &ami->user_paths, NULL);
+# else
+ UNUSED_VARS(iter, ptr);
+# endif
+}
+
+static int rna_XrActionMapItem_user_paths_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = (XrActionMapItem *)ptr->data;
+ return BLI_listbase_count(&ami->user_paths);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value)
{
# ifdef WITH_XR_OPENXR
@@ -395,6 +512,27 @@ static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool
# endif
}
+static void rna_XrActionMapItem_bindings_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = (XrActionMapItem *)ptr->data;
+ rna_iterator_listbase_begin(iter, &ami->bindings, NULL);
+# else
+ UNUSED_VARS(iter, ptr);
+# endif
+}
+
+static int rna_XrActionMapItem_bindings_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = (XrActionMapItem *)ptr->data;
+ return BLI_listbase_count(&ami->bindings);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -471,6 +609,27 @@ static XrActionMap *rna_XrActionMap_find(PointerRNA *ptr, const char *name)
# endif
}
+static void rna_XrActionMap_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMap *actionmap = (XrActionMap *)ptr->data;
+ rna_iterator_listbase_begin(iter, &actionmap->items, NULL);
+# else
+ UNUSED_VARS(iter, ptr);
+# endif
+}
+
+static int rna_XrActionMap_items_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMap *actionmap = (XrActionMap *)ptr->data;
+ return BLI_listbase_count(&actionmap->items);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -576,26 +735,8 @@ static bool rna_XrSessionState_action_create(bContext *C,
{
# ifdef WITH_XR_OPENXR
wmWindowManager *wm = CTX_wm_manager(C);
- unsigned int count_subaction_paths = 0;
- const char *subaction_paths[2];
-
- if (ami->user_path0[0]) {
- subaction_paths[0] = ami->user_path0;
- ++count_subaction_paths;
-
- if (ami->user_path1[0]) {
- subaction_paths[1] = ami->user_path1;
- ++count_subaction_paths;
- }
- }
- else {
- if (ami->user_path1[0]) {
- subaction_paths[0] = ami->user_path1;
- ++count_subaction_paths;
- }
- else {
- return false;
- }
+ if (BLI_listbase_is_empty(&ami->user_paths)) {
+ return false;
}
const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT);
@@ -621,8 +762,7 @@ static bool rna_XrSessionState_action_create(bContext *C,
actionmap->name,
ami->name,
ami->type,
- count_subaction_paths,
- subaction_paths,
+ &ami->user_paths,
ot,
op_properties,
is_button_action ? ami->haptic_name : NULL,
@@ -645,30 +785,10 @@ static bool rna_XrSessionState_action_binding_create(bContext *C,
{
# ifdef WITH_XR_OPENXR
wmWindowManager *wm = CTX_wm_manager(C);
- unsigned int count_subaction_paths = 0;
- const char *subaction_paths[2];
- const char *component_paths[2];
-
- if (ami->user_path0[0]) {
- subaction_paths[0] = ami->user_path0;
- component_paths[0] = amb->component_path0;
- ++count_subaction_paths;
-
- if (ami->user_path1[0]) {
- subaction_paths[1] = ami->user_path1;
- component_paths[1] = amb->component_path1;
- ++count_subaction_paths;
- }
- }
- else {
- if (ami->user_path1[0]) {
- subaction_paths[0] = ami->user_path1;
- component_paths[0] = amb->component_path1;
- ++count_subaction_paths;
- }
- else {
- return false;
- }
+ const int count_user_paths = BLI_listbase_count(&ami->user_paths);
+ const int count_component_paths = BLI_listbase_count(&amb->component_paths);
+ if (count_user_paths < 1 || (count_user_paths != count_component_paths)) {
+ return false;
}
const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT);
@@ -695,9 +815,8 @@ static bool rna_XrSessionState_action_binding_create(bContext *C,
actionmap->name,
ami->name,
amb->profile,
- count_subaction_paths,
- subaction_paths,
- component_paths,
+ &ami->user_paths,
+ &amb->component_paths,
is_float_action ? float_thresholds : NULL,
is_button_action ? axis_flags : NULL,
is_pose_action ? poses : NULL);
@@ -954,6 +1073,18 @@ static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter
# endif
}
+static int rna_XrSessionState_actionmaps_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ ListBase *lb = WM_xr_actionmaps_get(xr->runtime);
+ return BLI_listbase_count(lb);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -1240,6 +1371,42 @@ static const EnumPropertyItem rna_enum_xr_axis1_flags[] = {
/** \name XR Action Map
* \{ */
+static void rna_def_xr_component_paths(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "XrComponentPaths");
+ srna = RNA_def_struct(brna, "XrComponentPaths", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMapBinding");
+ RNA_def_struct_ui_text(srna, "XR Component Paths", "Collection of OpenXR component paths");
+
+ func = RNA_def_function(srna, "new", "rna_XrComponentPath_new");
+ parm = RNA_def_string(
+ func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "component_path", "XrComponentPath", "Component Path", "Added component path");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrComponentPath_remove");
+ parm = RNA_def_pointer(func, "component_path", "XrComponentPath", "Component Path", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "find", "rna_XrComponentPath_find");
+ parm = RNA_def_string(
+ func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "component_path",
+ "XrComponentPath",
+ "Component Path",
+ "The component path with the given path");
+ RNA_def_function_return(func, parm);
+}
+
static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -1289,6 +1456,36 @@ static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
}
+static void rna_def_xr_user_paths(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "XrUserPaths");
+ srna = RNA_def_struct(brna, "XrUserPaths", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMapItem");
+ RNA_def_struct_ui_text(srna, "XR User Paths", "Collection of OpenXR user paths");
+
+ func = RNA_def_function(srna, "new", "rna_XrUserPath_new");
+ parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", "Added user path");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrUserPath_remove");
+ parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "find", "rna_XrUserPath_find");
+ parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "user_path", "XrUserPath", "User Path", "The user path with the given path");
+ RNA_def_function_return(func, parm);
+}
+
static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -1404,6 +1601,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "items", NULL);
RNA_def_property_struct_type(prop, "XrActionMapItem");
+ RNA_def_property_collection_funcs(prop,
+ "rna_XrActionMap_items_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ "rna_XrActionMap_items_length",
+ NULL,
+ NULL,
+ NULL);
RNA_def_property_ui_text(
prop,
"Items",
@@ -1414,6 +1620,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "selitem");
RNA_def_property_ui_text(prop, "Selected Item", "");
+ /* XrUserPath */
+ srna = RNA_def_struct(brna, "XrUserPath", NULL);
+ RNA_def_struct_sdna(srna, "XrUserPath");
+ RNA_def_struct_ui_text(srna, "XR User Path", "");
+
+ prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, XR_MAX_USER_PATH_LENGTH);
+ RNA_def_property_ui_text(prop, "Path", "OpenXR user path");
+
/* XrActionMapItem */
srna = RNA_def_struct(brna, "XrActionMapItem", NULL);
RNA_def_struct_sdna(srna, "XrActionMapItem");
@@ -1429,13 +1644,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Type", "Action type");
RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update");
- prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE);
- RNA_def_property_string_maxlength(prop, 64);
- RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path");
-
- prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE);
- RNA_def_property_string_maxlength(prop, 64);
- RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path");
+ prop = RNA_def_property(srna, "user_paths", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrUserPath");
+ RNA_def_property_collection_funcs(prop,
+ "rna_XrActionMapItem_user_paths_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ "rna_XrActionMapItem_user_paths_length",
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_ui_text(prop, "User Paths", "OpenXR user paths");
+ rna_def_xr_user_paths(brna, prop);
prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE);
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME);
@@ -1520,6 +1741,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "XrActionMapBinding");
+ RNA_def_property_collection_funcs(prop,
+ "rna_XrActionMapItem_bindings_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ "rna_XrActionMapItem_bindings_length",
+ NULL,
+ NULL,
+ NULL);
RNA_def_property_ui_text(
prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input");
rna_def_xr_actionmap_bindings(brna, prop);
@@ -1528,6 +1758,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "selbinding");
RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding");
+ /* XrComponentPath */
+ srna = RNA_def_struct(brna, "XrComponentPath", NULL);
+ RNA_def_struct_sdna(srna, "XrComponentPath");
+ RNA_def_struct_ui_text(srna, "XR Component Path", "");
+
+ prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, XR_MAX_COMPONENT_PATH_LENGTH);
+ RNA_def_property_ui_text(prop, "Path", "OpenXR component path");
+
/* XrActionMapBinding */
srna = RNA_def_struct(brna, "XrActionMapBinding", NULL);
RNA_def_struct_sdna(srna, "XrActionMapBinding");
@@ -1542,13 +1781,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna)
RNA_def_property_string_maxlength(prop, 256);
RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path");
- prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE);
- RNA_def_property_string_maxlength(prop, 192);
- RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path");
-
- prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE);
- RNA_def_property_string_maxlength(prop, 192);
- RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path");
+ prop = RNA_def_property(srna, "component_paths", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrComponentPath");
+ RNA_def_property_collection_funcs(prop,
+ "rna_XrActionMapBinding_component_paths_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ "rna_XrActionMapBinding_component_paths_length",
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_ui_text(prop, "Component Paths", "OpenXR component paths");
+ rna_def_xr_component_paths(brna, prop);
prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "float_threshold");
@@ -1812,7 +2057,7 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name");
+ parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_boolean(func, "result", 0, "Result", "");
RNA_def_function_return(func, parm);
@@ -1823,19 +2068,19 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name");
+ parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_string(func,
"grip_action",
NULL,
- 64,
+ MAX_NAME,
"Grip Action",
"Name of the action representing the controller grips");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_string(func,
"aim_action",
NULL,
- 64,
+ MAX_NAME,
"Aim Action",
"Name of the action representing the controller aims");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
@@ -1847,11 +2092,12 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path");
+ parm = RNA_def_string(
+ func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_float_array(
func,
@@ -1871,15 +2117,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_string(
func,
"user_path",
NULL,
- 64,
+ XR_MAX_USER_PATH_LENGTH,
"User Path",
"Optional OpenXR user path. If not set, the action will be applied to all paths");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
@@ -1922,15 +2168,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_string(
func,
"user_path",
NULL,
- 64,
+ XR_MAX_USER_PATH_LENGTH,
"User Path",
"Optional OpenXR user path. If not set, the action will be stopped for all paths");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
@@ -2068,16 +2314,16 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
"Additional scale multiplier to apply to base scale when determining viewer scale");
prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrActionMap");
RNA_def_property_collection_funcs(prop,
"rna_XrSessionState_actionmaps_begin",
"rna_iterator_listbase_next",
"rna_iterator_listbase_end",
"rna_iterator_listbase_get",
- NULL,
+ "rna_XrSessionState_actionmaps_length",
NULL,
NULL,
NULL);
- RNA_def_property_struct_type(prop, "XrActionMap");
RNA_def_property_ui_text(prop, "XR Action Maps", "");
rna_def_xr_actionmaps(brna, prop);
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 2cc3bb8c916..ea42ac761ea 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -371,7 +371,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
int tot_doubles;
const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0;
- const bool use_recalc_normals = (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || use_merge;
+ const bool use_recalc_normals = BKE_mesh_vertex_normals_are_dirty(mesh) || use_merge;
const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != NULL);
int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0;
@@ -819,7 +819,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
* In other cases it should be impossible to have a type mismatch.
*/
- if (amd->curve_ob && amd->curve_ob->type != OB_CURVE) {
+ if (amd->curve_ob && amd->curve_ob->type != OB_CURVES_LEGACY) {
return true;
}
if (amd->start_cap && amd->start_cap->type != OB_MESH) {
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index ca5d59433f0..d1c7dd43f15 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -71,7 +71,7 @@ static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED
*
* In other cases it should be impossible to have a type mismatch.
*/
- return !cmd->object || cmd->object->type != OB_CURVE;
+ return !cmd->object || cmd->object->type != OB_CURVES_LEGACY;
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index 0411bcd7c34..ddbed4f498e 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -313,8 +313,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd,
if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) {
float(*clnors)[3] = NULL;
- if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) ||
- !CustomData_has_layer(ldata, CD_NORMAL)) {
+ if (!CustomData_has_layer(ldata, CD_NORMAL)) {
BKE_mesh_calc_normals_split(mesh);
}
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index f54ed8f5a25..210a7edac54 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -454,6 +454,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
ui_data->base.rna_subtype = PROP_COLOR;
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__);
ui_data->default_array_len = 4;
+ ui_data->min = 0.0;
+ ui_data->max = FLT_MAX;
+ ui_data->soft_min = 0.0;
+ ui_data->soft_max = 1.0;
for (const int i : IndexRange(4)) {
ui_data->default_array[i] = double(value->value[i]);
}
@@ -1381,7 +1385,7 @@ static void add_attribute_search_button(const bContext &C,
0.0f,
0.0f,
0.0f,
- "");
+ socket.description);
const Object *object = ED_object_context(&C);
BLI_assert(object != nullptr);
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index e6aebbfeb56..3649808f4f0 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -535,8 +535,8 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
CustomData *ldata = &result->ldata;
- const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
- const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(mesh);
+ const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(result);
+ const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(result);
clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);
if (use_current_clnors) {
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 0313c37283a..fdaf7bd41d1 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -953,7 +953,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
}
/* must recalculate normals with vgroups since they can displace unevenly T26888. */
- if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || do_rim || dvert) {
+ if (BKE_mesh_vertex_normals_are_dirty(mesh) || do_rim || dvert) {
BKE_mesh_normals_tag_dirty(result);
}
else if (do_shell) {
@@ -1009,9 +1009,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
#define SOLIDIFY_SIDE_NORMALS
#ifdef SOLIDIFY_SIDE_NORMALS
- /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above,
+ /* NOTE(@sybren): due to the code setting normals dirty a few lines above,
* do_side_normals is always false. */
- const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL);
+ const bool do_side_normals = !BKE_mesh_vertex_normals_are_dirty(result);
/* annoying to allocate these since we only need the edge verts, */
float(*edge_vert_nos)[3] = do_side_normals ?
MEM_calloc_arrayN(numVerts, sizeof(float[3]), __func__) :
diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
index ed7acef4cdc..ff25c1afd49 100644
--- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
+++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
@@ -1405,21 +1405,26 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
for (uint j = 0; g->valid; j++, g++) {
if (!g->is_singularity) {
float *nor = g->no;
+ /* During vertex position calculation, the algorithm decides if it wants to disable the
+ * boundary fix to maintain correct thickness. If the used algorithm does not produce a
+ * free move direction (move_nor), it can use approximate_free_direction to decide on
+ * a movement direction based on the connected edges. */
float move_nor[3] = {0, 0, 0};
bool disable_boundary_fix = (smd->nonmanifold_boundary_mode ==
MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE ||
(g->is_orig_closed || g->split));
+ bool approximate_free_direction = false;
/* Constraints Method. */
if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) {
NewEdgeRef *first_edge = NULL;
NewEdgeRef **edge_ptr = g->edges;
/* Contains normal and offset [nx, ny, nz, ofs]. */
- float(*normals_queue)[4] = MEM_malloc_arrayN(
- g->edges_len + 1, sizeof(*normals_queue), "normals_queue in solidify");
+ float(*planes_queue)[4] = MEM_malloc_arrayN(
+ g->edges_len + 1, sizeof(*planes_queue), "planes_queue in solidify");
uint queue_index = 0;
- float face_nors[3][3];
- float nor_ofs[3];
+ float fallback_nor[3];
+ float fallback_ofs = 0.0f;
const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split;
for (uint k = 0; k < g->edges_len; k++, edge_ptr++) {
@@ -1436,17 +1441,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
if (!null_faces[face->index]) {
- /* And normal to the queue. */
- mul_v3_v3fl(normals_queue[queue_index],
+ /* And plane to the queue. */
+ mul_v3_v3fl(planes_queue[queue_index],
poly_nors[face->index],
face->reversed ? -1 : 1);
- normals_queue[queue_index++][3] = ofs;
+ planes_queue[queue_index++][3] = ofs;
}
else {
/* Just use this approximate normal of the null face if there is no other
* normal to use. */
- mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1);
- nor_ofs[0] = ofs;
+ mul_v3_v3fl(fallback_nor, poly_nors[face->index], face->reversed ? -1 : 1);
+ fallback_ofs = ofs;
}
}
}
@@ -1455,131 +1460,173 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
}
}
- uint face_nors_len = 0;
- const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
- while (queue_index > 0) {
- if (face_nors_len == 0) {
- if (queue_index <= 2) {
- for (uint k = 0; k < queue_index; k++) {
- copy_v3_v3(face_nors[k], normals_queue[k]);
- nor_ofs[k] = normals_queue[k][3];
+ if (queue_index > 2) {
+ /* Find the two most different normals. */
+ float min_p = 2.0f;
+ uint min_n0 = 0;
+ uint min_n1 = 0;
+ for (uint k = 0; k < queue_index; k++) {
+ for (uint m = k + 1; m < queue_index; m++) {
+ float p = dot_v3v3(planes_queue[k], planes_queue[m]);
+ if (p < min_p) {
+ min_p = p;
+ min_n0 = k;
+ min_n1 = m;
}
- face_nors_len = queue_index;
- queue_index = 0;
}
- else {
- /* Find most different two normals. */
- float min_p = 2;
- uint min_n0 = 0;
- uint min_n1 = 0;
- for (uint k = 0; k < queue_index; k++) {
- for (uint m = k + 1; m < queue_index; m++) {
- float p = dot_v3v3(normals_queue[k], normals_queue[m]);
- if (p <= min_p + FLT_EPSILON) {
- min_p = p;
- min_n0 = m;
- min_n1 = k;
- }
- }
- }
- copy_v3_v3(face_nors[0], normals_queue[min_n0]);
- copy_v3_v3(face_nors[1], normals_queue[min_n1]);
- nor_ofs[0] = normals_queue[min_n0][3];
- nor_ofs[1] = normals_queue[min_n1][3];
- face_nors_len = 2;
- queue_index--;
- memmove(normals_queue + min_n0,
- normals_queue + min_n0 + 1,
- (queue_index - min_n0) * sizeof(*normals_queue));
- queue_index--;
- memmove(normals_queue + min_n1,
- normals_queue + min_n1 + 1,
- (queue_index - min_n1) * sizeof(*normals_queue));
- min_p = 1;
- min_n1 = 0;
- float max_p = -1;
- for (uint k = 0; k < queue_index; k++) {
- max_p = -1;
- for (uint m = 0; m < face_nors_len; m++) {
- float p = dot_v3v3(face_nors[m], normals_queue[k]);
- if (p > max_p + FLT_EPSILON) {
- max_p = p;
- }
- }
- if (max_p <= min_p + FLT_EPSILON) {
- min_p = max_p;
- min_n1 = k;
- }
- }
- if (min_p < 0.8) {
- copy_v3_v3(face_nors[2], normals_queue[min_n1]);
- nor_ofs[2] = normals_queue[min_n1][3];
- face_nors_len++;
- queue_index--;
- memmove(normals_queue + min_n1,
- normals_queue + min_n1 + 1,
- (queue_index - min_n1) * sizeof(*normals_queue));
+ }
+ /* Put the two found normals, first in the array queue. */
+ if (min_n1 != 0) {
+ swap_v4_v4(planes_queue[min_n0], planes_queue[0]);
+ swap_v4_v4(planes_queue[min_n1], planes_queue[1]);
+ }
+ else {
+ swap_v4_v4(planes_queue[min_n0], planes_queue[1]);
+ }
+ /* Find the third most important/different normal. */
+ min_p = 1.0f;
+ min_n1 = 2;
+ float max_p = -1.0f;
+ for (uint k = 2; k < queue_index; k++) {
+ max_p = max_ff(dot_v3v3(planes_queue[0], planes_queue[k]),
+ dot_v3v3(planes_queue[1], planes_queue[k]));
+ if (max_p <= min_p) {
+ min_p = max_p;
+ min_n1 = k;
+ }
+ }
+ swap_v4_v4(planes_queue[min_n1], planes_queue[2]);
+ }
+ /* Remove/average duplicate normals in planes_queue. */
+ while (queue_index > 2) {
+ uint best_n0 = 0;
+ uint best_n1 = 0;
+ float best_p = -1.0f;
+ float best_ofs_diff = 0.0f;
+ for (uint k = 0; k < queue_index; k++) {
+ for (uint m = k + 1; m < queue_index; m++) {
+ float p = dot_v3v3(planes_queue[m], planes_queue[k]);
+ float ofs_diff = fabsf(planes_queue[m][3] - planes_queue[k][3]);
+ if (p > best_p + FLT_EPSILON || (p >= best_p && ofs_diff < best_ofs_diff)) {
+ best_p = p;
+ best_ofs_diff = ofs_diff;
+ best_n0 = k;
+ best_n1 = m;
}
}
}
- else {
- uint best = 0;
- uint best_group = 0;
- float best_p = -1.0f;
- for (uint k = 0; k < queue_index; k++) {
- for (uint m = 0; m < face_nors_len; m++) {
- float p = dot_v3v3(face_nors[m], normals_queue[k]);
- if (p > best_p + FLT_EPSILON) {
- best_p = p;
- best = m;
- best_group = k;
+ /* Make sure there are no equal planes. This threshold is crucial for the
+ * methods below to work without numerical issues. */
+ if (best_p < 0.98f) {
+ break;
+ }
+ add_v3_v3(planes_queue[best_n0], planes_queue[best_n1]);
+ normalize_v3(planes_queue[best_n0]);
+ planes_queue[best_n0][3] = (planes_queue[best_n0][3] + planes_queue[best_n1][3]) *
+ 0.5f;
+ queue_index--;
+ memmove(planes_queue + best_n1,
+ planes_queue + best_n1 + 1,
+ (queue_index - best_n1) * sizeof(*planes_queue));
+ }
+ const uint size = queue_index;
+ /* If there is more than 2 planes at this vertex, the boundary fix should be disabled
+ * to stay at the correct thickness for all the faces. This is not very good in
+ * practice though, since that will almost always disable the boundary fix. Instead
+ * introduce a threshold which decides whether the boundary fix can be used without
+ * major thickness changes. If the following constant is 1.0, it would always
+ * prioritize correct thickness. At 0.7 the thickness is allowed to change a bit if
+ * necessary for the fix (~10%). Note this only applies if a boundary fix is used. */
+ const float boundary_fix_threshold = 0.7f;
+ if (size > 3) {
+ /* Use the most general least squares method to find the best position. */
+ float mat[3][3];
+ zero_m3(mat);
+ for (int k = 0; k < 3; k++) {
+ for (int m = 0; m < size; m++) {
+ madd_v3_v3fl(mat[k], planes_queue[m], planes_queue[m][k]);
+ }
+ /* Add a small epsilon to ensure the invert is going to work.
+ * This addition makes the inverse more stable and the results
+ * seem to get more precise. */
+ mat[k][k] += 5e-5f;
+ }
+ /* NOTE: this matrix invert fails if there is less than 3 different normals. */
+ invert_m3(mat);
+ zero_v3(nor);
+ for (int k = 0; k < size; k++) {
+ madd_v3_v3fl(nor, planes_queue[k], planes_queue[k][3]);
+ }
+ mul_v3_m3v3(nor, mat, nor);
+
+ if (!disable_boundary_fix) {
+ /* Figure out if the approximate boundary fix can get use here. */
+ float greatest_angle_cos = 1.0f;
+ for (uint k = 0; k < 2; k++) {
+ for (uint m = 2; m < size; m++) {
+ float p = dot_v3v3(planes_queue[m], planes_queue[k]);
+ if (p < greatest_angle_cos) {
+ greatest_angle_cos = p;
}
}
}
- add_v3_v3(face_nors[best], normals_queue[best_group]);
- normalize_v3(face_nors[best]);
- nor_ofs[best] = (nor_ofs[best] + normals_queue[best_group][3]) * 0.5f;
- queue_index--;
- memmove(normals_queue + best_group,
- normals_queue + best_group + 1,
- (queue_index - best_group) * sizeof(*normals_queue));
+ if (greatest_angle_cos > boundary_fix_threshold) {
+ approximate_free_direction = true;
+ }
+ else {
+ disable_boundary_fix = true;
+ }
}
}
- MEM_freeN(normals_queue);
-
- /* When up to 3 constraint normals are found. */
- if (ELEM(face_nors_len, 2, 3)) {
- const float q = dot_v3v3(face_nors[0], face_nors[1]);
+ else if (size > 1) {
+ /* When up to 3 constraint normals are found, there is a simple solution. */
+ const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
+ const float q = dot_v3v3(planes_queue[0], planes_queue[1]);
float d = 1.0f - q * q;
- cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]);
+ cross_v3_v3v3(move_nor, planes_queue[0], planes_queue[1]);
+ normalize_v3(move_nor);
if (d > FLT_EPSILON * 10 && q < stop_explosion) {
d = 1.0f / d;
- mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d);
- mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d);
+ mul_v3_fl(planes_queue[0], (planes_queue[0][3] - planes_queue[1][3] * q) * d);
+ mul_v3_fl(planes_queue[1], (planes_queue[1][3] - planes_queue[0][3] * q) * d);
}
else {
d = 1.0f / (fabsf(q) + 1.0f);
- mul_v3_fl(face_nors[0], nor_ofs[0] * d);
- mul_v3_fl(face_nors[1], nor_ofs[1] * d);
+ mul_v3_fl(planes_queue[0], planes_queue[0][3] * d);
+ mul_v3_fl(planes_queue[1], planes_queue[1][3] * d);
}
- add_v3_v3v3(nor, face_nors[0], face_nors[1]);
- if (face_nors_len == 3) {
- float *free_nor = move_nor;
- mul_v3_fl(face_nors[2], nor_ofs[2]);
- d = dot_v3v3(face_nors[2], free_nor);
- if (LIKELY(fabsf(d) > FLT_EPSILON)) {
- sub_v3_v3v3(face_nors[0], nor, face_nors[2]); /* Override face_nor[0]. */
- mul_v3_fl(free_nor, dot_v3v3(face_nors[2], face_nors[0]) / d);
- sub_v3_v3(nor, free_nor);
+ add_v3_v3v3(nor, planes_queue[0], planes_queue[1]);
+ if (size == 3) {
+ d = dot_v3v3(planes_queue[2], move_nor);
+ /* The following threshold ignores the third plane if it is almost orthogonal to
+ * the still free direction. */
+ if (fabsf(d) > 0.02f) {
+ float tmp[3];
+ madd_v3_v3v3fl(tmp, nor, planes_queue[2], -planes_queue[2][3]);
+ mul_v3_v3fl(tmp, move_nor, dot_v3v3(planes_queue[2], tmp) / d);
+ sub_v3_v3(nor, tmp);
+ /* Disable boundary fix if the constraints would be majorly unsatisfied. */
+ if (fabsf(d) > 1.0f - boundary_fix_threshold) {
+ disable_boundary_fix = true;
+ }
}
+ }
+ approximate_free_direction = false;
+ }
+ else if (size == 1) {
+ /* Face corner case. */
+ mul_v3_v3fl(nor, planes_queue[0], planes_queue[0][3]);
+ if (g->edges_len > 2) {
disable_boundary_fix = true;
+ approximate_free_direction = true;
}
}
else {
- BLI_assert(face_nors_len < 2);
- mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]);
+ /* Fallback case for null faces. */
+ mul_v3_v3fl(nor, fallback_nor, fallback_ofs);
disable_boundary_fix = true;
}
+ MEM_freeN(planes_queue);
}
/* Fixed/Even Method. */
else {
@@ -1707,26 +1754,29 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
/* Set move_nor for boundary fix. */
if (!disable_boundary_fix && g->edges_len > 2) {
- edge_ptr = g->edges + 1;
- float tmp[3];
- uint k;
- for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
- MEdge *e = orig_medge + (*edge_ptr)->old_edge;
- sub_v3_v3v3(
- tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
- add_v3_v3(move_nor, tmp);
- }
- if (k == 1) {
- disable_boundary_fix = true;
- }
- else {
- disable_boundary_fix = normalize_v3(move_nor) == 0.0f;
- }
+ approximate_free_direction = true;
}
else {
disable_boundary_fix = true;
}
}
+ if (approximate_free_direction) {
+ /* Set move_nor for boundary fix. */
+ NewEdgeRef **edge_ptr = g->edges + 1;
+ float tmp[3];
+ int k;
+ for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
+ MEdge *e = orig_medge + (*edge_ptr)->old_edge;
+ sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
+ add_v3_v3(move_nor, tmp);
+ }
+ if (k == 1) {
+ disable_boundary_fix = true;
+ }
+ else {
+ disable_boundary_fix = normalize_v3(move_nor) == 0.0f;
+ }
+ }
/* Fix boundary verts. */
if (!disable_boundary_fix) {
/* Constraint normal, nor * constr_nor == 0 after this fix. */
@@ -1743,8 +1793,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
orig_mvert_co[i]);
if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) {
cross_v3_v3v3(constr_nor, e0, e1);
+ normalize_v3(constr_nor);
}
else {
+ BLI_assert(smd->nonmanifold_boundary_mode ==
+ MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND);
float f0[3];
float f1[3];
if (g->edges[0]->faces[0]->reversed) {
@@ -1766,9 +1819,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
normalize_v3(n0);
normalize_v3(n1);
add_v3_v3v3(constr_nor, n0, n1);
+ normalize_v3(constr_nor);
}
float d = dot_v3v3(constr_nor, move_nor);
- if (LIKELY(fabsf(d) > FLT_EPSILON)) {
+ /* Only allow the thickness to increase about 10 times. */
+ if (fabsf(d) > 0.1f) {
mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d);
sub_v3_v3(nor, move_nor);
}
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index cad36959f2b..b791e28d15b 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -234,7 +234,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
* assigned at this stage of modifier stack evaluation. */
const bool is_editmode = (mesh->edit_mesh != NULL);
const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode);
- if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) {
+ if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(
+ scene, ctx->object, mesh, smd, required_mode, false)) {
subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd);
return result;
}
@@ -246,9 +247,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Happens on bad topology, but also on empty input mesh. */
return result;
}
- const bool use_clnors = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) &&
- (mesh->flag & ME_AUTOSMOOTH) &&
- CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
+ const bool use_clnors = BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh);
if (use_clnors) {
/* If custom normals are present and the option is turned on calculate the split
* normals and clear flag so the normals get interpolated to the result mesh. */
@@ -412,6 +411,13 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, ptr, "show_only_control_edges", 0, NULL, ICON_NONE);
+ SubsurfModifierData *smd = ptr->data;
+ Object *ob = ob_ptr.data;
+ Mesh *mesh = ob->data;
+ if (BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(smd, mesh)) {
+ uiItemL(layout, "Autosmooth or custom normals detected, disabling GPU subdivision", ICON_INFO);
+ }
+
modifier_panel_end(layout, ptr);
}
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index 94482742831..90652c180c6 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -324,7 +324,7 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
buttons_number++;
}
} /* Tessellation point for curve-typed objects. */
- else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
+ else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
/* Some modifiers can work with pre-tessellated curves only. */
if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) {
/* Add button (appearing to be ON) and add tip why this can't be changed. */
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index 41b600e386b..a8c52108cc0 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -204,7 +204,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
BKE_mesh_orco_ensure(ob, mesh);
}
}
- else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) {
+ else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) {
/* TODO(sybren): get evaluated mesh from depsgraph once
* that's properly generated for curves. */
mesh = BKE_mesh_new_nomain_from_curve(ob);
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 51ec0a46d32..d6f493267f8 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -285,9 +285,6 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
mesh->runtime.is_original = false;
- /* Mark tessellated CD layers as dirty. */
- mesh->runtime.cd_dirty_vert |= CD_MASK_TESSLOOPNORMAL;
-
return mesh;
}
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index 3e71d1fb106..5ce76046294 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -189,10 +189,8 @@ struct DisplaceGridOp {
template<typename GridType> void operator()()
{
- if constexpr (blender::is_same_any_v<GridType,
- openvdb::points::PointDataGrid,
- openvdb::StringGrid,
- openvdb::MaskGrid>) {
+ if constexpr (blender::
+ is_same_any_v<GridType, openvdb::points::PointDataGrid, openvdb::MaskGrid>) {
/* We don't support displacing these grid types yet. */
return;
}
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 1078ebfaeb2..1b6472e2d42 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -105,6 +105,12 @@ static float mix_weight(float weight, float weight2, char mix_mode)
if (mix_mode == MOD_WVG_MIX_AVG) {
return (weight + weight2) * 0.5f;
}
+ if (mix_mode == MOD_WVG_MIX_MIN) {
+ return (weight < weight2 ? weight : weight2);
+ }
+ if (mix_mode == MOD_WVG_MIX_MAX) {
+ return (weight > weight2 ? weight : weight2);
+ }
return weight2;
}
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 86dde999ffc..cfcc30655b5 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -82,6 +82,7 @@ void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
+void register_node_type_geo_duplicate_elements(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
void register_node_type_geo_edge_split(void);
@@ -100,6 +101,7 @@ void register_node_type_geo_input_mesh_edge_angle(void);
void register_node_type_geo_input_mesh_edge_neighbors(void);
void register_node_type_geo_input_mesh_edge_vertices(void);
void register_node_type_geo_input_mesh_face_area(void);
+void register_node_type_geo_input_mesh_face_is_planar(void);
void register_node_type_geo_input_mesh_face_neighbors(void);
void register_node_type_geo_input_mesh_island(void);
void register_node_type_geo_input_mesh_vertex_neighbors(void);
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 3bcd4b87b15..4e78f6c1142 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -110,7 +110,7 @@ class SocketDeclaration {
/**
* Change the node such that the socket will become visible. The node type's update method
* should be called afterwards.
- * \note Note that this is not necessarily implemented for all node types.
+ * \note this is not necessarily implemented for all node types.
*/
void make_available(bNode &node) const;
diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh
index e3927488612..7a1aff13020 100644
--- a/source/blender/nodes/NOD_socket_search_link.hh
+++ b/source/blender/nodes/NOD_socket_search_link.hh
@@ -16,7 +16,7 @@ struct bContext;
namespace blender::nodes {
/**
- * Parameters for the operation operation of adding a node after the link drag search menu closes.
+ * Parameters for the operation of adding a node after the link drag search menu closes.
*/
class LinkSearchOpParams {
private:
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 1f1d7e4dc3a..efd3c420330 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -338,6 +338,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CU
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "")
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
@@ -358,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", Inpu
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "")
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 15e7163d7db..162ef07a6dd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -10,6 +10,7 @@
#include "BLI_assert.h"
#include "BLI_dynstr.h"
#include "BLI_hash_mm3.h"
+#include "BLI_math_vector.h"
#include "BLI_string_ref.hh"
#include "BLI_utildefines.h"
diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc
index 2e2e5227a65..e6fdf1820fa 100644
--- a/source/blender/nodes/function/nodes/node_fn_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_compare.cc
@@ -3,6 +3,7 @@
#include <cmath>
#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "UI_interface.h"
diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc
index f76bb904153..46787f7575d 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc
@@ -2,6 +2,8 @@
#include "node_function_util.hh"
+#include "BLI_math_vector.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index f38562a8926..48a83dc825b 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -98,6 +98,7 @@ set(SRC
nodes/node_geo_curve_to_points.cc
nodes/node_geo_curve_trim.cc
nodes/node_geo_delete_geometry.cc
+ nodes/node_geo_duplicate_elements.cc
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_edge_split.cc
@@ -116,6 +117,7 @@ set(SRC
nodes/node_geo_input_mesh_edge_neighbors.cc
nodes/node_geo_input_mesh_edge_vertices.cc
nodes/node_geo_input_mesh_face_area.cc
+ nodes/node_geo_input_mesh_face_is_planar.cc
nodes/node_geo_input_mesh_face_neighbors.cc
nodes/node_geo_input_mesh_island.cc
nodes/node_geo_input_mesh_vertex_neighbors.cc
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
index b83aa8b69a9..0980c2d6e72 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
@@ -137,15 +137,15 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_default_remaining_outputs();
return;
}
const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component.get_for_read();
- const Span<SplinePtr> splines = curve.splines();
- curve.assert_valid_point_attributes();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ const Span<SplinePtr> splines = curve->splines();
+ curve->assert_valid_point_attributes();
evaluate_splines(splines);
@@ -167,9 +167,9 @@ static void node_geo_exec(GeoNodeExecParams params)
end_result.get_component_for_write<PointCloudComponent>();
CurveToPointsResults start_attributes = curve_to_points_create_result_attributes(
- start_point_component, curve);
+ start_point_component, *curve);
CurveToPointsResults end_attributes = curve_to_points_create_result_attributes(
- end_point_component, curve);
+ end_point_component, *curve);
copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes);
copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
index 6deaf5b554a..2fe06a17adf 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
@@ -19,15 +19,15 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_output("Curve", geometry_set);
return;
}
/* Retrieve data for write access so we can avoid new allocations for the reversed data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ MutableSpan<SplinePtr> splines = curve->splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component.attribute_get_for_read(
@@ -41,6 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
});
+ geometry_set.replace_curve(curve_eval_to_curves(*curve));
+
params.set_output("Curve", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
index f0a201c5adf..729ccca5f04 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
@@ -33,24 +33,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
+static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
- return BezierSpline::HandleType::Free;
+ return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
- return BezierSpline::HandleType::Vector;
+ return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
}
static void select_curve_by_handle_type(const CurveEval &curve,
- const BezierSpline::HandleType type,
+ const HandleType type,
const GeometryNodeCurveHandleMode mode,
const MutableSpan<bool> r_selection)
{
@@ -59,10 +59,10 @@ static void select_curve_by_handle_type(const CurveEval &curve,
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i_spline : range) {
const Spline &spline = *splines[i_spline];
- if (spline.type() == Spline::Type::Bezier) {
+ if (spline.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
- Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left();
- Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right();
+ Span<int8_t> types_left = bezier_spline.handle_types_left();
+ Span<int8_t> types_right = bezier_spline.handle_types_right();
for (const int i_point : IndexRange(bezier_spline.size())) {
r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT &&
types_left[i_point] == type) ||
@@ -81,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSelectHandles *storage =
(const NodeGeometryCurveSelectHandles *)params.node().storage;
- const BezierSpline::HandleType handle_type = handle_type_from_input_type(
+ const HandleType handle_type = handle_type_from_input_type(
(GeometryNodeCurveHandleType)storage->handle_type);
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;
@@ -89,9 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = geometry::realize_instances_legacy(geometry_set);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- const CurveEval *curve = curve_component.get_for_read();
-
- if (curve != nullptr) {
+ if (curve_component.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
const std::string selection_name = params.extract_input<std::string>("Selection");
OutputAttribute_Typed<bool> selection =
curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
index b574b2e3cff..537c7c42610 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
@@ -31,20 +31,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
- return BezierSpline::HandleType::Free;
+ return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
- return BezierSpline::HandleType::Vector;
+ return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -56,31 +56,31 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_output("Curve", geometry_set);
return;
}
/* Retrieve data for write access so we can avoid new allocations for the handles data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ MutableSpan<SplinePtr> splines = curve->splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_POINT, true);
- const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ const HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;
bool has_bezier_spline = false;
for (SplinePtr &spline : splines) {
- if (spline->type() != Spline::Type::Bezier) {
+ if (spline->type() != CURVE_TYPE_BEZIER) {
point_index += spline->positions().size();
continue;
}
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
- if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) {
/* In this case the automatically calculated handle types need to be "baked", because
* they're possibly changing from a type that is calculated automatically to a type that
* is positioned manually. */
@@ -101,6 +101,8 @@ static void node_geo_exec(GeoNodeExecParams params)
bezier_spline.mark_cache_invalid();
}
+ geometry_set.replace_curve(curve_eval_to_curves(*curve));
+
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
index b8696e1eb52..4e3b0839da7 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
@@ -148,8 +148,8 @@ static SplinePtr poly_to_bezier(const Spline &input)
output->positions().copy_from(input.positions());
output->radii().copy_from(input.radii());
output->tilts().copy_from(input.tilts());
- output->handle_types_left().fill(BezierSpline::HandleType::Vector);
- output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
+ output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
output->set_resolution(12);
Spline::copy_base_settings(input, *output);
output->attributes = input.attributes;
@@ -166,8 +166,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
scale_input_assign<float>(input.radii(), 3, 2, output->radii());
scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
- output->handle_types_left().fill(BezierSpline::HandleType::Align);
- output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
+ output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
output->set_resolution(nurbs_spline.resolution());
Spline::copy_base_settings(input, *output);
output->attributes.reallocate(output->size());
@@ -183,11 +183,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
{
switch (input.type()) {
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
return input.copy();
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
return poly_to_bezier(input);
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
if (input.size() < 6) {
params.error_message_add(
NodeWarningType::Info,
@@ -202,6 +202,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
}
return nurbs_to_bezier(input);
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ return {};
+ }
}
BLI_assert_unreachable();
return {};
@@ -210,12 +214,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
static SplinePtr convert_to_nurbs(const Spline &input)
{
switch (input.type()) {
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
return input.copy();
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
return bezier_to_nurbs(input);
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
return poly_to_nurbs(input);
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ return {};
}
BLI_assert_unreachable();
return {};
@@ -229,40 +236,40 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_output("Curve", geometry_set);
return;
}
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component->get_for_read();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read());
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component->attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
- for (const int i : curve.splines().index_range()) {
+ for (const int i : curve->splines().index_range()) {
if (selection[i]) {
switch (output_type) {
case GEO_NODE_SPLINE_TYPE_POLY:
- new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ new_curve->add_spline(convert_to_poly_spline(*curve->splines()[i]));
break;
case GEO_NODE_SPLINE_TYPE_BEZIER:
- new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ new_curve->add_spline(convert_to_bezier(*curve->splines()[i], params));
break;
case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ new_curve->add_spline(convert_to_nurbs(*curve->splines()[i]));
break;
}
}
else {
- new_curve->add_spline(curve.splines()[i]->copy());
+ new_curve->add_spline(curve->splines()[i]->copy());
}
}
- new_curve->attributes = curve.attributes;
- params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+ new_curve->attributes = curve->attributes;
+ params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*new_curve)));
}
} // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
index 8ae9df78936..03f7aec8838 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
@@ -111,8 +111,8 @@ static void subdivide_bezier_segment(const BezierSpline &src,
MutableSpan<float3> dst_positions,
MutableSpan<float3> dst_handles_left,
MutableSpan<float3> dst_handles_right,
- MutableSpan<BezierSpline::HandleType> dst_type_left,
- MutableSpan<BezierSpline::HandleType> dst_type_right)
+ MutableSpan<int8_t> dst_type_left,
+ MutableSpan<int8_t> dst_type_right)
{
const bool is_last_cyclic_segment = index == (src.size() - 1);
const int next_index = is_last_cyclic_segment ? 0 : index + 1;
@@ -122,10 +122,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
if (src.segment_is_vector(index)) {
if (is_last_cyclic_segment) {
- dst_type_left.first() = BezierSpline::HandleType::Vector;
+ dst_type_left.first() = BEZIER_HANDLE_VECTOR;
}
- dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
- dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
+ dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR);
+ dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR);
const float factor_delta = 1.0f / result_size;
for (const int cut : IndexRange(result_size)) {
@@ -136,10 +136,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
}
else {
if (is_last_cyclic_segment) {
- dst_type_left.first() = BezierSpline::HandleType::Free;
+ dst_type_left.first() = BEZIER_HANDLE_FREE;
}
- dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
- dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
+ dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE);
+ dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE);
const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
@@ -187,8 +187,8 @@ static void subdivide_bezier_spline(const BezierSpline &src,
MutableSpan<float3> dst_positions = dst.positions();
MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
- MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
- MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
+ MutableSpan<int8_t> dst_type_left = dst.handle_types_left();
+ MutableSpan<int8_t> dst_type_right = dst.handle_types_right();
threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
for (const int i : range) {
@@ -235,26 +235,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline,
subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
switch (src_spline.type()) {
- case Spline::Type::Poly: {
+ case CURVE_TYPE_POLY: {
const PolySpline &src = static_cast<const PolySpline &>(src_spline);
PolySpline &dst = static_cast<PolySpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
break;
}
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
subdivide_bezier_spline(src, offsets, dst);
dst.mark_cache_invalid();
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
@@ -338,7 +342,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_output("Geometry", geometry_set);
return;
}
@@ -350,9 +354,11 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(
+ *curves_to_curve_eval(*component.get_for_read()), cuts);
- params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+ params.set_output("Geometry",
+ GeometrySet::create_with_curves(curve_eval_to_curves(*output_curve)));
}
} // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
index 64627e61910..f8fcc3cc363 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
@@ -283,19 +283,19 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = geometry::realize_instances_legacy(geometry_set);
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
params.set_output("Geometry", GeometrySet());
return;
}
const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component.get_for_read();
- const Span<SplinePtr> splines = curve.splines();
- curve.assert_valid_point_attributes();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ const Span<SplinePtr> splines = curve->splines();
+ curve->assert_valid_point_attributes();
evaluate_splines(splines);
- const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
+ const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_size = offsets.last();
if (total_size == 0) {
params.set_output("Geometry", GeometrySet());
@@ -306,7 +306,7 @@ static void node_geo_exec(GeoNodeExecParams params)
PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>();
CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component,
- curve);
+ *curve);
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT:
case GEO_NODE_CURVE_RESAMPLE_LENGTH:
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
index a0b862546bc..ca98d83c137 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
@@ -111,9 +111,9 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data(spline.radii(), r_spline.radii(), mask);
copy_data(spline.tilts(), r_spline.tilts(), mask);
switch (spline.type()) {
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
break;
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask);
@@ -122,12 +122,16 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data(src.handle_types_right(), dst.handle_types_right(), mask);
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
copy_data(src.weights(), dst.weights(), mask);
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
@@ -231,9 +235,9 @@ static void delete_curve_selection(const CurveComponent &in_component,
const bool invert)
{
std::unique_ptr<CurveEval> r_curve = curve_delete(
- *in_component.get_for_read(), selection_name, invert);
+ *curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert);
if (r_curve) {
- r_component.replace(r_curve.release());
+ r_component.replace(curve_eval_to_curves(*r_curve));
}
else {
r_component.clear();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
index ff86a92f2c7..8991261a21a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
@@ -48,7 +48,7 @@ static void node_geo_exec(GeoNodeExecParams params)
std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(
component, IndexMask(selected_edge_indices));
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
index b6d677154d0..b3fe9d160b3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE));
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 6424fccbe04..cb7132d5ea2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -33,25 +33,18 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
- if (geometry_set.has<MeshComponent>()) {
- remove_attribute(
- geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
- }
- if (geometry_set.has<PointCloudComponent>()) {
- 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);
- }
- if (geometry_set.has<InstancesComponent>()) {
- remove_attribute(
- geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names);
+ for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES}) {
+ if (geometry_set.has(type)) {
+ remove_attribute(geometry_set.get_component_for_write(type), params, attribute_names);
+ }
}
params.set_output("Geometry", geometry_set);
}
+
} // namespace blender::nodes::node_geo_attribute_remove_cc
void register_node_type_geo_attribute_remove()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 59147e9b23f..412f35d62fd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -161,9 +161,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
positions_span = varray.get_internal_span();
}
- if (geometry_set.has_curve()) {
- const CurveEval &curve = *geometry_set.get_curve_for_read();
- for (const SplinePtr &spline : curve.splines()) {
+ if (geometry_set.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ for (const SplinePtr &spline : curve->splines()) {
positions_span = spline->evaluated_positions();
total_size += positions_span.size();
count++;
@@ -201,9 +202,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
offset += varray.size();
}
- if (geometry_set.has_curve()) {
- const CurveEval &curve = *geometry_set.get_curve_for_read();
- for (const SplinePtr &spline : curve.splines()) {
+ if (geometry_set.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ for (const SplinePtr &spline : curve->splines()) {
Span<float3> array = spline->evaluated_positions();
positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
offset += array.size();
@@ -272,8 +274,8 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
if (set.has_mesh()) {
read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
}
- if (set.has_curve()) {
- read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+ if (set.has_curves()) {
+ read_curve_positions(*curves_to_curve_eval(*set.get_curves_for_read()), transforms, &coords);
}
}
return hull_from_bullet(nullptr, coords);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
index 65aad0fcbf1..ce3058c7d42 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -59,10 +59,13 @@ class EndpointFieldInput final : public GeometryFieldInput {
}
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
+ if (!curve_component.has_curves()) {
+ return nullptr;
+ }
- Array<int> control_point_offsets = curve->control_point_offsets();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ Array<int> control_point_offsets = curve->control_point_offsets();
if (curve == nullptr || control_point_offsets.last() == 0) {
return nullptr;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index 9824b2b2ece..6702ee6c0aa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -113,12 +113,13 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul
static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode)
{
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
- const CurveEval &curve = *geometry_set.get_curve_for_read();
- if (curve.splines().is_empty()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ if (curve->splines().is_empty()) {
geometry_set.replace_curve(nullptr);
return;
}
@@ -127,7 +128,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu
CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES :
CDT_INSIDE_WITH_HOLES;
- const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type);
+ const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type);
Mesh *mesh = cdt_to_mesh(results);
geometry_set.replace_mesh(mesh);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 94425ab48f4..24d72ad553b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -394,9 +394,9 @@ static void update_bezier_positions(const FilletData &fd,
dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] -
handle_length * next_dir;
dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] =
- BezierSpline::HandleType::Align;
+ BEZIER_HANDLE_ALIGN;
dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] =
- BezierSpline::HandleType::Vector;
+ BEZIER_HANDLE_VECTOR;
dst_spline.mark_cache_invalid();
/* Calculate the center of the radius to be formed. */
@@ -406,8 +406,8 @@ static void update_bezier_positions(const FilletData &fd,
float radius;
radius_vec = math::normalize_and_get_length(radius_vec, radius);
- dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
- dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
+ dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
+ dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
/* For each of the vertices in between the end points. */
for (const int j : IndexRange(1, count - 2)) {
@@ -512,12 +512,12 @@ static SplinePtr fillet_spline(const Spline &spline,
copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src);
switch (spline.type()) {
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline);
BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr);
if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector);
- dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector);
+ dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR);
+ dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR);
update_poly_positions(fd, dst_spline, src_spline, point_counts);
}
else {
@@ -525,17 +525,21 @@ static SplinePtr fillet_spline(const Spline &spline,
}
break;
}
- case Spline::Type::Poly: {
+ case CURVE_TYPE_POLY: {
update_poly_positions(fd, *dst_spline_ptr, spline, point_counts);
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline);
NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr);
copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src);
update_poly_positions(fd, dst_spline, src_spline, point_counts);
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
return dst_spline_ptr;
@@ -568,7 +572,7 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
const std::optional<Field<int>> &count_field,
const bool limit_radius)
{
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
@@ -599,10 +603,10 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
fillet_param.limit_radius = limit_radius;
- const CurveEval &input_curve = *geometry_set.get_curve_for_read();
- std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param);
+ const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read());
+ std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param);
- geometry_set.replace_curve(output_curve.release());
+ geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
index 26a8ad2d988..ccd3a587e63 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
@@ -31,30 +31,30 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
+static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
- return BezierSpline::HandleType::Free;
+ return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
- return BezierSpline::HandleType::Vector;
+ return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
}
static void select_by_handle_type(const CurveEval &curve,
- const BezierSpline::HandleType type,
+ const HandleType type,
const GeometryNodeCurveHandleMode mode,
const MutableSpan<bool> r_selection)
{
int offset = 0;
for (const SplinePtr &spline : curve.splines()) {
- if (spline->type() != Spline::Type::Bezier) {
+ if (spline->type() != CURVE_TYPE_BEZIER) {
r_selection.slice(offset, spline->size()).fill(false);
offset += spline->size();
}
@@ -71,11 +71,11 @@ static void select_by_handle_type(const CurveEval &curve,
}
class HandleTypeFieldInput final : public GeometryFieldInput {
- BezierSpline::HandleType type_;
+ HandleType type_;
GeometryNodeCurveHandleMode mode_;
public:
- HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode)
+ HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode)
: GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
type_(type),
mode_(mode)
@@ -92,14 +92,14 @@ class HandleTypeFieldInput final : public GeometryFieldInput {
}
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
+ const Curves *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
if (domain == ATTR_DOMAIN_POINT) {
Array<bool> selection(mask.min_array_size());
- select_by_handle_type(*curve, type_, mode_, selection);
+ select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection);
return VArray<bool>::ForContainer(std::move(selection));
}
return {};
@@ -124,7 +124,7 @@ class HandleTypeFieldInput final : public GeometryFieldInput {
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSelectHandles &storage = node_storage(params.node());
- const BezierSpline::HandleType handle_type = handle_type_from_input_type(
+ const HandleType handle_type = handle_type_from_input_type(
(GeometryNodeCurveHandleType)storage.handle_type);
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
index 82621189964..d5769c691c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -14,13 +14,13 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
- if (!curve_set.has_curve()) {
+ if (!curve_set.has_curves()) {
params.set_default_remaining_outputs();
return;
}
- const CurveEval &curve = *curve_set.get_curve_for_read();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curves_for_read());
float length = 0.0f;
- for (const SplinePtr &spline : curve.splines()) {
+ for (const SplinePtr &spline : curve->splines()) {
length += spline->length();
}
params.set_output("Length", length);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
index 9919e24473e..6c7d7ed375b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
@@ -1,11 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include <numeric>
+
#include "BLI_math_base_safe.h"
+
+#include "BKE_curves.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+
#include "node_geometry_util.hh"
-#include <numeric>
namespace blender::nodes::node_geo_curve_primitive_arc_cc {
@@ -139,32 +143,24 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
return (ELEM(a, b, b * -1.0f));
}
-static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution,
- const float3 a,
- const float3 b,
- const float3 c,
- float angle_offset,
- const bool connect_center,
- const bool invert_arc,
- float3 &r_center,
- float3 &r_normal,
- float &r_radius)
+static Curves *create_arc_curve_from_points(const int resolution,
+ const float3 a,
+ const float3 b,
+ const float3 c,
+ float angle_offset,
+ const bool connect_center,
+ const bool invert_arc,
+ float3 &r_center,
+ float3 &r_normal,
+ float &r_radius)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
-
- if (connect_center) {
- spline->resize(resolution + 1);
- }
- else {
- spline->resize(resolution);
- }
+ const int size = connect_center ? resolution + 1 : resolution;
+ Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
const int stepcount = resolution - 1;
const int centerpoint = resolution;
- MutableSpan<float3> positions = spline->positions();
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
+ MutableSpan<float3> positions = curves.positions();
const bool is_colinear = colinear_f3_f3_f3(a, b, c);
@@ -254,7 +250,7 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut
}
if (connect_center) {
- spline->set_cyclic(true);
+ curves.cyclic().first() = true;
positions[centerpoint] = center;
}
@@ -263,36 +259,26 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut
normal = -normal;
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
r_center = center;
r_radius = radius;
r_normal = normal;
- return curve;
+ return curves_id;
}
-static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution,
- const float radius,
- const float start_angle,
- const float sweep_angle,
- const bool connect_center,
- const bool invert_arc)
+static Curves *create_arc_curve_from_radius(const int resolution,
+ const float radius,
+ const float start_angle,
+ const float sweep_angle,
+ const bool connect_center,
+ const bool invert_arc)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
-
- if (connect_center) {
- spline->resize(resolution + 1);
- }
- else {
- spline->resize(resolution);
- }
+ const int size = connect_center ? resolution + 1 : resolution;
+ Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
const int stepcount = resolution - 1;
const int centerpoint = resolution;
- MutableSpan<float3> positions = spline->positions();
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
+ MutableSpan<float3> positions = curves.positions();
const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle;
@@ -305,13 +291,11 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolut
}
if (connect_center) {
- spline->set_cyclic(true);
+ curves.cyclic().first() = true;
positions[centerpoint] = float3(0.0f, 0.0f, 0.0f);
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+ return curves_id;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -322,35 +306,35 @@ static void node_geo_exec(GeoNodeExecParams params)
switch (mode) {
case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: {
- std::unique_ptr<CurveEval> curve;
float3 r_center, r_normal;
float r_radius;
- curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2),
- params.extract_input<float3>("Start"),
- params.extract_input<float3>("Middle"),
- params.extract_input<float3>("End"),
- params.extract_input<float>("Offset Angle"),
- params.extract_input<bool>("Connect Center"),
- params.extract_input<bool>("Invert Arc"),
- r_center,
- r_normal,
- r_radius);
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ Curves *curves = create_arc_curve_from_points(
+ std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float3>("Start"),
+ params.extract_input<float3>("Middle"),
+ params.extract_input<float3>("End"),
+ params.extract_input<float>("Offset Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"),
+ r_center,
+ r_normal,
+ r_radius);
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
params.set_output("Center", r_center);
params.set_output("Normal", r_normal);
params.set_output("Radius", r_radius);
break;
}
case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: {
- std::unique_ptr<CurveEval> curve;
- curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2),
- params.extract_input<float>("Radius"),
- params.extract_input<float>("Start Angle"),
- params.extract_input<float>("Sweep Angle"),
- params.extract_input<bool>("Connect Center"),
- params.extract_input<bool>("Invert Arc"));
-
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ Curves *curves = create_arc_curve_from_radius(
+ std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float>("Radius"),
+ params.extract_input<float>("Start Angle"),
+ params.extract_input<float>("Sweep Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"));
+
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
break;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
index c6b9018f0db..78e1613b630 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
@@ -69,21 +69,30 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve(
spline->resize(2);
MutableSpan<float3> positions = spline->positions();
- spline->handle_types_left().fill(BezierSpline::HandleType::Align);
- spline->handle_types_right().fill(BezierSpline::HandleType::Align);
+ spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
+ spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
spline->radii().fill(1.0f);
spline->tilts().fill(0.0f);
positions.first() = start;
positions.last() = end;
+ MutableSpan<float3> handles_right = spline->handle_positions_right();
+ MutableSpan<float3> handles_left = spline->handle_positions_left();
+
if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) {
- spline->set_handle_position_right(0, start_handle_right);
- spline->set_handle_position_left(1, end_handle_left);
+ handles_left.first() = 2.0f * start - start_handle_right;
+ handles_right.first() = start_handle_right;
+
+ handles_left.last() = end_handle_left;
+ handles_right.last() = 2.0f * end - end_handle_left;
}
else {
- spline->set_handle_position_right(0, start + start_handle_right);
- spline->set_handle_position_left(1, end + end_handle_left);
+ handles_left.first() = start - start_handle_right;
+ handles_right.first() = start + start_handle_right;
+
+ handles_left.last() = end + end_handle_left;
+ handles_right.last() = end - end_handle_left;
}
curve->add_spline(std::move(spline));
@@ -104,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float3>("End Handle"),
std::max(params.extract_input<int>("Resolution"), 1),
mode);
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
index 44505b61a27..874e29dda86 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -92,7 +92,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
return (ELEM(a, b, b * -1.0f));
}
-static std::unique_ptr<CurveEval> create_point_circle_curve(
+static Curves *create_point_circle_curve(
const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center)
{
if (colinear_f3_f3_f3(p1, p2, p3)) {
@@ -100,11 +100,11 @@ static std::unique_ptr<CurveEval> create_point_circle_curve(
return nullptr;
}
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ curves.cyclic().first() = true;
- spline->resize(resolution);
- MutableSpan<float3> positions = spline->positions();
+ MutableSpan<float3> positions = curves.positions();
float3 center;
/* Midpoints of `P1->P2` and `P2->P3`. */
@@ -147,24 +147,17 @@ static std::unique_ptr<CurveEval> create_point_circle_curve(
positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4;
}
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- spline->set_cyclic(true);
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
-
r_center = center;
- return curve;
+ return curves_id;
}
-static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolution,
- const float radius)
+static Curves *create_radius_circle_curve(const int resolution, const float radius)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ curves.cyclic().first() = true;
- spline->resize(resolution);
- MutableSpan<float3> positions = spline->positions();
+ MutableSpan<float3> positions = curves.positions();
const float theta_step = (2.0f * M_PI) / float(resolution);
for (int i : IndexRange(resolution)) {
@@ -173,12 +166,8 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio
const float y = radius * sin(theta);
positions[i] = float3(x, y, 0.0f);
}
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- spline->set_cyclic(true);
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+
+ return curves_id;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -187,23 +176,23 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode)
storage.mode;
- std::unique_ptr<CurveEval> curve;
+ Curves *curves;
if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) {
float3 center_point;
- curve = create_point_circle_curve(params.extract_input<float3>("Point 1"),
- params.extract_input<float3>("Point 2"),
- params.extract_input<float3>("Point 3"),
- std::max(params.extract_input<int>("Resolution"), 3),
- center_point);
+ curves = create_point_circle_curve(params.extract_input<float3>("Point 1"),
+ params.extract_input<float3>("Point 2"),
+ params.extract_input<float3>("Point 3"),
+ std::max(params.extract_input<int>("Resolution"), 3),
+ center_point);
params.set_output("Center", center_point);
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) {
- curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3),
- params.extract_input<float>("Radius"));
+ curves = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3),
+ params.extract_input<float>("Radius"));
}
- if (curve) {
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ if (curves) {
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
}
else {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
index d11af3b1cc0..2e2f4254752 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -60,39 +60,28 @@ static void node_update(bNodeTree *ntree, bNode *node)
ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION);
}
-static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end)
+static Curves *create_point_line_curve(const float3 start, const float3 end)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
-
- spline->resize(2);
- MutableSpan<float3> positions = spline->positions();
- positions[0] = start;
- positions[1] = end;
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+ Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+
+ curves.positions().first() = start;
+ curves.positions().last() = end;
+
+ return curves_id;
}
-static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start,
- const float3 direction,
- const float length)
+static Curves *create_direction_line_curve(const float3 start,
+ const float3 direction,
+ const float length)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
-
- spline->resize(2);
- MutableSpan<float3> positions = spline->positions();
- positions[0] = start;
- positions[1] = math::normalize(direction) * length + start;
-
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+ Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+
+ curves.positions().first() = start;
+ curves.positions().last() = math::normalize(direction) * length + start;
+
+ return curves_id;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -100,18 +89,18 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node());
const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode;
- std::unique_ptr<CurveEval> curve;
+ Curves *curves = nullptr;
if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) {
- curve = create_point_line_curve(params.extract_input<float3>("Start"),
- params.extract_input<float3>("End"));
+ curves = create_point_line_curve(params.extract_input<float3>("Start"),
+ params.extract_input<float3>("End"));
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION) {
- curve = create_direction_line_curve(params.extract_input<float3>("Start"),
- params.extract_input<float3>("Direction"),
- params.extract_input<float>("Length"));
+ curves = create_direction_line_curve(params.extract_input<float3>("Start"),
+ params.extract_input<float3>("Direction"),
+ params.extract_input<float>("Length"));
}
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
}
} // namespace blender::nodes::node_geo_curve_primitive_line_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
index 456f6e55c1e..37810ccaff5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc {
@@ -28,18 +28,15 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1,
- const float3 p2,
- const float3 p3,
- const int resolution)
+static Curves *create_quadratic_bezier_curve(const float3 p1,
+ const float3 p2,
+ const float3 p3,
+ const int resolution)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Curves *curves_id = bke::curves_new_nomain_single(resolution + 1, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
- spline->resize(resolution + 1);
- MutableSpan<float3> positions = spline->positions();
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
+ MutableSpan<float3> positions = curves.positions();
const float step = 1.0f / resolution;
for (const int i : IndexRange(resolution + 1)) {
@@ -49,19 +46,17 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1,
positions[i] = math::interpolate(q1, q2, factor);
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+ return curves_id;
}
static void node_geo_exec(GeoNodeExecParams params)
{
- std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve(
+ Curves *curves = create_quadratic_bezier_curve(
params.extract_input<float3>("Start"),
params.extract_input<float3>("Middle"),
params.extract_input<float3>("End"),
std::max(params.extract_input<int>("Resolution"), 3));
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
}
} // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
index b6a847eebf4..ad3123a6a4a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -216,13 +216,11 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node());
const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode;
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->resize(4);
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- spline->set_cyclic(true);
- MutableSpan<float3> positions = spline->positions();
+ Curves *curves_id = bke::curves_new_nomain_single(4, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ curves.cyclic().first() = true;
+
+ MutableSpan<float3> positions = curves.positions();
switch (mode) {
case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE:
@@ -262,9 +260,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curves(curves_id));
}
} // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
index f448ddabd2b..22619577d04 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "node_geometry_util.hh"
@@ -35,26 +35,23 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
- const int resolution,
- const float start_radius,
- const float end_radius,
- const float height,
- const bool direction)
+static Curves *create_spiral_curve(const float rotations,
+ const int resolution,
+ const float start_radius,
+ const float end_radius,
+ const float height,
+ const bool direction)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
-
const int totalpoints = std::max(int(resolution * rotations), 1);
const float delta_radius = (end_radius - start_radius) / (float)totalpoints;
const float delta_height = height / (float)totalpoints;
const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints *
(direction ? 1.0f : -1.0f);
- spline->resize(totalpoints + 1);
- MutableSpan<float3> positions = spline->positions();
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
+ Curves *curves_id = bke::curves_new_nomain_single(totalpoints + 1, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+
+ MutableSpan<float3> positions = curves.positions();
for (const int i : IndexRange(totalpoints + 1)) {
const float theta = i * delta_theta;
@@ -66,9 +63,7 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
positions[i] = {x, y, z};
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
- return curve;
+ return curves_id;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -79,14 +74,13 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- std::unique_ptr<CurveEval> curve = create_spiral_curve(
- rotations,
- std::max(params.extract_input<int>("Resolution"), 1),
- params.extract_input<float>("Start Radius"),
- params.extract_input<float>("End Radius"),
- params.extract_input<float>("Height"),
- params.extract_input<bool>("Reverse"));
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ Curves *curves = create_spiral_curve(rotations,
+ std::max(params.extract_input<int>("Resolution"), 1),
+ params.extract_input<float>("Start Radius"),
+ params.extract_input<float>("End Radius"),
+ params.extract_input<float>("Height"),
+ params.extract_input<bool>("Reverse"));
+ params.set_output("Curve", GeometrySet::create_with_curves(curves));
}
} // namespace blender::nodes::node_geo_curve_primitive_spiral_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
index 5969af43bc1..e7e899881cf 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "node_geometry_util.hh"
@@ -33,19 +33,16 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("An attribute field with a selection of the outer points"));
}
-static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius,
- const float outer_radius,
- const float twist,
- const int points)
+static Curves *create_star_curve(const float inner_radius,
+ const float outer_radius,
+ const float twist,
+ const int points)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->set_cyclic(true);
+ Curves *curves_id = bke::curves_new_nomain_single(points * 2, CURVE_TYPE_POLY);
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ curves.cyclic().first() = true;
- spline->resize(points * 2);
- MutableSpan<float3> positions = spline->positions();
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
+ MutableSpan<float3> positions = curves.positions();
const float theta_step = (2.0f * M_PI) / float(points);
for (const int i : IndexRange(points)) {
@@ -58,10 +55,7 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius,
positions[i * 2 + 1] = {inner_x, inner_y, 0.0f};
}
- curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
-
- return curve;
+ return curves_id;
}
static void create_selection_output(CurveComponent &component,
@@ -78,12 +72,11 @@ static void create_selection_output(CurveComponent &component,
static void node_geo_exec(GeoNodeExecParams params)
{
- std::unique_ptr<CurveEval> curve = create_star_curve(
- std::max(params.extract_input<float>("Inner Radius"), 0.0f),
- std::max(params.extract_input<float>("Outer Radius"), 0.0f),
- params.extract_input<float>("Twist"),
- std::max(params.extract_input<int>("Points"), 3));
- GeometrySet output = GeometrySet::create_with_curve(curve.release());
+ Curves *curves = create_star_curve(std::max(params.extract_input<float>("Inner Radius"), 0.0f),
+ std::max(params.extract_input<float>("Outer Radius"), 0.0f),
+ params.extract_input<float>("Twist"),
+ std::max(params.extract_input<int>("Points"), 3));
+ GeometrySet output = GeometrySet::create_with_curves(curves);
if (params.output_is_required("Outer Points")) {
StrongAnonymousAttributeID attribute_output("Outer Points");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index d2afeaa7094..c5814a9a1dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -158,7 +158,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component,
const SampleModeParam &mode_param)
{
- const CurveEval *input_curve = component->get_for_read();
+ const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read());
GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE};
const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE);
@@ -235,14 +235,14 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component
static void geometry_set_curve_resample(GeometrySet &geometry_set,
const SampleModeParam &mode_param)
{
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
std::unique_ptr<CurveEval> output_curve = resample_curve(
geometry_set.get_component_for_read<CurveComponent>(), mode_param);
- geometry_set.replace_curve(output_curve.release());
+ geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index 0ef3230937b..8393f9615aa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -20,7 +20,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
@@ -34,13 +34,15 @@ static void node_geo_exec(GeoNodeExecParams params)
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- CurveEval &curve = *component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write());
+ MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
splines[selection[i]]->reverse();
}
});
+
+ component.replace(curve_eval_to_curves(*curve));
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index 152828b284c..6661d03a851 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -122,12 +122,13 @@ class SampleCurveFunction : public fn::MultiFunction {
}
};
- if (!geometry_set_.has_curve()) {
+ if (!geometry_set_.has_curves()) {
return return_default();
}
const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
- const CurveEval *curve = curve_component->get_for_read();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *curve_component->get_for_read());
Span<SplinePtr> splines = curve->splines();
if (splines.is_empty()) {
return return_default();
@@ -234,12 +235,13 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const CurveEval *curve = component->get_for_read();
- if (curve == nullptr) {
+ if (!component->has_curves()) {
params.set_default_remaining_outputs();
return;
}
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read());
+
if (curve->splines().is_empty()) {
params.set_default_remaining_outputs();
return;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
index 3d7b2fddf72..e8da4154586 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -33,20 +33,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
- return BezierSpline::HandleType::Align;
+ return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
- return BezierSpline::HandleType::Free;
+ return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
- return BezierSpline::HandleType::Vector;
+ return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
- return BezierSpline::HandleType::Auto;
+ return BEZIER_HANDLE_AUTO;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -60,14 +60,14 @@ static void node_geo_exec(GeoNodeExecParams params)
bool has_bezier_spline = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
/* Retrieve data for write access so we can avoid new allocations for the handles data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
+ MutableSpan<SplinePtr> splines = curve->splines();
GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
@@ -77,18 +77,18 @@ static void node_geo_exec(GeoNodeExecParams params)
selection_evaluator.evaluate();
const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
- const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ const HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;
for (SplinePtr &spline : splines) {
- if (spline->type() != Spline::Type::Bezier) {
+ if (spline->type() != CURVE_TYPE_BEZIER) {
point_index += spline->positions().size();
continue;
}
has_bezier_spline = true;
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
- if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) {
/* In this case the automatically calculated handle types need to be "baked", because
* they're possibly changing from a type that is calculated automatically to a type that
* is positioned manually. */
@@ -108,6 +108,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
bezier_spline.mark_cache_invalid();
}
+
+ curve_component.replace(curve_eval_to_curves(*curve));
});
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
index a303be99242..3edaccba506 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
@@ -100,18 +100,22 @@ static Array<float> curve_length_point_domain(const CurveEval &curve)
MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())};
spline_factors.first() = 0.0f;
switch (splines[i]->type()) {
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors);
break;
}
- case Spline::Type::Poly: {
+ case CURVE_TYPE_POLY: {
calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors);
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors);
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
});
@@ -201,8 +205,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
- if (curve) {
+ if (curve_component.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *curve_component.get_for_read());
return construct_curve_parameter_varray(*curve, mask, domain);
}
}
@@ -234,8 +239,8 @@ class CurveLengthFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
- if (curve) {
+ if (curve_component.has_curves()) {
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
return construct_curve_length_varray(*curve, mask, domain);
}
}
@@ -267,8 +272,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
- if (curve) {
+ if (curve_component.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *curve_component.get_for_read());
return construct_index_on_spline_varray(*curve, mask, domain);
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 4e4cabd3c33..55610ec86ab 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -259,8 +259,8 @@ static SplinePtr poly_to_bezier(const Spline &input)
output->positions().copy_from(input.positions());
output->radii().copy_from(input.radii());
output->tilts().copy_from(input.tilts());
- output->handle_types_left().fill(BezierSpline::HandleType::Vector);
- output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
+ output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
output->set_resolution(12);
Spline::copy_base_settings(input, *output);
output->attributes = input.attributes;
@@ -298,8 +298,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode);
scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left());
scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right());
- output->handle_types_left().fill(BezierSpline::HandleType::Align);
- output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
+ output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
output->set_resolution(nurbs_spline.resolution());
Spline::copy_base_settings(nurbs_spline, *output);
output->attributes.reallocate(output->size());
@@ -315,11 +315,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
{
switch (input.type()) {
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
return input.copy();
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
return poly_to_bezier(input);
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
if (input.size() < 4) {
params.error_message_add(
NodeWarningType::Info,
@@ -327,6 +327,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
return input.copy();
}
return nurbs_to_bezier(input);
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ return {};
+ }
}
BLI_assert_unreachable();
return {};
@@ -335,12 +339,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
static SplinePtr convert_to_nurbs(const Spline &input)
{
switch (input.type()) {
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
return input.copy();
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
return bezier_to_nurbs(input);
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
return poly_to_nurbs(input);
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ return {};
}
BLI_assert_unreachable();
return {};
@@ -355,45 +362,48 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component->get_for_read();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *curve_component->get_for_read());
GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+ Span<SplinePtr> src_splines = curve->splines();
+
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
- new_curve->resize(curve.splines().size());
+ new_curve->resize(src_splines.size());
- threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
if (selection[i]) {
switch (output_type) {
case GEO_NODE_SPLINE_TYPE_POLY:
- new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]);
+ new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]);
break;
case GEO_NODE_SPLINE_TYPE_BEZIER:
- new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params);
+ new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params);
break;
case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]);
+ new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]);
break;
}
}
else {
- new_curve->splines()[i] = curve.splines()[i]->copy();
+ new_curve->splines()[i] = src_splines[i]->copy();
}
}
});
- new_curve->attributes = curve.attributes;
- geometry_set.replace_curve(new_curve.release());
+ new_curve->attributes = curve->attributes;
+ geometry_set.replace_curve(curve_eval_to_curves(*new_curve));
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 9daf3ff0594..bbe57b2b3fa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -93,8 +93,8 @@ static void subdivide_bezier_segment(const BezierSpline &src,
MutableSpan<float3> dst_positions,
MutableSpan<float3> dst_handles_left,
MutableSpan<float3> dst_handles_right,
- MutableSpan<BezierSpline::HandleType> dst_type_left,
- MutableSpan<BezierSpline::HandleType> dst_type_right)
+ MutableSpan<int8_t> dst_type_left,
+ MutableSpan<int8_t> dst_type_right)
{
const bool is_last_cyclic_segment = index == (src.size() - 1);
const int next_index = is_last_cyclic_segment ? 0 : index + 1;
@@ -104,10 +104,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
if (src.segment_is_vector(index)) {
if (is_last_cyclic_segment) {
- dst_type_left.first() = BezierSpline::HandleType::Vector;
+ dst_type_left.first() = BEZIER_HANDLE_VECTOR;
}
- dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
- dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
+ dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR);
+ dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR);
const float factor_delta = 1.0f / result_size;
for (const int cut : IndexRange(result_size)) {
@@ -118,10 +118,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
}
else {
if (is_last_cyclic_segment) {
- dst_type_left.first() = BezierSpline::HandleType::Free;
+ dst_type_left.first() = BEZIER_HANDLE_FREE;
}
- dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
- dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
+ dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE);
+ dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE);
const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
@@ -169,8 +169,8 @@ static void subdivide_bezier_spline(const BezierSpline &src,
MutableSpan<float3> dst_positions = dst.positions();
MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
- MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
- MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
+ MutableSpan<int8_t> dst_type_left = dst.handle_types_left();
+ MutableSpan<int8_t> dst_type_right = dst.handle_types_right();
threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
for (const int i : range) {
@@ -217,26 +217,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline,
subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
switch (src_spline.type()) {
- case Spline::Type::Poly: {
+ case CURVE_TYPE_POLY: {
const PolySpline &src = static_cast<const PolySpline &>(src_spline);
PolySpline &dst = static_cast<PolySpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
break;
}
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
subdivide_bezier_spline(src, offsets, dst);
dst.mark_cache_invalid();
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
@@ -320,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<int> cuts_field = params.extract_input<Field<int>>("Cuts");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
@@ -336,9 +340,9 @@ static void node_geo_exec(GeoNodeExecParams params)
if (cuts.is_single() && cuts.get_internal_single() < 1) {
return;
}
-
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
- geometry_set.replace_curve(output_curve.release());
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(
+ *curves_to_curve_eval(*component.get_for_read()), cuts);
+ geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
});
params.set_output("Curve", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index ed497b6fbe0..e7a8c61290b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -27,14 +27,16 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const bool fill_caps)
{
- const CurveEval *curve = geometry_set.get_curve_for_read();
- const CurveEval *profile_curve = profile_set.get_curve_for_read();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ const Curves *profile_curves = profile_set.get_curves_for_read();
- if (profile_curve == nullptr) {
+ if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(*curve);
geometry_set.replace_mesh(mesh);
}
else {
+ const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves);
Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
geometry_set.replace_mesh(mesh);
}
@@ -46,10 +48,10 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
const bool fill_caps = params.extract_input<bool>("Fill Caps");
- bool has_curve = false;
+ bool has_curves = false;
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve()) {
- has_curve = true;
+ if (geometry_set.has_curves()) {
+ has_curves = true;
geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
}
geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 7481f7248a1..1eb18b2f910 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -321,15 +321,16 @@ static void node_geo_exec(GeoNodeExecParams params)
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- const CurveEval &curve = *geometry_set.get_curve_for_read();
- const Span<SplinePtr> splines = curve.splines();
- curve.assert_valid_point_attributes();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ const Span<SplinePtr> splines = curve->splines();
+ curve->assert_valid_point_attributes();
- const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
+ const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_size = offsets.last();
if (total_size == 0) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
@@ -339,7 +340,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size));
PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
ResultAttributes point_attributes = create_attributes_for_transfer(
- points, curve, attribute_outputs);
+ points, *curve, attribute_outputs);
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT:
@@ -351,7 +352,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
- copy_spline_domain_attributes(curve, offsets, points);
+ copy_spline_domain_attributes(*curve, offsets, points);
if (!point_attributes.rotations.is_empty()) {
curve_create_default_rotation_attribute(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index abc5b1649c7..a3dab1b50fe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -367,15 +367,18 @@ static void trim_spline(SplinePtr &spline,
const Spline::LookupResult end)
{
switch (spline->type()) {
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
trim_bezier_spline(*spline, start, end);
break;
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
trim_poly_spline(*spline, start, end);
break;
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end));
break;
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ spline = {};
}
spline->mark_cache_invalid();
}
@@ -400,8 +403,8 @@ static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &l
const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion(
trim.left_index, trim.right_index, trim.factor);
bezier.positions().first() = new_point.position;
- bezier.handle_types_left().first() = BezierSpline::HandleType::Free;
- bezier.handle_types_right().first() = BezierSpline::HandleType::Free;
+ bezier.handle_types_left().first() = BEZIER_HANDLE_FREE;
+ bezier.handle_types_right().first() = BEZIER_HANDLE_FREE;
bezier.handle_positions_left().first() = new_point.left_handle;
bezier.handle_positions_right().first() = new_point.right_handle;
@@ -477,15 +480,18 @@ static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::Look
static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup)
{
switch (spline->type()) {
- case Spline::Type::Bezier:
+ case CURVE_TYPE_BEZIER:
to_single_point_bezier(*spline, lookup);
break;
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
to_single_point_poly(*spline, lookup);
break;
- case Spline::Type::NURBS:
+ case CURVE_TYPE_NURBS:
spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup));
break;
+ case CURVE_TYPE_CATMULL_ROM:
+ BLI_assert_unreachable();
+ spline = {};
}
}
@@ -494,7 +500,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
Field<float> &start_field,
Field<float> &end_field)
{
- if (!geometry_set.has_curve()) {
+ if (!geometry_set.has_curves()) {
return;
}
@@ -509,8 +515,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0);
const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1);
- CurveEval &curve = *geometry_set.get_curve_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read());
+ MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
@@ -559,6 +565,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
}
}
});
+
+ geometry_set.replace_curve(curve_eval_to_curves(*curve));
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index 2eeb957c123..3baee8a25bb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -332,9 +332,9 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask);
copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask);
switch (spline.type()) {
- case Spline::Type::Poly:
+ case CURVE_TYPE_POLY:
break;
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask);
@@ -343,12 +343,16 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask);
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
copy_data_based_on_mask(src.weights(), dst.weights(), mask);
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
@@ -471,9 +475,9 @@ static void separate_curve_selection(GeometrySet &geometry_set,
selection_evaluator.evaluate();
const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
std::unique_ptr<CurveEval> r_curve = curve_separate(
- *src_component.get_for_read(), selection, selection_domain, invert);
+ *curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert);
if (r_curve) {
- geometry_set.replace_curve(r_curve.release());
+ geometry_set.replace_curve(curve_eval_to_curves(*r_curve));
}
else {
geometry_set.replace_curve(nullptr);
@@ -1282,7 +1286,7 @@ void separate_geometry(GeometrySet &geometry_set,
some_valid_domain = true;
}
}
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert);
some_valid_domain = true;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index 51932d341bc..bdd4d74fe4b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -152,6 +152,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
}
KDTree_3d *kdtree = build_kdtree(positions);
+ BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
for (const int i : positions.index_range()) {
if (elimination_mask[i]) {
@@ -176,8 +177,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
},
&callback_data);
}
-
- BLI_kdtree_3d_free(kdtree);
}
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
index a526f4f9e65..5a2c32a6c8e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -272,7 +272,7 @@ static void create_vertex_poly_map(const Mesh &mesh,
* boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The
* `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and
* `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the
- * corner in the i-th sorted polygon.
+ * corner in the i-th sorted polygon. If the polygons couldn't be sorted, `false` is returned.
*
* How the faces are sorted (see diagrams below):
* (For this explanation we'll assume all faces are oriented clockwise)
@@ -321,7 +321,7 @@ static void create_vertex_poly_map(const Mesh &mesh,
* - Finally if we are in the normal case we also need to add the last "shared edge" to close the
* loop.
*/
-static void sort_vertex_polys(const Mesh &mesh,
+static bool sort_vertex_polys(const Mesh &mesh,
const int vertex_index,
const bool boundary_vertex,
const Span<EdgeType> edge_types,
@@ -330,7 +330,7 @@ static void sort_vertex_polys(const Mesh &mesh,
MutableSpan<int> r_sorted_corners)
{
if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) {
- return;
+ return true;
}
/* For each polygon store the two corners whose edge contains the vertex. */
@@ -434,8 +434,11 @@ static void sort_vertex_polys(const Mesh &mesh,
break;
}
}
-
- BLI_assert(j != connected_polygons.size());
+ if (j == connected_polygons.size()) {
+ /* The vertex is not manifold because the polygons around the vertex don't form a loop, and
+ * hence can't be sorted. */
+ return false;
+ }
std::swap(connected_polygons[i + 1], connected_polygons[j]);
std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]);
@@ -445,6 +448,7 @@ static void sort_vertex_polys(const Mesh &mesh,
/* Shared edge between first and last polygon. */
r_shared_edges.last() = shared_edge_i;
}
+ return true;
}
/**
@@ -637,19 +641,26 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
}
MutableSpan<int> loop_indices = vertex_poly_indices[i];
Array<int> sorted_corners(loop_indices.size());
+ bool vertex_ok = true;
if (vertex_types[i] == VertexType::Normal) {
Array<int> shared_edges(loop_indices.size());
- sort_vertex_polys(
+ vertex_ok = sort_vertex_polys(
mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners);
- vertex_shared_edges[i] = shared_edges;
+ vertex_shared_edges[i] = std::move(shared_edges);
}
else {
Array<int> shared_edges(loop_indices.size() - 1);
- sort_vertex_polys(
+ vertex_ok = sort_vertex_polys(
mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners);
- vertex_shared_edges[i] = shared_edges;
+ vertex_shared_edges[i] = std::move(shared_edges);
+ }
+ if (!vertex_ok) {
+ /* The sorting failed which means that the vertex is non-manifold and should be ignored
+ * further on. */
+ vertex_types[i] = VertexType::NonManifold;
+ continue;
}
- vertex_corners[i] = sorted_corners;
+ vertex_corners[i] = std::move(sorted_corners);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
new file mode 100644
index 00000000000..1ceab18c01b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -0,0 +1,1119 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_map.hh"
+#include "BLI_noise.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_duplicate_elements_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryDuplicateElements);
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field();
+ b.add_input<decl::Int>(N_("Amount"))
+ .min(0)
+ .default_value(1)
+ .supports_field()
+ .description(N_("The number of duplicates to create for each element"));
+
+ b.add_output<decl::Geometry>(N_("Geometry"))
+ .description(
+ N_("The duplicated geometry only. The output does not contain the original geometry"));
+ b.add_output<decl::Int>(N_("Duplicate Index"))
+ .field_source()
+ .description(N_("The indices of the duplicates for each element"));
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__);
+ data->domain = ATTR_DOMAIN_POINT;
+ node->storage = data;
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+struct IndexAttributes {
+ StrongAnonymousAttributeID duplicate_index;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Copy/Creation Functions
+ * \{ */
+
+static void gather_attributes_without_id(const GeometrySet &geometry_set,
+ const GeometryComponentType component_type,
+ const Span<std::string> skip_attributes,
+ const bool include_instances,
+ Map<AttributeIDRef, AttributeKind> &r_gathered_attributes)
+{
+ geometry_set.gather_attributes_for_propagation(
+ {component_type}, component_type, include_instances, r_gathered_attributes);
+ for (const std::string &attribute : skip_attributes) {
+ r_gathered_attributes.remove(attribute);
+ }
+ r_gathered_attributes.remove("id");
+};
+
+static IndexRange range_for_offsets_index(const Span<int> offsets, const int index)
+{
+ return {offsets[index], offsets[index + 1] - offsets[index]};
+}
+
+static Array<int> accumulate_counts_to_offsets(const IndexMask selection,
+ const VArray<int> &counts)
+{
+ Array<int> offsets(selection.size() + 1);
+ int dst_points_size = 0;
+ for (const int i_point : selection.index_range()) {
+ offsets[i_point] = dst_points_size;
+ dst_points_size += std::max(counts[selection[i_point]], 0);
+ }
+ offsets.last() = dst_points_size;
+ return offsets;
+}
+
+/* Utility functions for threaded copying of attribute data where possible. */
+template<typename T>
+static void threaded_slice_fill(Span<int> offsets, Span<T> src, MutableSpan<T> dst)
+{
+ BLI_assert(offsets.last() == dst.size());
+ threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]);
+ }
+ });
+}
+
+template<typename T>
+static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst)
+{
+ threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst[i] = src[mapping[i]];
+ }
+ });
+}
+
+static void threaded_id_offset_copy(const Span<int> offsets,
+ const Span<int> src,
+ MutableSpan<int> dst)
+{
+ BLI_assert(offsets.last() == dst.size());
+ threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst[offsets[i]] = src[i];
+ const int count = offsets[i + 1] - offsets[i];
+ for (const int i_duplicate : IndexRange(1, count - 1)) {
+ dst[offsets[i] + i_duplicate] = noise::hash(src[i], i_duplicate);
+ }
+ }
+ });
+}
+
+/** Create the copy indices for the duplication domain. */
+static void create_duplicate_index_attribute(GeometryComponent &component,
+ const AttributeDomain output_domain,
+ const IndexMask selection,
+ const IndexAttributes &attributes,
+ const Span<int> offsets)
+{
+ OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>(
+ attributes.duplicate_index.get(), output_domain);
+ MutableSpan<int> duplicate_indices = copy_attribute.as_span();
+ for (const int i : IndexRange(selection.size())) {
+ const IndexRange range = range_for_offsets_index(offsets, i);
+ MutableSpan<int> indices = duplicate_indices.slice(range);
+ for (const int i : indices.index_range()) {
+ indices[i] = i;
+ }
+ }
+ copy_attribute.save();
+}
+
+/**
+ * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id
+ * and the duplicate number. This function is used for the point domain elements.
+ */
+static void copy_stable_id_point(const Span<int> offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ if (!src_attribute) {
+ return;
+ }
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ if (!dst_attribute) {
+ return;
+ }
+
+ VArray_Span<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.as_span<int>();
+ threaded_id_offset_copy(offsets, src, dst);
+ dst_attribute.save();
+}
+
+/**
+ * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id
+ * and the duplicate number. This function is used for points when duplicating the edge domain.
+ */
+static void copy_stable_id_edges(const Mesh &mesh,
+ const IndexMask selection,
+ const Span<int> edge_offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ if (!src_attribute) {
+ return;
+ }
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ if (!dst_attribute) {
+ return;
+ }
+
+ Span<MEdge> edges(mesh.medge, mesh.totedge);
+
+ VArray_Span<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.as_span<int>();
+ threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) {
+ for (const int i_edge : range) {
+ const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge);
+ if (edge_range.size() == 0) {
+ continue;
+ }
+ const MEdge &edge = edges[i_edge];
+ const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2};
+
+ dst[vert_range[0]] = src[edge.v1];
+ dst[vert_range[1]] = src[edge.v2];
+ for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) {
+ dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate);
+ dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate);
+ }
+ }
+ });
+ dst_attribute.save();
+}
+
+/**
+ * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id
+ * and the duplicate number. This function is used for points when duplicating the face domain.
+ *
+ * This function could be threaded in the future, but since it is only 1 attribute and the
+ * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it?
+ */
+static void copy_stable_id_faces(const Mesh &mesh,
+ const IndexMask selection,
+ const Span<int> poly_offsets,
+ const Span<int> vert_mapping,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ if (!src_attribute) {
+ return;
+ }
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ if (!dst_attribute) {
+ return;
+ }
+
+ VArray_Span<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.as_span<int>();
+
+ Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ int loop_index = 0;
+ for (const int i_poly : selection.index_range()) {
+ const IndexRange range = range_for_offsets_index(poly_offsets, i_poly);
+ if (range.size() == 0) {
+ continue;
+ }
+ const MPoly &source = polys[i_poly];
+ for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) {
+ for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) {
+ if (i_duplicate == 0) {
+ dst[loop_index] = src[vert_mapping[loop_index]];
+ }
+ else {
+ dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate);
+ }
+ loop_index++;
+ }
+ }
+ }
+
+ dst_attribute.save();
+}
+
+/**
+ * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id
+ * and the duplicate number. In the spline case, copy the entire spline's points to the
+ * destination,
+ * then loop over the remaining ones point by point, hashing their ids to the new ids.
+ */
+static void copy_stable_id_splines(const CurveEval &curve,
+ const IndexMask selection,
+ const Span<int> curve_offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ if (!src_attribute) {
+ return;
+ }
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ if (!dst_attribute) {
+ return;
+ }
+
+ Array<int> control_point_offsets = curve.control_point_offsets();
+ VArray_Span<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.as_span<int>();
+
+ Array<int> curve_point_offsets(selection.size() + 1);
+ int dst_point_size = 0;
+ for (const int i_curve : selection.index_range()) {
+ const int spline_size = curve.splines()[i_curve]->size();
+ const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve);
+
+ curve_point_offsets[i_curve] = dst_point_size;
+ dst_point_size += curve_range.size() * spline_size;
+ }
+ curve_point_offsets.last() = dst_point_size;
+
+ threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) {
+ for (const int i_curve : range) {
+ const int spline_size = curve.splines()[i_curve]->size();
+ const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve);
+
+ dst.slice(curve_point_offsets[i_curve], spline_size)
+ .copy_from(src.slice(control_point_offsets[i_curve], spline_size));
+ for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) {
+ for (const int i_point : IndexRange(spline_size)) {
+ dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash(
+ src[control_point_offsets[i_curve] + i_point], i_duplicate);
+ }
+ }
+ }
+ });
+ dst_attribute.save();
+}
+
+/* The attributes for the point (also instance) duplicated elements are stored sequentially
+ * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if
+ * elements are ordered differently a custom function is called to copy the attributes.
+ */
+
+static void copy_point_attributes_without_id(GeometrySet &geometry_set,
+ const GeometryComponentType component_type,
+ const bool include_instances,
+ const Span<int> offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ Map<AttributeIDRef, AttributeKind> gathered_attributes;
+ gather_attributes_without_id(
+ geometry_set, component_type, {}, include_instances, gathered_attributes);
+
+ for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) {
+ continue;
+ }
+ AttributeDomain out_domain = src_attribute.domain;
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
+ src_attribute.varray.type());
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, out_domain, data_type);
+ if (!dst_attribute) {
+ continue;
+ }
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> src = src_attribute.varray.typed<T>();
+ MutableSpan<T> dst = dst_attribute.as_span<T>();
+ threaded_slice_fill<T>(offsets, src, dst);
+ });
+ dst_attribute.save();
+ }
+}
+
+/**
+ * Copies the attributes for spline duplicates. If copying the spline domain, the attributes are
+ * copied with an offset fill, otherwise a mapping is used.
+ */
+static void copy_spline_attributes_without_id(const GeometrySet &geometry_set,
+ const Span<int> point_mapping,
+ const Span<int> offsets,
+ const Span<std::string> attributes_to_ignore,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ Map<AttributeIDRef, AttributeKind> gathered_attributes;
+ gather_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes);
+
+ for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) {
+
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ if (!src_attribute) {
+ continue;
+ }
+
+ AttributeDomain out_domain = src_attribute.domain;
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
+ src_attribute.varray.type());
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, out_domain, data_type);
+ if (!dst_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.as_span<T>();
+
+ switch (out_domain) {
+ case ATTR_DOMAIN_CURVE:
+ threaded_slice_fill<T>(offsets, src, dst);
+ break;
+ case ATTR_DOMAIN_POINT:
+ threaded_mapped_copy<T>(point_mapping, src, dst);
+ break;
+ default:
+ break;
+ }
+ });
+ dst_attribute.save();
+ }
+}
+
+/**
+ * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are
+ * copied with an offset fill, for point domain a mapping is used.
+ */
+static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
+ const Span<int> point_mapping,
+ const Span<int> offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ Map<AttributeIDRef, AttributeKind> gathered_attributes;
+ gather_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes);
+
+ for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ if (!src_attribute) {
+ continue;
+ }
+
+ const AttributeDomain out_domain = src_attribute.domain;
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
+ src_attribute.varray.type());
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, out_domain, data_type);
+ if (!dst_attribute) {
+ continue;
+ }
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.as_span<T>();
+
+ switch (out_domain) {
+ case ATTR_DOMAIN_EDGE:
+ threaded_slice_fill<T>(offsets, src, dst);
+ break;
+ case ATTR_DOMAIN_POINT:
+ threaded_mapped_copy<T>(point_mapping, src, dst);
+ break;
+ default:
+ break;
+ }
+ });
+ dst_attribute.save();
+ }
+}
+
+/**
+ * Copies the attributes for face duplicates. If copying the face domain, the attributes are
+ * copied with an offset fill, otherwise a mapping is used.
+ */
+static void copy_face_attributes_without_id(GeometrySet &geometry_set,
+ const Span<int> edge_mapping,
+ const Span<int> vert_mapping,
+ const Span<int> loop_mapping,
+ const Span<int> offsets,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ Map<AttributeIDRef, AttributeKind> gathered_attributes;
+ gather_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes);
+
+ for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ if (!src_attribute) {
+ continue;
+ }
+
+ AttributeDomain out_domain = src_attribute.domain;
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
+ src_attribute.varray.type());
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, out_domain, data_type);
+ if (!dst_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.as_span<T>();
+
+ switch (out_domain) {
+ case ATTR_DOMAIN_FACE:
+ threaded_slice_fill<T>(offsets, src, dst);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ threaded_mapped_copy<T>(edge_mapping, src, dst);
+ break;
+ case ATTR_DOMAIN_POINT:
+ threaded_mapped_copy<T>(vert_mapping, src, dst);
+ break;
+ case ATTR_DOMAIN_CORNER:
+ threaded_mapped_copy<T>(loop_mapping, src, dst);
+ break;
+ default:
+ break;
+ }
+ });
+ dst_attribute.save();
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Duplication Functions
+ * \{ */
+
+static void duplicate_splines(GeometrySet &geometry_set,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ IndexAttributes &attributes)
+{
+ if (!geometry_set.has_curves()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+
+ const GeometryComponent &src_component = *geometry_set.get_component_for_read(
+ GEO_COMPONENT_TYPE_CURVE);
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ Array<int> curve_offsets(selection.size() + 1);
+
+ int dst_splines_size = 0;
+ int dst_points_size = 0;
+ for (const int i_spline : selection.index_range()) {
+ int count = std::max(counts[selection[i_spline]], 0);
+ curve_offsets[i_spline] = dst_splines_size;
+ dst_splines_size += count;
+ dst_points_size += count * curve->splines()[selection[i_spline]]->size();
+ }
+ curve_offsets.last() = dst_splines_size;
+
+ Array<int> control_point_offsets = curve->control_point_offsets();
+ Array<int> point_mapping(dst_points_size);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ int point_index = 0;
+ for (const int i_spline : selection.index_range()) {
+ const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline);
+ for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) {
+ SplinePtr spline = curve->splines()[selection[i_spline]]->copy();
+ for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) {
+ point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point;
+ }
+ new_curve->add_spline(std::move(spline));
+ }
+ }
+ new_curve->attributes.reallocate(new_curve->splines().size());
+
+ CurveComponent dst_component;
+ dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable);
+
+ Vector<std::string> skip(
+ {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"});
+
+ copy_spline_attributes_without_id(
+ geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component);
+
+ copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets);
+ }
+
+ geometry_set.replace_curve(dst_component.get_for_write());
+}
+
+static void duplicate_faces(GeometrySet &geometry_set,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ IndexAttributes &attributes)
+{
+ if (!geometry_set.has_mesh()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+
+ GeometryComponent &component = geometry_set.get_component_for_write(GEO_COMPONENT_TYPE_MESH);
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator(field_context, domain_size);
+
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+ const Mesh &mesh = *mesh_component.get_for_read();
+ Span<MVert> verts(mesh.mvert, mesh.totvert);
+ Span<MEdge> edges(mesh.medge, mesh.totedge);
+ Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ Span<MLoop> loops(mesh.mloop, mesh.totloop);
+
+ int total_polys = 0;
+ int total_loops = 0;
+ Array<int> offsets(selection.size() + 1);
+ for (const int i_selection : selection.index_range()) {
+ const int count = std::max(counts[selection[i_selection]], 0);
+ offsets[i_selection] = total_polys;
+ total_polys += count;
+ total_loops += count * polys[selection[i_selection]].totloop;
+ }
+ offsets[selection.size()] = total_polys;
+
+ Array<int> vert_mapping(total_loops);
+ Array<int> edge_mapping(total_loops);
+ Array<int> loop_mapping(total_loops);
+
+ Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys);
+
+ MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
+ MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
+ MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop);
+ MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly);
+
+ int poly_index = 0;
+ int loop_index = 0;
+ for (const int i_selection : selection.index_range()) {
+ const IndexRange poly_range = range_for_offsets_index(offsets, i_selection);
+
+ const MPoly &source = polys[selection[i_selection]];
+ for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) {
+ new_poly[poly_index] = source;
+ new_poly[poly_index].loopstart = loop_index;
+ for (const int i_loops : IndexRange(source.totloop)) {
+ const MLoop &current_loop = loops[source.loopstart + i_loops];
+ loop_mapping[loop_index] = source.loopstart + i_loops;
+ new_verts[loop_index] = verts[current_loop.v];
+ vert_mapping[loop_index] = current_loop.v;
+ new_edges[loop_index] = edges[current_loop.e];
+ edge_mapping[loop_index] = current_loop.e;
+ new_edges[loop_index].v1 = loop_index;
+ if (i_loops + 1 != source.totloop) {
+ new_edges[loop_index].v2 = loop_index + 1;
+ }
+ else {
+ new_edges[loop_index].v2 = new_poly[poly_index].loopstart;
+ }
+ new_loops[loop_index].v = loop_index;
+ new_loops[loop_index].e = loop_index;
+ loop_index++;
+ }
+ poly_index++;
+ }
+ }
+ MeshComponent dst_component;
+ dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
+
+ copy_face_attributes_without_id(geometry_set,
+ edge_mapping,
+ vert_mapping,
+ loop_mapping,
+ offsets,
+ mesh_component,
+ dst_component);
+
+ copy_stable_id_faces(mesh, selection, offsets, vert_mapping, mesh_component, dst_component);
+ mesh_component.replace(dst_component.get_for_write());
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_FACE, selection, attributes, offsets);
+ }
+}
+
+static void duplicate_edges(GeometrySet &geometry_set,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ IndexAttributes &attributes)
+{
+ if (!geometry_set.has_mesh()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ };
+ const GeometryComponent &src_component = *geometry_set.get_component_for_read(
+ GEO_COMPONENT_TYPE_MESH);
+ const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts);
+
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ Span<MVert> verts(mesh->mvert, mesh->totvert);
+ Span<MEdge> edges(mesh->medge, mesh->totedge);
+
+ Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0);
+ MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
+ MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
+
+ Array<int> vert_orig_indices(edge_offsets.last() * 2);
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i_edge : range) {
+ const MEdge &edge = edges[i_edge];
+ const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge);
+ const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
+
+ for (const int i_duplicate : IndexRange(edge_range.size())) {
+ vert_orig_indices[vert_range[i_duplicate * 2]] = edge.v1;
+ vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge.v2;
+ }
+ }
+ });
+
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i_edge : range) {
+ const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge);
+ const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
+ for (const int i_duplicate : IndexRange(edge_range.size())) {
+ MEdge &new_edge = new_edges[edge_range[i_duplicate]];
+ new_edge.v1 = vert_range[i_duplicate * 2];
+ new_edge.v2 = vert_range[i_duplicate * 2] + 1;
+ }
+ }
+ });
+
+ MeshComponent dst_component;
+ dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
+
+ copy_edge_attributes_without_id(
+ geometry_set, vert_orig_indices, edge_offsets, src_component, dst_component);
+
+ copy_stable_id_edges(*mesh, selection, edge_offsets, src_component, dst_component);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_EDGE, selection, attributes, edge_offsets);
+ }
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(dst_component.get_for_write());
+}
+
+static void duplicate_points_curve(const GeometryComponentType component_type,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ GeometrySet &geometry_set,
+ IndexAttributes &attributes)
+{
+ const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type);
+ const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
+
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ Array<int> control_point_offsets = curve->control_point_offsets();
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+
+ Array<int> parent(domain_size);
+ int spline = 0;
+ for (const int i_spline : IndexRange(domain_size)) {
+ if (i_spline == control_point_offsets[spline + 1]) {
+ spline++;
+ }
+ parent[i_spline] = spline;
+ }
+
+ for (const int i_point : selection) {
+ const IndexRange point_range = range_for_offsets_index(offsets, i_point);
+ for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) {
+ const SplinePtr &parent_spline = curve->splines()[parent[i_point]];
+ switch (parent_spline->type()) {
+ case CurveType::CURVE_TYPE_BEZIER: {
+ std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->resize(1);
+ spline->set_resolution(2);
+ new_curve->add_spline(std::move(spline));
+ break;
+ }
+ case CurveType::CURVE_TYPE_NURBS: {
+ std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
+ spline->resize(1);
+ spline->set_resolution(2);
+ new_curve->add_spline(std::move(spline));
+ break;
+ }
+ case CurveType::CURVE_TYPE_POLY: {
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->resize(1);
+ new_curve->add_spline(std::move(spline));
+ break;
+ }
+ case CurveType::CURVE_TYPE_CATMULL_ROM: {
+ /* Catmull Rom curves are not supported yet. */
+ break;
+ }
+ }
+ }
+ }
+ new_curve->attributes.reallocate(new_curve->splines().size());
+ CurveComponent dst_component;
+ dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable);
+
+ copy_point_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component);
+
+ copy_stable_id_point(offsets, src_component, dst_component);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span());
+ }
+
+ curve_component.replace(dst_component.get_for_write());
+}
+
+static void duplicate_points_mesh(const GeometryComponentType component_type,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ GeometrySet &geometry_set,
+ IndexAttributes &attributes)
+{
+ const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type);
+ const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
+
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ Span<MVert> src_verts(mesh->mvert, mesh->totvert);
+
+ Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0);
+ MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert);
+
+ threaded_slice_fill<MVert>(offsets.as_span(), src_verts, dst_verts);
+
+ MeshComponent dst_component;
+ dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
+ copy_point_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_MESH, false, offsets, src_component, dst_component);
+
+ copy_stable_id_point(offsets, src_component, dst_component);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span());
+ }
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(dst_component.get_for_write());
+}
+
+static void duplicate_points_pointcloud(const GeometryComponentType component_type,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ GeometrySet &geometry_set,
+ IndexAttributes &attributes)
+{
+ const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type);
+ const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
+ PointCloudComponent dst_component;
+ dst_component.replace(pointcloud, GeometryOwnershipType::Editable);
+
+ copy_point_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_component, dst_component);
+
+ copy_stable_id_point(offsets, src_component, dst_component);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets);
+ }
+ geometry_set.replace_pointcloud(pointcloud);
+}
+
+static void duplicate_points(GeometrySet &geometry_set,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ IndexAttributes &attributes)
+{
+ if (!geometry_set.has_mesh() && !geometry_set.has_curves() && !geometry_set.has_pointcloud()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true);
+ Vector<GeometryComponentType> types_to_keep;
+ for (const GeometryComponentType component_type : component_types) {
+ switch (component_type) {
+ case GEO_COMPONENT_TYPE_POINT_CLOUD:
+ types_to_keep.append(component_type);
+ duplicate_points_pointcloud(
+ component_type, count_field, selection_field, geometry_set, attributes);
+ break;
+ case GEO_COMPONENT_TYPE_MESH:
+ types_to_keep.append(component_type);
+ duplicate_points_mesh(
+ component_type, count_field, selection_field, geometry_set, attributes);
+ break;
+ case GEO_COMPONENT_TYPE_CURVE:
+ types_to_keep.append(component_type);
+ duplicate_points_curve(
+ component_type, count_field, selection_field, geometry_set, attributes);
+ break;
+ default:
+ break;
+ }
+ }
+ types_to_keep.append(GEO_COMPONENT_TYPE_INSTANCES);
+ geometry_set.keep_only(types_to_keep);
+}
+
+static void duplicate_instances(GeometrySet &geometry_set,
+ const Field<int> &count_field,
+ const Field<bool> &selection_field,
+ IndexAttributes &attributes)
+{
+ if (!geometry_set.has_instances()) {
+ geometry_set.clear();
+ return;
+ }
+
+ const InstancesComponent &src_instances =
+ *geometry_set.get_component_for_read<InstancesComponent>();
+
+ const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
+ GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
+ FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(count_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<int> counts = evaluator.get_evaluated<int>(0);
+
+ Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
+
+ if (offsets.last() == 0) {
+ geometry_set.clear();
+ return;
+ }
+
+ GeometrySet instances_geometry;
+ InstancesComponent &dst_instances =
+ instances_geometry.get_component_for_write<InstancesComponent>();
+ dst_instances.resize(offsets.last());
+ for (const int i_selection : selection.index_range()) {
+ const int count = offsets[i_selection + 1] - offsets[i_selection];
+ if (count == 0) {
+ continue;
+ }
+ const int old_handle = src_instances.instance_reference_handles()[i_selection];
+ const InstanceReference reference = src_instances.references()[old_handle];
+ const int new_handle = dst_instances.add_reference(reference);
+ const float4x4 transform = src_instances.instance_transforms()[i_selection];
+ dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform);
+ dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle);
+ }
+
+ copy_point_attributes_without_id(
+ geometry_set, GEO_COMPONENT_TYPE_INSTANCES, true, offsets, src_instances, dst_instances);
+
+ if (attributes.duplicate_index) {
+ create_duplicate_index_attribute(
+ dst_instances, ATTR_DOMAIN_INSTANCE, selection, attributes, offsets);
+ }
+
+ geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES);
+ geometry_set.add(dst_instances);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const NodeGeometryDuplicateElements &storage = node_storage(params.node());
+ const AttributeDomain duplicate_domain = AttributeDomain(storage.domain);
+
+ Field<int> count_field = params.extract_input<Field<int>>("Amount");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ IndexAttributes attributes;
+ if (params.output_is_required("Duplicate Index")) {
+ attributes.duplicate_index = StrongAnonymousAttributeID("duplicate_index");
+ }
+
+ if (duplicate_domain == ATTR_DOMAIN_INSTANCE) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ duplicate_instances(geometry_set, count_field, selection_field, attributes);
+ }
+ else {
+ if (geometry_set.is_empty()) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ switch (duplicate_domain) {
+ case ATTR_DOMAIN_CURVE:
+ duplicate_splines(geometry_set, count_field, selection_field, attributes);
+ break;
+ case ATTR_DOMAIN_FACE:
+ duplicate_faces(geometry_set, count_field, selection_field, attributes);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ duplicate_edges(geometry_set, count_field, selection_field, attributes);
+ break;
+ case ATTR_DOMAIN_POINT:
+ duplicate_points(geometry_set, count_field, selection_field, attributes);
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+ }
+
+ if (geometry_set.is_empty()) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ if (attributes.duplicate_index) {
+ params.set_output(
+ "Duplicate Index",
+ AnonymousAttributeFieldInput::Create<int>(std::move(attributes.duplicate_index),
+ params.attribute_producer_name()));
+ }
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes::node_geo_duplicate_elements_cc
+
+void register_node_type_geo_duplicate_elements()
+{
+ namespace file_ns = blender::nodes::node_geo_duplicate_elements_cc;
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY);
+
+ node_type_storage(&ntype,
+ "NodeGeometryDuplicateElements",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ 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 4e053dbc1a3..c03a340a0c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -25,6 +25,9 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection)
BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
BMeshFromMeshParams bmesh_from_mesh_params{};
+ bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX;
+ bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX;
+ bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX;
BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params);
BM_mesh_elem_table_ensure(bm, BM_EDGE);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index 5aeeb72961d..82584f6f413 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -302,7 +302,6 @@ static void extrude_mesh_vertices(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
- BKE_mesh_normals_tag_dirty(&mesh);
}
static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
@@ -626,7 +625,6 @@ static void extrude_mesh_edges(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
- BKE_mesh_normals_tag_dirty(&mesh);
}
/**
@@ -995,7 +993,6 @@ static void extrude_mesh_face_regions(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
- BKE_mesh_normals_tag_dirty(&mesh);
}
/* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */
@@ -1263,7 +1260,6 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
- BKE_mesh_normals_tag_dirty(&mesh);
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
new file mode 100644
index 00000000000..62af0476057
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>("Threshold")
+ .field_source()
+ .default_value(0.01f)
+ .subtype(PROP_DISTANCE)
+ .supports_field()
+ .description(N_("The distance a point can be from the surface before the face is no longer "
+ "considered planar"))
+ .min(0.0f);
+ b.add_output<decl::Bool>("Planar").field_source();
+}
+
+class PlanarFieldInput final : public GeometryFieldInput {
+ private:
+ Field<float> threshold_;
+
+ public:
+ PlanarFieldInput(Field<float> threshold)
+ : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ [[maybe_unused]] IndexMask mask) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{context, mesh->totpoly};
+ evaluator.add(threshold_);
+ evaluator.evaluate();
+ const VArray<float> &thresholds = evaluator.get_evaluated<float>(0);
+
+ Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly};
+
+ auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool {
+ if (mesh->mpoly[i_poly].totloop <= 3) {
+ return true;
+ }
+ const int loopstart = mesh->mpoly[i_poly].loopstart;
+ const int loops = mesh->mpoly[i_poly].totloop;
+ Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops);
+ float3 reference_normal = poly_normals[i_poly];
+
+ float min = FLT_MAX;
+ float max = FLT_MIN;
+
+ for (const int i_loop : poly_loops.index_range()) {
+ const float3 vert = mesh->mvert[poly_loops[i_loop].v].co;
+ float dot = math::dot(reference_normal, vert);
+ if (dot > max) {
+ max = dot;
+ }
+ if (dot < min) {
+ min = dot;
+ }
+ }
+ return max - min < thresholds[i_poly] / 2.0f;
+ };
+
+ return component.attribute_try_adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 2356235652;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void geo_node_exec(GeoNodeExecParams params)
+{
+ Field<float> threshold = params.extract_input<Field<float>>("Threshold");
+ Field<bool> planar_field{std::make_shared<PlanarFieldInput>(threshold)};
+ params.set_output("Planar", std::move(planar_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc
+
+void register_node_type_geo_input_mesh_face_is_planar()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_face_is_planar_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, "Face is Planar", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::geo_node_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index 4537721d173..f952e15fbbe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -19,10 +19,10 @@ static void node_declare(NodeDeclarationBuilder &b)
static VArray<float> construct_spline_length_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
+ if (!component.has_curves()) {
return {};
}
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
Span<SplinePtr> splines = curve->splines();
auto length_fn = [splines](int i) { return splines[i]->length(); };
@@ -76,10 +76,10 @@ class SplineLengthFieldInput final : public GeometryFieldInput {
static VArray<int> construct_spline_count_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
+ if (!component.has_curves()) {
return {};
}
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
Span<SplinePtr> splines = curve->splines();
auto count_fn = [splines](int i) { return splines[i]->size(); };
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index 9ae99b4d83e..435dd969c03 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -52,18 +52,22 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve)
const Spline &spline = *splines[i];
MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())};
switch (splines[i]->type()) {
- case Spline::Type::Bezier: {
+ case CURVE_TYPE_BEZIER: {
calculate_bezier_tangents(static_cast<const BezierSpline &>(spline), spline_tangents);
break;
}
- case Spline::Type::Poly: {
+ case CURVE_TYPE_POLY: {
calculate_poly_tangents(static_cast<const PolySpline &>(spline), spline_tangents);
break;
}
- case Spline::Type::NURBS: {
+ case CURVE_TYPE_NURBS: {
calculate_nurbs_tangents(static_cast<const NURBSpline &>(spline), spline_tangents);
break;
}
+ case CURVE_TYPE_CATMULL_ROM: {
+ BLI_assert_unreachable();
+ break;
+ }
}
}
});
@@ -73,21 +77,12 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve)
static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
- return nullptr;
+ if (!component.has_curves()) {
+ return {};
}
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
if (domain == ATTR_DOMAIN_POINT) {
- const Span<SplinePtr> splines = curve->splines();
-
- /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy.
- * This is only possible when there is only one poly spline. */
- if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
- const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
- return VArray<float3>::ForSpan(spline.evaluated_tangents());
- }
-
Array<float3> tangents = curve_tangent_point_domain(*curve);
return VArray<float3>::ForContainer(std::move(tangents));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index df6d10991fb..61f719ade4e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -195,35 +195,24 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const Array<GeometryComponentType> types{
+ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
+
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
geometry_set.gather_attributes_for_propagation(
- {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE},
- GEO_COMPONENT_TYPE_INSTANCES,
- false,
- attributes_to_propagate);
+ types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate);
attributes_to_propagate.remove("position");
- if (geometry_set.has<MeshComponent>()) {
- add_instances_from_component(instances,
- *geometry_set.get_component_for_read<MeshComponent>(),
- instance,
- params,
- attributes_to_propagate);
- }
- if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_component(instances,
- *geometry_set.get_component_for_read<PointCloudComponent>(),
- instance,
- params,
- attributes_to_propagate);
- }
- if (geometry_set.has<CurveComponent>()) {
- add_instances_from_component(instances,
- *geometry_set.get_component_for_read<CurveComponent>(),
- instance,
- params,
- attributes_to_propagate);
+ for (const GeometryComponentType type : types) {
+ if (geometry_set.has(type)) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read(type),
+ instance,
+ params,
+ attributes_to_propagate);
+ }
}
+
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
index 3284378a2cb..91cde52f9eb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection);
- geometry_set.replace_curve(curve.release());
+ geometry_set.replace_curve(curve_eval_to_curves(*curve));
geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
});
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 1731ba64b97..c99b51ffd4c 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
@@ -198,17 +198,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
Vector<float3> positions;
Vector<float> radii;
- if (r_geometry_set.has<MeshComponent>()) {
- gather_point_data_from_component(
- params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii);
- }
- if (r_geometry_set.has<PointCloudComponent>()) {
- gather_point_data_from_component(
- params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii);
- }
- if (r_geometry_set.has<CurveComponent>()) {
- gather_point_data_from_component(
- params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii);
+ for (const GeometryComponentType type :
+ {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
+ if (r_geometry_set.has(type)) {
+ gather_point_data_from_component(
+ params, *r_geometry_set.get_component_for_read(type), positions, radii);
+ }
}
const float max_radius = *std::max_element(radii.begin(), radii.end());
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index 1797364ad72..231ef547a8b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -141,10 +141,13 @@ static void raycast_to_mesh(IndexMask mask,
{
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&tree_data); });
+
if (tree_data.tree == nullptr) {
- free_bvhtree_from_mesh(&tree_data);
return;
}
+ /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
+ BLI_assert(tree_data.cached);
for (const int i : mask) {
const float ray_length = ray_lengths[i];
@@ -197,10 +200,6 @@ static void raycast_to_mesh(IndexMask mask,
}
}
}
-
- /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
- BLI_assert(tree_data.cached);
- free_bvhtree_from_mesh(&tree_data);
}
class RaycastFunction : public fn::MultiFunction {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
index fb648baad08..301410f5126 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -35,7 +35,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
}
static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
- GeometryComponent &component,
+ CurveComponent &component,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
@@ -53,37 +53,31 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- CurveComponent *curve_component = static_cast<CurveComponent *>(&component);
- CurveEval *curve = curve_component->get_for_write();
-
- StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right";
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
int current_point = 0;
int current_mask = 0;
-
for (const SplinePtr &spline : curve->splines()) {
- if (spline->type() == Spline::Type::Bezier) {
+ if (spline->type() == CURVE_TYPE_BEZIER) {
BezierSpline &bezier = static_cast<BezierSpline &>(*spline);
- for (int i : bezier.positions().index_range()) {
+
+ bezier.ensure_auto_handles();
+ for (const int i : bezier.positions().index_range()) {
if (current_mask < selection.size() && selection[current_mask] == current_point) {
if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) {
- bezier.ensure_auto_handles();
- bezier.handle_types_left()[i] = BezierSpline::HandleType::Free;
+ if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) {
+ bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE;
}
- else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) {
- bezier.ensure_auto_handles();
- bezier.handle_types_left()[i] = BezierSpline::HandleType::Align;
+ else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) {
+ bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN;
}
}
else {
- if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) {
- bezier.ensure_auto_handles();
- bezier.handle_types_right()[i] = BezierSpline::HandleType::Free;
+ if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) {
+ bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE;
}
- else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) {
- bezier.ensure_auto_handles();
- bezier.handle_types_right()[i] = BezierSpline::HandleType::Align;
+ else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) {
+ bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN;
}
}
current_mask++;
@@ -104,15 +98,35 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0);
const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1);
- OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
- side, ATTR_DOMAIN_POINT, {0, 0, 0});
- MutableSpan<float3> position_mutable = positions.as_span();
-
- for (int i : selection) {
- position_mutable[i] = positions_input[i] + offsets_input[i];
+ current_point = 0;
+ current_mask = 0;
+ for (const SplinePtr &spline : curve->splines()) {
+ if (spline->type() == CURVE_TYPE_BEZIER) {
+ BezierSpline &bezier = static_cast<BezierSpline &>(*spline);
+ for (const int i : bezier.positions().index_range()) {
+ if (current_mask < selection.size() && selection[current_mask] == current_point) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier.set_handle_position_left(i, positions_input[i] + offsets_input[i]);
+ }
+ else {
+ bezier.set_handle_position_right(i, positions_input[i] + offsets_input[i]);
+ }
+ current_mask++;
+ }
+ current_point++;
+ }
+ }
+ else {
+ for ([[maybe_unused]] int i : spline->positions().index_range()) {
+ if (current_mask < selection.size() && selection[current_mask] == current_point) {
+ current_mask++;
+ }
+ current_point++;
+ }
+ }
}
- positions.save();
+ component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -127,9 +141,11 @@ static void node_geo_exec(GeoNodeExecParams params)
bool has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve() &&
- geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) {
- has_bezier = true;
+ if (geometry_set.has_curves()) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER);
+
set_position_in_component(mode,
geometry_set.get_component_for_write<CurveComponent>(),
selection_field,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
index 2f59d008df0..a23a6c09551 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -44,7 +44,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
set_radius_in_component(
geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
index b94782b8c4c..1155c97dc38 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
set_tilt_in_component(
geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 361a75ee0a8..eb035aa9b6b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -61,6 +61,41 @@ static void set_computed_position_and_offset(GeometryComponent &component,
}
break;
}
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (component.attribute_exists("handle_right") &&
+ component.attribute_exists("handle_left")) {
+ OutputAttribute_Typed<float3> handle_right_attribute =
+ component.attribute_try_get_for_output<float3>(
+ "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0});
+ OutputAttribute_Typed<float3> handle_left_attribute =
+ component.attribute_try_get_for_output<float3>(
+ "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0});
+ MutableSpan<float3> handle_right = handle_right_attribute.as_span();
+ MutableSpan<float3> handle_left = handle_left_attribute.as_span();
+
+ MutableSpan<float3> out_positions_span = positions.as_span();
+ devirtualize_varray2(
+ in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
+ threading::parallel_for(
+ selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const float3 new_position = in_positions[i] + in_offsets[i];
+ const float3 delta = new_position - out_positions_span[i];
+ handle_right[i] += delta;
+ handle_left[i] += delta;
+ out_positions_span[i] = new_position;
+ }
+ });
+ });
+
+ handle_right_attribute.save();
+ handle_left_attribute.save();
+ break;
+ }
+ else {
+ ATTR_FALLTHROUGH;
+ }
+ }
default: {
MutableSpan<float3> out_positions_span = positions.as_span();
if (in_positions.is_same(positions.varray())) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
index 70e363064cd..dc7f3b1343a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
set_cyclic_in_component(
geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
index e4702035eec..da8d7bcf255 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -43,10 +43,12 @@ static void node_geo_exec(GeoNodeExecParams params)
bool only_poly = true;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curve()) {
+ if (geometry_set.has_curves()) {
if (only_poly) {
- for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) {
- if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) {
+ const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
+ *geometry_set.get_curves_for_read());
+ for (const SplinePtr &spline : curve->splines()) {
+ if (ELEM(spline->type(), CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS)) {
only_poly = false;
break;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
index ddc0bb2bc11..bc34a1a6f2c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -298,7 +298,8 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
layout.pivot_points.add_new(layout.char_codes[i], pivot_point);
}
- GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
+ GeometrySet geometry_set_curve = GeometrySet::create_with_curves(
+ curve_eval_to_curves(*curve_eval));
handles.add_new(layout.char_codes[i],
instance_component.add_reference(std::move(geometry_set_curve)));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index 789478873f6..2d5b0e58367 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -778,7 +778,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
- if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
+ if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
params.error_message_add(NodeWarningType::Error,
TIP_("The source geometry must contain a mesh or a point cloud"));
return return_default();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 5950a2a16d2..95cec8eab11 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -10,6 +10,7 @@
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
+#include "BKE_curves.hh"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
@@ -125,8 +126,10 @@ static void translate_geometry_set(GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
{
- if (CurveEval *curve = geometry.get_curve_for_write()) {
+ if (Curves *curves = geometry.get_curves_for_write()) {
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
curve->translate(translation);
+ geometry.replace_curve(curve_eval_to_curves(*curve));
}
if (Mesh *mesh = geometry.get_mesh_for_write()) {
translate_mesh(*mesh, translation);
@@ -146,8 +149,10 @@ void transform_geometry_set(GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph)
{
- if (CurveEval *curve = geometry.get_curve_for_write()) {
+ if (Curves *curves = geometry.get_curves_for_write()) {
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
curve->transform(transform);
+ geometry.replace_curve(curve_eval_to_curves(*curve));
}
if (Mesh *mesh = geometry.get_mesh_for_write()) {
transform_mesh(*mesh, transform);
diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc
index d6a4af7ef39..c4befd5828c 100644
--- a/source/blender/nodes/intern/node_common.cc
+++ b/source/blender/nodes/intern/node_common.cc
@@ -171,7 +171,7 @@ static void group_verify_socket_list(bNodeTree &node_tree,
BLI_addtail(&verify_lb, matching_socket);
}
else {
- /* If there was no socket withe the same identifier already, simply create a new socket
+ /* If there was no socket with the same identifier already, simply create a new socket
* based on the interface socket, which will already add it to the new list. */
add_new_socket_from_interface(node_tree, node, *interface_socket, in_out);
}
@@ -326,7 +326,7 @@ void ntree_update_reroute_nodes(bNodeTree *ntree)
}
/* Propagate socket types from right to left. This affects reroute nodes that haven't been
- * changed in the the loop above. */
+ * changed in the loop above. */
for (bNode *start_node : nodes_linked_with_reroutes) {
LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) {
propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types);
diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
index 8a3b0258b55..a24b4379e69 100644
--- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
@@ -38,7 +38,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
- float inverted = node->custom2 ? 1.0f : 0.0f;
+ float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f;
float f_samples = divide_ceil_u(node->custom1, 4);
return GPU_stack_link(mat,
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 2c5012216f2..e3ffd3cc823 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -151,7 +151,7 @@ static PyObject *pygpu_state_depth_test_set(PyObject *UNUSED(self), PyObject *va
}
PyDoc_STRVAR(pygpu_state_depth_test_get_doc,
- ".. function:: blend_depth_test_get()\n"
+ ".. function:: depth_test_get()\n"
"\n"
" Current depth_test equation.\n"
"\n");
@@ -179,7 +179,7 @@ static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *va
}
PyDoc_STRVAR(pygpu_state_depth_mask_get_doc,
- ".. function:: depth_mask_set_get()\n"
+ ".. function:: depth_mask_get()\n"
"\n"
" Writing status in the depth component.\n");
static PyObject *pygpu_state_depth_mask_get(PyObject *UNUSED(self))
@@ -326,7 +326,7 @@ static PyObject *pygpu_state_front_facing_set(PyObject *UNUSED(self), PyObject *
}
PyDoc_STRVAR(pygpu_state_program_point_size_set_doc,
- ".. function:: use_program_point_size(enable)\n"
+ ".. function:: program_point_size_set(enable)\n"
"\n"
" If enabled, the derived point size is taken from the (potentially clipped) "
"shader builtin gl_PointSize.\n"
diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h
index 4a2e5fdebb7..223c6ad5f7e 100644
--- a/source/blender/python/intern/bpy_capi_utils.h
+++ b/source/blender/python/intern/bpy_capi_utils.h
@@ -6,8 +6,8 @@
#pragma once
-#if PY_VERSION_HEX < 0x03090000
-# error "Python 3.9 or greater is required, you'll need to update your Python."
+#if PY_VERSION_HEX < 0x030a0000
+# error "Python 3.10 or greater is required, you'll need to update your Python."
#endif
#ifdef __cplusplus
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 89fb89cfd41..8ed156a7e55 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -76,11 +76,7 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
x = 0x345678UL;
i = 0;
while (--len >= 0) {
-#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */
y = _Py_HashDouble(NULL, (double)(array[i++]));
-#else
- y = _Py_HashDouble((double)(array[i++]));
-#endif
if (y == -1) {
return -1;
}
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index 8adde14d82f..ead255a6716 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -1166,10 +1166,8 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO
tree = BLI_bvhtree_new((int)tris_len, epsilon, PY_BVH_TREE_TYPE_DEFAULT, PY_BVH_AXIS_DEFAULT);
if (tree) {
orig_index = MEM_mallocN(sizeof(*orig_index) * (size_t)tris_len, __func__);
- CustomData *pdata = &mesh->pdata;
- orig_normal = CustomData_get_layer(pdata, CD_NORMAL); /* can be NULL */
- if (orig_normal) {
- orig_normal = MEM_dupallocN(orig_normal);
+ if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
+ orig_normal = MEM_dupallocN(BKE_mesh_poly_normals_ensure(mesh));
}
for (i = 0; i < tris_len; i++, lt++) {
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 4971071316e..596adafb2c9 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -468,7 +468,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__);
- const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL);
+ const float(*precomputed_normals)[3] = BKE_mesh_poly_normals_are_dirty(me) ?
+ NULL :
+ BKE_mesh_poly_normals_ensure(me);
const bool calculate_normal = precomputed_normals ? false : true;
if (precomputed_normals != NULL) {
diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c
index d99b2e729d1..a5c13c26590 100644
--- a/source/blender/render/intern/multires_bake.c
+++ b/source/blender/render/intern/multires_bake.c
@@ -467,7 +467,6 @@ static void do_multires_bake(MultiresBakeRender *bkr,
MPoly *mpoly = dm->getPolyArray(dm);
MLoop *mloop = dm->getLoopArray(dm);
MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV);
- const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL);
float *pvtangent = NULL;
ListBase threads;
@@ -482,6 +481,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
+ const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh);
if (require_tangent) {
if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) {
@@ -497,7 +497,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
NULL,
0,
vert_normals,
- (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
+ poly_normals,
(const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
(const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
/* result */
@@ -542,7 +542,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
handle->data.mlooptri = mlooptri;
handle->data.mloop = mloop;
handle->data.pvtangent = pvtangent;
- handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */
+ handle->data.precomputed_normals = (float *)poly_normals; /* don't strictly need this */
handle->data.w = ibuf->x;
handle->data.h = ibuf->y;
handle->data.lores_dm = dm;
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index ed4326bce69..2a93fb2c46b 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -1106,6 +1106,8 @@ static void do_render_compositor_scenes(Render *re)
return;
}
+ bool changed_scene = false;
+
/* now foreach render-result node we do a full render */
/* results are stored in a way compositor will find it */
GSet *scenes_rendered = BLI_gset_ptr_new(__func__);
@@ -1118,11 +1120,20 @@ static void do_render_compositor_scenes(Render *re)
do_render_compositor_scene(re, scene, cfra);
BLI_gset_add(scenes_rendered, scene);
node->typeinfo->updatefunc(restore_scene->nodetree, node);
+
+ if (scene != re->scene) {
+ changed_scene = true;
+ }
}
}
}
}
BLI_gset_free(scenes_rendered, NULL);
+
+ if (changed_scene) {
+ /* If rendered another scene, switch back to the current scene with compositing nodes. */
+ re->current_scene_update(re->suh, re->scene);
+ }
}
/* bad call... need to think over proper method still */
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 3196ca08155..aa433eeed09 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -1918,7 +1918,7 @@ static void RVBlurBitmap2_float(float *map, int width, int height, float blur, i
* I changed the math around to implement an actual Gaussian distribution.
*
* Watch out though, it tends to misbehave with large blur values on
- * a small bitmap. Avoid avoid! */
+ * a small bitmap. Avoid! */
float *temp = NULL, *swap;
float *filter = NULL;
diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c
index d87da1557e0..91b69bfe01f 100644
--- a/source/blender/sequencer/intern/proxy.c
+++ b/source/blender/sequencer/intern/proxy.c
@@ -549,18 +549,6 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context,
}
}
-static bool seq_orig_free_anims(Sequence *seq_iter, void *data)
-{
- SessionUUID orig_seq_uuid = ((SeqIndexBuildContext *)data)->orig_seq_uuid;
-
- if (BLI_session_uuid_is_equal(&seq_iter->runtime.session_uuid, &orig_seq_uuid)) {
- for (StripAnim *sanim = seq_iter->anims.first; sanim; sanim = sanim->next) {
- IMB_close_anim_proxies(sanim->anim);
- }
- }
- return true;
-}
-
void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop)
{
if (context->index_context) {
@@ -570,9 +558,6 @@ void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop)
IMB_close_anim_proxies(sanim->anim);
}
- /* `context->seq_orig` may have been removed during building. */
- SEQ_for_each_callback(&context->scene->ed->seqbase, seq_orig_free_anims, context);
-
IMB_anim_index_rebuild_finish(context->index_context, stop);
}
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index c1281632cc1..21d76ce93bd 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -127,11 +127,7 @@ void WM_reinit_gizmomap_all(struct Main *bmain);
*/
void WM_script_tag_reload(void);
-wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm,
- const wmWindow *win_ignore,
- const wmWindow *win,
- const int mval[2],
- int r_mval[2]);
+wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2]);
void WM_window_pixel_sample_read(const wmWindowManager *wm,
const wmWindow *win,
const int pos[2],
@@ -1247,6 +1243,8 @@ enum {
WM_JOB_TYPE_COMPOSITE,
WM_JOB_TYPE_RENDER,
WM_JOB_TYPE_RENDER_PREVIEW, /* UI preview */
+ /** Job for the UI to load previews from the file system (uses OS thumbnail cache). */
+ WM_JOB_TYPE_LOAD_PREVIEW, /* UI preview */
WM_JOB_TYPE_OBJECT_SIM_OCEAN,
WM_JOB_TYPE_OBJECT_SIM_FLUID,
WM_JOB_TYPE_OBJECT_BAKE_TEXTURE,
@@ -1425,15 +1423,17 @@ bool WM_window_modal_keymap_status_draw(struct bContext *C,
*/
void WM_event_print(const struct wmEvent *event);
-int WM_event_modifier_flag(const struct wmEvent *event);
-
/**
- * For modal callbacks, check configuration for how to interpret exit with tweaks.
+ * For modal callbacks, check configuration for how to interpret exit when dragging.
*/
-bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
+bool WM_event_is_modal_drag_exit(const struct wmEvent *event,
+ short init_event_type,
+ short init_event_val);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
bool WM_event_is_mouse_drag(const struct wmEvent *event);
bool WM_event_is_mouse_drag_or_press(const wmEvent *event);
+int WM_event_drag_direction(const wmEvent *event);
+
/**
* Detect motion between selection (callers should only use this for selection picking),
* typically mouse press/click events.
@@ -1583,8 +1583,7 @@ 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 ListBase *user_paths,
struct wmOperatorType *ot,
struct IDProperty *op_properties,
const char *haptic_name,
@@ -1599,9 +1598,8 @@ bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
const char *action_name,
const char *profile_path,
- unsigned int count_subaction_paths,
- const char **subaction_paths,
- const char **component_paths,
+ const ListBase *user_paths,
+ const ListBase *component_paths,
const float *float_thresholds,
const eXrAxisFlag *axis_flags,
const struct wmXrPose *poses);
diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h
index 32315d72ba7..068dbb32be2 100644
--- a/source/blender/windowmanager/WM_keymap.h
+++ b/source/blender/windowmanager/WM_keymap.h
@@ -42,8 +42,13 @@ void WM_keymap_clear(struct wmKeyMap *keymap);
/**
* Always add item.
*/
-wmKeyMapItem *WM_keymap_add_item(
- struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_item(struct wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction);
wmKeyMapItem *WM_keymap_add_item_copy(struct wmKeyMap *keymap, wmKeyMapItem *kmi_src);
bool WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi);
@@ -80,32 +85,43 @@ bool WM_keymap_item_compare(const struct wmKeyMapItem *k1, const struct wmKeyMap
/**
* Menu wrapper for #WM_keymap_add_item.
*/
-wmKeyMapItem *WM_keymap_add_menu(
- struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_menu(struct wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction);
/**
* Pie-menu wrapper for #WM_keymap_add_item.
*/
-wmKeyMapItem *WM_keymap_add_menu_pie(
- struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction);
/**
* Panel (popover) wrapper for #WM_keymap_add_item.
*/
-wmKeyMapItem *WM_keymap_add_panel(
- struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_panel(struct wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction);
/**
* Tool wrapper for #WM_keymap_add_item.
*/
-wmKeyMapItem *WM_keymap_add_tool(
- struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier);
-
-/** Useful for mapping numbers to an enum. */
-void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap,
- const struct EnumPropertyItem *items,
- const char *data_path,
- int type_start,
- int val,
- int modifier,
- int keymodifier);
+wmKeyMapItem *WM_keymap_add_tool(struct wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction);
wmKeyMap *WM_keymap_guess_from_context(const struct bContext *C);
@@ -137,10 +153,20 @@ wmKeyMap *WM_modalkeymap_ensure(struct wmKeyConfig *keyconf,
const char *idname,
const struct EnumPropertyItem *items);
wmKeyMap *WM_modalkeymap_find(struct wmKeyConfig *keyconf, const char *idname);
-wmKeyMapItem *WM_modalkeymap_add_item(
- struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value);
-wmKeyMapItem *WM_modalkeymap_add_item_str(
- struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value);
+wmKeyMapItem *WM_modalkeymap_add_item(struct wmKeyMap *km,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction,
+ int value);
+wmKeyMapItem *WM_modalkeymap_add_item_str(struct wmKeyMap *km,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction,
+ const char *value);
const wmKeyMapItem *WM_modalkeymap_find_propvalue(const wmKeyMap *km, int propvalue);
void WM_modalkeymap_assign(struct wmKeyMap *km, const char *opname);
diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h
index 4886e39943f..a9e1495d9bf 100644
--- a/source/blender/windowmanager/WM_toolsystem.h
+++ b/source/blender/windowmanager/WM_toolsystem.h
@@ -103,6 +103,7 @@ void WM_toolsystem_do_msg_notify_tag_refresh(struct bContext *C,
struct wmMsgSubscribeKey *msg_key,
struct wmMsgSubscribeValue *msg_val);
+struct IDProperty *WM_toolsystem_ref_properties_get_idprops(struct bToolRef *tref);
struct IDProperty *WM_toolsystem_ref_properties_ensure_idprops(struct bToolRef *tref);
void WM_toolsystem_ref_properties_ensure_ex(struct bToolRef *tref,
const char *idname,
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 4799483157c..c4858a8a1fa 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -225,35 +225,68 @@ typedef enum eOperatorPropTags {
} eOperatorPropTags;
#define OP_PROP_TAG_ADVANCED ((eOperatorPropTags)OP_PROP_TAG_ADVANCED)
-/* ************** wmKeyMap ************************ */
-
-/* modifier */
-#define KM_SHIFT 1
-#define KM_CTRL 2
-#define KM_ALT 4
-#define KM_OSKEY 8
-
-/* Used for key-map item creation function arguments (never stored in DNA). */
-#define KM_SHIFT_ANY 16
-#define KM_CTRL_ANY 32
-#define KM_ALT_ANY 64
-#define KM_OSKEY_ANY 128
-
-/* KM_MOD_ flags for `wmKeyMapItem` and `wmEvent.alt/shift/oskey/ctrl`. */
-/* note that KM_ANY and KM_NOTHING are used with these defines too */
+/* -------------------------------------------------------------------- */
+/** \name #wmKeyMapItem
+ * \{ */
+
+/**
+ * Modifier keys, not actually used for #wmKeyMapItem (never stored in DNA), used for:
+ * - #wmEvent.modifier without the `KM_*_ANY` flags.
+ * - #WM_keymap_add_item & #WM_modalkeymap_add_item
+ */
+enum {
+ KM_SHIFT = (1 << 0),
+ KM_CTRL = (1 << 1),
+ KM_ALT = (1 << 2),
+ KM_OSKEY = (1 << 3),
+
+ /* Used for key-map item creation function arguments. */
+ KM_SHIFT_ANY = (1 << 4),
+ KM_CTRL_ANY = (1 << 5),
+ KM_ALT_ANY = (1 << 6),
+ KM_OSKEY_ANY = (1 << 7),
+};
+
+/* `KM_MOD_*` flags for #wmKeyMapItem and `wmEvent.alt/shift/oskey/ctrl`. */
+/* Note that #KM_ANY and #KM_NOTHING are used with these defines too. */
#define KM_MOD_HELD 1
-/* type: defined in wm_event_types.c */
-#define KM_TEXTINPUT -2
+/**
+ * #wmKeyMapItem.type
+ * NOTE: most types are defined in `wm_event_types.h`.
+ */
+enum {
+ KM_TEXTINPUT = -2,
+};
-/* val */
-#define KM_ANY -1
-#define KM_NOTHING 0
-#define KM_PRESS 1
-#define KM_RELEASE 2
-#define KM_CLICK 3
-#define KM_DBL_CLICK 4
-#define KM_CLICK_DRAG 5
+/** #wmKeyMapItem.val */
+enum {
+ KM_ANY = -1,
+ KM_NOTHING = 0,
+ KM_PRESS = 1,
+ KM_RELEASE = 2,
+ KM_CLICK = 3,
+ KM_DBL_CLICK = 4,
+ KM_CLICK_DRAG = 5,
+};
+
+/**
+ * #wmKeyMapItem.direction
+ *
+ * Value of tweaks and line gestures. #KM_ANY (-1) works for this case too.
+ */
+enum {
+ KM_DIRECTION_N = 1,
+ KM_DIRECTION_NE = 2,
+ KM_DIRECTION_E = 3,
+ KM_DIRECTION_SE = 4,
+ KM_DIRECTION_S = 5,
+ KM_DIRECTION_SW = 6,
+ KM_DIRECTION_W = 7,
+ KM_DIRECTION_NW = 8,
+};
+
+/** \} */
/* ************** UI Handler ***************** */
@@ -470,6 +503,7 @@ typedef struct wmNotifier {
#define NS_EDITMODE_ARMATURE (8 << 8)
#define NS_MODE_POSE (9 << 8)
#define NS_MODE_PARTICLE (10 << 8)
+#define NS_EDITMODE_CURVES (11 << 8)
/* subtype 3d view editing */
#define NS_VIEW3D_GPU (16 << 8)
@@ -493,7 +527,6 @@ typedef struct wmNotifier {
/* ************** Gesture Manager data ************** */
/* wmGesture->type */
-#define WM_GESTURE_TWEAK 0
#define WM_GESTURE_LINES 1
#define WM_GESTURE_RECT 2
#define WM_GESTURE_CROSS_RECT 3
@@ -503,12 +536,15 @@ typedef struct wmNotifier {
/**
* wmGesture is registered to #wmWindow.gesture, handled by operator callbacks.
- * Tweak gesture is builtin feature.
*/
typedef struct wmGesture {
struct wmGesture *next, *prev;
/** #wmEvent.type */
int event_type;
+ /** #wmEvent.modifier */
+ uint8_t event_modifier;
+ /** #wmEvent.keymodifier */
+ short event_keymodifier;
/** Gesture type define. */
int type;
/** bounds of region to draw gesture within. */
@@ -555,6 +591,22 @@ typedef struct wmGesture {
/* ************** wmEvent ************************ */
+typedef enum eWM_EventFlag {
+ /**
+ * True if the operating system inverted the delta x/y values and resulting
+ * `prev_xy` values, for natural scroll direction.
+ * For absolute scroll direction, the delta must be negated again.
+ */
+ WM_EVENT_SCROLL_INVERT = (1 << 0),
+ /**
+ * Generated by auto-repeat, note that this must only ever be set for keyboard events
+ * where `ISKEYBOARD(event->type) == true`.
+ *
+ * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this.
+ */
+ WM_EVENT_IS_REPEAT = (1 << 1),
+} eWM_EventFlag;
+
typedef struct wmTabletData {
/** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */
int active;
@@ -611,22 +663,10 @@ typedef struct wmEvent {
/** From ghost, fallback if utf8 isn't set. */
char ascii;
- /**
- * Generated by auto-repeat, note that this must only ever be set for keyboard events
- * where `ISKEYBOARD(event->type) == true`.
- *
- * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this.
- */
- char is_repeat;
-
/** The previous value of `type`. */
short prev_type;
/** The previous value of `val`. */
short prev_val;
- /** The time when the key is pressed, see #PIL_check_seconds_timer. */
- double prev_click_time;
- /** The location when the key is pressed (used to enforce drag thresholds). */
- int prev_click_xy[2];
/**
* The previous value of #wmEvent.xy,
* Unlike other previous state variables, this is set on any mouse motion.
@@ -634,29 +674,40 @@ typedef struct wmEvent {
*/
int prev_xy[2];
- /** Modifier states. */
- /** 'oskey' is apple or windows-key, value denotes order of pressed. */
- short shift, ctrl, alt, oskey;
+ /** The `type` at the point of the click action. */
+ short prev_click_type;
+ /** The time when the key is pressed, see #PIL_check_seconds_timer. */
+ double prev_click_time;
+ /** The location when the key is pressed (used to enforce drag thresholds). */
+ int prev_click_xy[2];
+ /** The `modifier` at the point of the click action. */
+ uint8_t prev_click_modifier;
+ /** The `keymodifier` at the point of the click action. */
+ short prev_click_keymodifier;
+
+ /**
+ * Modifier states.
+ * #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY is apple or windows-key.
+ */
+ uint8_t modifier;
+
+ /** The direction (for #KM_CLICK_DRAG events only). */
+ int8_t direction;
+
/** Raw-key modifier (allow using any key as a modifier). */
short keymodifier;
/** Tablet info, available for mouse move and button events. */
wmTabletData tablet;
+ eWM_EventFlag flag;
+
/* Custom data. */
/** Custom data type, stylus, 6dof, see wm_event_types.h */
short custom;
short customdata_free;
- int pad2;
/** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */
void *customdata;
-
- /**
- * True if the operating system inverted the delta x/y values and resulting
- * `prev_xy` values, for natural scroll direction.
- * For absolute scroll direction, the delta must be negated again.
- */
- char is_direction_inverted;
} wmEvent;
/**
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index c46a2b6afe5..2971cdf40c3 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -640,24 +640,29 @@ wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf)
keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
/* items for modal map */
- WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
- WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
+ WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL);
+ WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL);
- WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
- WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM);
WM_modalkeymap_add_item(
- keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON);
WM_modalkeymap_add_item(
- keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
- WM_modalkeymap_add_item(keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF);
WM_modalkeymap_add_item(
- keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
+ keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON);
+ WM_modalkeymap_add_item(
+ keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF);
- WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
- WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
- WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
- WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
+ WM_modalkeymap_add_item(
+ keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(
+ keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF);
+ WM_modalkeymap_add_item(
+ keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(
+ keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF);
WM_modalkeymap_assign(keymap, "GIZMOGROUP_OT_gizmo_tweak");
@@ -709,24 +714,26 @@ static wmKeyMap *WM_gizmogroup_keymap_template_select_ex(
const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R;
const int action_mouse = (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE;
#else
- const int select_mouse = RIGHTMOUSE;
- const int select_tweak = EVT_TWEAK_R;
- const int action_mouse = LEFTMOUSE;
+ const int select_mouse = RIGHTMOUSE, select_mouse_val = KM_PRESS;
+ const int select_tweak = RIGHTMOUSE, select_tweak_val = KM_CLICK_DRAG;
+ const int action_mouse = LEFTMOUSE, action_mouse_val = KM_PRESS;
#endif
if (do_init) {
- WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, KM_PRESS, KM_ANY, 0);
- WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, KM_ANY, 0, 0);
+ WM_keymap_add_item(
+ km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, action_mouse_val, KM_ANY, 0, KM_ANY);
+ WM_keymap_add_item(
+ km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, select_tweak_val, 0, 0, KM_ANY);
}
if (do_init) {
wmKeyMapItem *kmi = WM_keymap_add_item(
- km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, 0, 0);
+ km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, 0, 0, KM_ANY);
RNA_boolean_set(kmi->ptr, "extend", false);
RNA_boolean_set(kmi->ptr, "deselect", false);
RNA_boolean_set(kmi->ptr, "toggle", false);
kmi = WM_keymap_add_item(
- km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, KM_SHIFT, 0);
+ km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, KM_SHIFT, 0, KM_ANY);
RNA_boolean_set(kmi->ptr, "extend", false);
RNA_boolean_set(kmi->ptr, "deselect", false);
RNA_boolean_set(kmi->ptr, "toggle", true);
@@ -1129,7 +1136,8 @@ void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup)
ARegion *region = CTX_wm_region(C);
BLI_assert(region->gizmo_map == gzmap);
/* Check if the tweak event originated from this region. */
- if ((win->tweak != NULL) && BLI_rcti_compare(&region->winrct, &win->tweak->winrct)) {
+ if ((win->eventstate != NULL) && (win->event_queue_check_drag) &&
+ BLI_rcti_isect_pt_v(&region->winrct, win->eventstate->prev_click_xy)) {
/* We need to run refresh again. */
gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup));
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 4aba287aefb..f1ac19f4651 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -726,7 +726,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
* - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos.
* If there are no gizmos or only one - early exit, otherwise.
*
- * - Bind the depth buffer and and use selection picking logic.
+ * - Bind the depth buffer and use selection picking logic.
* This is much slower than occlusion queries (since it's reading depths while drawing).
* When there is a single gizmo under the cursor (quite common), early exit, otherwise.
*
@@ -819,8 +819,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
do_step[i] = WM_gizmo_context_check_drawstep(C, i);
}
- const int event_modifier = WM_event_modifier_flag(event);
-
LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
/* If it were important we could initialize here,
@@ -839,11 +837,11 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
}
if (step == WM_GIZMOMAP_DRAWSTEP_3D) {
wm_gizmogroup_intersectable_gizmos_to_list(
- wm, gzgroup, event_modifier, &visible_3d_gizmos);
+ wm, gzgroup, event->modifier, &visible_3d_gizmos);
}
else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
if ((gz = wm_gizmogroup_find_intersected_gizmo(
- wm, gzgroup, C, event_modifier, event->mval, r_part))) {
+ wm, gzgroup, C, event->modifier, event->mval, r_part))) {
break;
}
}
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 13bf902f02c..c333d8149ed 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -156,7 +156,6 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
win->gpuctx = NULL;
win->eventstate = NULL;
win->cursor_keymap_status = NULL;
- win->tweak = NULL;
#if defined(WIN32) || defined(__APPLE__)
win->ime_data = NULL;
#endif
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index f69a612df19..4ffb6b90e11 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -318,6 +318,10 @@ static wmDropBox *dropbox_active(bContext *C,
wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
if (handler->dropboxes) {
LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
+ if (drag->drop_state.ui_context) {
+ CTX_store_set(C, drag->drop_state.ui_context);
+ }
+
if (!drop->poll(C, drag, event)) {
/* If the drop's poll fails, don't set the disabled-info. This would be too aggressive.
* Instead show it only if the drop box could be used in principle, but the operator
@@ -326,10 +330,6 @@ static wmDropBox *dropbox_active(bContext *C,
}
const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop);
- if (drag->drop_state.ui_context) {
- CTX_store_set(C, drag->drop_state.ui_context);
- }
-
if (WM_operator_poll_context(C, drop->ot, opcontext)) {
return drop;
}
@@ -651,8 +651,12 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *
}
ID *id = BKE_libblock_find_name(bmain, asset_drag->id_type, name);
- if (id) {
- BKE_id_delete(bmain, id);
+ if (id != NULL) {
+ /* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will
+ * cause T95636. Note that we need first to add the user that we want to remove in
+ * #BKE_id_free_us. */
+ id_us_plus(id);
+ BKE_id_free_us(bmain, id);
}
}
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 304d7f73eb1..ee13e1832ed 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -47,12 +47,7 @@ static void event_ids_from_type_and_value(const short type,
RNA_enum_identifier(rna_enum_event_type_items, type, r_type_id);
/* Value. */
- if (ISTWEAK(type)) {
- RNA_enum_identifier(rna_enum_event_value_tweak_items, val, r_val_id);
- }
- else {
- RNA_enum_identifier(rna_enum_event_value_all_items, val, r_val_id);
- }
+ RNA_enum_identifier(rna_enum_event_value_items, val, r_val_id);
}
void WM_event_print(const wmEvent *event)
@@ -80,12 +75,12 @@ void WM_event_print(const wmEvent *event)
prev_type_id,
event->prev_val,
prev_val_id,
- event->shift,
- event->ctrl,
- event->alt,
- event->oskey,
+ (event->modifier & KM_SHIFT) != 0,
+ (event->modifier & KM_CTRL) != 0,
+ (event->modifier & KM_ALT) != 0,
+ (event->modifier & KM_OSKEY) != 0,
event->keymodifier,
- event->is_repeat,
+ (event->flag & WM_EVENT_IS_REPEAT) != 0,
event->xy[0],
event->xy[1],
event->ascii,
@@ -129,24 +124,6 @@ void WM_event_print(const wmEvent *event)
/** \name Event Modifier/Type Queries
* \{ */
-int WM_event_modifier_flag(const wmEvent *event)
-{
- int flag = 0;
- if (event->ctrl) {
- flag |= KM_CTRL;
- }
- if (event->alt) {
- flag |= KM_ALT;
- }
- if (event->shift) {
- flag |= KM_SHIFT;
- }
- if (event->oskey) {
- flag |= KM_OSKEY;
- }
- return flag;
-}
-
bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask)
{
/* Keyboard. */
@@ -178,13 +155,6 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma
}
}
- /* Tweak. */
- if (mask & EVT_TYPE_MASK_TWEAK) {
- if (ISTWEAK(event_type)) {
- return true;
- }
- }
-
/* Action Zone. */
if (mask & EVT_TYPE_MASK_ACTIONZONE) {
if (IS_EVENT_ACTIONZONE(event_type)) {
@@ -201,34 +171,30 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma
/** \name Event Motion Queries
* \{ */
-bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event)
+bool WM_event_is_modal_drag_exit(const wmEvent *event,
+ const short init_event_type,
+ const short init_event_val)
{
- /* if the release-confirm userpref setting is enabled,
- * tweak events can be canceled when mouse is released
- */
+ /* If the release-confirm preference setting is enabled,
+ * drag events can be canceled when mouse is released. */
if (U.flag & USER_RELEASECONFIRM) {
/* option on, so can exit with km-release */
if (event->val == KM_RELEASE) {
- switch (tweak_event) {
- case EVT_TWEAK_L:
- case EVT_TWEAK_M:
- case EVT_TWEAK_R:
- return 1;
+ if ((init_event_val == KM_CLICK_DRAG) && (event->type == init_event_type)) {
+ return 1;
}
}
else {
- /* if the initial event wasn't a tweak event then
- * ignore USER_RELEASECONFIRM setting: see T26756. */
- if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) {
+ /* If the initial event wasn't a drag event then
+ * ignore #USER_RELEASECONFIRM setting: see T26756. */
+ if (init_event_val != KM_CLICK_DRAG) {
return 1;
}
}
}
else {
- /* this is fine as long as not doing km-release, otherwise
- * some items (i.e. markers) being tweaked may end up getting
- * dropped all over
- */
+ /* This is fine as long as not doing km-release, otherwise some items (i.e. markers)
+ * being tweaked may end up getting dropped all over. */
if (event->val != KM_RELEASE) {
return 1;
}
@@ -249,7 +215,7 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
bool WM_event_is_mouse_drag(const wmEvent *event)
{
- return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
+ return (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
}
bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
@@ -258,6 +224,68 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
(ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS));
}
+int WM_event_drag_direction(const wmEvent *event)
+{
+ const int delta[2] = {
+ event->xy[0] - event->prev_click_xy[0],
+ event->xy[1] - event->prev_click_xy[1],
+ };
+
+ int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI);
+ int val = KM_DIRECTION_W;
+
+ if (theta == 0) {
+ val = KM_DIRECTION_E;
+ }
+ else if (theta == 1) {
+ val = KM_DIRECTION_NE;
+ }
+ else if (theta == 2) {
+ val = KM_DIRECTION_N;
+ }
+ else if (theta == 3) {
+ val = KM_DIRECTION_NW;
+ }
+ else if (theta == -1) {
+ val = KM_DIRECTION_SE;
+ }
+ else if (theta == -2) {
+ val = KM_DIRECTION_S;
+ }
+ else if (theta == -3) {
+ val = KM_DIRECTION_SW;
+ }
+
+#if 0
+ /* debug */
+ if (val == 1) {
+ printf("tweak north\n");
+ }
+ if (val == 2) {
+ printf("tweak north-east\n");
+ }
+ if (val == 3) {
+ printf("tweak east\n");
+ }
+ if (val == 4) {
+ printf("tweak south-east\n");
+ }
+ if (val == 5) {
+ printf("tweak south\n");
+ }
+ if (val == 6) {
+ printf("tweak south-west\n");
+ }
+ if (val == 7) {
+ printf("tweak west\n");
+ }
+ if (val == 8) {
+ printf("tweak north-west\n");
+ }
+#endif
+ return val;
+}
+
bool WM_cursor_test_motion_and_update(const int mval[2])
{
static int mval_prev[2] = {-1, -1};
@@ -278,8 +306,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2])
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (ISMOUSE(event->prev_type)) {
- BLI_assert(event->prev_type != MOUSEMOVE);
+ if (ISMOUSE(event->prev_click_type)) {
+ BLI_assert(event->prev_click_type != MOUSEMOVE);
/* Using the previous type is important is we want to check the last pressed/released button,
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
* and does not help us know which threshold to use. */
@@ -331,12 +359,6 @@ int WM_userdef_event_map(int kmitype)
int WM_userdef_event_type_from_keymap_type(int kmitype)
{
switch (kmitype) {
- case EVT_TWEAK_L:
- return LEFTMOUSE;
- case EVT_TWEAK_M:
- return MIDDLEMOUSE;
- case EVT_TWEAK_R:
- return RIGHTMOUSE;
case WHEELOUTMOUSE:
return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
case WHEELINMOUSE:
@@ -458,7 +480,7 @@ int WM_event_absolute_delta_x(const struct wmEvent *event)
{
int dx = event->xy[0] - event->prev_xy[0];
- if (!event->is_direction_inverted) {
+ if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) {
dx = -dx;
}
@@ -469,7 +491,7 @@ int WM_event_absolute_delta_y(const struct wmEvent *event)
{
int dy = event->xy[1] - event->prev_xy[1];
- if (!event->is_direction_inverted) {
+ if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) {
dy = -dy;
}
@@ -491,8 +513,8 @@ int WM_event_absolute_delta_y(const struct wmEvent *event)
*/
bool WM_event_is_ime_switch(const struct wmEvent *event)
{
- return event->val == KM_PRESS && event->type == EVT_SPACEKEY &&
- (event->ctrl || event->oskey || event->alt);
+ return (event->val == KM_PRESS) && (event->type == EVT_SPACEKEY) &&
+ (event->modifier & (KM_CTRL | KM_OSKEY | KM_ALT));
}
#endif
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 8a19c99f59b..375cc4e785e 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -153,7 +153,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
win->eventstate->type = event->type;
if (event->val == KM_PRESS) {
- if (event->is_repeat == false) {
+ if ((event->flag & WM_EVENT_IS_REPEAT) == 0) {
copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy);
}
}
@@ -166,7 +166,7 @@ void wm_event_free(wmEvent *event)
#ifndef NDEBUG
/* Don't use assert here because it's fairly harmless in most cases,
* more an issue of correctness, something we should avoid in general. */
- if (event->is_repeat && !ISKEYBOARD(event->type)) {
+ if ((event->flag & WM_EVENT_IS_REPEAT) && !ISKEYBOARD(event->type)) {
printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__);
WM_event_print(event);
}
@@ -739,7 +739,7 @@ void wm_event_handler_ui_cancel_ex(bContext *C,
wm_event_init_from_window(win, &event);
event.type = EVT_BUT_CANCEL;
event.val = reactivate_button ? 0 : 1;
- event.is_repeat = false;
+ event.flag = 0;
handler->handle_fn(C, &event, handler->user_data);
}
}
@@ -1982,7 +1982,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
return false;
}
- if (winevent->is_repeat) {
+ if (winevent->flag & WM_EVENT_IS_REPEAT) {
if (kmi->flag & KMI_REPEAT_IGNORE) {
return false;
}
@@ -2029,29 +2029,41 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
}
}
+ if (kmi->val == KM_CLICK_DRAG) {
+ if (kmi->direction != KM_ANY) {
+ if (kmi->direction != winevent->direction) {
+ return false;
+ }
+ }
+ }
+
+ const bool shift = (winevent->modifier & KM_SHIFT) != 0;
+ const bool ctrl = (winevent->modifier & KM_CTRL) != 0;
+ const bool alt = (winevent->modifier & KM_ALT) != 0;
+ const bool oskey = (winevent->modifier & KM_OSKEY) != 0;
+
/* Modifiers also check bits, so it allows modifier order.
* Account for rare case of when these keys are used as the 'type' not as modifiers. */
if (kmi->shift != KM_ANY) {
- if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) &&
+ if ((shift != kmi->shift) && !(shift & kmi->shift) &&
!ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
return false;
}
}
if (kmi->ctrl != KM_ANY) {
- if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) &&
+ if (ctrl != kmi->ctrl && !(ctrl & kmi->ctrl) &&
!ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
return false;
}
}
if (kmi->alt != KM_ANY) {
- if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) &&
+ if (alt != kmi->alt && !(alt & kmi->alt) &&
!ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) {
return false;
}
}
if (kmi->oskey != KM_ANY) {
- if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) &&
- (winevent->type != EVT_OSKEY)) {
+ if (oskey != kmi->oskey && !(oskey & kmi->oskey) && (winevent->type != EVT_OSKEY)) {
return false;
}
}
@@ -2771,7 +2783,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
{
/* Drag events use the previous click location to highlight the gizmos,
* Get the highlight again in case the user dragged off the gizmo. */
- const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
+ const bool is_event_drag = (event->val == KM_CLICK_DRAG);
const bool is_event_modifier = ISKEYMODIFIER(event->type);
/* Only keep the highlight if the gizmo becomes modal as result of event handling.
* Without this check, even un-handled drag events will set the highlight if the drag
@@ -2882,15 +2894,10 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
wmEvent event_test_click_drag = *event;
event_test_click_drag.val = KM_CLICK_DRAG;
- wmEvent event_test_tweak = *event;
- event_test_tweak.type = EVT_TWEAK_L + (event->type - LEFTMOUSE);
- event_test_tweak.val = KM_ANY;
-
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
if ((kmi->flag & KMI_INACTIVE) == 0) {
if (wm_eventmatch(&event_test_click, kmi) ||
- wm_eventmatch(&event_test_click_drag, kmi) ||
- wm_eventmatch(&event_test_tweak, kmi)) {
+ wm_eventmatch(&event_test_click_drag, kmi)) {
wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) {
is_event_handle_all = true;
@@ -3151,39 +3158,45 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
}
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
- /* Test for CLICK_DRAG events. */
- if (wm_action_not_handled(action)) {
- if (win->event_queue_check_drag) {
- if (WM_event_drag_test(event, event->prev_click_xy)) {
- win->event_queue_check_drag_handled = true;
-
- int xy[2] = {UNPACK2(event->xy)};
- short val = event->val;
- short type = event->type;
-
- copy_v2_v2_int(event->xy, event->prev_click_xy);
- event->val = KM_CLICK_DRAG;
- event->type = event->prev_type;
-
- CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
+ /* Test for #WM_CLICK_DRAG events. */
+
+ /* NOTE(@campbellbarton): Ignore `action` so drag can be used for editors that use both click
+ * selection and passing through the drag action to box select. See #WM_generic_select_modal.
+ * In the case of marker select-drag the combinations of (pass-through / finished / modal)
+ * can accumulate to have flags set that they can't be properly interpreted here.
+ * Instead `win->event_queue_check_drag` is cleared in `wm_event_do_handlers`. */
+ if (win->event_queue_check_drag) {
+ if (WM_event_drag_test(event, event->prev_click_xy)) {
+ win->event_queue_check_drag_handled = true;
+ const int direction = WM_event_drag_direction(event);
+
+ const int prev_xy[2] = {UNPACK2(event->xy)};
+ const short prev_val = event->val;
+ const short prev_type = event->type;
+ const uint8_t prev_modifier = event->modifier;
+ const short prev_keymodifier = event->keymodifier;
+
+ copy_v2_v2_int(event->xy, event->prev_click_xy);
+ event->val = KM_CLICK_DRAG;
+ event->type = event->prev_click_type;
+ event->modifier = event->prev_click_modifier;
+ event->keymodifier = event->prev_click_keymodifier;
+ event->direction = direction;
+
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
+
+ action |= wm_handlers_do_intern(C, win, event, handlers);
+
+ event->direction = 0;
+ event->keymodifier = prev_keymodifier;
+ event->modifier = prev_modifier;
+ event->val = prev_val;
+ event->type = prev_type;
+ copy_v2_v2_int(event->xy, prev_xy);
- action |= wm_handlers_do_intern(C, win, event, handlers);
-
- event->val = val;
- event->type = type;
- copy_v2_v2_int(event->xy, xy);
-
- win->event_queue_check_click = false;
- if (!wm_action_not_handled(action)) {
- /* Only disable when handled as other handlers may use this drag event. */
- win->event_queue_check_drag = false;
- }
- }
+ win->event_queue_check_click = false;
}
}
- else {
- win->event_queue_check_drag = false;
- }
}
else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) {
/* All events that don't set wmEvent.prev_type must be ignored. */
@@ -3194,17 +3207,26 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
* wasn't handled, the KM_RELEASE will become a KM_CLICK */
if (event->val == KM_PRESS) {
- if (event->is_repeat == false) {
+ if ((event->flag & WM_EVENT_IS_REPEAT) == 0) {
win->event_queue_check_click = true;
win->event_queue_check_drag = true;
win->event_queue_check_drag_handled = false;
}
}
else if (event->val == KM_RELEASE) {
- win->event_queue_check_drag = false;
+ if (win->event_queue_check_drag) {
+ if ((event->prev_click_type != event->type) &&
+ (ISKEYMODIFIER(event->type) || (event->type == event->prev_click_keymodifier))) {
+ /* Support releasing modifier keys without canceling the drag event, see T89989.
+ * NOTE: this logic is replicated for tweak gestures. */
+ }
+ else {
+ win->event_queue_check_drag = false;
+ }
+ }
}
- if (event->prev_type == event->type) {
+ if (event->prev_click_type == event->type) {
if (event->val == KM_RELEASE) {
if (event->prev_val == KM_PRESS) {
@@ -3245,7 +3267,6 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
}
else {
win->event_queue_check_click = false;
- win->event_queue_check_drag = false;
}
}
else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) {
@@ -3692,8 +3713,10 @@ void wm_event_do_handlers(bContext *C)
/* Check dragging, creates new event or frees, adds draw tag. */
wm_event_drag_and_drop_test(wm, win, event);
- /* Builtin tweak, if action is break it removes tweak. */
- wm_tweakevent_test(C, event, action);
+ /* Builtin drag: #KM_CLICK_DRAG. */
+ if (action & WM_HANDLER_BREAK) {
+ win->event_queue_check_drag = false;
+ }
if ((action & WM_HANDLER_BREAK) == 0) {
/* NOTE: setting subwin active should be done here, after modal handlers have been done. */
@@ -3795,7 +3818,7 @@ void wm_event_do_handlers(bContext *C)
tevent.type = MOUSEMOVE;
tevent.prev_xy[0] = tevent.xy[0];
tevent.prev_xy[1] = tevent.xy[1];
- tevent.is_repeat = false;
+ tevent.flag = 0;
wm_event_add(win, &tevent);
win->addmousemove = 0;
}
@@ -4487,19 +4510,18 @@ static void wm_eventemulation(wmEvent *event, bool test_only)
if (U.flag & USER_TWOBUTTONMOUSE) {
if (event->type == LEFTMOUSE) {
- short *mod = (
+ const uint8_t mod_test = (
#if !defined(WIN32)
- (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? &event->oskey :
- &event->alt
+ (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? KM_OSKEY : KM_ALT
#else
/* Disable for WIN32 for now because it accesses the start menu. */
- &event->alt
+ KM_ALT
#endif
);
if (event->val == KM_PRESS) {
- if (*mod) {
- *mod = 0;
+ if (event->modifier & mod_test) {
+ event->modifier &= ~mod_test;
event->type = MIDDLEMOUSE;
if (!test_only) {
@@ -4511,7 +4533,7 @@ static void wm_eventemulation(wmEvent *event, bool test_only)
/* Only send middle-mouse release if emulated. */
if (emulating_event == MIDDLEMOUSE) {
event->type = MIDDLEMOUSE;
- *mod = 0;
+ event->modifier &= ~mod_test;
}
if (!test_only) {
@@ -4649,8 +4671,8 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi
}
}
- wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval);
- if (win_other) {
+ wmWindow *win_other = WM_window_find_under_cursor(win, mval, mval);
+ if (win_other && win_other != win) {
copy_v2_v2_int(event->xy, mval);
return win_other;
}
@@ -4687,6 +4709,9 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state)
static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state)
{
event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer();
+ event->prev_click_type = event_state->prev_click_type = event_state->type;
+ event->prev_click_modifier = event_state->prev_click_modifier = event_state->modifier;
+ event->prev_click_keymodifier = event_state->prev_click_keymodifier = event_state->keymodifier;
event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0];
event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1];
}
@@ -4700,7 +4725,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
* them for better performance. */
if (event_last && event_last->type == MOUSEMOVE) {
event_last->type = INBETWEEN_MOUSEMOVE;
- event_last->is_repeat = false;
+ event_last->flag = 0;
}
wmEvent *event_new = wm_event_add(win, event);
@@ -4752,7 +4777,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
/* Initialize and copy state (only mouse x y and modifiers). */
event = *event_state;
- event.is_repeat = false;
+ event.flag = 0;
/**
* Always support accessing the last key press/release. This is set from `win->eventstate`,
@@ -4850,7 +4875,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.val = KM_NOTHING;
/* The direction is inverted from the device due to system preferences. */
- event.is_direction_inverted = pd->isDirectionInverted;
+ if (pd->isDirectionInverted) {
+ event.flag |= WM_EVENT_SCROLL_INVERT;
+ }
wm_event_add_trackpad(win, &event, pd->deltaX, -pd->deltaY);
break;
@@ -4931,12 +4958,16 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
case GHOST_kEventKeyDown:
case GHOST_kEventKeyUp: {
GHOST_TEventKeyData *kd = customdata;
- short keymodifier = KM_NOTHING;
+ /* Only copy these flags into the `event_state`. */
+ const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT;
+ bool keymodifier = 0;
event.type = convert_key(kd->key);
event.ascii = kd->ascii;
/* Might be not NULL terminated. */
memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf));
- event.is_repeat = kd->is_repeat;
+ if (kd->is_repeat) {
+ event.flag |= WM_EVENT_IS_REPEAT;
+ }
event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE;
wm_eventemulation(&event, false);
@@ -4945,7 +4976,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
/* Copy to event state. */
event_state->val = event.val;
event_state->type = event.type;
- event_state->is_repeat = event.is_repeat;
+ event_state->flag = (event.flag & event_state_flag_mask);
/* Exclude arrow keys, esc, etc from text input. */
if (type == GHOST_kEventKeyUp) {
@@ -4981,29 +5012,57 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
case EVT_LEFTSHIFTKEY:
case EVT_RIGHTSHIFTKEY:
if (event.val == KM_PRESS) {
- keymodifier = KM_MOD_HELD;
+ keymodifier = true;
+ }
+ if (keymodifier) {
+ event.modifier |= KM_SHIFT;
+ event_state->modifier |= KM_SHIFT;
+ }
+ else {
+ event.modifier &= ~KM_SHIFT;
+ event_state->modifier &= ~KM_SHIFT;
}
- event.shift = event_state->shift = keymodifier;
break;
case EVT_LEFTCTRLKEY:
case EVT_RIGHTCTRLKEY:
if (event.val == KM_PRESS) {
- keymodifier = KM_MOD_HELD;
+ keymodifier = true;
+ }
+ if (keymodifier) {
+ event.modifier |= KM_CTRL;
+ event_state->modifier |= KM_CTRL;
+ }
+ else {
+ event.modifier &= ~KM_CTRL;
+ event_state->modifier &= ~KM_CTRL;
}
- event.ctrl = event_state->ctrl = keymodifier;
break;
case EVT_LEFTALTKEY:
case EVT_RIGHTALTKEY:
if (event.val == KM_PRESS) {
- keymodifier = KM_MOD_HELD;
+ keymodifier = true;
+ }
+ if (keymodifier) {
+ event.modifier |= KM_ALT;
+ event_state->modifier |= KM_ALT;
+ }
+ else {
+ event.modifier &= ~KM_ALT;
+ event_state->modifier &= ~KM_ALT;
}
- event.alt = event_state->alt = keymodifier;
break;
case EVT_OSKEY:
if (event.val == KM_PRESS) {
- keymodifier = KM_MOD_HELD;
+ keymodifier = true;
+ }
+ if (keymodifier) {
+ event.modifier |= KM_OSKEY;
+ event_state->modifier |= KM_OSKEY;
+ }
+ else {
+ event.modifier &= ~KM_OSKEY;
+ event_state->modifier &= ~KM_OSKEY;
}
- event.oskey = event_state->oskey = keymodifier;
break;
default:
if (event.val == KM_PRESS && event.keymodifier == 0) {
@@ -5041,14 +5100,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
* XXX Keep global for now? */
if ((event.type == EVT_ESCKEY && event.val == KM_PRESS) &&
/* Check other modifiers because ms-windows uses these to bring up the task manager. */
- (event.shift == 0 && event.ctrl == 0 && event.alt == 0)) {
+ ((event.modifier & (KM_SHIFT | KM_CTRL | KM_ALT)) == 0)) {
G.is_break = true;
}
/* Double click test - only for press. */
if (event.val == KM_PRESS) {
/* Don't reset timer & location when holding the key generates repeat events. */
- if (event.is_repeat == false) {
+ if ((event.flag & WM_EVENT_IS_REPEAT) == 0) {
wm_event_prev_click_set(&event, event_state);
}
}
@@ -5169,7 +5228,7 @@ void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val)
wmEvent event = {
.type = EVT_XR_ACTION,
.val = val,
- .is_repeat = false,
+ .flag = 0,
.custom = EVT_DATA_XR,
.customdata = actiondata,
.customdata_free = true,
@@ -5301,9 +5360,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(
/** State storage to detect changes between calls to refresh the information. */
struct CursorKeymapInfo_State {
- struct {
- short shift, ctrl, alt, oskey;
- } modifiers;
+ uint8_t modifier;
short space_type;
short region_type;
/* Never use, just compare memory for changes. */
@@ -5326,10 +5383,7 @@ static void wm_event_cursor_store(struct CursorKeymapInfo_State *state,
short region_type,
const bToolRef *tref)
{
- state->modifiers.shift = event->shift;
- state->modifiers.ctrl = event->ctrl;
- state->modifiers.alt = event->alt;
- state->modifiers.oskey = event->oskey;
+ state->modifier = event->modifier;
state->space_type = space_type;
state->region_type = region_type;
state->tref = tref ? *tref : (bToolRef){0};
@@ -5460,15 +5514,15 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
} event_data[] = {
{0, 0, LEFTMOUSE, KM_PRESS},
{0, 0, LEFTMOUSE, KM_CLICK},
- {0, 1, EVT_TWEAK_L, KM_ANY},
+ {0, 0, LEFTMOUSE, KM_CLICK_DRAG},
{1, 0, MIDDLEMOUSE, KM_PRESS},
{1, 0, MIDDLEMOUSE, KM_CLICK},
- {1, 1, EVT_TWEAK_M, KM_ANY},
+ {1, 0, MIDDLEMOUSE, KM_CLICK_DRAG},
{2, 0, RIGHTMOUSE, KM_PRESS},
{2, 0, RIGHTMOUSE, KM_CLICK},
- {2, 1, EVT_TWEAK_R, KM_ANY},
+ {2, 0, RIGHTMOUSE, KM_CLICK_DRAG},
};
for (int button_index = 0; button_index < 3; button_index++) {
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 581c5f8a198..a6fbad8b171 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -42,6 +42,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent
gesture->type = type;
gesture->event_type = event->type;
+ gesture->event_modifier = event->modifier;
+ gesture->event_keymodifier = event->keymodifier;
gesture->winrct = region->winrct;
gesture->user_data.use_free = true; /* Free if userdata is set. */
gesture->modal_state = GESTURE_MODAL_NOP;
@@ -50,7 +52,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent
if (ELEM(type,
WM_GESTURE_RECT,
WM_GESTURE_CROSS_RECT,
- WM_GESTURE_TWEAK,
WM_GESTURE_CIRCLE,
WM_GESTURE_STRAIGHTLINE)) {
rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
@@ -81,9 +82,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent
void WM_gesture_end(wmWindow *win, wmGesture *gesture)
{
- if (win->tweak == gesture) {
- win->tweak = NULL;
- }
BLI_remlink(&win->gesture, gesture);
MEM_freeN(gesture->customdata);
WM_generic_user_data_free(&gesture->user_data);
@@ -112,74 +110,6 @@ bool WM_gesture_is_modal_first(const wmGesture *gesture)
return (gesture->is_active_prev == false);
}
-int wm_gesture_evaluate(wmGesture *gesture, const wmEvent *event)
-{
- if (gesture->type == WM_GESTURE_TWEAK) {
- rcti *rect = gesture->customdata;
- const int delta[2] = {
- BLI_rcti_size_x(rect),
- BLI_rcti_size_y(rect),
- };
-
- if (WM_event_drag_test_with_delta(event, delta)) {
- int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI);
- int val = EVT_GESTURE_W;
-
- if (theta == 0) {
- val = EVT_GESTURE_E;
- }
- else if (theta == 1) {
- val = EVT_GESTURE_NE;
- }
- else if (theta == 2) {
- val = EVT_GESTURE_N;
- }
- else if (theta == 3) {
- val = EVT_GESTURE_NW;
- }
- else if (theta == -1) {
- val = EVT_GESTURE_SE;
- }
- else if (theta == -2) {
- val = EVT_GESTURE_S;
- }
- else if (theta == -3) {
- val = EVT_GESTURE_SW;
- }
-
-#if 0
- /* debug */
- if (val == 1) {
- printf("tweak north\n");
- }
- if (val == 2) {
- printf("tweak north-east\n");
- }
- if (val == 3) {
- printf("tweak east\n");
- }
- if (val == 4) {
- printf("tweak south-east\n");
- }
- if (val == 5) {
- printf("tweak south\n");
- }
- if (val == 6) {
- printf("tweak south-west\n");
- }
- if (val == 7) {
- printf("tweak west\n");
- }
- if (val == 8) {
- printf("tweak north-west\n");
- }
-#endif
- return val;
- }
- }
- return 0;
-}
-
/* ******************* gesture draw ******************* */
static void wm_gesture_draw_line_active_side(rcti *rect, const bool flip)
@@ -509,11 +439,6 @@ void wm_gesture_draw(wmWindow *win)
if (gt->type == WM_GESTURE_RECT) {
wm_gesture_draw_rect(gt);
}
-#if 0
- else if (gt->type == WM_GESTURE_TWEAK) {
- wm_gesture_draw_line(gt);
- }
-#endif
else if (gt->type == WM_GESTURE_CIRCLE) {
wm_gesture_draw_circle(gt);
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 2a27a8df411..1fdc8bbe2c8 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -471,118 +471,6 @@ void WM_OT_circle_gesture(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Tweak Gesture
- * \{ */
-
-static void gesture_tweak_modal(bContext *C, const wmEvent *event)
-{
- wmWindow *window = CTX_wm_window(C);
- wmGesture *gesture = window->tweak;
- rcti *rect = gesture->customdata;
- bool gesture_end = false;
-
- switch (event->type) {
- case MOUSEMOVE:
- case INBETWEEN_MOUSEMOVE: {
-
- rect->xmax = event->xy[0] - gesture->winrct.xmin;
- rect->ymax = event->xy[1] - gesture->winrct.ymin;
-
- const int val = wm_gesture_evaluate(gesture, event);
- if (val != 0) {
- wmEvent tevent;
-
- wm_event_init_from_window(window, &tevent);
- /* We want to get coord from start of drag,
- * not from point where it becomes a tweak event, see T40549. */
- tevent.xy[0] = rect->xmin + gesture->winrct.xmin;
- tevent.xy[1] = rect->ymin + gesture->winrct.ymin;
- if (gesture->event_type == LEFTMOUSE) {
- tevent.type = EVT_TWEAK_L;
- }
- else if (gesture->event_type == RIGHTMOUSE) {
- tevent.type = EVT_TWEAK_R;
- }
- else {
- tevent.type = EVT_TWEAK_M;
- }
- tevent.val = val;
- tevent.is_repeat = false;
- /* mouse coords! */
-
- /* important we add immediately after this event, so future mouse releases
- * (which may be in the queue already), are handled in order, see T44740 */
- wm_event_add_ex(window, &tevent, event);
-
- gesture_end = true;
- }
-
- break;
- }
-
- case LEFTMOUSE:
- case RIGHTMOUSE:
- case MIDDLEMOUSE:
- if (gesture->event_type == event->type) {
- gesture_end = true;
-
- /* when tweak fails we should give the other keymap entries a chance */
-
- /* XXX, assigning to readonly, BAD JUJU! */
- ((wmEvent *)event)->val = KM_RELEASE;
- }
- break;
- default:
- if (!ISTIMER(event->type) && event->type != EVENT_NONE) {
- gesture_end = true;
- }
- break;
- }
-
- if (gesture_end) {
- /* Frees gesture itself, and unregisters from window. */
- WM_gesture_end(window, gesture);
-
- /* This isn't very nice but needed to redraw gizmos which are hidden while tweaking,
- * See #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */
- ARegion *region = CTX_wm_region(C);
- if ((region != NULL) && (region->gizmo_map != NULL)) {
- if (WM_gizmomap_tag_delay_refresh_for_tweak_check(region->gizmo_map)) {
- ED_region_tag_redraw(region);
- }
- }
- }
-}
-
-void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
-{
- wmWindow *win = CTX_wm_window(C);
-
- if (win->tweak == NULL) {
- const ARegion *region = CTX_wm_region(C);
-
- if (region) {
- if (event->val == KM_PRESS) {
- if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
- win->tweak = WM_gesture_new(win, region, event, WM_GESTURE_TWEAK);
- }
- }
- }
- }
- else {
- /* no tweaks if event was handled */
- if (action & WM_HANDLER_BREAK) {
- WM_gesture_end(win, win->tweak);
- }
- else {
- gesture_tweak_modal(C, event);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Lasso Gesture
* \{ */
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index f7bc138f163..ffac585cde7 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -169,6 +169,7 @@ static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b)
return (wm_keymap_item_equals_result(a, b) && a->type == b->type && a->val == b->val &&
a->shift == b->shift && a->ctrl == b->ctrl && a->alt == b->alt && a->oskey == b->oskey &&
a->keymodifier == b->keymodifier && a->maptype == b->maptype &&
+ ((a->val != KM_CLICK_DRAG) || (a->direction == b->direction)) &&
((ISKEYBOARD(a->type) == 0) ||
(a->flag & KMI_REPEAT_IGNORE) == (b->flag & KMI_REPEAT_IGNORE)));
}
@@ -195,9 +196,6 @@ int WM_keymap_item_map_type_get(const wmKeyMapItem *kmi)
if (ISKEYBOARD(kmi->type)) {
return KMI_TYPE_KEYBOARD;
}
- if (ISTWEAK(kmi->type)) {
- return KMI_TYPE_TWEAK;
- }
if (ISMOUSE(kmi->type)) {
return KMI_TYPE_MOUSE;
}
@@ -459,11 +457,12 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
}
static void keymap_event_set(
- wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier)
+ wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier, int direction)
{
kmi->type = type;
kmi->val = val;
kmi->keymodifier = keymodifier;
+ kmi->direction = direction;
if (modifier == KM_ANY) {
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY;
@@ -497,15 +496,20 @@ static void keymap_item_set_id(wmKeyMap *keymap, wmKeyMapItem *kmi)
}
}
-wmKeyMapItem *WM_keymap_add_item(
- wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction)
{
wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
BLI_addtail(&keymap->items, kmi);
BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME);
- keymap_event_set(kmi, type, val, modifier, keymodifier);
+ keymap_event_set(kmi, type, val, modifier, keymodifier, direction);
wm_keymap_item_properties_set(kmi);
keymap_item_set_id(keymap, kmi);
@@ -919,14 +923,14 @@ wmKeyMap *WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
}
wmKeyMapItem *WM_modalkeymap_add_item(
- wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value)
+ wmKeyMap *km, int type, int val, int modifier, int keymodifier, int direction, int value)
{
wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
BLI_addtail(&km->items, kmi);
kmi->propvalue = value;
- keymap_event_set(kmi, type, val, modifier, keymodifier);
+ keymap_event_set(kmi, type, val, modifier, keymodifier, direction);
keymap_item_set_id(km, kmi);
@@ -935,15 +939,20 @@ wmKeyMapItem *WM_modalkeymap_add_item(
return kmi;
}
-wmKeyMapItem *WM_modalkeymap_add_item_str(
- wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value)
+wmKeyMapItem *WM_modalkeymap_add_item_str(wmKeyMap *km,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction,
+ const char *value)
{
wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
BLI_addtail(&km->items, kmi);
BLI_strncpy(kmi->propvalue_str, value, sizeof(kmi->propvalue_str));
- keymap_event_set(kmi, type, val, modifier, keymodifier);
+ keymap_event_set(kmi, type, val, modifier, keymodifier, direction);
keymap_item_set_id(km, kmi);
@@ -1730,6 +1739,9 @@ bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2)
if (k1->val != k2->val) {
return 0;
}
+ if (k1->val == KM_CLICK_DRAG && (k1->direction != k2->direction)) {
+ return 0;
+ }
}
if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift) {
diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c
index 162246798de..24c221221d1 100644
--- a/source/blender/windowmanager/intern/wm_keymap_utils.c
+++ b/source/blender/windowmanager/intern/wm_keymap_utils.c
@@ -29,64 +29,64 @@
/** \name Wrappers for #WM_keymap_add_item
* \{ */
-wmKeyMapItem *WM_keymap_add_menu(
- wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction)
{
wmKeyMapItem *kmi = WM_keymap_add_item(
- keymap, "WM_OT_call_menu", type, val, modifier, keymodifier);
+ keymap, "WM_OT_call_menu", type, val, modifier, keymodifier, direction);
RNA_string_set(kmi->ptr, "name", idname);
return kmi;
}
-wmKeyMapItem *WM_keymap_add_menu_pie(
- wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction)
{
wmKeyMapItem *kmi = WM_keymap_add_item(
- keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier);
+ keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier, direction);
RNA_string_set(kmi->ptr, "name", idname);
return kmi;
}
-wmKeyMapItem *WM_keymap_add_panel(
- wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction)
{
wmKeyMapItem *kmi = WM_keymap_add_item(
- keymap, "WM_OT_call_panel", type, val, modifier, keymodifier);
+ keymap, "WM_OT_call_panel", type, val, modifier, keymodifier, direction);
RNA_string_set(kmi->ptr, "name", idname);
/* TODO: we might want to disable this. */
RNA_boolean_set(kmi->ptr, "keep_open", false);
return kmi;
}
-wmKeyMapItem *WM_keymap_add_tool(
- wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap,
+ const char *idname,
+ int type,
+ int val,
+ int modifier,
+ int keymodifier,
+ int direction)
{
wmKeyMapItem *kmi = WM_keymap_add_item(
- keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier);
+ keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier, direction);
RNA_string_set(kmi->ptr, "name", idname);
return kmi;
}
-void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap,
- const EnumPropertyItem *items,
- const char *data_path,
- int type_start,
- int val,
- int modifier,
- int keymodifier)
-{
- for (int i = 0, type_offset = 0; items[i].identifier; i++) {
- if (items[i].identifier[0] == '\0') {
- continue;
- }
- wmKeyMapItem *kmi = WM_keymap_add_item(
- keymap, "WM_OT_context_set_enum", type_start + type_offset, val, modifier, keymodifier);
- RNA_string_set(kmi->ptr, "data_path", data_path);
- RNA_string_set(kmi->ptr, "value", items[i].identifier);
- type_offset += 1;
- }
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -106,6 +106,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
case CTX_MODE_EDIT_CURVE:
km_id = "Curve";
break;
+ case CTX_MODE_EDIT_CURVES:
+ km_id = "Curves";
+ break;
case CTX_MODE_EDIT_SURFACE:
km_id = "Curve";
break;
@@ -158,7 +161,7 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
km_id = "Grease Pencil Stroke Vertex Mode";
break;
case CTX_MODE_SCULPT_CURVES:
- km_id = "Curves Sculpt Mode";
+ km_id = "Curves Sculpt";
break;
}
}
diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c
index 6f3f42bee53..5a817075cd5 100644
--- a/source/blender/windowmanager/intern/wm_operator_utils.c
+++ b/source/blender/windowmanager/intern/wm_operator_utils.c
@@ -115,11 +115,11 @@ static bool interactive_value_update(ValueInteraction *inter,
(((float)(mval_curr - mval_init) / inter->context_vars.region->winx) *
value_range)) *
value_scale;
- if (event->ctrl) {
+ if (event->modifier & KM_CTRL) {
const double snap = 0.1;
value_delta = (float)roundf((double)value_delta / snap) * snap;
}
- if (event->shift) {
+ if (event->modifier & KM_SHIFT) {
value_delta *= 0.1f;
}
const float value_final = inter->init.prop_value + value_delta;
@@ -133,8 +133,8 @@ static bool interactive_value_update(ValueInteraction *inter,
}
inter->prev.prop_value = value_final;
- inter->prev.is_snap = event->ctrl;
- inter->prev.is_precise = event->shift;
+ inter->prev.is_snap = (event->modifier & KM_CTRL) != 0;
+ inter->prev.is_precise = (event->modifier & KM_SHIFT) != 0;
*r_value_final = value_final;
return changed;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index a476fb4fa13..7e680af4537 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -911,7 +911,6 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
ret_value = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(ret_value);
-
op->customdata = POINTER_FROM_INT((int)event->type);
if (ret_value & OPERATOR_RUNNING_MODAL) {
WM_event_add_modal_handler(C, op);
@@ -2822,7 +2821,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
float numValue;
/* TODO: fix hardcoded events */
- bool snap = event->ctrl != 0;
+ bool snap = (event->modifier & KM_CTRL) != 0;
/* Modal numinput active, try to handle numeric inputs first... */
if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &rc->num_input, event)) {
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 2ee608c0755..95879829d42 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -483,7 +483,6 @@ static void draw_display_buffer(PlayState *ps, ImBuf *ibuf)
if (!glsl_used) {
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
- immUniform1i("image", 0);
}
immBegin(GPU_PRIM_TRI_FAN, 4);
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index 6bdd11df776..182308cbe5e 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -67,7 +67,7 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
const float halfx = GLA_PIXEL_OFS / sizex;
const float halfy = GLA_PIXEL_OFS / sizex;
- immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
+ /* Texture is already bound to GL_TEXTURE0 unit. */
immBegin(GPU_PRIM_TRI_FAN, 4);
@@ -111,7 +111,7 @@ void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
const float halfx = GLA_PIXEL_OFS / sizex;
const float halfy = GLA_PIXEL_OFS / sizex;
- immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
+ /* Texture is already bound to GL_TEXTURE0 unit. */
immBegin(GPU_PRIM_TRI_FAN, 4);
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c
index 4ae935b14f2..51e4bc9faa8 100644
--- a/source/blender/windowmanager/intern/wm_toolsystem.c
+++ b/source/blender/windowmanager/intern/wm_toolsystem.c
@@ -663,6 +663,8 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
return "builtin_brush.Weight";
case CTX_MODE_VERTEX_GPENCIL:
return "builtin_brush.Draw";
+ case CTX_MODE_SCULPT_CURVES:
+ return "builtin_brush.Test 1";
/* end temporary hack. */
case CTX_MODE_PARTICLE:
@@ -804,13 +806,34 @@ void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C,
WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
}
+static IDProperty *idprops_ensure_named_group(IDProperty *group, const char *idname)
+{
+ IDProperty *prop = IDP_GetPropertyFromGroup(group, idname);
+ if ((prop == NULL) || (prop->type != IDP_GROUP)) {
+ IDPropertyTemplate val = {0};
+ prop = IDP_New(IDP_GROUP, &val, __func__);
+ STRNCPY(prop->name, idname);
+ IDP_ReplaceInGroup_ex(group, prop, NULL);
+ }
+ return prop;
+}
+
+IDProperty *WM_toolsystem_ref_properties_get_idprops(bToolRef *tref)
+{
+ IDProperty *group = tref->properties;
+ if (group == NULL) {
+ return NULL;
+ }
+ return IDP_GetPropertyFromGroup(group, tref->idname);
+}
+
IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref)
{
if (tref->properties == NULL) {
IDPropertyTemplate val = {0};
- tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
+ tref->properties = IDP_New(IDP_GROUP, &val, __func__);
}
- return tref->properties;
+ return idprops_ensure_named_group(tref->properties, tref->idname);
}
bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref,
@@ -818,7 +841,7 @@ bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref,
StructRNA *type,
PointerRNA *r_ptr)
{
- IDProperty *group = tref->properties;
+ IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref);
IDProperty *prop = group ? IDP_GetPropertyFromGroup(group, idname) : NULL;
RNA_pointer_create(NULL, type, prop, r_ptr);
return (prop != NULL);
@@ -830,17 +853,7 @@ void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref,
PointerRNA *r_ptr)
{
IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref);
- IDProperty *prop = IDP_GetPropertyFromGroup(group, idname);
- if (prop == NULL) {
- IDPropertyTemplate val = {0};
- prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties");
- STRNCPY(prop->name, idname);
- IDP_ReplaceInGroup_ex(group, prop, NULL);
- }
- else {
- BLI_assert(prop->type == IDP_GROUP);
- }
-
+ IDProperty *prop = idprops_ensure_named_group(group, idname);
RNA_pointer_create(NULL, type, prop, r_ptr);
}
@@ -857,8 +870,9 @@ void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref,
IDPropertyTemplate val = {0};
dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
}
- if (tref->properties != NULL) {
- IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname);
+ IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref);
+ if (group != NULL) {
+ IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname);
if (prop) {
/* Important key-map items properties don't get overwritten by the tools.
* - When a key-map item doesn't set a property, the tool-systems is used.
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index a983150b504..e93ffe48aba 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1104,10 +1104,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
win->active = 0; /* XXX */
/* clear modifiers for inactive windows */
- win->eventstate->alt = 0;
- win->eventstate->ctrl = 0;
- win->eventstate->shift = 0;
- win->eventstate->oskey = 0;
+ win->eventstate->modifier = 0;
win->eventstate->keymodifier = 0;
break;
@@ -1138,7 +1135,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
kdata.ascii = '\0';
kdata.utf8_buf[0] = '\0';
- if (win->eventstate->shift) {
+ if (win->eventstate->modifier & KM_SHIFT) {
if ((keymodifier & KM_SHIFT) == 0) {
kdata.key = GHOST_kKeyLeftShift;
wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
@@ -1147,11 +1144,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
#ifdef USE_WIN_ACTIVATE
else {
if (keymodifier & KM_SHIFT) {
- win->eventstate->shift = KM_MOD_HELD;
+ win->eventstate->modifier |= KM_SHIFT;
}
}
#endif
- if (win->eventstate->ctrl) {
+ if (win->eventstate->modifier & KM_CTRL) {
if ((keymodifier & KM_CTRL) == 0) {
kdata.key = GHOST_kKeyLeftControl;
wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
@@ -1160,11 +1157,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
#ifdef USE_WIN_ACTIVATE
else {
if (keymodifier & KM_CTRL) {
- win->eventstate->ctrl = KM_MOD_HELD;
+ win->eventstate->modifier |= KM_CTRL;
}
}
#endif
- if (win->eventstate->alt) {
+ if (win->eventstate->modifier & KM_ALT) {
if ((keymodifier & KM_ALT) == 0) {
kdata.key = GHOST_kKeyLeftAlt;
wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
@@ -1173,11 +1170,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
#ifdef USE_WIN_ACTIVATE
else {
if (keymodifier & KM_ALT) {
- win->eventstate->alt = KM_MOD_HELD;
+ win->eventstate->modifier |= KM_ALT;
}
}
#endif
- if (win->eventstate->oskey) {
+ if (win->eventstate->modifier & KM_OSKEY) {
if ((keymodifier & KM_OSKEY) == 0) {
kdata.key = GHOST_kKeyOS;
wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
@@ -1186,7 +1183,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
#ifdef USE_WIN_ACTIVATE
else {
if (keymodifier & KM_OSKEY) {
- win->eventstate->oskey = KM_MOD_HELD;
+ win->eventstate->modifier |= KM_OSKEY;
}
}
#endif
@@ -1216,7 +1213,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
wm_event_init_from_window(win, &event);
event.type = MOUSEMOVE;
copy_v2_v2_int(event.prev_xy, event.xy);
- event.is_repeat = false;
+ event.flag = 0;
wm_event_add(win, &event);
@@ -1347,7 +1344,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
/* activate region */
event.type = MOUSEMOVE;
copy_v2_v2_int(event.prev_xy, event.xy);
- event.is_repeat = false;
+ event.flag = 0;
/* No context change! C->wm->windrawable is drawable, or for area queues. */
wm->winactive = win;
@@ -1488,7 +1485,7 @@ static bool wm_window_timer(const bContext *C)
event.type = wt->event_type;
event.val = KM_NOTHING;
event.keymodifier = 0;
- event.is_repeat = false;
+ event.flag = 0;
event.custom = EVT_DATA_TIMER;
event.customdata = wt;
wm_event_add(win, &event);
@@ -1844,56 +1841,23 @@ bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut)
/** \name Find Window Utility
* \{ */
-static void wm_window_desktop_pos_get(const wmWindow *win,
- const int screen_pos[2],
- int r_desk_pos[2])
+wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2])
{
- /* To desktop space. */
- r_desk_pos[0] = screen_pos[0] + (int)(U.pixelsize * win->posx);
- r_desk_pos[1] = screen_pos[1] + (int)(U.pixelsize * win->posy);
-}
-
-static void wm_window_screen_pos_get(const wmWindow *win,
- const int desktop_pos[2],
- int r_scr_pos[2])
-{
- /* To window space. */
- r_scr_pos[0] = desktop_pos[0] - (int)(U.pixelsize * win->posx);
- r_scr_pos[1] = desktop_pos[1] - (int)(U.pixelsize * win->posy);
-}
-
-wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm,
- const wmWindow *win_ignore,
- const wmWindow *win,
- const int mval[2],
- int r_mval[2])
-{
- int desk_pos[2];
- wm_window_desktop_pos_get(win, mval, desk_pos);
-
- /* TODO: This should follow the order of the activated windows.
- * The current solution is imperfect but usable in most cases. */
- LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
- if (win_iter == win_ignore) {
- continue;
- }
-
- if (win_iter->windowstate == GHOST_kWindowStateMinimized) {
- continue;
- }
-
- int scr_pos[2];
- wm_window_screen_pos_get(win_iter, desk_pos, scr_pos);
+ int tmp[2];
+ copy_v2_v2_int(tmp, mval);
+ wm_cursor_position_to_ghost(win, &tmp[0], &tmp[1]);
- if (scr_pos[0] >= 0 && scr_pos[1] >= 0 && scr_pos[0] <= WM_window_pixels_x(win_iter) &&
- scr_pos[1] <= WM_window_pixels_y(win_iter)) {
+ GHOST_WindowHandle ghostwin = GHOST_GetWindowUnderCursor(g_system, tmp[0], tmp[1]);
- copy_v2_v2_int(r_mval, scr_pos);
- return win_iter;
- }
+ if (!ghostwin) {
+ return NULL;
}
- return NULL;
+ wmWindow *r_win = GHOST_GetWindowUserData(ghostwin);
+ wm_cursor_position_from_ghost(r_win, &tmp[0], &tmp[1]);
+ copy_v2_v2_int(r_mval, tmp);
+
+ return r_win;
}
void WM_window_pixel_sample_read(const wmWindowManager *wm,
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 68b16d46746..172a879e118 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -74,16 +74,8 @@ void wm_gesture_draw(struct wmWindow *win);
/**
* Tweak and line gestures.
*/
-int wm_gesture_evaluate(wmGesture *gesture, const struct wmEvent *event);
void wm_gesture_tag_redraw(struct wmWindow *win);
-/* wm_gesture_ops.c */
-
-/**
- * Standard tweak, called after window handlers passed on event.
- */
-void wm_tweakevent_test(bContext *C, const wmEvent *event, int action);
-
/* wm_jobs.c */
/**
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 0ff181db9b1..d5c8c5022cc 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -325,16 +325,6 @@ enum {
/* NOTE: these values are saved in key-map files, do not change them but just add new ones. */
- /* Tweak events:
- * Sent as additional event with the mouse coordinates
- * from where the initial click was placed. */
-
- /* Tweak events for L M R mouse-buttons. */
- EVT_TWEAK_L = 0x5002, /* 20482 */
- EVT_TWEAK_M = 0x5003, /* 20483 */
- EVT_TWEAK_R = 0x5004, /* 20484 */
- /* 0x5010 (and lower) should be left to add other tweak types in the future. */
-
/* 0x5011 is taken, see EVT_ACTIONZONE_FULLSCREEN */
/* Misc Blender internals: 0x502x */
@@ -394,9 +384,6 @@ enum {
BUTTON6MOUSE, \
BUTTON7MOUSE))
-/** Test whether the event is tweak event. */
-#define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_TWEAK_R)
-
/** Test whether the event is a NDOF event. */
#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX)
@@ -408,15 +395,6 @@ enum {
((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \
(ISKEYMODIFIER(event_type) == false))
-/* Internal helpers. */
-#define _VA_IS_EVENT_MOD2(v, a) (CHECK_TYPE_INLINE(v, wmEvent *), ((v)->a))
-#define _VA_IS_EVENT_MOD3(v, a, b) (_VA_IS_EVENT_MOD2(v, a) || ((v)->b))
-#define _VA_IS_EVENT_MOD4(v, a, b, c) (_VA_IS_EVENT_MOD3(v, a, b) || ((v)->c))
-#define _VA_IS_EVENT_MOD5(v, a, b, c, d) (_VA_IS_EVENT_MOD4(v, a, b, c) || ((v)->d))
-
-/** Reusable `IS_EVENT_MOD(event, shift, ctrl, alt, oskey)` macro. */
-#define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__)
-
enum eEventType_Mask {
/** #ISKEYMODIFIER */
EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0),
@@ -432,14 +410,11 @@ enum eEventType_Mask {
EVT_TYPE_MASK_MOUSE = (1 << 5),
/** #ISNDOF */
EVT_TYPE_MASK_NDOF = (1 << 6),
- /** #ISTWEAK */
- EVT_TYPE_MASK_TWEAK = (1 << 7),
/** #IS_EVENT_ACTIONZONE */
- EVT_TYPE_MASK_ACTIONZONE = (1 << 8),
+ EVT_TYPE_MASK_ACTIONZONE = (1 << 7),
};
#define EVT_TYPE_MASK_ALL \
- (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_TWEAK | \
- EVT_TYPE_MASK_ACTIONZONE)
+ (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_ACTIONZONE)
#define EVT_TYPE_MASK_HOTKEY_INCLUDE \
(EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF)
@@ -454,18 +429,6 @@ bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask);
* \{ */
/* Gestures */
-/* NOTE: these values are saved in keymap files, do not change them but just add new ones */
-enum {
- /* Value of tweaks and line gestures. #KM_ANY (-1) works for this case too. */
- EVT_GESTURE_N = 1,
- EVT_GESTURE_NE = 2,
- EVT_GESTURE_E = 3,
- EVT_GESTURE_SE = 4,
- EVT_GESTURE_S = 5,
- EVT_GESTURE_SW = 6,
- EVT_GESTURE_W = 7,
- EVT_GESTURE_NW = 8,
-};
/* File select */
enum {
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c
index f6003428700..6750e7a7d77 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_action.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c
@@ -56,8 +56,7 @@ static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
static wmXrAction *action_create(const char *action_name,
eXrActionType type,
- unsigned int count_subaction_paths,
- const char **subaction_paths,
+ const ListBase *user_paths,
wmOperatorType *ot,
IDProperty *op_properties,
const char *haptic_name,
@@ -73,15 +72,16 @@ static wmXrAction *action_create(const char *action_name,
strcpy(action->name, action_name);
action->type = type;
- const unsigned int count = count_subaction_paths;
+ const unsigned int count = (unsigned int)BLI_listbase_count(user_paths);
+ unsigned int subaction_idx = 0;
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]);
+ LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) {
+ action->subaction_paths[subaction_idx] = MEM_mallocN(strlen(user_path->path) + 1,
+ "XrAction_SubactionPath");
+ strcpy(action->subaction_paths[subaction_idx], user_path->path);
}
size_t size;
@@ -140,10 +140,9 @@ static void action_destroy(void *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) {
+ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
MEM_SAFE_FREE(subaction_paths[i]);
}
MEM_freeN(subaction_paths);
@@ -214,8 +213,7 @@ 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 ListBase *user_paths,
wmOperatorType *ot,
IDProperty *op_properties,
const char *haptic_name,
@@ -232,8 +230,7 @@ bool WM_xr_action_create(wmXrData *xr,
wmXrAction *action = action_create(action_name,
type,
- count_subaction_paths,
- subaction_paths,
+ user_paths,
ot,
op_properties,
haptic_name,
@@ -244,10 +241,20 @@ bool WM_xr_action_create(wmXrData *xr,
action_flag,
haptic_flag);
+ const unsigned int count = (unsigned int)BLI_listbase_count(user_paths);
+ unsigned int subaction_idx = 0;
+
+ char **subaction_paths = MEM_calloc_arrayN(
+ count, sizeof(*subaction_paths), "XrAction_SubactionPathPointers");
+
+ LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) {
+ subaction_paths[subaction_idx] = (char *)user_path->path;
+ }
+
GHOST_XrActionInfo info = {
.name = action_name,
- .count_subaction_paths = count_subaction_paths,
- .subaction_paths = subaction_paths,
+ .count_subaction_paths = count,
+ .subaction_paths = (const char **)subaction_paths,
.states = action->states,
.float_thresholds = action->float_thresholds,
.axis_flags = (int16_t *)action->axis_flags,
@@ -273,11 +280,11 @@ bool WM_xr_action_create(wmXrData *xr,
break;
}
- if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) {
- return false;
- }
+ const bool success = GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info);
- return true;
+ MEM_freeN(subaction_paths);
+
+ return success;
}
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
@@ -323,19 +330,29 @@ bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
const char *action_name,
const char *profile_path,
- unsigned int count_subaction_paths,
- const char **subaction_paths,
- const char **component_paths,
+ const ListBase *user_paths,
+ const ListBase *component_paths,
const float *float_thresholds,
const eXrAxisFlag *axis_flags,
const struct wmXrPose *poses)
{
+ const unsigned int count = (unsigned int)BLI_listbase_count(user_paths);
+ BLI_assert(count == (unsigned int)BLI_listbase_count(component_paths));
+
GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN(
- count_subaction_paths, sizeof(*binding_infos), __func__);
+ count, sizeof(*binding_infos), "XrActionBinding_Infos");
- for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ char **subaction_paths = MEM_calloc_arrayN(
+ count, sizeof(*subaction_paths), "XrActionBinding_SubactionPathPointers");
+
+ for (unsigned int i = 0; i < count; ++i) {
GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
- binding_info->component_path = component_paths[i];
+ const XrUserPath *user_path = BLI_findlink(user_paths, i);
+ const XrComponentPath *component_path = BLI_findlink(component_paths, i);
+
+ subaction_paths[i] = (char *)user_path->path;
+
+ binding_info->component_path = component_path->path;
if (float_thresholds) {
binding_info->float_threshold = float_thresholds[i];
}
@@ -351,15 +368,18 @@ bool WM_xr_action_binding_create(wmXrData *xr,
GHOST_XrActionProfileInfo profile_info = {
.action_name = action_name,
.profile_path = profile_path,
- .count_subaction_paths = count_subaction_paths,
- .subaction_paths = subaction_paths,
+ .count_subaction_paths = count,
+ .subaction_paths = (const char **)subaction_paths,
.bindings = binding_infos,
};
- bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+ const bool success = GHOST_XrCreateActionBindings(
+ xr->runtime->context, action_set_name, 1, &profile_info);
+ MEM_freeN(subaction_paths);
MEM_freeN(binding_infos);
- return ret;
+
+ return success;
}
void WM_xr_action_binding_destroy(wmXrData *xr,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
index 0c356ab2b2e..8a1982fa8b5 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
@@ -103,6 +103,12 @@ static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_
XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src);
amb_dst->prev = amb_dst->next = NULL;
+ BLI_listbase_clear(&amb_dst->component_paths);
+ LISTBASE_FOREACH (XrComponentPath *, path, &amb_src->component_paths) {
+ XrComponentPath *path_new = MEM_dupallocN(path);
+ BLI_addtail(&amb_dst->component_paths, path_new);
+ }
+
return amb_dst;
}
@@ -118,11 +124,17 @@ XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
return amb_dst;
}
+static void wm_xr_actionmap_binding_clear(XrActionMapBinding *amb)
+{
+ BLI_freelistN(&amb->component_paths);
+}
+
bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb)
{
int idx = BLI_findindex(&ami->bindings, amb);
if (idx != -1) {
+ wm_xr_actionmap_binding_clear(amb);
BLI_freelinkN(&ami->bindings, amb);
if (idx <= ami->selbinding) {
@@ -155,12 +167,6 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha
* Item in an XR action map, that maps an XR event to an operator, pose, or haptic output.
* \{ */
-static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami)
-{
- BLI_freelistN(&ami->bindings);
- ami->selbinding = 0;
-}
-
static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
{
WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op);
@@ -180,6 +186,19 @@ static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami)
}
}
+static void wm_xr_actionmap_item_clear(XrActionMapItem *ami)
+{
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) {
+ wm_xr_actionmap_binding_clear(amb);
+ }
+ BLI_freelistN(&ami->bindings);
+ ami->selbinding = 0;
+
+ wm_xr_actionmap_item_properties_free(ami);
+
+ BLI_freelistN(&ami->user_paths);
+}
+
void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
{
switch (ami->type) {
@@ -305,6 +324,12 @@ static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src)
ami_dst->op_properties_ptr = NULL;
}
+ BLI_listbase_clear(&ami_dst->user_paths);
+ LISTBASE_FOREACH (XrUserPath *, path, &ami_src->user_paths) {
+ XrUserPath *path_new = MEM_dupallocN(path);
+ BLI_addtail(&ami_dst->user_paths, path_new);
+ }
+
return ami_dst;
}
@@ -324,8 +349,7 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
int idx = BLI_findindex(&actionmap->items, ami);
if (idx != -1) {
- wm_xr_actionmap_item_bindings_clear(ami);
- wm_xr_actionmap_item_properties_free(ami);
+ wm_xr_actionmap_item_clear(ami);
BLI_freelinkN(&actionmap->items, ami);
if (idx <= actionmap->selitem) {
@@ -480,12 +504,9 @@ XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name)
void WM_xr_actionmap_clear(XrActionMap *actionmap)
{
LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
- wm_xr_actionmap_item_bindings_clear(ami);
- wm_xr_actionmap_item_properties_free(ami);
+ wm_xr_actionmap_item_clear(ami);
}
-
BLI_freelistN(&actionmap->items);
-
actionmap->selitem = 0;
}
@@ -494,9 +515,7 @@ void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
WM_xr_actionmap_clear(am);
}
-
BLI_freelistN(&runtime->actionmaps);
-
runtime->actactionmap = runtime->selactionmap = 0;
}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 0e7c4d18753..9480104150a 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -114,12 +114,8 @@ typedef struct wmXrDrawData {
typedef struct wmXrController {
struct wmXrController *next, *prev;
- /** 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];
+ /** OpenXR user path identifier. */
+ char subaction_path[64]; /* XR_MAX_USER_PATH_LENGTH */
/** Pose (in world space) that represents the user's hand when holding the controller. */
GHOST_XrPose grip_pose;
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 59b4eb00363..dfeaeae196c 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -1188,8 +1188,9 @@ void wm_xr_session_actions_update(wmWindowManager *wm)
&state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat);
}
- int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
- if (!ret) {
+ const bool synced = GHOST_XrSyncActions(xr_context,
+ active_action_set ? active_action_set->name : NULL);
+ if (!synced) {
return;
}
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index e6e122508a9..d17afad0918 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -319,16 +319,23 @@ elseif(WIN32)
elseif(APPLE)
if(WITH_PYTHON_MODULE)
if(WITH_INSTALL_PORTABLE)
+ set(BPY_INSTALL_DIR)
set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/../Resources/${BLENDER_VERSION})
# Keep the `BLENDER_VERSION` folder and bpy.so in the build folder.
set(INSTALL_BPY_TO_SITE_PACKAGES OFF)
else()
- set(TARGETDIR_VER "${PYTHON_LIBPATH}/Resources/${BLENDER_VERSION}")
+ # Parent directory of bpy.so for installation.
+ set(BPY_INSTALL_DIR ${PYTHON_LIBPATH}/site-packages)
+ # Defined in terms of site-packages since the site-packages
+ # directory can be a symlink (brew for example).
+ set(TARGETDIR_VER "${BPY_INSTALL_DIR}/../Resources/${BLENDER_VERSION}")
set(INSTALL_BPY_TO_SITE_PACKAGES ON)
endif()
else()
set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION})
endif()
+ # License, copyright, readme files.
+ set(BLENDER_TEXT_FILES_DESTINATION "${TARGETDIR_VER}/../text")
set(MAC_BLENDER_TARGET_DYLIBS_DIR "${TARGETDIR_VER}/lib")
# Skip relinking on cpack / install
set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true)
@@ -1052,9 +1059,6 @@ elseif(APPLE)
DESTINATION "."
)
- # install release and app files
- set(BLENDER_TEXT_FILES_DESTINATION Blender.app/Contents/Resources/text)
-
install(
FILES ${OSX_APP_SOURCEDIR}/Contents/PkgInfo
DESTINATION Blender.app/Contents
@@ -1108,11 +1112,12 @@ elseif(APPLE)
)
unset(_py_inc_suffix)
endif()
+
if(WITH_PYTHON_MODULE)
if(INSTALL_BPY_TO_SITE_PACKAGES)
install(
TARGETS blender
- LIBRARY DESTINATION ${PYTHON_LIBPATH}/site-packages
+ LIBRARY DESTINATION ${BPY_INSTALL_DIR}
)
endif()
endif()
diff --git a/source/tools b/source/tools
-Subproject 3fc56d7bc28f83c864c37503700a8a182f77a59
+Subproject 5715a567950258c17089c9b9cb175a6ef8c602c
diff --git a/tests/check_deprecated.py b/tests/check_deprecated.py
deleted file mode 100644
index 089e0275229..00000000000
--- a/tests/check_deprecated.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-
-import os
-from os.path import splitext
-
-DEPRECATE_DAYS = 120
-
-SKIP_DIRS = ("extern",
- "tests", # not this dir
- )
-
-
-def is_c_header(filename):
- ext = splitext(filename)[1]
- return (ext in {".h", ".hpp", ".hxx", ".hh"})
-
-
-def is_c(filename):
- ext = splitext(filename)[1]
- return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl"})
-
-
-def is_c_any(filename):
- return is_c(filename) or is_c_header(filename)
-
-
-def is_py(filename):
- ext = splitext(filename)[1]
- return (ext == ".py")
-
-
-def is_source_any(filename):
- return is_c_any(filename) or is_py(filename)
-
-
-def source_list(path, filename_check=None):
- for dirpath, dirnames, filenames in os.walk(path):
- # skip '.git'
- dirnames[:] = [d for d in dirnames if not d.startswith(".")]
-
- for filename in filenames:
- if filename_check is None or filename_check(filename):
- yield os.path.join(dirpath, filename)
-
-
-def deprecations():
- """
- Searches out source code for lines like
-
- /* *DEPRECATED* 2011/7/17 bgl.Buffer.list info text */
-
- Or...
-
- # *DEPRECATED* 2010/12/22 some.py.func more info */
-
- """
- import datetime
- SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))))
-
- SKIP_DIRS_ABS = [os.path.join(SOURCE_DIR, p) for p in SKIP_DIRS]
-
- deprecations_ls = []
-
- scan_tot = 0
-
- print("scanning in %r for '*DEPRECATED* YYYY/MM/DD info'" % SOURCE_DIR)
-
- for fn in source_list(SOURCE_DIR, is_source_any):
- # print(fn)
- skip = False
- for p in SKIP_DIRS_ABS:
- if fn.startswith(p):
- skip = True
- break
- if skip:
- continue
-
- file = open(fn, 'r', encoding="utf8")
- for i, l in enumerate(file):
- # logic for deprecation warnings
- if '*DEPRECATED*' in l:
- try:
- l = l.strip()
- data = l.split('*DEPRECATED*', 1)[-1].strip().strip()
- data = [w.strip() for w in data.split('/', 2)]
- data[-1], info = data[-1].split(' ', 1)
- info = info.split("*/", 1)[0]
- if len(data) != 3:
- print(" poorly formatting line:\n"
- " %r:%d\n"
- " %s" %
- (fn, i + 1, l)
- )
- else:
- data = datetime.datetime(*tuple([int(w) for w in data]))
-
- deprecations_ls.append((data, (fn, i + 1), info))
- except:
- print("Error file - %r:%d" % (fn, i + 1))
- import traceback
- traceback.print_exc()
-
- scan_tot += 1
-
- print(" scanned %d files" % scan_tot)
-
- return deprecations_ls
-
-
-def main():
- import datetime
- now = datetime.datetime.now()
-
- deps = deprecations()
-
- print("\nAll deprecations...")
- for data, fileinfo, info in deps:
- days_old = (now - data).days
- if days_old > DEPRECATE_DAYS:
- info = "*** REMOVE! *** " + info
- print(" %r, days-old(%.2d), %s:%d - %s" % (data, days_old, fileinfo[0], fileinfo[1], info))
- if deps:
- print("\ndone!")
- else:
- print("\nnone found!")
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/python/bl_keymap_validate.py b/tests/python/bl_keymap_validate.py
index ce2022f6ae7..1743893dc8a 100644
--- a/tests/python/bl_keymap_validate.py
+++ b/tests/python/bl_keymap_validate.py
@@ -16,6 +16,7 @@ This catches the following kinds of issues:
- Unused keymaps (keymaps which are defined but not used anywhere).
- Event values that don't make sense for the event type, e.g.
An escape key could have the value "NORTH" instead of "PRESS".
+- Identical key-map items.
This works by taking the keymap data (before it's loaded into Blender),
then comparing it with that same keymap after exporting and importing.
@@ -27,16 +28,46 @@ NOTE:
"""
import types
-import typing
-
+from typing import (
+ Any,
+ Dict,
+ Generator,
+ List,
+ Optional,
+ Sequence,
+ Tuple,
+)
+
+KeyConfigData = List[Tuple[str, Tuple[Any], Dict[str, Any]]]
+
+import os
import contextlib
-import bpy
+import bpy # type: ignore
# Useful for diffing the output to see what changed in context.
# this writes keymaps into the current directory with `.orig.py` & `.rewrite.py` extensions.
-WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory.
+WRITE_OUTPUT_DIR = "" # "/tmp", defaults to the systems temp directory.
+# For each preset, test all of these options.
+# The key is the preset name, containing a sequence of (attribute, value) pairs to test.
+#
+# NOTE(@campbellbarton): only add these for preferences which impact multiple keys as exposing all preferences
+# this way would create too many combinations making the tests take too long to complete.
+PRESET_PREFS = {
+ "Blender": (
+ (("select_mouse", 'LEFT'), ("use_alt_tool", False)),
+ (("select_mouse", 'LEFT'), ("use_alt_tool", True)),
+ (("select_mouse", 'RIGHT'), ("rmb_action", 'TWEAK')),
+ (("select_mouse", 'RIGHT'), ("rmb_action", 'FALLBACK_TOOL')),
+ ),
+}
+
+# Don't report duplicates for these presets.
+ALLOW_DUPLICATES = {
+ # This key-map manipulates the default key-map, making it difficult to avoid duplicates entirely.
+ "Industry_Compatible"
+}
# -----------------------------------------------------------------------------
# Generic Utilities
@@ -45,7 +76,7 @@ WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory.
def temp_fn_argument_extractor(
mod: types.ModuleType,
mod_attr: str,
-) -> typing.Iterator[typing.List[typing.Tuple[list, dict]]]:
+) -> Generator[List[Tuple[Tuple[Tuple[Any], ...], Dict[str, Dict[str, Any]]]], None, None]:
"""
Temporarily intercept a function, so it's arguments can be extracted.
The context manager gives us a list where each item is a tuple of
@@ -54,7 +85,7 @@ def temp_fn_argument_extractor(
args_collected = []
real_fn = getattr(mod, mod_attr)
- def wrap_fn(*args, **kw):
+ def wrap_fn(*args: Tuple[Any], **kw: Dict[str, Any]) -> Any:
args_collected.append((args, kw))
return real_fn(*args, **kw)
setattr(mod, mod_attr, wrap_fn)
@@ -66,10 +97,10 @@ def temp_fn_argument_extractor(
def round_float_32(f: float) -> float:
from struct import pack, unpack
- return unpack("f", pack("f", f))[0]
+ return unpack("f", pack("f", f))[0] # type: ignore
-def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.Optional[str]:
+def report_humanly_readable_difference(a: Any, b: Any) -> Optional[str]:
"""
Compare strings, return None whrn they match,
otherwise a humanly readable difference message.
@@ -86,7 +117,7 @@ def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.O
# -----------------------------------------------------------------------------
# Keymap Utilities.
-def keyconfig_preset_scan() -> typing.List[str]:
+def keyconfig_preset_scan() -> List[str]:
"""
Return all bundled presets (keymaps), not user presets.
"""
@@ -104,7 +135,7 @@ def keyconfig_preset_scan() -> typing.List[str]:
]
-def keymap_item_property_clean(value: typing.Any) -> typing.Any:
+def keymap_item_property_clean(value: Any) -> Any:
"""
Recursive property sanitize.
@@ -118,12 +149,13 @@ def keymap_item_property_clean(value: typing.Any) -> typing.Any:
return sorted(
# Convert to `dict` to de-duplicate.
dict([(k, keymap_item_property_clean(v)) for k, v in value]).items(),
- key=lambda item: item[0],
+ # Ignore type checking, these are strings which we know can be sorted.
+ key=lambda item: item[0], # type: ignore
)
return value
-def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None:
+def keymap_data_clean(keyconfig_data: KeyConfigData, *, relaxed: bool) -> None:
"""
Order & sanitize keymap data so the result
from the hand written Python script is comparable with data exported & imported.
@@ -153,22 +185,82 @@ def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None:
items[i] = item_op, item_event, None
-def keyconfig_activate_and_extract_data(filepath: str, *, relaxed: bool) -> typing.List:
+def keyconfig_config_as_filename_component(values: Sequence[Tuple[str, Any]]) -> str:
+ """
+ Takes a configuration, eg:
+
+ [("select_mouse", 'LEFT'), ("rmb_action", 'TWEAK')]
+
+ And returns a filename compatible path:
+ """
+ from urllib.parse import quote
+ if not values:
+ return ""
+
+ return "(" + quote(
+ ".".join([
+ "-".join((str(key), str(val)))
+ for key, val in values
+ ]),
+ # Needed so forward slashes aren't included in the resulting name.
+ safe="",
+ ) + ")"
+
+
+def keyconfig_activate_and_extract_data(
+ filepath: str,
+ *,
+ relaxed: bool,
+ config: Sequence[Tuple[str, Any]],
+) -> KeyConfigData:
"""
Activate the key-map by filepath,
return the key-config data (cleaned for comparison).
"""
- import bl_keymap_utils.io
+ import bl_keymap_utils.io # type: ignore
+
+ if config:
+ bpy.ops.preferences.keyconfig_activate(filepath=filepath)
+ km_prefs = bpy.context.window_manager.keyconfigs.active.preferences
+ for attr, value in config:
+ setattr(km_prefs, attr, value)
+
with temp_fn_argument_extractor(bl_keymap_utils.io, "keyconfig_init_from_data") as args_collected:
bpy.ops.preferences.keyconfig_activate(filepath=filepath)
+
# If called multiple times, something strange is happening.
assert(len(args_collected) == 1)
args, _kw = args_collected[0]
- keyconfig_data = args[1]
+ # Ignore the type check as `temp_fn_argument_extractor` is a generic function
+ # which doesn't contain type information of the function being wrapped.
+ keyconfig_data: KeyConfigData = args[1] # type: ignore
keymap_data_clean(keyconfig_data, relaxed=relaxed)
return keyconfig_data
+def keyconfig_report_duplicates(keyconfig_data: KeyConfigData) -> str:
+ """
+ Return true if any of the key-maps have duplicate items.
+
+ Duplicate items are reported so they can be resolved.
+ """
+ error_text = []
+ for km_idname, km_args, km_items_data in keyconfig_data:
+ items = tuple(km_items_data["items"])
+ unique: Dict[str, List[int]] = {}
+ for i, (item_op, item_event, item_prop) in enumerate(items):
+ # Ensure stable order as `repr` will use order of definition.
+ item_event = {key: item_event[key] for key in sorted(item_event.keys())}
+ if item_prop is not None:
+ item_prop = {key: item_prop[key] for key in sorted(item_prop.keys())}
+ item_repr = repr((item_op, item_event, item_prop))
+ unique.setdefault(item_repr, []).append(i)
+ for key, value in unique.items():
+ if len(value) > 1:
+ error_text.append("\"%s\" %r indices %r for item %r" % (km_idname, km_args, value, key))
+ return "\n".join(error_text)
+
+
def main() -> None:
import os
import sys
@@ -185,38 +277,64 @@ def main() -> None:
presets = keyconfig_preset_scan()
for filepath in presets:
name_only = os.path.splitext(os.path.basename(filepath))[0]
-
- print("KeyMap Validate:", name_only, end=" ... ")
-
- data_orig = keyconfig_activate_and_extract_data(filepath, relaxed=relaxed)
-
- with tempfile.TemporaryDirectory() as dir_temp:
- filepath_temp = os.path.join(dir_temp, name_only + ".test.py")
- bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True)
- data_reimport = keyconfig_activate_and_extract_data(filepath_temp, relaxed=relaxed)
-
- # Comparing a pretty printed string tends to give more useful
- # text output compared to the data-structure. Both will work.
- if (cmp_message := report_humanly_readable_difference(
- pprint.pformat(data_orig, indent=0, width=120),
- pprint.pformat(data_reimport, indent=0, width=120),
- )):
- print("FAILED!")
- sys.stdout.write((
- "Keymap %s has inconsistency on re-importing:\n"
- " %r"
- ) % (filepath, cmp_message))
- has_error = True
- else:
- print("OK!")
-
- if WRITE_OUTPUT_DIR:
- name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only)
- print("Writing data to:", name_only_temp + ".*.py")
- with open(name_only_temp + ".orig.py", 'w') as fh:
- fh.write(pprint.pformat(data_orig, indent=0, width=120))
- with open(name_only_temp + ".rewrite.py", 'w') as fh:
- fh.write(pprint.pformat(data_reimport, indent=0, width=120))
+ for config in PRESET_PREFS.get(name_only, ((),)):
+ name_only_with_config = name_only + keyconfig_config_as_filename_component(config)
+ print("KeyMap Validate:", name_only_with_config, end=" ... ")
+ data_orig = keyconfig_activate_and_extract_data(
+ filepath,
+ relaxed=relaxed,
+ config=config,
+ )
+
+ with tempfile.TemporaryDirectory() as dir_temp:
+ filepath_temp = os.path.join(
+ dir_temp,
+ name_only_with_config + ".test" + ".py",
+ )
+
+ bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True)
+ data_reimport = keyconfig_activate_and_extract_data(
+ filepath_temp,
+ relaxed=relaxed,
+ # No configuration supported when loading exported key-maps.
+ config=(),
+ )
+
+ # Comparing a pretty printed string tends to give more useful
+ # text output compared to the data-structure. Both will work.
+ if (cmp_message := report_humanly_readable_difference(
+ pprint.pformat(data_orig, indent=0, width=120),
+ pprint.pformat(data_reimport, indent=0, width=120),
+ )):
+ error_text_consistency = "Keymap %s has inconsistency on re-importing." % cmp_message
+ else:
+ error_text_consistency = ""
+
+ # Perform an additional sanity check:
+ # That there are no identical key-map items.
+ if name_only not in ALLOW_DUPLICATES:
+ error_text_duplicates = keyconfig_report_duplicates(data_orig)
+ else:
+ error_text_duplicates = ""
+
+ if error_text_consistency or error_text_duplicates:
+ print("FAILED!")
+ print("%r has errors!" % filepath)
+ if error_text_consistency:
+ print(error_text_consistency)
+ if error_text_duplicates:
+ print(error_text_duplicates)
+ else:
+ print("OK!")
+
+ if WRITE_OUTPUT_DIR:
+ os.makedirs(WRITE_OUTPUT_DIR, exist_ok=True)
+ name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only_with_config)
+ print("Writing data to:", name_only_temp + ".*.py")
+ with open(name_only_temp + ".orig.py", 'w') as fh:
+ fh.write(pprint.pformat(data_orig, indent=0, width=120))
+ with open(name_only_temp + ".rewrite.py", 'w') as fh:
+ fh.write(pprint.pformat(data_reimport, indent=0, width=120))
if has_error:
sys.exit(1)