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:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blendthumb/CMakeLists.txt11
-rw-r--r--source/blender/blenfont/intern/blf_font.c142
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c110
-rw-r--r--source/blender/blenfont/intern/blf_internal.h7
-rw-r--r--source/blender/blenkernel/BKE_asset.h17
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh4
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_curve_to_mesh.hh2
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh33
-rw-r--r--source/blender/blenkernel/BKE_idtype.h5
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h22
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h3
-rw-r--r--source/blender/blenkernel/BKE_main.h4
-rw-r--r--source/blender/blenkernel/BKE_node.h6
-rw-r--r--source/blender/blenkernel/BKE_preferences.h3
-rw-r--r--source/blender/blenkernel/BKE_spline.hh2
-rw-r--r--source/blender/blenkernel/BKE_volume.h5
-rw-r--r--source/blender/blenkernel/BKE_volume_to_mesh.hh32
-rw-r--r--source/blender/blenkernel/intern/action.c4
-rw-r--r--source/blender/blenkernel/intern/anim_data.c6
-rw-r--r--source/blender/blenkernel/intern/appdir.c21
-rw-r--r--source/blender/blenkernel/intern/armature.c20
-rw-r--r--source/blender/blenkernel/intern/armature_test.cc2
-rw-r--r--source/blender/blenkernel/intern/asset.cc25
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path.cc2
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path_test.cc40
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc43
-rw-r--r--source/blender/blenkernel/intern/asset_library_service_test.cc3
-rw-r--r--source/blender/blenkernel/intern/asset_library_test.cc3
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc47
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh4
-rw-r--r--source/blender/blenkernel/intern/brush.c13
-rw-r--r--source/blender/blenkernel/intern/callbacks.c29
-rw-r--r--source/blender/blenkernel/intern/camera.c6
-rw-r--r--source/blender/blenkernel/intern/collection.c9
-rw-r--r--source/blender/blenkernel/intern/curve.c18
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc7
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc63
-rw-r--r--source/blender/blenkernel/intern/fcurve.c16
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc60
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc131
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc2
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc34
-rw-r--r--source/blender/blenkernel/intern/gpencil.c4
-rw-r--r--source/blender/blenkernel/intern/hair.c2
-rw-r--r--source/blender/blenkernel/intern/icons.cc30
-rw-r--r--source/blender/blenkernel/intern/image.c6
-rw-r--r--source/blender/blenkernel/intern/lattice.c2
-rw-r--r--source/blender/blenkernel/intern/lib_query.c158
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c68
-rw-r--r--source/blender/blenkernel/intern/library.c2
-rw-r--r--source/blender/blenkernel/intern/light.c3
-rw-r--r--source/blender/blenkernel/intern/lightprobe.c4
-rw-r--r--source/blender/blenkernel/intern/linestyle.c12
-rw-r--r--source/blender/blenkernel/intern/material.c11
-rw-r--r--source/blender/blenkernel/intern/mball.c2
-rw-r--r--source/blender/blenkernel/intern/mesh.c6
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.c10
-rw-r--r--source/blender/blenkernel/intern/movieclip.c8
-rw-r--r--source/blender/blenkernel/intern/nla.c6
-rw-r--r--source/blender/blenkernel/intern/node.cc52
-rw-r--r--source/blender/blenkernel/intern/object.c131
-rw-r--r--source/blender/blenkernel/intern/particle.c27
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc2
-rw-r--r--source/blender/blenkernel/intern/preferences.c3
-rw-r--r--source/blender/blenkernel/intern/scene.c350
-rw-r--r--source/blender/blenkernel/intern/screen.c81
-rw-r--r--source/blender/blenkernel/intern/simulation.cc3
-rw-r--r--source/blender/blenkernel/intern/speaker.c2
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc6
-rw-r--r--source/blender/blenkernel/intern/texture.c9
-rw-r--r--source/blender/blenkernel/intern/volume.cc19
-rw-r--r--source/blender/blenkernel/intern/volume_to_mesh.cc93
-rw-r--r--source/blender/blenkernel/intern/workspace.c2
-rw-r--r--source/blender/blenkernel/intern/world.c3
-rw-r--r--source/blender/blenlib/BLI_serialize.hh329
-rw-r--r--source/blender/blenlib/CMakeLists.txt8
-rw-r--r--source/blender/blenlib/intern/noise.cc204
-rw-r--r--source/blender/blenlib/intern/serialize.cc216
-rw-r--r--source/blender/blenlib/intern/winstuff.c2
-rw-r--r--source/blender/blenlib/tests/BLI_serialize_test.cc207
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/blenloader/intern/versioning_280.c55
-rw-r--r--source/blender/blenloader/intern/versioning_290.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c135
-rw-r--r--source/blender/blenloader/intern/versioning_common.cc54
-rw-r--r--source/blender/blenloader/intern/versioning_common.h8
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c35
-rw-r--r--source/blender/draw/CMakeLists.txt1
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c9
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.c9
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h23
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c352
-rw-r--r--source/blender/draw/intern/draw_manager_data.c8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.h1
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc398
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc64
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c79
-rw-r--r--source/blender/editors/armature/armature_select.c6
-rw-r--r--source/blender/editors/asset/ED_asset_catalog.hh3
-rw-r--r--source/blender/editors/asset/ED_asset_mark_clear.h3
-rw-r--r--source/blender/editors/asset/ED_asset_type.h5
-rw-r--r--source/blender/editors/asset/intern/asset_catalog.cc35
-rw-r--r--source/blender/editors/asset/intern/asset_mark_clear.cc19
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc9
-rw-r--r--source/blender/editors/asset/intern/asset_type.cc2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c10
-rw-r--r--source/blender/editors/include/ED_fileselect.h6
-rw-r--r--source/blender/editors/include/ED_node.h3
-rw-r--r--source/blender/editors/include/ED_object.h4
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h5
-rw-r--r--source/blender/editors/include/UI_interface.h34
-rw-r--r--source/blender/editors/include/UI_interface.hh25
-rw-r--r--source/blender/editors/include/UI_tree_view.hh94
-rw-r--r--source/blender/editors/include/UI_view2d.h7
-rw-r--r--source/blender/editors/interface/CMakeLists.txt1
-rw-r--r--source/blender/editors/interface/interface.c3
-rw-r--r--source/blender/editors/interface/interface_context_path.cc85
-rw-r--r--source/blender/editors/interface/interface_draw.c29
-rw-r--r--source/blender/editors/interface/interface_dropboxes.cc39
-rw-r--r--source/blender/editors/interface/interface_handlers.c128
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_layout.c8
-rw-r--r--source/blender/editors/interface/interface_ops.c34
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/interface_style.c31
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc1
-rw-r--r--source/blender/editors/interface/interface_template_attribute_search.cc7
-rw-r--r--source/blender/editors/interface/interface_widgets.c56
-rw-r--r--source/blender/editors/interface/resources.c7
-rw-r--r--source/blender/editors/interface/tree_view.cc95
-rw-r--r--source/blender/editors/interface/view2d.c111
-rw-r--r--source/blender/editors/interface/view2d_edge_pan.c11
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c63
-rw-r--r--source/blender/editors/mesh/editmesh_select.c8
-rw-r--r--source/blender/editors/object/object_add.c235
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c24
-rw-r--r--source/blender/editors/object/object_utils.c68
-rw-r--r--source/blender/editors/physics/particle_object.c12
-rw-r--r--source/blender/editors/screen/area.c6
-rw-r--r--source/blender/editors/screen/screen_edit.c4
-rw-r--r--source/blender/editors/screen/screen_ops.c54
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c2
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc259
-rw-r--r--source/blender/editors/space_file/file_draw.c54
-rw-r--r--source/blender/editors/space_file/file_intern.h3
-rw-r--r--source/blender/editors/space_file/file_ops.c32
-rw-r--r--source/blender/editors/space_file/file_panels.c2
-rw-r--r--source/blender/editors/space_file/filelist.c209
-rw-r--r--source/blender/editors/space_file/filesel.c27
-rw-r--r--source/blender/editors/space_file/space_file.c3
-rw-r--r--source/blender/editors/space_nla/nla_draw.c54
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_node/drawnode.cc62
-rw-r--r--source/blender/editors/space_node/node_add.cc14
-rw-r--r--source/blender/editors/space_node/node_context_path.cc184
-rw-r--r--source/blender/editors/space_node/node_draw.cc502
-rw-r--r--source/blender/editors/space_node/node_intern.h10
-rw-r--r--source/blender/editors/space_node/node_ops.c1
-rw-r--r--source/blender/editors/space_node/node_relationships.cc360
-rw-r--r--source/blender/editors/space_node/space_node.c26
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c8
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c61
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c16
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c259
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c9
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc66
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.cc79
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.hh78
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc25
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh6
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc333
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh38
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_intern.hh22
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c209
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c150
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c21
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c7
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c11
-rw-r--r--source/blender/editors/transform/transform_snap_object.c7
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c12
-rw-r--r--source/blender/functions/FN_field.hh16
-rw-r--r--source/blender/functions/intern/field.cc7
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencildash.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c75
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h15
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c173
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c48
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c4
-rw-r--r--source/blender/gpu/GPU_batch.h8
-rw-r--r--source/blender/gpu/GPU_immediate_util.h8
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c38
-rw-r--r--source/blender/makesdna/DNA_asset_types.h7
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h7
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h2
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h4
-rw-r--r--source/blender/makesdna/DNA_node_types.h10
-rw-r--r--source/blender/makesdna/DNA_object_types.h3
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h6
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h9
-rw-r--r--source/blender/makesdna/DNA_xr_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_asset.c42
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c6
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c42
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c72
-rw-r--r--source/blender/makesrna/intern/rna_space.c34
-rw-r--r--source/blender/makesrna/intern/rna_space_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c8
-rw-r--r--source/blender/makesrna/intern/rna_world.c1
-rw-r--r--source/blender/makesrna/intern/rna_xr.c102
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c44
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc9
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc1
-rw-r--r--source/blender/nodes/CMakeLists.txt10
-rw-r--r--source/blender/nodes/NOD_geometry.h26
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh16
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh25
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh28
-rw-r--r--source/blender/nodes/NOD_socket_declarations.hh10
-rw-r--r--source/blender/nodes/NOD_socket_declarations_geometry.hh58
-rw-r--r--source/blender/nodes/NOD_static_types.h36
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc8
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc16
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_idMask.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc12
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc8
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_valToRgb.cc10
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc6
-rw-r--r--source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc8
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc12
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc6
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc8
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_bool.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_special_characters.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_value.cc26
-rw-r--r--source/blender/nodes/function/nodes/node_fn_replace_string.cc9
-rw-r--r--source/blender/nodes/function/nodes/node_fn_rotate_euler.cc10
-rw-r--r--source/blender/nodes/function/nodes/node_fn_slice_string.cc8
-rw-r--r--source/blender/nodes/function/nodes/node_fn_string_length.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_value_to_string.cc6
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh1
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc173
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_image_texture.cc429
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_id.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_position.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_radius.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc49
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_proximity.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc59
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc57
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_components.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc8
-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_join.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc89
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc57
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_viewer.cc63
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc137
-rw-r--r--source/blender/nodes/intern/extern_implementations.cc1
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc63
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc71
-rw-r--r--source/blender/nodes/intern/node_socket_declarations.cc41
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc135
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc22
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_brick.cc198
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_checker.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_magic.cc10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc505
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_wave.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc12
-rw-r--r--source/blender/python/generic/bl_math_py_api.c30
-rw-r--r--source/blender/sequencer/SEQ_iterator.h5
-rw-r--r--source/blender/sequencer/SEQ_time.h2
-rw-r--r--source/blender/sequencer/intern/effects.c11
-rw-r--r--source/blender/sequencer/intern/iterator.c26
-rw-r--r--source/blender/sequencer/intern/strip_time.c31
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h30
-rw-r--r--source/blender/windowmanager/WM_types.h33
-rw-r--r--source/blender/windowmanager/intern/wm.c11
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c449
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c4
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c22
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c59
-rw-r--r--source/blender/windowmanager/intern/wm_files.c5
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c11
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c1
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c88
-rw-r--r--source/blender/windowmanager/wm_event_system.h4
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c45
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h21
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_operators.c1537
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c187
-rw-r--r--source/blender/windowmanager/xr/wm_xr.h3
468 files changed, 12878 insertions, 4215 deletions
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
index 4bcd27082c0..4c2e72418a0 100644
--- a/source/blender/blendthumb/CMakeLists.txt
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -56,11 +56,6 @@ if(WIN32)
target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib)
set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt")
- install(
- FILES $<TARGET_FILE:BlendThumb>
- COMPONENT Blender
- DESTINATION "."
- )
else()
# -----------------------------------------------------------------------------
# Build `blender-thumbnailer` executable
@@ -68,10 +63,4 @@ else()
add_executable(blender-thumbnailer ${SRC} src/blender_thumbnailer.cc)
target_link_libraries(blender-thumbnailer bf_blenlib)
target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES})
-
- install(
- FILES $<TARGET_FILE:blender-thumbnailer>
- COMPONENT Blender
- DESTINATION "."
- )
endif()
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 27478bd7f8e..90c8d6357de 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -297,44 +297,27 @@ static void blf_batch_draw_end(void)
* characters.
*/
-BLI_INLINE GlyphBLF *blf_utf8_next_fast(
- FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p, uint *r_c)
+BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step(
+ FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p)
{
- GlyphBLF *g;
- if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) {
- g = (gc->glyph_ascii_table)[*r_c];
- if (UNLIKELY(g == NULL)) {
- g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c);
- gc->glyph_ascii_table[*r_c] = g;
- }
- (*i_p)++;
- }
- else {
- *r_c = BLI_str_utf8_as_unicode_step(str, str_len, i_p);
- g = blf_glyph_search(gc, *r_c);
- if (UNLIKELY(g == NULL)) {
- g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c);
- }
- }
- return g;
+ uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p);
+ /* Invalid unicode sequences return the byte value, stepping forward one.
+ * This allows `latin1` to display (which is sometimes used for file-paths). */
+ BLI_assert(charcode != BLI_UTF8_ERR);
+ return blf_glyph_ensure(font, gc, charcode);
}
-BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
- const GlyphBLF *g_prev,
- const GlyphBLF *g,
- const uint c_prev,
- const uint c,
- int *pen_x_p)
+BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g)
{
if (!FT_HAS_KERNING(font->face) || g_prev == NULL) {
- return;
+ return 0;
}
FT_Vector delta = {KERNING_ENTRY_UNSET};
/* Get unscaled kerning value from our cache if ASCII. */
- if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
- delta.x = font->kerning_cache->ascii_table[c][c_prev];
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c];
}
/* If not ASCII or not found in cache, ask FreeType for kerning. */
@@ -344,14 +327,16 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
}
/* If ASCII we save this value to our cache for quicker access next time. */
- if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
- font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x;
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x;
}
if (delta.x != 0) {
/* Convert unscaled design units to pixels and move pen. */
- *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x);
+ return blf_unscaled_F26Dot6_to_pixels(font, delta.x);
}
+
+ return 0;
}
/** \} */
@@ -367,7 +352,6 @@ static void blf_font_draw_ex(FontBLF *font,
struct ResultBLF *r_info,
int pen_y)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
int pen_x = 0;
size_t i = 0;
@@ -380,22 +364,18 @@ static void blf_font_draw_ex(FontBLF *font,
blf_batch_draw_begin(font);
while ((i < str_len) && str[i]) {
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ pen_x += blf_kerning(font, g_prev, g);
/* do not return this loop if clipped, we want every character tested */
- blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y);
+ blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y);
pen_x += g->advance_i;
g_prev = g;
- c_prev = c;
}
blf_batch_draw_end();
@@ -415,7 +395,6 @@ void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct
/* use fixed column width, but an utf8 character may occupy multiple columns */
int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth)
{
- unsigned int c;
GlyphBLF *g;
int col, columns = 0;
int pen_x = 0, pen_y = 0;
@@ -426,19 +405,15 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int
blf_batch_draw_begin(font);
while ((i < str_len) && str[i]) {
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
-
/* do not return this loop if clipped, we want every character tested */
- blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y);
+ blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y);
- col = BLI_wcwidth((char32_t)c);
+ col = BLI_wcwidth((char32_t)g->c);
if (col < 0) {
col = 1;
}
@@ -467,7 +442,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
struct ResultBLF *r_info,
int pen_y)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
int pen_x = (int)font->pos[0];
int pen_y_basis = (int)font->pos[1] + pen_y;
@@ -483,15 +457,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
/* another buffer specific call for color conversion */
while ((i < str_len) && str[i]) {
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ pen_x += blf_kerning(font, g_prev, g);
chx = pen_x + ((int)g->pos[0]);
chy = pen_y_basis + g->dims[1];
@@ -588,7 +559,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
pen_x += g->advance_i;
g_prev = g;
- c_prev = c;
}
if (r_info) {
@@ -617,31 +587,22 @@ void blf_font_draw_buffer(FontBLF *font,
* - #BLF_width_to_rstrlen
* \{ */
-static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
- const uint c_prev,
- const uint c,
- GlyphBLF *g_prev,
- GlyphBLF *g,
- int *pen_x,
- const int width_i)
+static bool blf_font_width_to_strlen_glyph_process(
+ FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i)
{
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- return true; /* break the calling loop. */
- }
if (UNLIKELY(g == NULL)) {
return false; /* continue the calling loop. */
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x);
-
+ *pen_x += blf_kerning(font, g_prev, g);
*pen_x += g->advance_i;
+ /* When true, break the calling loop. */
return (*pen_x >= width_i);
}
size_t blf_font_width_to_strlen(
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
int pen_x, width_new;
size_t i, i_prev;
@@ -649,11 +610,11 @@ size_t blf_font_width_to_strlen(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
- for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i];
- i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) {
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL; (i < str_len) && str[i];
+ i_prev = i, width_new = pen_x, g_prev = g) {
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) {
+ if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) {
break;
}
}
@@ -669,7 +630,6 @@ size_t blf_font_width_to_strlen(
size_t blf_font_width_to_rstrlen(
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
int pen_x, width_new;
size_t i, i_prev, i_tmp;
@@ -685,19 +645,19 @@ size_t blf_font_width_to_rstrlen(
i_prev = (size_t)(s_prev - str);
i_tmp = i;
- g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp);
for (width_new = pen_x = 0; (s != NULL);
- i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) {
+ i = i_prev, s = s_prev, g = g_prev, g_prev = NULL, width_new = pen_x) {
s_prev = BLI_str_find_prev_char_utf8(s, str);
i_prev = (size_t)(s_prev - str);
if (s_prev != NULL) {
i_tmp = i_prev;
- g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c_prev);
+ g_prev = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp);
BLI_assert(i_tmp == i);
}
- if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) {
+ if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) {
break;
}
}
@@ -724,7 +684,6 @@ static void blf_font_boundbox_ex(FontBLF *font,
struct ResultBLF *r_info,
int pen_y)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
int pen_x = 0;
size_t i = 0;
@@ -736,15 +695,12 @@ static void blf_font_boundbox_ex(FontBLF *font,
box->ymax = -32000.0f;
while ((i < str_len) && str[i]) {
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ pen_x += blf_kerning(font, g_prev, g);
gbox.xmin = (float)pen_x;
gbox.xmax = (float)pen_x + g->advance;
@@ -767,7 +723,6 @@ static void blf_font_boundbox_ex(FontBLF *font,
pen_x += g->advance_i;
g_prev = g;
- c_prev = c;
}
if (box->xmin > box->xmax) {
@@ -874,7 +829,7 @@ float blf_font_fixed_width(FontBLF *font)
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
GlyphBLF *g = blf_glyph_search(gc, c);
if (!g) {
- g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c);
+ g = blf_glyph_ensure(font, gc, FT_Get_Char_Index(font->face, c));
/* if we don't find the glyph. */
if (!g) {
@@ -896,7 +851,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
struct ResultBLF *r_info,
int pen_y)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
int pen_x = 0;
size_t i = 0, i_curr;
@@ -909,15 +863,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
while ((i < str_len) && str[i]) {
i_curr = i;
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ pen_x += blf_kerning(font, g_prev, g);
gbox.xmin = pen_x;
gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]);
@@ -931,7 +882,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
}
g_prev = g;
- c_prev = c;
}
if (r_info) {
@@ -978,7 +928,6 @@ static void blf_font_wrap_apply(FontBLF *font,
void *userdata),
void *userdata)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
int pen_x = 0, pen_y = 0;
size_t i = 0;
@@ -999,15 +948,12 @@ static void blf_font_wrap_apply(FontBLF *font,
size_t i_curr = i;
bool do_draw = false;
- g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c);
+ g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
if (UNLIKELY(g == NULL)) {
continue;
}
- blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+ pen_x += blf_kerning(font, g_prev, g);
/**
* Implementation Detail (utf8).
@@ -1047,14 +993,12 @@ static void blf_font_wrap_apply(FontBLF *font,
pen_x = 0;
pen_y -= gc->glyph_height_max;
g_prev = NULL;
- c_prev = BLI_UTF8_ERR;
lines += 1;
continue;
}
pen_x = pen_x_next;
g_prev = g;
- c_prev = c;
}
// printf("done! lines: %d, width, %d\n", lines, pen_x_next);
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 6cdf5fc5996..9170a1c0ac4 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -175,33 +175,8 @@ GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c)
return NULL;
}
-GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, unsigned int c)
+static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index)
{
- FT_GlyphSlot slot;
- GlyphBLF *g;
- FT_Error err;
- FT_Bitmap bitmap, tempbitmap;
- FT_BBox bbox;
- unsigned int key;
-
- g = blf_glyph_search(gc, c);
- if (g) {
- return g;
- }
-
- /* glyphs are dynamically created as needed by font rendering. this means that
- * to make font rendering thread safe we have to do locking here. note that this
- * must be a lock for the whole library and not just per font, because the font
- * renderer uses a shared buffer internally */
- BLI_spin_lock(font->ft_lib_mutex);
-
- /* search again after locking */
- g = blf_glyph_search(gc, c);
- if (g) {
- BLI_spin_unlock(font->ft_lib_mutex);
- return g;
- }
-
int load_flags;
int render_mode;
@@ -228,7 +203,10 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
}
}
- err = FT_Load_Glyph(font->face, (FT_UInt)index, load_flags);
+ FT_Error err = FT_Load_Glyph(font->face, glyph_index, load_flags);
+ if (err != 0) {
+ return false;
+ }
/* Do not oblique a font that is designed to be italic! */
if (((font->flags & BLF_ITALIC) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_ITALIC) &&
@@ -243,9 +221,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
}
/* Do not embolden an already bold font! */
- if (((font->flags & BLF_BOLD) != 0) &&
- !(font->face->style_flags & FT_STYLE_FLAG_BOLD) &
- (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
+ if (((font->flags & BLF_BOLD) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_BOLD) &&
+ (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
/* Strengthen the width more than the height. */
const FT_Pos extra_x = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.x_scale) /
14;
@@ -263,15 +240,12 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
}
}
- if (err) {
- BLI_spin_unlock(font->ft_lib_mutex);
- return NULL;
- }
-
/* get the glyph. */
- slot = font->face->glyph;
+ FT_GlyphSlot slot = font->face->glyph;
err = FT_Render_Glyph(slot, render_mode);
+ FT_Bitmap tempbitmap;
+
if (font->flags & BLF_MONOCHROME) {
/* Convert result from 1 bit per pixel to 8 bit per pixel */
/* Accum errors for later, fine if not interested beyond "ok vs any error" */
@@ -284,45 +258,69 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
}
if (err || slot->format != FT_GLYPH_FORMAT_BITMAP) {
- BLI_spin_unlock(font->ft_lib_mutex);
- return NULL;
+ return false;
}
- g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add");
- g->c = c;
- g->idx = (FT_UInt)index;
- bitmap = slot->bitmap;
- g->dims[0] = (int)bitmap.width;
- g->dims[1] = (int)bitmap.rows;
+ return true;
+}
- const int buffer_size = g->dims[0] * g->dims[1];
+GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode)
+{
+ GlyphBLF *g = (charcode < GLYPH_ASCII_TABLE_SIZE) ? (gc->glyph_ascii_table)[charcode] :
+ blf_glyph_search(gc, charcode);
+ if (g) {
+ return g;
+ }
- if (buffer_size != 0) {
- if (font->flags & BLF_MONOCHROME) {
- /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */
- for (int i = 0; i < buffer_size; i++) {
- bitmap.buffer[i] = bitmap.buffer[i] ? 255 : 0;
- }
- }
+ FT_UInt glyph_index = FT_Get_Char_Index(font->face, charcode);
- g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap");
- memcpy(g->bitmap, bitmap.buffer, (size_t)buffer_size);
+ if (!blf_glyph_render(font, glyph_index)) {
+ return NULL;
}
+ FT_GlyphSlot slot = font->face->glyph;
+
+ /* glyphs are dynamically created as needed by font rendering. this means that
+ * to make font rendering thread safe we have to do locking here. note that this
+ * must be a lock for the whole library and not just per font, because the font
+ * renderer uses a shared buffer internally */
+ BLI_spin_lock(font->ft_lib_mutex);
+
+ g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get");
+ g->c = charcode;
+ g->idx = glyph_index;
g->advance = ((float)slot->advance.x) / 64.0f;
g->advance_i = (int)g->advance;
g->pos[0] = slot->bitmap_left;
g->pos[1] = slot->bitmap_top;
+ g->dims[0] = (int)slot->bitmap.width;
+ g->dims[1] = (int)slot->bitmap.rows;
g->pitch = slot->bitmap.pitch;
+ FT_BBox bbox;
FT_Outline_Get_CBox(&(slot->outline), &bbox);
g->box.xmin = ((float)bbox.xMin) / 64.0f;
g->box.xmax = ((float)bbox.xMax) / 64.0f;
g->box.ymin = ((float)bbox.yMin) / 64.0f;
g->box.ymax = ((float)bbox.yMax) / 64.0f;
- key = blf_hash(g->c);
+ const int buffer_size = (int)(slot->bitmap.width * slot->bitmap.rows);
+ if (buffer_size != 0) {
+ if (font->flags & BLF_MONOCHROME) {
+ /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */
+ for (int i = 0; i < buffer_size; i++) {
+ slot->bitmap.buffer[i] = slot->bitmap.buffer[i] ? 255 : 0;
+ }
+ }
+ g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap");
+ memcpy(g->bitmap, slot->bitmap.buffer, (size_t)buffer_size);
+ }
+
+ unsigned int key = blf_hash(g->c);
BLI_addhead(&(gc->bucket[key]), g);
+ if (charcode < GLYPH_ASCII_TABLE_SIZE) {
+ gc->glyph_ascii_table[charcode] = g;
+ }
BLI_spin_unlock(font->ft_lib_mutex);
@@ -419,7 +417,7 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y
blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y);
}
-void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y)
+void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y)
{
if ((!g->dims[0]) || (!g->dims[1])) {
return;
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 6fd5e8b7503..ba871ea2496 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -140,13 +140,10 @@ void blf_glyph_cache_clear(struct FontBLF *font);
void blf_glyph_cache_free(struct GlyphCacheBLF *gc);
struct GlyphBLF *blf_glyph_search(struct GlyphCacheBLF *gc, unsigned int c);
-struct GlyphBLF *blf_glyph_add(struct FontBLF *font,
- struct GlyphCacheBLF *gc,
- unsigned int index,
- unsigned int c);
+struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc, uint charcode);
void blf_glyph_free(struct GlyphBLF *g);
-void blf_glyph_render(
+void blf_glyph_draw(
struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y);
#ifdef WIN32
diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h
index 42eea41b7a7..722d142b56c 100644
--- a/source/blender/blenkernel/BKE_asset.h
+++ b/source/blender/blenkernel/BKE_asset.h
@@ -20,6 +20,7 @@
#pragma once
+#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "DNA_asset_types.h"
@@ -29,11 +30,23 @@ extern "C" {
#endif
struct AssetLibraryReference;
+struct AssetMetaData;
struct BlendDataReader;
struct BlendWriter;
struct ID;
+struct IDProperty;
struct PreviewImage;
+typedef void (*PreSaveFn)(void *asset_ptr, struct AssetMetaData *asset_data);
+
+typedef struct AssetTypeInfo {
+ /**
+ * For local assets (assets in the current .blend file), a callback to execute before the file is
+ * saved.
+ */
+ PreSaveFn pre_save_fn;
+} AssetTypeInfo;
+
struct AssetMetaData *BKE_asset_metadata_create(void);
void BKE_asset_metadata_free(struct AssetMetaData **asset_data);
@@ -56,6 +69,10 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data,
void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref);
+void BKE_asset_metadata_idprop_ensure(struct AssetMetaData *asset_data, struct IDProperty *prop);
+struct IDProperty *BKE_asset_metadata_idprop_find(const struct AssetMetaData *asset_data,
+ const char *name) ATTR_WARN_UNUSED_RESULT;
+
struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
const struct ID *owner_id);
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 4a1c29badb4..cf19eb29a0e 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -183,6 +183,8 @@ struct WriteAttributeLookup {
GVMutableArrayPtr varray;
/* Domain the attributes lives on in the geometry. */
AttributeDomain domain;
+ /* Call this after changing the attribute to invalidate caches that depend on this attribute. */
+ std::function<void()> tag_modified_fn;
/* Convenience function to check if the attribute has been found. */
operator bool() const
@@ -208,7 +210,7 @@ class OutputAttribute {
private:
GVMutableArrayPtr varray_;
- AttributeDomain domain_;
+ AttributeDomain domain_ = ATTR_DOMAIN_AUTO;
SaveFn save_;
std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_;
bool ignore_old_values_ = false;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 3f8b56b2736..6fc2fa37d9f 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,7 +31,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 300
+#define BLENDER_VERSION 301
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 38
+#define BLENDER_FILE_SUBVERSION 0
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh
index 87bec6203a9..fb077425336 100644
--- a/source/blender/blenkernel/BKE_curve_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh
@@ -25,7 +25,7 @@ struct CurveEval;
namespace blender::bke {
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile);
+Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps);
Mesh *curve_to_wire_mesh(const CurveEval &curve);
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index f57765e373b..58a89d0207a 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -318,6 +318,9 @@ struct GeometrySet {
bool include_instances,
blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const;
+ blender::Vector<GeometryComponentType> gather_component_types(bool include_instances,
+ bool ignore_empty) const;
+
using ForeachSubGeometryCallback = blender::FunctionRef<void(GeometrySet &geometry_set)>;
void modify_geometry_sets(ForeachSubGeometryCallback callback);
@@ -621,7 +624,9 @@ class InstancesComponent : public GeometryComponent {
blender::Vector<blender::float4x4> instance_transforms_;
/**
* IDs of the instances. They are used for consistency over multiple frames for things like
- * motion blur.
+ * motion blur. Proper stable ID data that actually helps when rendering can only be generated
+ * in some situations, so this vector is allowed to be empty, in which case the index of each
+ * instance will be used for the final ID.
*/
blender::Vector<int> instance_ids_;
@@ -643,7 +648,7 @@ class InstancesComponent : public GeometryComponent {
void resize(int capacity);
int add_reference(const InstanceReference &reference);
- void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
+ void add_instance(int instance_handle, const blender::float4x4 &transform);
blender::Span<InstanceReference> references() const;
void remove_unused_references();
@@ -658,6 +663,9 @@ class InstancesComponent : public GeometryComponent {
blender::MutableSpan<int> instance_ids();
blender::Span<int> instance_ids() const;
+ blender::MutableSpan<int> instance_ids_ensure();
+ void instance_ids_clear();
+
int instances_amount() const;
int references_amount() const;
@@ -736,6 +744,7 @@ class AttributeFieldInput : public fn::FieldInput {
AttributeFieldInput(std::string name, const CPPType &type)
: fn::FieldInput(type, name), name_(std::move(name))
{
+ category_ = Category::NamedAttribute;
}
template<typename T> static fn::Field<T> Create(std::string name)
@@ -764,10 +773,9 @@ class IDAttributeFieldInput : public fn::FieldInput {
public:
IDAttributeFieldInput() : fn::FieldInput(CPPType::get<int>())
{
+ category_ = Category::Generated;
}
- static fn::Field<int> Create();
-
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
@@ -785,18 +793,25 @@ class AnonymousAttributeFieldInput : public fn::FieldInput {
* automatically.
*/
StrongAnonymousAttributeID anonymous_id_;
+ std::string producer_name_;
public:
- AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type)
- : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id))
+ AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id,
+ const CPPType &type,
+ std::string producer_name)
+ : fn::FieldInput(type, anonymous_id.debug_name()),
+ anonymous_id_(std::move(anonymous_id)),
+ producer_name_(producer_name)
{
+ category_ = Category::AnonymousAttribute;
}
- template<typename T> static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id)
+ template<typename T>
+ static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name)
{
const CPPType &type = CPPType::get<T>();
- auto field_input = std::make_shared<AnonymousAttributeFieldInput>(std::move(anonymous_id),
- type);
+ auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
+ std::move(anonymous_id), type, std::move(producer_name));
return fn::Field<T>{field_input};
}
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index cd656d94fce..d33c24f2c75 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -228,6 +228,11 @@ typedef struct IDTypeInfo {
* \note Currently needed for some update operation on point caches.
*/
IDTypeLibOverrideApplyPost lib_override_apply_post;
+
+ /**
+ * Callbacks for assets, based on the type of asset.
+ */
+ struct AssetTypeInfo *asset_type_info;
} IDTypeInfo;
/* ********** Declaration of each IDTypeInfo. ********** */
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index 9c49514e7b8..30c742e3af6 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -143,7 +143,8 @@ enum {
typedef struct LibraryForeachIDData LibraryForeachIDData;
-bool BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data,
+bool BKE_lib_query_foreachid_iter_stop(struct LibraryForeachIDData *data);
+void BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data,
struct ID **id_pp,
int cb_flag);
int BKE_lib_query_foreachid_process_flags_get(struct LibraryForeachIDData *data);
@@ -154,22 +155,33 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach
#define BKE_LIB_FOREACHID_PROCESS_ID(_data, _id, _cb_flag) \
{ \
CHECK_TYPE_ANY((_id), ID *, void *); \
- if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag))) { \
+ BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag)); \
+ if (BKE_lib_query_foreachid_iter_stop((_data))) { \
return; \
} \
} \
((void)0)
-#define BKE_LIB_FOREACHID_PROCESS(_data, _id_super, _cb_flag) \
+#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \
{ \
CHECK_TYPE(&((_id_super)->id), ID *); \
- if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \
+ BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \
+ if (BKE_lib_query_foreachid_iter_stop((_data))) { \
return; \
} \
} \
((void)0)
-bool BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp);
+#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(_data, _func_call) \
+ { \
+ _func_call; \
+ if (BKE_lib_query_foreachid_iter_stop((_data))) { \
+ return; \
+ } \
+ } \
+ ((void)0)
+
+void BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp);
void BKE_lib_query_idpropertiesForeachIDLink_callback(struct IDProperty *id_prop, void *user_data);
/* Loop over all of the ID's this datablock links to. */
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index c70521f9593..8df6a803358 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -111,8 +111,7 @@ void BKE_libblock_relink_ex(struct Main *bmain,
void *new_idv,
const short remap_flags) ATTR_NONNULL(1, 2);
-void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL();
-void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL();
+void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id) ATTR_NONNULL();
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 68b1b55f47f..9ded97e0003 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -244,9 +244,9 @@ void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_refe
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \
{ \
- ID *_id_next = (_lb)->first; \
+ ID *_id_next = (ID *)(_lb)->first; \
for ((_id) = _id_next; (_id) != NULL; (_id) = _id_next) { \
- _id_next = (_id)->next;
+ _id_next = (ID *)(_id)->next;
#define FOREACH_MAIN_LISTBASE_ID_END \
} \
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index ef1e7249658..58fea6d462c 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1442,7 +1442,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_COLLECTION_INFO 1023
#define GEO_NODE_IS_VIEWPORT 1024
#define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025
-#define GEO_NODE_VOLUME_TO_MESH 1026
+#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026
#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027
#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028
#define GEO_NODE_SUBDIVIDE_MESH 1029
@@ -1547,6 +1547,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_RAYCAST 1128
#define GEO_NODE_CURVE_TO_POINTS 1130
#define GEO_NODE_INSTANCES_TO_POINTS 1131
+#define GEO_NODE_IMAGE_TEXTURE 1132
+#define GEO_NODE_VOLUME_TO_MESH 1133
+#define GEO_NODE_INPUT_ID 1134
+#define GEO_NODE_SET_ID 1135
/** \} */
diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h
index bd887c1ea0d..fce2d3178aa 100644
--- a/source/blender/blenkernel/BKE_preferences.h
+++ b/source/blender/blenkernel/BKE_preferences.h
@@ -29,6 +29,9 @@ extern "C" {
struct UserDef;
struct bUserAssetLibrary;
+/** Name of the asset library added by default. */
+#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME DATA_("User Library")
+
struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef,
const char *name,
const char *path) ATTR_NONNULL(1);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 97e0d8415a5..8509b730709 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -570,6 +570,8 @@ struct CurveEval {
blender::Array<int> evaluated_point_offsets() const;
blender::Array<float> accumulated_spline_lengths() const;
+ void mark_cache_invalid();
+
void assert_valid_point_attributes() const;
};
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index 5fe0d54c2cf..601e0cf26a9 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume,
#ifdef __cplusplus
# include "BLI_float3.hh"
# include "BLI_float4x4.hh"
+# include "BLI_string_ref.hh"
bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max);
@@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f
# include <openvdb/openvdb.h>
# include <openvdb/points/PointDataGrid.h>
+VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume,
+ blender::StringRef name,
+ openvdb::GridBase::Ptr vdb_grid);
+
bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
blender::float3 &r_min,
blender::float3 &r_max);
diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh
index 1f6e89636c4..9532da8c23c 100644
--- a/source/blender/blenkernel/BKE_volume_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_span.hh"
+
#include "DNA_modifier_types.h"
#ifdef WITH_OPENVDB
@@ -33,10 +35,40 @@ struct VolumeToMeshResolution {
};
#ifdef WITH_OPENVDB
+
+/**
+ * The result of converting a volume grid to mesh data, in the format used by the OpenVDB API.
+ */
+struct OpenVDBMeshData {
+ std::vector<openvdb::Vec3s> verts;
+ std::vector<openvdb::Vec3I> tris;
+ std::vector<openvdb::Vec4I> quads;
+ bool is_empty() const
+ {
+ return verts.empty();
+ }
+};
+
struct Mesh *volume_to_mesh(const openvdb::GridBase &grid,
const VolumeToMeshResolution &resolution,
const float threshold,
const float adaptivity);
+
+struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
+ const VolumeToMeshResolution &resolution,
+ const float threshold,
+ const float adaptivity);
+
+void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
+ const Span<openvdb::Vec3I> vdb_tris,
+ const Span<openvdb::Vec4I> vdb_quads,
+ const int vert_offset,
+ const int poly_offset,
+ const int loop_offset,
+ MutableSpan<MVert> verts,
+ MutableSpan<MPoly> polys,
+ MutableSpan<MLoop> loops);
+
#endif
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 65900ec0f4b..cae72ddf68c 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -175,11 +175,11 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data)
bAction *act = (bAction *)id;
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
- BKE_fcurve_foreach_id(fcu, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data));
}
LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
- BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP);
}
}
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 7e4ab754500..21887d514d9 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -294,11 +294,11 @@ bool BKE_animdata_id_is_animated(const struct ID *id)
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data)
{
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
- BKE_fcurve_foreach_id(fcu, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data));
}
- BKE_LIB_FOREACHID_PROCESS(data, adt->action, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, adt->tmpact, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->tmpact, IDWALK_CB_USER);
LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) {
LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) {
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index ce4ab8a4ba1..fb6656a4b1c 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -259,23 +259,24 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len)
/**
* Gets a good default directory for fonts.
*/
-bool BKE_appdir_font_folder_default(
- /* This parameter can only be `const` on non-windows platforms.
- * NOLINTNEXTLINE: readability-non-const-parameter. */
- char *dir)
+bool BKE_appdir_font_folder_default(char *dir)
{
- bool success = false;
#ifdef WIN32
wchar_t wpath[FILE_MAXDIR];
- success = SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0);
- if (success) {
+ if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) {
wcscat(wpath, L"\\");
BLI_strncpy_wchar_as_utf8(dir, wpath, FILE_MAXDIR);
+ return (BLI_exists(dir));
}
+ return false;
+#elif defined(__APPLE__)
+ const char *home = BLI_getenv("HOME");
+ BLI_snprintf(dir, FILE_MAXDIR, "%s/Library/Fonts/", home);
+ return (BLI_exists(dir));
+#else
+ BLI_strncpy(dir, "/usr/share/fonts/", FILE_MAXDIR);
+ return (BLI_exists(dir));
#endif
- /* TODO: Values for other platforms. */
- UNUSED_VARS(dir);
- return success;
}
/** \} */
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index b64b050f4e7..b830c9de5f5 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -161,30 +161,36 @@ static void armature_free_data(struct ID *id)
static void armature_foreach_id_bone(Bone *bone, LibraryForeachIDData *data)
{
- IDP_foreach_property(
- bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(
+ bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data));
LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) {
- armature_foreach_id_bone(curbone, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(curbone, data));
}
}
static void armature_foreach_id_editbone(EditBone *edit_bone, LibraryForeachIDData *data)
{
- IDP_foreach_property(
- edit_bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(edit_bone->prop,
+ IDP_TYPE_FILTER_ID,
+ BKE_lib_query_idpropertiesForeachIDLink_callback,
+ data));
}
static void armature_foreach_id(ID *id, LibraryForeachIDData *data)
{
bArmature *arm = (bArmature *)id;
LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
- armature_foreach_id_bone(bone, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(bone, data));
}
if (arm->edbo != NULL) {
LISTBASE_FOREACH (EditBone *, edit_bone, arm->edbo) {
- armature_foreach_id_editbone(edit_bone, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_editbone(edit_bone, data));
}
}
}
diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc
index 2375c606247..a6d9a1f41e9 100644
--- a/source/blender/blenkernel/intern/armature_test.cc
+++ b/source/blender/blenkernel/intern/armature_test.cc
@@ -133,7 +133,7 @@ static double test_vec_roll_to_mat3_normalized(const float input[3],
float roll_mat[3][3];
if (normalize) {
- /* The vector is renormalized to replicate the actual usage. */
+ /* The vector is re-normalized to replicate the actual usage. */
normalize_v3_v3(input_normalized, input);
}
else {
diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc
index ae9ded3c754..7bea089b9bf 100644
--- a/source/blender/blenkernel/intern/asset.cc
+++ b/source/blender/blenkernel/intern/asset.cc
@@ -53,6 +53,7 @@ void BKE_asset_metadata_free(AssetMetaData **asset_data)
if ((*asset_data)->properties) {
IDP_FreeProperty((*asset_data)->properties);
}
+ MEM_SAFE_FREE((*asset_data)->author);
MEM_SAFE_FREE((*asset_data)->description);
BLI_freelistN(&(*asset_data)->tags);
@@ -140,6 +141,25 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data,
trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length);
}
+void BKE_asset_metadata_idprop_ensure(AssetMetaData *asset_data, IDProperty *prop)
+{
+ if (!asset_data->properties) {
+ IDPropertyTemplate val = {0};
+ asset_data->properties = IDP_New(IDP_GROUP, &val, "AssetMetaData.properties");
+ }
+ /* Important: The property may already exist. For now just allow always allow a newly allocated
+ * property, and replace the existing one as a way of updating. */
+ IDP_ReplaceInGroup(asset_data->properties, prop);
+}
+
+IDProperty *BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name)
+{
+ if (!asset_data->properties) {
+ return nullptr;
+ }
+ return IDP_GetPropertyFromGroup(asset_data->properties, name);
+}
+
/* Queries -------------------------------------------- */
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
@@ -158,6 +178,9 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data)
IDP_BlendWrite(writer, asset_data->properties);
}
+ if (asset_data->author) {
+ BLO_write_string(writer, asset_data->author);
+ }
if (asset_data->description) {
BLO_write_string(writer, asset_data->description);
}
@@ -169,12 +192,14 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data)
void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
{
/* asset_data itself has been read already. */
+ asset_data->local_type_info = nullptr;
if (asset_data->properties) {
BLO_read_data_address(reader, &asset_data->properties);
IDP_BlendDataRead(reader, &asset_data->properties);
}
+ BLO_read_data_address(reader, &asset_data->author);
BLO_read_data_address(reader, &asset_data->description);
BLO_read_list(reader, &asset_data->tags);
BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc
index fec2b76e7a1..20cac76b40b 100644
--- a/source/blender/blenkernel/intern/asset_catalog_path.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_path.cc
@@ -197,6 +197,8 @@ void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const
for (const char *path_component = this->path_.data(); path_component && path_component[0];
/* Jump to one after the next slash if there is any. */
path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
+ /* Note that this also treats backslashes as component separators, which
+ * helps in cleaning up backslash-separated paths. */
next_slash_ptr = BLI_path_slash_find(path_component);
const bool is_last_component = next_slash_ptr == nullptr;
diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
index be50f2fc001..f248863ce77 100644
--- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
@@ -193,19 +193,33 @@ TEST(AssetCatalogPathTest, is_contained_in)
TEST(AssetCatalogPathTest, cleanup)
{
- AssetCatalogPath ugly_path("/ some / родитель / ");
- AssetCatalogPath clean_path = ugly_path.cleanup();
-
- EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path)
- << "cleanup should not modify the path instance itself";
-
- EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path);
-
- AssetCatalogPath double_slashed("some//родитель");
- EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup());
-
- AssetCatalogPath with_colons("some/key:subkey=value/path");
- EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup());
+ {
+ AssetCatalogPath ugly_path("/ some / родитель / ");
+ AssetCatalogPath clean_path = ugly_path.cleanup();
+ EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path)
+ << "cleanup should not modify the path instance itself";
+ EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path);
+ }
+ {
+ AssetCatalogPath double_slashed("some//родитель");
+ EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup());
+ }
+ {
+ AssetCatalogPath with_colons("some/key:subkey=value/path");
+ EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup());
+ }
+ {
+ const AssetCatalogPath with_backslashes("windows\\for\\life");
+ EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_backslashes.cleanup());
+ }
+ {
+ const AssetCatalogPath with_mixed("windows\\for/life");
+ EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_mixed.cleanup());
+ }
+ {
+ const AssetCatalogPath with_punctuation("is!/this?/¿valid?");
+ EXPECT_EQ(AssetCatalogPath("is!/this?/¿valid?"), with_punctuation.cleanup());
+ }
}
TEST(AssetCatalogPathTest, iterate_components)
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
index abe6d384247..2cef34966f8 100644
--- a/source/blender/blenkernel/intern/asset_catalog_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -902,8 +902,6 @@ TEST_F(AssetCatalogTest, update_catalog_path)
const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA);
ASSERT_NE(nullptr, renamed_cat);
ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog.";
- EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name)
- << "Changing the path should not change the simple name.";
EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id)
<< "Changing the path should not change the catalog ID.";
@@ -932,6 +930,47 @@ TEST_F(AssetCatalogTest, update_catalog_path_simple_name)
<< "Changing the path should update the simplename of children.";
}
+TEST_F(AssetCatalogTest, update_catalog_path_add_slashes)
+{
+ AssetCatalogService service(asset_library_root_);
+ service.load_from_disk(asset_library_root_ + "/" +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME);
+
+ const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA);
+ const AssetCatalogPath orig_path = orig_cat->path;
+
+ /* Original path is `character/Ružena/poselib`.
+ * This rename will also create a new catalog for `character/Ružena/poses`. */
+ service.update_catalog_path(UUID_POSES_RUZENA, "character/Ružena/poses/general");
+
+ EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path))
+ << "The original (pre-rename) path should not be associated with a catalog any more.";
+
+ const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA);
+ ASSERT_NE(nullptr, renamed_cat);
+ EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id)
+ << "Changing the path should not change the catalog ID.";
+
+ EXPECT_EQ("character/Ružena/poses/general", renamed_cat->path.str())
+ << "When creating a new catalog by renaming + adding a slash, the renamed catalog should be "
+ "assigned the path passed to update_catalog_path()";
+
+ /* Test the newly created catalog. */
+ const AssetCatalog *new_cat = service.find_catalog_by_path("character/Ružena/poses");
+ ASSERT_NE(nullptr, new_cat) << "Renaming to .../X/Y should cause .../X to exist as well.";
+ EXPECT_EQ("character/Ružena/poses", new_cat->path.str());
+ EXPECT_EQ("character-Ružena-poses", new_cat->simple_name);
+ EXPECT_TRUE(new_cat->flags.has_unsaved_changes);
+
+ /* Test the children. */
+ EXPECT_EQ("character/Ružena/poses/general/hand",
+ service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str())
+ << "Changing the path should update children.";
+ EXPECT_EQ("character/Ružena/poses/general/face",
+ service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str())
+ << "Changing the path should update children.";
+}
+
TEST_F(AssetCatalogTest, merge_catalog_files)
{
const CatalogFilePath cdf_dir = create_temp_path();
diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc
index 80504bbdc05..e26ae05301e 100644
--- a/source/blender/blenkernel/intern/asset_library_service_test.cc
+++ b/source/blender/blenkernel/intern/asset_library_service_test.cc
@@ -23,6 +23,7 @@
#include "BLI_path_util.h"
#include "BKE_appdir.h"
+#include "BKE_callbacks.h"
#include "CLG_log.h"
@@ -40,10 +41,12 @@ class AssetLibraryServiceTest : public testing::Test {
static void SetUpTestSuite()
{
CLG_init();
+ BKE_callback_global_init();
}
static void TearDownTestSuite()
{
CLG_exit();
+ BKE_callback_global_finalize();
}
void SetUp() override
diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc
index c6c949a7ec4..702008fed96 100644
--- a/source/blender/blenkernel/intern/asset_library_test.cc
+++ b/source/blender/blenkernel/intern/asset_library_test.cc
@@ -20,6 +20,7 @@
#include "BKE_appdir.h"
#include "BKE_asset_catalog.hh"
#include "BKE_asset_library.hh"
+#include "BKE_callbacks.h"
#include "asset_library_service.hh"
@@ -34,10 +35,12 @@ class AssetLibraryTest : public testing::Test {
static void SetUpTestSuite()
{
CLG_init();
+ BKE_callback_global_init();
}
static void TearDownTestSuite()
{
CLG_exit();
+ BKE_callback_global_finalize();
}
void TearDown() override
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 3386d346364..01b53baf237 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -360,7 +360,7 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
-GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
+WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@@ -397,10 +397,14 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
data = new_data;
}
+ std::function<void()> tag_modified_fn;
if (update_on_write_ != nullptr) {
- update_on_write_(component);
+ tag_modified_fn = [component = &component, update = update_on_write_]() {
+ update(*component);
+ };
}
- return as_write_attribute_(data, domain_size);
+
+ return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)};
}
bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const
@@ -925,7 +929,7 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
if (builtin_provider != nullptr) {
- return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
+ return builtin_provider->try_get_for_write(*this);
}
}
for (const DynamicAttributesProvider *dynamic_provider :
@@ -1249,6 +1253,20 @@ static void save_output_attribute(OutputAttribute &output_attribute)
varray.get(i, buffer);
write_attribute.varray->set_by_relocate(i, buffer);
}
+ if (write_attribute.tag_modified_fn) {
+ write_attribute.tag_modified_fn();
+ }
+}
+
+static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method(
+ const blender::bke::WriteAttributeLookup &attribute)
+{
+ if (!attribute.tag_modified_fn) {
+ return {};
+ }
+ return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) {
+ tag_modified_fn();
+ };
}
static OutputAttribute create_output_attribute(GeometryComponent &component,
@@ -1293,14 +1311,21 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
/* Builtin attribute is on different domain. */
return {};
}
+
GVMutableArrayPtr varray = std::move(attribute.varray);
if (varray->type() == *cpp_type) {
/* Builtin attribute matches exactly. */
- return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ return OutputAttribute(std::move(varray),
+ domain,
+ get_simple_output_attribute_save_method(attribute),
+ ignore_old_values);
}
/* Builtin attribute is on the same domain but has a different data type. */
varray = conversions.try_convert(std::move(varray), *cpp_type);
- return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ return OutputAttribute(std::move(varray),
+ domain,
+ get_simple_output_attribute_save_method(attribute),
+ ignore_old_values);
}
const int domain_size = component.attribute_domain_size(domain);
@@ -1324,7 +1349,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
}
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
/* Existing generic attribute matches exactly. */
- return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
+
+ return OutputAttribute(std::move(attribute.varray),
+ domain,
+ get_simple_output_attribute_save_method(attribute),
+ ignore_old_values);
}
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
@@ -1385,7 +1414,7 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex
std::string AttributeFieldInput::socket_inspection_name() const
{
std::stringstream ss;
- ss << TIP_("Attribute: ") << name_;
+ ss << '"' << name_ << '"' << TIP_(" attribute from geometry");
return ss.str();
}
@@ -1468,7 +1497,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
std::string AnonymousAttributeFieldInput::socket_inspection_name() const
{
std::stringstream ss;
- ss << TIP_("Anonymous Attribute: ") << debug_name_;
+ ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_;
return ss.str();
}
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 5cedcf69953..140498bdb01 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -87,7 +87,7 @@ class BuiltinAttributeProvider {
}
virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const = 0;
@@ -267,7 +267,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 9facb146361..dc3c2a8e55e 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -207,14 +207,15 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
{
Brush *brush = (Brush *)id;
- BKE_LIB_FOREACHID_PROCESS(data, brush->toggle_brush, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, brush->clone.image, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, brush->paint_curve, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->toggle_brush, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->clone.image, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER);
if (brush->gpencil_settings) {
- BKE_LIB_FOREACHID_PROCESS(data, brush->gpencil_settings->material, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER);
}
- BKE_texture_mtex_foreach_id(data, &brush->mtex);
- BKE_texture_mtex_foreach_id(data, &brush->mask_mtex);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex));
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
+ BKE_texture_mtex_foreach_id(data, &brush->mask_mtex));
}
static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address)
diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c
index dbc213907ac..72dd51a940d 100644
--- a/source/blender/blenkernel/intern/callbacks.c
+++ b/source/blender/blenkernel/intern/callbacks.c
@@ -30,11 +30,20 @@
static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}};
+static bool callbacks_initialized = false;
+
+#define ASSERT_CALLBACKS_INITIALIZED() \
+ BLI_assert_msg(callbacks_initialized, \
+ "Callbacks should be initialized with BKE_callback_global_init() before using " \
+ "the callback system.")
+
void BKE_callback_exec(struct Main *bmain,
struct PointerRNA **pointers,
const int num_pointers,
eCbEvent evt)
{
+ ASSERT_CALLBACKS_INITIALIZED();
+
/* Use mutable iteration so handlers are able to remove themselves. */
ListBase *lb = &callback_slots[evt];
LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) {
@@ -75,18 +84,26 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain,
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
{
+ ASSERT_CALLBACKS_INITIALIZED();
ListBase *lb = &callback_slots[evt];
BLI_addtail(lb, funcstore);
}
void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt)
{
- ListBase *lb = &callback_slots[evt];
-
- /* Be safe, as the callback may have already been removed by BKE_callback_global_finalize(), for
+ /* The callback may have already been removed by BKE_callback_global_finalize(), for
* example when removing callbacks in response to a BKE_blender_atexit_register callback
* function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */
- BLI_remlink_safe(lb, funcstore);
+ if (!callbacks_initialized) {
+ return;
+ }
+
+ ListBase *lb = &callback_slots[evt];
+
+ /* Be noisy about potential programming errors. */
+ BLI_assert_msg(BLI_findindex(lb, funcstore) != -1, "To-be-removed callback not found");
+
+ BLI_remlink(lb, funcstore);
if (funcstore->alloc) {
MEM_freeN(funcstore);
@@ -95,7 +112,7 @@ void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt)
void BKE_callback_global_init(void)
{
- /* do nothing */
+ callbacks_initialized = true;
}
/* call on application exit */
@@ -111,4 +128,6 @@ void BKE_callback_global_finalize(void)
BKE_callback_remove(funcstore, evt);
}
}
+
+ callbacks_initialized = false;
}
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index ed1f6fcb40a..9455eed7f3f 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -103,13 +103,13 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data)
{
Camera *camera = (Camera *)id;
- BKE_LIB_FOREACHID_PROCESS(data, camera->dof.focus_object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, camera->dof.focus_object, IDWALK_CB_NOP);
LISTBASE_FOREACH (CameraBGImage *, bgpic, &camera->bg_images) {
if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) {
- BKE_LIB_FOREACHID_PROCESS(data, bgpic->ima, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->ima, IDWALK_CB_USER);
}
else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) {
- BKE_LIB_FOREACHID_PROCESS(data, bgpic->clip, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->clip, IDWALK_CB_USER);
}
}
}
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 8e50b9e9534..14097ecd8a7 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -158,10 +158,11 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
Collection *collection = (Collection *)id;
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- BKE_LIB_FOREACHID_PROCESS(data, cob->ob, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- BKE_LIB_FOREACHID_PROCESS(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
+ data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER);
}
LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) {
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad
@@ -170,7 +171,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
(parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED :
IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag);
}
}
@@ -715,7 +716,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
collection_new->id.tag &= ~LIB_TAG_NEW;
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */
- BKE_libblock_relink_to_newid(&collection_new->id);
+ BKE_libblock_relink_to_newid(bmain, &collection_new->id);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 620110f7881..aae9ac383a4 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -130,17 +130,17 @@ static void curve_free_data(ID *id)
static void curve_foreach_id(ID *id, LibraryForeachIDData *data)
{
Curve *curve = (Curve *)id;
- BKE_LIB_FOREACHID_PROCESS(data, curve->bevobj, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, curve->taperobj, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, curve->textoncurve, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, curve->key, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->bevobj, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->taperobj, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->textoncurve, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->key, IDWALK_CB_USER);
for (int i = 0; i < curve->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, curve->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->mat[i], IDWALK_CB_USER);
}
- BKE_LIB_FOREACHID_PROCESS(data, curve->vfont, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, curve->vfontb, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, curve->vfonti, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, curve->vfontbi, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfont, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontb, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfonti, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontbi, IDWALK_CB_USER);
}
static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address)
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 8eec7f5dfab..0e3da9e0789 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -160,6 +160,13 @@ blender::Array<float> CurveEval::accumulated_spline_lengths() const
return spline_lengths;
}
+void CurveEval::mark_cache_invalid()
+{
+ for (SplinePtr &spline : splines_) {
+ spline->mark_cache_invalid();
+ }
+}
+
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 5f2f945192c..cd40d5e8a41 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -88,6 +88,7 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges)
}
static void spline_extrude_to_mesh_data(const ResultInfo &info,
+ const bool fill_caps,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
MutableSpan<MLoop> r_loops,
@@ -180,6 +181,37 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
}
+ if (fill_caps && profile.is_cyclic()) {
+ 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;
+
+ MPoly &poly_start = r_polys[cap_poly_offset];
+ poly_start.loopstart = cap_loop_offset;
+ poly_start.totloop = info.profile_edge_len;
+ MPoly &poly_end = r_polys[cap_poly_offset + 1];
+ poly_end.loopstart = cap_loop_offset + info.profile_edge_len;
+ poly_end.totloop = info.profile_edge_len;
+
+ const int last_ring_index = info.spline_vert_len - 1;
+ const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index;
+ const int last_ring_edge_offset = profile_edges_start +
+ info.profile_edge_len * last_ring_index;
+
+ for (const int i : IndexRange(info.profile_edge_len)) {
+ const int i_inv = info.profile_edge_len - i - 1;
+ MLoop &loop_start = r_loops[cap_loop_offset + i];
+ loop_start.v = info.vert_offset + i_inv;
+ loop_start.e = profile_edges_start + i_inv;
+ MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i];
+ loop_end.v = last_ring_vert_offset + i;
+ loop_end.e = last_ring_edge_offset + i;
+ }
+
+ mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len));
+ mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len));
+ }
+
/* Calculate the positions of each profile ring profile along the spline. */
Span<float3> positions = spline.evaluated_positions();
Span<float3> tangents = spline.evaluated_tangents();
@@ -226,14 +258,22 @@ static inline int spline_extrude_edge_size(const Spline &curve, const Spline &pr
curve.evaluated_edges_size() * profile.evaluated_points_size();
}
-static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile)
+static inline int spline_extrude_loop_size(const Spline &curve,
+ const Spline &profile,
+ const bool fill_caps)
{
- return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
+ 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;
+ return tube + caps;
}
-static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile)
+static inline int spline_extrude_poly_size(const Spline &curve,
+ const Spline &profile,
+ const bool fill_caps)
{
- return curve.evaluated_edges_size() * profile.evaluated_edges_size();
+ const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size();
+ const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0;
+ return tube + caps;
}
struct ResultOffsets {
@@ -242,7 +282,9 @@ struct ResultOffsets {
Array<int> loop;
Array<int> poly;
};
-static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<SplinePtr> curves)
+static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles,
+ Span<SplinePtr> curves,
+ const bool fill_caps)
{
const int total = profiles.size() * curves.size();
Array<int> vert(total + 1);
@@ -263,8 +305,8 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl
poly[mesh_index] = poly_offset;
vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]);
edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]);
- loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]);
- poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]);
+ loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile], fill_caps);
+ poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile], fill_caps);
mesh_index++;
}
}
@@ -652,12 +694,12 @@ static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve,
* changed anyway in a way that affects the normals. So currently this code uses the safer /
* simpler solution of deferring normal calculation to the rest of Blender.
*/
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile)
+Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps)
{
Span<SplinePtr> profiles = profile.splines();
Span<SplinePtr> curves = curve.splines();
- const ResultOffsets offsets = calculate_result_offsets(profiles, curves);
+ const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps);
if (offsets.vert.last() == 0) {
return nullptr;
}
@@ -696,6 +738,7 @@ Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile)
};
spline_extrude_to_mesh_data(info,
+ fill_caps,
{mesh->mvert, mesh->totvert},
{mesh->medge, mesh->totedge},
{mesh->mloop, mesh->totloop},
@@ -733,7 +776,7 @@ static CurveEval get_curve_single_vert()
Mesh *curve_to_wire_mesh(const CurveEval &curve)
{
static const CurveEval vert_curve = get_curve_single_vert();
- return curve_to_mesh_sweep(curve, vert_curve);
+ return curve_to_mesh_sweep(curve, vert_curve, false);
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 8e9c504dcbf..bbf61c51bfb 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -203,14 +203,18 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
switch (fcm->type) {
case FMODIFIER_TYPE_PYTHON: {
FMod_Python *fcm_py = (FMod_Python *)fcm->data;
- BKE_LIB_FOREACHID_PROCESS(data, fcm_py->script, IDWALK_CB_NOP);
-
- IDP_foreach_property(fcm_py->prop,
- IDP_TYPE_FILTER_ID,
- BKE_lib_query_idpropertiesForeachIDLink_callback,
- data);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP);
+
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(fcm_py->prop,
+ IDP_TYPE_FILTER_ID,
+ BKE_lib_query_idpropertiesForeachIDLink_callback,
+ data));
break;
}
+ default:
+ break;
}
}
}
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index f30ff2a70a7..d3c3fcc1e67 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -433,7 +433,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
return as_read_attribute_(*curve);
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
if (writable_ != Writable) {
return {};
@@ -442,7 +442,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
if (curve == nullptr) {
return {};
}
- return as_write_attribute_(*curve);
+ return {as_write_attribute_(*curve), domain_};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -1122,7 +1122,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return point_data_gvarray(spans, offsets);
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@@ -1133,22 +1133,30 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return {};
}
+ std::function<void()> tag_modified_fn;
+ if (update_on_write_ != nullptr) {
+ tag_modified_fn = [curve, update = update_on_write_]() {
+ for (SplinePtr &spline : curve->splines()) {
+ update(*spline);
+ }
+ };
+ }
+
MutableSpan<SplinePtr> splines = curve->splines();
if (splines.size() == 1) {
- return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
- get_mutable_span_(*splines.first()));
+ return {std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
+ get_mutable_span_(*splines.first())),
+ domain_,
+ std::move(tag_modified_fn)};
}
Array<int> offsets = curve->control_point_offsets();
Array<MutableSpan<T>> spans(splines.size());
for (const int i : splines.index_range()) {
spans[i] = get_mutable_span_(*splines[i]);
- if (update_on_write_) {
- update_on_write_(*splines[i]);
- }
}
- return point_data_gvarray(spans, offsets);
+ return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn};
}
bool try_delete(GeometryComponent &component) const final
@@ -1220,7 +1228,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
{
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@@ -1233,16 +1241,19 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
- /* Changing the positions requires recalculation of cached evaluated data in many cases.
- * This could set more specific flags in the future to avoid unnecessary recomputation. */
- for (SplinePtr &spline : curve->splines()) {
- spline->mark_cache_invalid();
- }
+ auto tag_modified_fn = [curve]() {
+ /* Changing the positions requires recalculation of cached evaluated data in many cases.
+ * This could set more specific flags in the future to avoid unnecessary recomputation. */
+ curve->mark_cache_invalid();
+ };
Array<int> offsets = curve->control_point_offsets();
- return std::make_unique<
- fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
- offsets.last(), curve->splines(), std::move(offsets));
+ return {std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3,
+ VMutableArray_For_SplinePosition>>(
+ offsets.last(), curve->splines(), std::move(offsets)),
+ domain_,
+ tag_modified_fn};
}
};
@@ -1278,7 +1289,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
offsets.last(), curve->splines(), std::move(offsets), is_right_);
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@@ -1289,10 +1300,15 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
+ auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); };
+
Array<int> offsets = curve->control_point_offsets();
- return std::make_unique<
- fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
- offsets.last(), curve->splines(), std::move(offsets), is_right_);
+ return {
+ std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
+ offsets.last(), curve->splines(), std::move(offsets), is_right_),
+ domain_,
+ tag_modified_fn};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index 4204d62e1a7..5fe77000519 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -60,7 +60,9 @@ void InstancesComponent::reserve(int min_capacity)
{
instance_reference_handles_.reserve(min_capacity);
instance_transforms_.reserve(min_capacity);
- instance_ids_.reserve(min_capacity);
+ if (!instance_ids_.is_empty()) {
+ this->instance_ids_ensure();
+ }
}
/**
@@ -73,7 +75,9 @@ void InstancesComponent::resize(int capacity)
{
instance_reference_handles_.resize(capacity);
instance_transforms_.resize(capacity);
- instance_ids_.resize(capacity);
+ if (!instance_ids_.is_empty()) {
+ this->instance_ids_ensure();
+ }
}
void InstancesComponent::clear()
@@ -85,15 +89,15 @@ void InstancesComponent::clear()
references_.clear();
}
-void InstancesComponent::add_instance(const int instance_handle,
- const float4x4 &transform,
- const int id)
+void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform)
{
BLI_assert(instance_handle >= 0);
BLI_assert(instance_handle < references_.size());
instance_reference_handles_.append(instance_handle);
instance_transforms_.append(transform);
- instance_ids_.append(id);
+ if (!instance_ids_.is_empty()) {
+ this->instance_ids_ensure();
+ }
}
blender::Span<int> InstancesComponent::instance_reference_handles() const
@@ -125,6 +129,22 @@ blender::Span<int> InstancesComponent::instance_ids() const
}
/**
+ * Make sure the ID storage size matches the number of instances. By directly resizing the
+ * component's vectors internally, it is possible to be in a situation where the IDs are not
+ * empty but they do not have the correct size; this function resolves that.
+ */
+blender::MutableSpan<int> InstancesComponent::instance_ids_ensure()
+{
+ instance_ids_.append_n_times(0, this->instances_amount() - instance_ids_.size());
+ return instance_ids_;
+}
+
+void InstancesComponent::instance_ids_clear()
+{
+ instance_ids_.clear_and_make_inline();
+}
+
+/**
* With write access to the instances component, the data in the instanced geometry sets can be
* changed. This is a function on the component rather than each reference to ensure `const`
* correctness for that reason.
@@ -327,8 +347,16 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != instance_ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
+ if (instance_ids().is_empty()) {
+ almost_unique_ids_.reinitialize(this->instances_amount());
+ for (const int i : almost_unique_ids_.index_range()) {
+ almost_unique_ids_[i] = i;
+ }
+ }
+ else {
+ if (almost_unique_ids_.size() != instance_ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
+ }
}
return almost_unique_ids_;
}
@@ -370,15 +398,16 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
transforms);
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
InstancesComponent &instances_component = static_cast<InstancesComponent &>(component);
MutableSpan<float4x4> transforms = instances_component.instance_transforms();
- return std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4,
- float3,
- get_transform_position,
- set_transform_position>>(
- transforms);
+ return {
+ std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4,
+ float3,
+ get_transform_position,
+ set_transform_position>>(transforms),
+ domain_};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -398,11 +427,83 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
}
};
+class InstanceIDAttributeProvider final : public BuiltinAttributeProvider {
+ public:
+ InstanceIDAttributeProvider()
+ : BuiltinAttributeProvider(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32, Creatable, Writable, Deletable)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const InstancesComponent &instances = static_cast<const InstancesComponent &>(component);
+ if (instances.instance_ids().is_empty()) {
+ return {};
+ }
+ return std::make_unique<fn::GVArray_For_Span<int>>(instances.instance_ids());
+ }
+
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
+ {
+ InstancesComponent &instances = static_cast<InstancesComponent &>(component);
+ if (instances.instance_ids().is_empty()) {
+ return {};
+ }
+ return {std::make_unique<fn::GVMutableArray_For_MutableSpan<int>>(instances.instance_ids()),
+ domain_};
+ }
+
+ bool try_delete(GeometryComponent &component) const final
+ {
+ InstancesComponent &instances = static_cast<InstancesComponent &>(component);
+ if (instances.instance_ids().is_empty()) {
+ return false;
+ }
+ instances.instance_ids_clear();
+ return true;
+ }
+
+ bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final
+ {
+ InstancesComponent &instances = static_cast<InstancesComponent &>(component);
+ if (instances.instances_amount() == 0) {
+ return false;
+ }
+ MutableSpan<int> ids = instances.instance_ids_ensure();
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ ids.fill(0);
+ break;
+ }
+ case AttributeInit::Type::VArray: {
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), ids.data());
+ break;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ ids.copy_from({static_cast<int *>(source_data), instances.instances_amount()});
+ MEM_freeN(source_data);
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ const InstancesComponent &instances = static_cast<const InstancesComponent &>(component);
+ return !instances.instance_ids().is_empty();
+ }
+};
+
static ComponentAttributeProviders create_attribute_providers_for_instances()
{
static InstancePositionAttributeProvider position;
+ static InstanceIDAttributeProvider id;
- return ComponentAttributeProviders({&position}, {});
+ return ComponentAttributeProviders({&position, &id}, {});
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 6091d3f3dab..c3e39c0b2cb 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -1210,7 +1210,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
- GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 4753a9e0768..cd1bafe445a 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -527,6 +527,40 @@ void GeometrySet::gather_attributes_for_propagation(
delete dummy_component;
}
+static void gather_component_types_recursive(const GeometrySet &geometry_set,
+ const bool include_instances,
+ const bool ignore_empty,
+ Vector<GeometryComponentType> &r_types)
+{
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ if (ignore_empty) {
+ if (component->is_empty()) {
+ continue;
+ }
+ }
+ r_types.append_non_duplicates(component->type());
+ }
+ if (!include_instances) {
+ return;
+ }
+ const InstancesComponent *instances = geometry_set.get_component_for_read<InstancesComponent>();
+ if (instances == nullptr) {
+ return;
+ }
+ instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) {
+ gather_component_types_recursive(
+ instance_geometry_set, include_instances, ignore_empty, r_types);
+ });
+}
+
+blender::Vector<GeometryComponentType> GeometrySet::gather_component_types(
+ const bool include_instances, bool ignore_empty) const
+{
+ Vector<GeometryComponentType> types;
+ gather_component_types_recursive(*this, include_instances, ignore_empty, types);
+ return types;
+}
+
static void gather_mutable_geometry_sets(GeometrySet &geometry_set,
Vector<GeometrySet *> &r_geometry_sets)
{
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index fa0741d3a2e..bea65030c06 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -139,11 +139,11 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data)
bGPdata *gpencil = (bGPdata *)id;
/* materials */
for (int i = 0; i < gpencil->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, gpencil->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gpencil->mat[i], IDWALK_CB_USER);
}
LISTBASE_FOREACH (bGPDlayer *, gplayer, &gpencil->layers) {
- BKE_LIB_FOREACHID_PROCESS(data, gplayer->parent, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gplayer->parent, IDWALK_CB_NOP);
}
}
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index cf346e9cac7..7433ee7ac29 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -107,7 +107,7 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data)
{
Hair *hair = (Hair *)id;
for (int i = 0; i < hair->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, hair->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc
index 97c742b1ec1..f820b345c59 100644
--- a/source/blender/blenkernel/intern/icons.cc
+++ b/source/blender/blenkernel/intern/icons.cc
@@ -359,22 +359,30 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
PreviewImage **BKE_previewimg_id_get_p(const ID *id)
{
switch (GS(id->name)) {
+ case ID_OB: {
+ Object *ob = (Object *)id;
+ /* Currently, only object types with real geometry can be rendered as preview. */
+ if (!OB_TYPE_IS_GEOMETRY(ob->type)) {
+ return nullptr;
+ }
+ return &ob->preview;
+ }
+
#define ID_PRV_CASE(id_code, id_struct) \
case id_code: { \
return &((id_struct *)id)->preview; \
} \
((void)0)
- ID_PRV_CASE(ID_MA, Material);
- ID_PRV_CASE(ID_TE, Tex);
- ID_PRV_CASE(ID_WO, World);
- ID_PRV_CASE(ID_LA, Light);
- ID_PRV_CASE(ID_IM, Image);
- ID_PRV_CASE(ID_BR, Brush);
- ID_PRV_CASE(ID_OB, Object);
- ID_PRV_CASE(ID_GR, Collection);
- ID_PRV_CASE(ID_SCE, Scene);
- ID_PRV_CASE(ID_SCR, bScreen);
- ID_PRV_CASE(ID_AC, bAction);
+ ID_PRV_CASE(ID_MA, Material);
+ ID_PRV_CASE(ID_TE, Tex);
+ ID_PRV_CASE(ID_WO, World);
+ ID_PRV_CASE(ID_LA, Light);
+ ID_PRV_CASE(ID_IM, Image);
+ ID_PRV_CASE(ID_BR, Brush);
+ ID_PRV_CASE(ID_GR, Collection);
+ ID_PRV_CASE(ID_SCE, Scene);
+ ID_PRV_CASE(ID_SCR, bScreen);
+ ID_PRV_CASE(ID_AC, bAction);
#undef ID_PRV_CASE
default:
break;
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 5ae338aaaeb..3800cbec94b 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -211,8 +211,12 @@ static void image_foreach_cache(ID *id,
for (int eye = 0; eye < 2; eye++) {
for (int a = 0; a < TEXTARGET_COUNT; a++) {
for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) {
+ GPUTexture *texture = image->gputexture[a][eye][resolution];
+ if (texture == NULL) {
+ continue;
+ }
key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]);
- key.cache_v = image->gputexture[a][eye];
+ key.cache_v = texture;
function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data);
}
}
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 9bca8172e64..a2da59bca58 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -131,7 +131,7 @@ static void lattice_free_data(ID *id)
static void lattice_foreach_id(ID *id, LibraryForeachIDData *data)
{
Lattice *lattice = (Lattice *)id;
- BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lattice->key, IDWALK_CB_USER);
}
static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address)
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 2ac92828cec..74750a9b61a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -76,48 +76,52 @@ typedef struct LibraryForeachIDData {
BLI_LINKSTACK_DECLARE(ids_todo, ID *);
} LibraryForeachIDData;
-bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag)
+/** Check whether current iteration over ID usages should be stopped or not.
+ * \return true if the iteration should be stopped, false otherwise. */
+bool BKE_lib_query_foreachid_iter_stop(LibraryForeachIDData *data)
{
- if (!(data->status & IDWALK_STOP)) {
- const int flag = data->flag;
- ID *old_id = *id_pp;
-
- /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic
- * caller code. */
- cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear);
-
- /* Update the callback flags with some extra information regarding overrides: all 'loopback',
- * 'internal', 'embedded' etc. ID pointers are never overridable. */
- if (cb_flag &
- (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
- cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
- }
+ return (data->status & IDWALK_STOP) != 0;
+}
- const int callback_return = data->callback(
- &(struct LibraryIDLinkCallbackData){.user_data = data->user_data,
- .bmain = data->bmain,
- .id_owner = data->owner_id,
- .id_self = data->self_id,
- .id_pointer = id_pp,
- .cb_flag = cb_flag});
- if (flag & IDWALK_READONLY) {
- BLI_assert(*(id_pp) == old_id);
- }
- if (old_id && (flag & IDWALK_RECURSE)) {
- if (BLI_gset_add((data)->ids_handled, old_id)) {
- if (!(callback_return & IDWALK_RET_STOP_RECURSION)) {
- BLI_LINKSTACK_PUSH(data->ids_todo, old_id);
- }
+void BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag)
+{
+ if (BKE_lib_query_foreachid_iter_stop(data)) {
+ return;
+ }
+
+ const int flag = data->flag;
+ ID *old_id = *id_pp;
+
+ /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic
+ * caller code. */
+ cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear);
+
+ /* Update the callback flags with some extra information regarding overrides: all 'loopback',
+ * 'internal', 'embedded' etc. ID pointers are never overridable. */
+ if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
+ }
+
+ const int callback_return = data->callback(
+ &(struct LibraryIDLinkCallbackData){.user_data = data->user_data,
+ .bmain = data->bmain,
+ .id_owner = data->owner_id,
+ .id_self = data->self_id,
+ .id_pointer = id_pp,
+ .cb_flag = cb_flag});
+ if (flag & IDWALK_READONLY) {
+ BLI_assert(*(id_pp) == old_id);
+ }
+ if (old_id && (flag & IDWALK_RECURSE)) {
+ if (BLI_gset_add((data)->ids_handled, old_id)) {
+ if (!(callback_return & IDWALK_RET_STOP_RECURSION)) {
+ BLI_LINKSTACK_PUSH(data->ids_todo, old_id);
}
}
- if (callback_return & IDWALK_RET_STOP_ITER) {
- data->status |= IDWALK_STOP;
- return false;
- }
- return true;
}
-
- return false;
+ if (callback_return & IDWALK_RET_STOP_ITER) {
+ data->status |= IDWALK_STOP;
+ }
}
int BKE_lib_query_foreachid_process_flags_get(LibraryForeachIDData *data)
@@ -139,7 +143,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(LibraryForeachIDData
return cb_flag_backup;
}
-static void library_foreach_ID_link(Main *bmain,
+static bool library_foreach_ID_link(Main *bmain,
ID *id_owner,
ID *id,
LibraryIDLinkCallback callback,
@@ -158,19 +162,24 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void
BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag);
}
-bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
+/** Process embedded ID pointers (root nodetrees, master collections, ...).
+ *
+ * Those require specific care, since they are technically sub-data of their owner, yet in some
+ * cases they still behave as regular IDs. */
+void BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
{
/* Needed e.g. for callbacks handling relationships. This call shall be absolutely read-only. */
ID *id = *id_pp;
const int flag = data->flag;
- if (!BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED)) {
- return false;
+ BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED);
+ if (BKE_lib_query_foreachid_iter_stop(data)) {
+ return;
}
BLI_assert(id == *id_pp);
if (id == NULL) {
- return true;
+ return;
}
if (flag & IDWALK_IGNORE_EMBEDDED_ID) {
@@ -186,14 +195,24 @@ bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
}
}
else {
- library_foreach_ID_link(
- data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data);
+ if (!library_foreach_ID_link(
+ data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data)) {
+ data->status |= IDWALK_STOP;
+ return;
+ }
}
+}
- return true;
+static void library_foreach_ID_data_cleanup(LibraryForeachIDData *data)
+{
+ if (data->ids_handled != NULL) {
+ BLI_gset_free(data->ids_handled, NULL);
+ BLI_LINKSTACK_FREE(data->ids_todo);
+ }
}
-static void library_foreach_ID_link(Main *bmain,
+/** \return false in case iteration over ID pointers must be stopped, true otherwise. */
+static bool library_foreach_ID_link(Main *bmain,
ID *id_owner,
ID *id,
LibraryIDLinkCallback callback,
@@ -210,6 +229,10 @@ static void library_foreach_ID_link(Main *bmain,
flag |= IDWALK_READONLY;
flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS;
+ /* NOTE: This function itself should never be called recursively when IDWALK_RECURSE is set,
+ * see also comments in #BKE_library_foreach_ID_embedded.
+ * This is why we can always create this data here, and do not need to try and re-use it from
+ * `inherit_data`. */
data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
BLI_LINKSTACK_INIT(data.ids_todo);
@@ -224,10 +247,26 @@ static void library_foreach_ID_link(Main *bmain,
data.user_data = user_data;
#define CALLBACK_INVOKE_ID(check_id, cb_flag) \
- BKE_LIB_FOREACHID_PROCESS_ID(&data, check_id, cb_flag)
+ { \
+ CHECK_TYPE_ANY((check_id), ID *, void *); \
+ BKE_lib_query_foreachid_process(&data, (ID **)&(check_id), (cb_flag)); \
+ if (BKE_lib_query_foreachid_iter_stop(&data)) { \
+ library_foreach_ID_data_cleanup(&data); \
+ return false; \
+ } \
+ } \
+ ((void)0)
#define CALLBACK_INVOKE(check_id_super, cb_flag) \
- BKE_LIB_FOREACHID_PROCESS(&data, check_id_super, cb_flag)
+ { \
+ CHECK_TYPE(&((check_id_super)->id), ID *); \
+ BKE_lib_query_foreachid_process(&data, (ID **)&(check_id_super), (cb_flag)); \
+ if (BKE_lib_query_foreachid_iter_stop(&data)) { \
+ library_foreach_ID_data_cleanup(&data); \
+ return false; \
+ } \
+ } \
+ ((void)0)
for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) {
data.self_id = id;
@@ -269,6 +308,10 @@ static void library_foreach_ID_link(Main *bmain,
to_id_entry = to_id_entry->next) {
BKE_lib_query_foreachid_process(
&data, to_id_entry->id_pointer.to, to_id_entry->usage_flag);
+ if (BKE_lib_query_foreachid_iter_stop(&data)) {
+ library_foreach_ID_data_cleanup(&data);
+ return false;
+ }
}
continue;
}
@@ -292,26 +335,33 @@ static void library_foreach_ID_link(Main *bmain,
IDP_TYPE_FILTER_ID,
BKE_lib_query_idpropertiesForeachIDLink_callback,
&data);
+ if (BKE_lib_query_foreachid_iter_stop(&data)) {
+ library_foreach_ID_data_cleanup(&data);
+ return false;
+ }
AnimData *adt = BKE_animdata_from_id(id);
if (adt) {
BKE_animdata_foreach_id(adt, &data);
+ if (BKE_lib_query_foreachid_iter_stop(&data)) {
+ library_foreach_ID_data_cleanup(&data);
+ return false;
+ }
}
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
if (id_type->foreach_id != NULL) {
id_type->foreach_id(id, &data);
- if (data.status & IDWALK_STOP) {
- break;
+ if (BKE_lib_query_foreachid_iter_stop(&data)) {
+ library_foreach_ID_data_cleanup(&data);
+ return false;
}
}
}
- if (data.ids_handled) {
- BLI_gset_free(data.ids_handled, NULL);
- BLI_LINKSTACK_FREE(data.ids_todo);
- }
+ library_foreach_ID_data_cleanup(&data);
+ return true;
#undef CALLBACK_INVOKE_ID
#undef CALLBACK_INVOKE
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index b5c45c0902b..248d85bcae0 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -669,56 +669,10 @@ void BKE_libblock_relink_ex(
DEG_relations_tag_update(bmain);
}
+static void libblock_relink_to_newid(Main *bmain, ID *id);
static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data)
{
const int cb_flag = cb_data->cb_flag;
- if (cb_flag & IDWALK_CB_EMBEDDED) {
- return IDWALK_RET_NOP;
- }
-
- ID **id_pointer = cb_data->id_pointer;
- ID *id = *id_pointer;
- if (id) {
- /* See: NEW_ID macro */
- if (id->newid) {
- BKE_library_update_ID_link_user(id->newid, id, cb_flag);
- id = id->newid;
- *id_pointer = id;
- }
- if (id->tag & LIB_TAG_NEW) {
- id->tag &= ~LIB_TAG_NEW;
- BKE_libblock_relink_to_newid(id);
- }
- }
- return IDWALK_RET_NOP;
-}
-
-/**
- * Similar to #libblock_relink_ex,
- * but is remapping IDs to their newid value if non-NULL, in given \a id.
- *
- * Very specific usage, not sure we'll keep it on the long run,
- * currently only used in Object/Collection duplication code...
- *
- * WARNING: This is a deprecated version of this function, should not be used by new code. See
- * #BKE_libblock_relink_to_newid_new below.
- */
-void BKE_libblock_relink_to_newid(ID *id)
-{
- if (ID_IS_LINKED(id)) {
- return;
- }
-
- BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0);
-}
-
-/* ************************
- * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this
- * #BKE_libblock_relink_to_newid_new new code and remove old one.
- ************************** */
-static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data)
-{
- const int cb_flag = cb_data->cb_flag;
if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
return IDWALK_RET_NOP;
}
@@ -739,12 +693,22 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data)
}
if (id->tag & LIB_TAG_NEW) {
id->tag &= ~LIB_TAG_NEW;
- BKE_libblock_relink_to_newid_new(bmain, id);
+ libblock_relink_to_newid(bmain, id);
}
}
return IDWALK_RET_NOP;
}
+static void libblock_relink_to_newid(Main *bmain, ID *id)
+{
+ if (ID_IS_LINKED(id)) {
+ return;
+ }
+
+ id->tag &= ~LIB_TAG_NEW;
+ BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper, NULL, 0);
+}
+
/**
* Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively
* in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`.
@@ -754,7 +718,7 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data)
* Very specific usage, not sure we'll keep it on the long run,
* currently only used in Object/Collection duplication code...
*/
-void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id)
+void BKE_libblock_relink_to_newid(Main *bmain, ID *id)
{
if (ID_IS_LINKED(id)) {
return;
@@ -762,6 +726,8 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id)
/* We do not want to have those cached relationship data here. */
BLI_assert(bmain->relations == NULL);
- id->tag &= ~LIB_TAG_NEW;
- BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0);
+ BKE_layer_collection_resync_forbid();
+ libblock_relink_to_newid(bmain, id);
+ BKE_layer_collection_resync_allow();
+ BKE_main_collection_sync_remap(bmain);
}
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 36958e36004..1dba353d8ce 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -57,7 +57,7 @@ static void library_free_data(ID *id)
static void library_foreach_id(ID *id, LibraryForeachIDData *data)
{
Library *lib = (Library *)id;
- BKE_LIB_FOREACHID_PROCESS(data, lib->parent, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->parent, IDWALK_CB_NEVER_SELF);
}
IDTypeInfo IDType_ID_LI = {
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index a6150028f46..05e8d4fe978 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -129,7 +129,8 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data)
Light *lamp = (Light *)id;
if (lamp->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree));
}
}
diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c
index 1f4abf36426..57ad6695db4 100644
--- a/source/blender/blenkernel/intern/lightprobe.c
+++ b/source/blender/blenkernel/intern/lightprobe.c
@@ -53,8 +53,8 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data)
{
LightProbe *probe = (LightProbe *)id;
- BKE_LIB_FOREACHID_PROCESS(data, probe->image, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, probe->visibility_grp, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->image, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->visibility_grp, IDWALK_CB_NOP);
}
static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address)
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index f4e4dd9f1ab..3c305d1fb3f 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -155,12 +155,14 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data)
for (int i = 0; i < MAX_MTEX; i++) {
if (linestyle->mtex[i]) {
- BKE_texture_mtex_foreach_id(data, linestyle->mtex[i]);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_texture_mtex_foreach_id(data, linestyle->mtex[i]));
}
}
if (linestyle->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree));
}
LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) {
@@ -168,7 +170,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data)
LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *)
lsm;
if (p->target) {
- BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP);
}
}
}
@@ -177,7 +179,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data)
LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *)
lsm;
if (p->target) {
- BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP);
}
}
}
@@ -186,7 +188,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data)
LineStyleThicknessModifier_DistanceFromObject *p =
(LineStyleThicknessModifier_DistanceFromObject *)lsm;
if (p->target) {
- BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP);
}
}
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 6c57d3139bb..5f726defb1a 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -166,15 +166,14 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data)
{
Material *material = (Material *)id;
/* Nodetrees **are owned by IDs**, treat them as mere sub-data and not real ID! */
- if (!BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)) {
- return;
- }
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree));
if (material->texpaintslot != NULL) {
- BKE_LIB_FOREACHID_PROCESS(data, material->texpaintslot->ima, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP);
}
if (material->gp_style != NULL) {
- BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->sima, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->ima, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->sima, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->ima, IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index 6c8664aefed..48d31361eac 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -112,7 +112,7 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data)
{
MetaBall *metaball = (MetaBall *)id;
for (int i = 0; i < metaball->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, metaball->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index ed3766ad6a3..7277f7ad209 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -176,10 +176,10 @@ static void mesh_free_data(ID *id)
static void mesh_foreach_id(ID *id, LibraryForeachIDData *data)
{
Mesh *mesh = (Mesh *)id;
- BKE_LIB_FOREACHID_PROCESS(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS(data, mesh->key, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->key, IDWALK_CB_USER);
for (int i = 0; i < mesh->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, mesh->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->mat[i], IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c
index 7ac4c29f0ee..1c8646a4bdd 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.c
@@ -52,6 +52,8 @@ void BKE_mesh_runtime_reset(Mesh *mesh)
memset(&mesh->runtime, 0, sizeof(mesh->runtime));
mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex");
BLI_mutex_init(mesh->runtime.eval_mutex);
+ mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex");
+ BLI_mutex_init(mesh->runtime.render_mutex);
}
/* Clear all pointers which we don't want to be shared on copying the datablock.
@@ -71,6 +73,9 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex");
BLI_mutex_init(mesh->runtime.eval_mutex);
+
+ mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex");
+ BLI_mutex_init(mesh->runtime.render_mutex);
}
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
@@ -80,6 +85,11 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh)
MEM_freeN(mesh->runtime.eval_mutex);
mesh->runtime.eval_mutex = NULL;
}
+ if (mesh->runtime.render_mutex != NULL) {
+ BLI_mutex_end(mesh->runtime.render_mutex);
+ MEM_freeN(mesh->runtime.render_mutex);
+ mesh->runtime.render_mutex = NULL;
+ }
if (mesh->runtime.mesh_eval != NULL) {
mesh->runtime.mesh_eval->edit_mesh = NULL;
BKE_id_free(NULL, mesh->runtime.mesh_eval);
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 0c2ac841b87..002f370cdcb 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -132,19 +132,19 @@ static void movie_clip_foreach_id(ID *id, LibraryForeachIDData *data)
MovieClip *movie_clip = (MovieClip *)id;
MovieTracking *tracking = &movie_clip->tracking;
- BKE_LIB_FOREACHID_PROCESS(data, movie_clip->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, movie_clip->gpd, IDWALK_CB_USER);
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking->tracks) {
- BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER);
}
LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) {
LISTBASE_FOREACH (MovieTrackingTrack *, track, &object->tracks) {
- BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER);
}
}
LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking->plane_tracks) {
- BKE_LIB_FOREACHID_PROCESS(data, plane_track->image, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, plane_track->image, IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 487e925df79..124db07298d 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -488,14 +488,14 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
*/
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
{
- BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, strip->act, IDWALK_CB_USER);
LISTBASE_FOREACH (FCurve *, fcu, &strip->fcurves) {
- BKE_fcurve_foreach_id(fcu, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data));
}
LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) {
- BKE_nla_strip_foreach_id(substrip, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_nla_strip_foreach_id(substrip, data));
}
}
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index eda57d9e984..55e8bdb2483 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -307,34 +307,36 @@ static void ntree_free_data(ID *id)
static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock)
{
- IDP_foreach_property(
- sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(
+ sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data));
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value;
- BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value;
- BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER);
break;
}
case SOCK_COLLECTION: {
bNodeSocketValueCollection *default_value = (bNodeSocketValueCollection *)
sock->default_value;
- BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER);
break;
}
case SOCK_TEXTURE: {
bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
- BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER);
break;
}
case SOCK_MATERIAL: {
bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
- BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER);
break;
}
case SOCK_FLOAT:
@@ -355,26 +357,30 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data)
{
bNodeTree *ntree = (bNodeTree *)id;
- BKE_LIB_FOREACHID_PROCESS(data, ntree->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER);
- IDP_foreach_property(
- node->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(node->prop,
+ IDP_TYPE_FILTER_ID,
+ BKE_lib_query_idpropertiesForeachIDLink_callback,
+ data));
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- library_foreach_node_socket(data, sock);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
- library_foreach_node_socket(data, sock);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
- library_foreach_node_socket(data, sock);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
- library_foreach_node_socket(data, sock);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
}
@@ -4679,7 +4685,7 @@ static OutputFieldDependency find_group_output_dependencies(
while (!sockets_to_check.is_empty()) {
const InputSocketRef *input_socket = sockets_to_check.pop();
- for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
+ for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) {
const NodeRef &origin_node = origin_socket->node();
const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()];
@@ -4717,10 +4723,10 @@ static OutputFieldDependency find_group_output_dependencies(
static void propagate_data_requirements_from_right_to_left(
const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
- const Vector<const NodeRef *> sorted_nodes = tree.toposort(
+ const NodeTreeRef::ToposortResult toposort_result = tree.toposort(
NodeTreeRef::ToposortDirection::RightToLeft);
- for (const NodeRef *node : sorted_nodes) {
+ for (const NodeRef *node : toposort_result.sorted_nodes) {
const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface(
*node);
@@ -4829,10 +4835,10 @@ static void determine_group_input_states(
static void propagate_field_status_from_left_to_right(
const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
- Vector<const NodeRef *> sorted_nodes = tree.toposort(
+ const NodeTreeRef::ToposortResult toposort_result = tree.toposort(
NodeTreeRef::ToposortDirection::LeftToRight);
- for (const NodeRef *node : sorted_nodes) {
+ for (const NodeRef *node : toposort_result.sorted_nodes) {
if (node->is_group_input_node()) {
continue;
}
@@ -4848,14 +4854,14 @@ static void propagate_field_status_from_left_to_right(
continue;
}
state.is_single = true;
- if (input_socket->logically_linked_sockets().is_empty()) {
+ if (input_socket->directly_linked_sockets().is_empty()) {
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
state.is_single = false;
}
}
else {
- for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
+ for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) {
if (!field_state_by_socket_id[origin_socket->id()].is_single) {
state.is_single = false;
break;
@@ -5753,6 +5759,7 @@ static void registerGeometryNodes()
register_node_type_geo_legacy_select_by_handle_type();
register_node_type_geo_legacy_select_by_material();
register_node_type_geo_legacy_subdivision_surface();
+ register_node_type_geo_legacy_volume_to_mesh();
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_attribute_capture();
@@ -5800,8 +5807,10 @@ static void registerGeometryNodes()
register_node_type_geo_delete_geometry();
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_edge_split();
+ register_node_type_geo_image_texture();
register_node_type_geo_input_curve_handles();
register_node_type_geo_input_curve_tilt();
+ register_node_type_geo_input_id();
register_node_type_geo_input_index();
register_node_type_geo_input_material_index();
register_node_type_geo_input_material();
@@ -5850,6 +5859,7 @@ static void registerGeometryNodes()
register_node_type_geo_set_curve_handles();
register_node_type_geo_set_curve_radius();
register_node_type_geo_set_curve_tilt();
+ register_node_type_geo_set_id();
register_node_type_geo_set_material_index();
register_node_type_geo_set_material();
register_node_type_geo_set_point_radius();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index e85c6b4c7c5..3b0825fb8db 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -82,6 +82,7 @@
#include "BKE_anim_visualization.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_asset.h"
#include "BKE_camera.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
@@ -388,7 +389,8 @@ static void library_foreach_modifiersForeachIDLink(void *user_data,
int cb_flag)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data,
@@ -397,7 +399,8 @@ static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data,
int cb_flag)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
static void library_foreach_shaderfxForeachIDLink(void *user_data,
@@ -406,7 +409,8 @@ static void library_foreach_shaderfxForeachIDLink(void *user_data,
int cb_flag)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con),
@@ -416,7 +420,8 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con),
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys),
@@ -425,7 +430,8 @@ static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(p
int cb_flag)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
static void object_foreach_id(ID *id, LibraryForeachIDData *data)
@@ -452,11 +458,11 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
- BKE_LIB_FOREACHID_PROCESS(data, object->parent, IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS(data, object->track, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF);
/* object->proxy is refcounted, but not object->proxy_group... *sigh* */
- BKE_LIB_FOREACHID_PROCESS(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS(data, object->proxy_group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP);
/* Special case!
* Since this field is set/owned by 'user' of this ID (and not ID itself),
@@ -468,22 +474,23 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
IDWALK_CB_INDIRECT_USAGE :
0,
true);
- BKE_LIB_FOREACHID_PROCESS(data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
+ data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
}
- BKE_LIB_FOREACHID_PROCESS(data, object->poselib, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER);
for (int i = 0; i < object->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER);
}
/* Note that ob->gpd is deprecated, so no need to handle it here. */
- BKE_LIB_FOREACHID_PROCESS(data, object->instance_collection, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER);
if (object->pd) {
- BKE_LIB_FOREACHID_PROCESS(data, object->pd->tex, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, object->pd->f_source, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP);
}
/* Note that ob->effect is deprecated, so no need to handle it here. */
@@ -491,34 +498,52 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override(
data, proxy_cb_flag, false);
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
- IDP_foreach_property(
- pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
- BKE_LIB_FOREACHID_PROCESS(data, pchan->custom, IDWALK_CB_USER);
- BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(pchan->prop,
+ IDP_TYPE_FILTER_ID,
+ BKE_lib_query_idpropertiesForeachIDLink_callback,
+ data));
+
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ BKE_constraints_id_loop(
+ &pchan->constraints, library_foreach_constraintObjectLooper, data));
}
BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
}
if (object->rigidbody_constraint) {
- BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF);
- }
-
- BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data);
- BKE_gpencil_modifiers_foreach_ID_link(
- object, library_foreach_gpencil_modifiersForeachIDLink, data);
- BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data);
- BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
+ data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
+ data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF);
+ }
+
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data));
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ BKE_gpencil_modifiers_foreach_ID_link(
+ object, library_foreach_gpencil_modifiersForeachIDLink, data));
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data));
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data));
LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
- BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data));
}
if (object->soft) {
- BKE_LIB_FOREACHID_PROCESS(data, object->soft->collision_group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP);
if (object->soft->effector_weights) {
- BKE_LIB_FOREACHID_PROCESS(data, object->soft->effector_weights->group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
+ data, object->soft->effector_weights->group, IDWALK_CB_NOP);
}
}
}
@@ -1158,8 +1183,10 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src)
for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL;
pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) {
/* If pid's do not match, just tag info of caches in dst as dirty and continue. */
- if (pid_src == NULL || pid_dst->type != pid_src->type ||
- pid_dst->file_type != pid_src->file_type ||
+ if (pid_src == NULL) {
+ continue;
+ }
+ if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type ||
pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step ||
pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) {
LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) {
@@ -1190,6 +1217,40 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src)
BLI_freelistN(&pidlist_src);
}
+static IDProperty *object_asset_dimensions_property(Object *ob)
+{
+ float dimensions[3];
+ BKE_object_dimensions_get(ob, dimensions);
+ if (is_zero_v3(dimensions)) {
+ return NULL;
+ }
+
+ IDPropertyTemplate idprop = {0};
+ idprop.array.len = ARRAY_SIZE(dimensions);
+ idprop.array.type = IDP_FLOAT;
+
+ IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions");
+ memcpy(IDP_Array(property), dimensions, sizeof(dimensions));
+
+ return property;
+}
+
+static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data)
+{
+ Object *ob = asset_ptr;
+ BLI_assert(GS(ob->id.name) == ID_OB);
+
+ /* Update dimensions hint for the asset. */
+ IDProperty *dimensions_prop = object_asset_dimensions_property(ob);
+ if (dimensions_prop) {
+ BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop);
+ }
+}
+
+AssetTypeInfo AssetType_OB = {
+ .pre_save_fn = object_asset_pre_save,
+};
+
IDTypeInfo IDType_ID_OB = {
.id_code = ID_OB,
.id_filter = FILTER_ID_OB,
@@ -1216,6 +1277,8 @@ IDTypeInfo IDType_ID_OB = {
.blend_read_undo_preserve = NULL,
.lib_override_apply_post = object_lib_override_apply_post,
+
+ .asset_type_info = &AssetType_OB,
};
void BKE_object_workob_clear(Object *workob)
@@ -2815,7 +2878,7 @@ Object *BKE_object_duplicate(Main *bmain,
if (!is_subprocess) {
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */
- BKE_libblock_relink_to_newid(&obn->id);
+ BKE_libblock_relink_to_newid(bmain, &obn->id);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 7b2a1af7086..5b62761bd91 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -173,28 +173,29 @@ static void particle_settings_free_data(ID *id)
static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data)
{
ParticleSettings *psett = (ParticleSettings *)id;
- BKE_LIB_FOREACHID_PROCESS(data, psett->instance_collection, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, psett->instance_object, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, psett->bb_ob, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, psett->collision_group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_collection, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->bb_ob, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->collision_group, IDWALK_CB_NOP);
for (int i = 0; i < MAX_MTEX; i++) {
if (psett->mtex[i]) {
- BKE_texture_mtex_foreach_id(data, psett->mtex[i]);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
+ BKE_texture_mtex_foreach_id(data, psett->mtex[i]));
}
}
if (psett->effector_weights) {
- BKE_LIB_FOREACHID_PROCESS(data, psett->effector_weights->group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_NOP);
}
if (psett->pd) {
- BKE_LIB_FOREACHID_PROCESS(data, psett->pd->tex, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, psett->pd->f_source, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->tex, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->f_source, IDWALK_CB_NOP);
}
if (psett->pd2) {
- BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->tex, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->f_source, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->tex, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->f_source, IDWALK_CB_NOP);
}
if (psett->boids) {
@@ -202,18 +203,18 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data)
LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
if (rule->type == eBoidRuleType_Avoid) {
BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
- BKE_LIB_FOREACHID_PROCESS(data, gabr->ob, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gabr->ob, IDWALK_CB_NOP);
}
else if (rule->type == eBoidRuleType_FollowLeader) {
BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
- BKE_LIB_FOREACHID_PROCESS(data, flbr->ob, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, flbr->ob, IDWALK_CB_NOP);
}
}
}
}
LISTBASE_FOREACH (ParticleDupliWeight *, dw, &psett->instance_weights) {
- BKE_LIB_FOREACHID_PROCESS(data, dw->ob, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, dw->ob, IDWALK_CB_NOP);
}
}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index 1db14dc3dc8..15c5a809118 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -105,7 +105,7 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data)
{
PointCloud *pointcloud = (PointCloud *)id;
for (int i = 0; i < pointcloud->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, pointcloud->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pointcloud->mat[i], IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c
index 0b8e8d7c311..79a8b591f72 100644
--- a/source/blender/blenkernel/intern/preferences.c
+++ b/source/blender/blenkernel/intern/preferences.c
@@ -120,7 +120,8 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef)
return;
}
- bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL);
+ bUserAssetLibrary *library = BKE_preferences_asset_library_add(
+ userdef, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME, NULL);
/* Add new "Default" library under '[doc_path]/Blender/Assets'. */
BLI_path_join(
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 2cb0213a192..a827e1c32a2 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -471,7 +471,8 @@ static void scene_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSE
int cb_flag)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_lib_query_foreachid_process(data, id_pointer, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag));
}
/**
@@ -522,7 +523,10 @@ static void scene_foreach_toolsettings_id_pointer_process(
}
}
-#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS( \
+/* Special handling is needed here, as `scene_foreach_toolsettings` (and its dependency
+ * `scene_foreach_paint`) are also used by `scene_undo_preserve`, where `LibraryForeachIDData
+ * *data` is NULL. */
+#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER( \
__data, __id, __do_undo_restore, __action, __reader, __id_old, __cb_flag) \
{ \
if (__do_undo_restore) { \
@@ -530,7 +534,21 @@ static void scene_foreach_toolsettings_id_pointer_process(
(ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \
} \
else { \
- BKE_LIB_FOREACHID_PROCESS(__data, __id, __cb_flag); \
+ BLI_assert((__data) != NULL); \
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(__data, __id, __cb_flag); \
+ } \
+ } \
+ (void)0
+
+#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( \
+ __data, __do_undo_restore, __func_call) \
+ { \
+ if (__do_undo_restore) { \
+ __func_call; \
+ } \
+ else { \
+ BLI_assert((__data) != NULL); \
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(__data, __func_call); \
} \
} \
(void)0
@@ -541,13 +559,13 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
BlendLibReader *reader,
Paint *paint_old)
{
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- paint->brush,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- paint_old->brush,
- IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ paint->brush,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ paint_old->brush,
+ IDWALK_CB_USER);
for (int i = 0; i < paint_old->tool_slots_len; i++) {
/* This is a bit tricky.
* - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so
@@ -559,21 +577,21 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
*/
Brush *brush_tmp = NULL;
Brush **brush_p = i < paint->tool_slots_len ? &paint->tool_slots[i].brush : &brush_tmp;
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- *brush_p,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- paint_old->brush,
- IDWALK_CB_USER);
- }
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- paint->palette,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- paint_old->palette,
- IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ *brush_p,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ paint_old->brush,
+ IDWALK_CB_USER);
+ }
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ paint->palette,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ paint_old->palette,
+ IDWALK_CB_USER);
}
static void scene_foreach_toolsettings(LibraryForeachIDData *data,
@@ -582,110 +600,152 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
BlendLibReader *reader,
ToolSettings *toolsett_old)
{
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->particle.scene,
- do_undo_restore,
- SCENE_FOREACH_UNDO_NO_RESTORE,
- reader,
- toolsett_old->particle.scene,
- IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->particle.object,
- do_undo_restore,
- SCENE_FOREACH_UNDO_NO_RESTORE,
- reader,
- toolsett_old->particle.object,
- IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->particle.shape_object,
- do_undo_restore,
- SCENE_FOREACH_UNDO_NO_RESTORE,
- reader,
- toolsett_old->particle.shape_object,
- IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->particle.scene,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_NO_RESTORE,
+ reader,
+ toolsett_old->particle.scene,
+ IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->particle.object,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_NO_RESTORE,
+ reader,
+ toolsett_old->particle.object,
+ IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->particle.shape_object,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_NO_RESTORE,
+ reader,
+ toolsett_old->particle.shape_object,
+ IDWALK_CB_NOP);
scene_foreach_paint(
data, &toolsett->imapaint.paint, do_undo_restore, reader, &toolsett_old->imapaint.paint);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->imapaint.stencil,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- toolsett_old->imapaint.stencil,
- IDWALK_CB_USER);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->imapaint.clone,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- toolsett_old->imapaint.clone,
- IDWALK_CB_USER);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->imapaint.canvas,
- do_undo_restore,
- SCENE_FOREACH_UNDO_RESTORE,
- reader,
- toolsett_old->imapaint.canvas,
- IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->imapaint.stencil,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ toolsett_old->imapaint.stencil,
+ IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->imapaint.clone,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ toolsett_old->imapaint.clone,
+ IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->imapaint.canvas,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_RESTORE,
+ reader,
+ toolsett_old->imapaint.canvas,
+ IDWALK_CB_USER);
if (toolsett->vpaint) {
- scene_foreach_paint(
- data, &toolsett->vpaint->paint, do_undo_restore, reader, &toolsett_old->vpaint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->vpaint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->vpaint->paint));
}
if (toolsett->wpaint) {
- scene_foreach_paint(
- data, &toolsett->wpaint->paint, do_undo_restore, reader, &toolsett_old->wpaint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->wpaint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->wpaint->paint));
}
if (toolsett->sculpt) {
- scene_foreach_paint(
- data, &toolsett->sculpt->paint, do_undo_restore, reader, &toolsett_old->sculpt->paint);
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->sculpt->gravity_object,
- do_undo_restore,
- SCENE_FOREACH_UNDO_NO_RESTORE,
- reader,
- toolsett_old->sculpt->gravity_object,
- IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->sculpt->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->sculpt->paint));
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->sculpt->gravity_object,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_NO_RESTORE,
+ reader,
+ toolsett_old->sculpt->gravity_object,
+ IDWALK_CB_NOP);
}
if (toolsett->uvsculpt) {
- scene_foreach_paint(
- data, &toolsett->uvsculpt->paint, do_undo_restore, reader, &toolsett_old->uvsculpt->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->uvsculpt->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->uvsculpt->paint));
}
if (toolsett->gp_paint) {
- scene_foreach_paint(
- data, &toolsett->gp_paint->paint, do_undo_restore, reader, &toolsett_old->gp_paint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->gp_paint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->gp_paint->paint));
}
if (toolsett->gp_vertexpaint) {
- scene_foreach_paint(data,
- &toolsett->gp_vertexpaint->paint,
- do_undo_restore,
- reader,
- &toolsett_old->gp_vertexpaint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->gp_vertexpaint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->gp_vertexpaint->paint));
}
if (toolsett->gp_sculptpaint) {
- scene_foreach_paint(data,
- &toolsett->gp_sculptpaint->paint,
- do_undo_restore,
- reader,
- &toolsett_old->gp_sculptpaint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->gp_sculptpaint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->gp_sculptpaint->paint));
}
if (toolsett->gp_weightpaint) {
- scene_foreach_paint(data,
- &toolsett->gp_weightpaint->paint,
- do_undo_restore,
- reader,
- &toolsett_old->gp_weightpaint->paint);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
+ data,
+ do_undo_restore,
+ scene_foreach_paint(data,
+ &toolsett->gp_weightpaint->paint,
+ do_undo_restore,
+ reader,
+ &toolsett_old->gp_weightpaint->paint));
}
- BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data,
- toolsett->gp_sculpt.guide.reference_object,
- do_undo_restore,
- SCENE_FOREACH_UNDO_NO_RESTORE,
- reader,
- toolsett_old->gp_sculpt.guide.reference_object,
- IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data,
+ toolsett->gp_sculpt.guide.reference_object,
+ do_undo_restore,
+ SCENE_FOREACH_UNDO_NO_RESTORE,
+ reader,
+ toolsett_old->gp_sculpt.guide.reference_object,
+ IDWALK_CB_NOP);
}
+#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER
+#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL
+
static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb)
{
LISTBASE_FOREACH (LayerCollection *, lc, lb) {
@@ -695,7 +755,7 @@ static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase
(lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED :
IDWALK_CB_NOP;
- BKE_LIB_FOREACHID_PROCESS(data, lc->collection, cb_flag);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lc->collection, cb_flag);
scene_foreach_layer_collection(data, &lc->layer_collections);
}
}
@@ -704,32 +764,33 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data)
{
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
-#define FOREACHID_PROCESS(_data, _id_super, _cb_flag) \
+#define FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \
{ \
CHECK_TYPE(&((_id_super)->id), ID *); \
- if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \
+ BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \
+ if (!BKE_lib_query_foreachid_iter_stop((_data))) { \
return false; \
} \
} \
((void)0)
- FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF);
- FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP);
- FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER);
- FOREACHID_PROCESS(data, seq->mask, IDWALK_CB_USER);
- FOREACHID_PROCESS(data, seq->sound, IDWALK_CB_USER);
+ FOREACHID_PROCESS_IDSUPER(data, seq->scene, IDWALK_CB_NEVER_SELF);
+ FOREACHID_PROCESS_IDSUPER(data, seq->scene_camera, IDWALK_CB_NOP);
+ FOREACHID_PROCESS_IDSUPER(data, seq->clip, IDWALK_CB_USER);
+ FOREACHID_PROCESS_IDSUPER(data, seq->mask, IDWALK_CB_USER);
+ FOREACHID_PROCESS_IDSUPER(data, seq->sound, IDWALK_CB_USER);
IDP_foreach_property(
seq->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) {
- FOREACHID_PROCESS(data, smd->mask_id, IDWALK_CB_USER);
+ FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER);
}
if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) {
TextVars *text_data = seq->effectdata;
- FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER);
+ FOREACHID_PROCESS_IDSUPER(data, text_data->text_font, IDWALK_CB_USER);
}
-#undef FOREACHID_PROCESS
+#undef FOREACHID_PROCESS_IDSUPER
return true;
}
@@ -738,66 +799,77 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
{
Scene *scene = (Scene *)id;
- BKE_LIB_FOREACHID_PROCESS(data, scene->camera, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, scene->world, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, scene->set, IDWALK_CB_NEVER_SELF);
- BKE_LIB_FOREACHID_PROCESS(data, scene->clip, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, scene->gpd, IDWALK_CB_USER);
- BKE_LIB_FOREACHID_PROCESS(data, scene->r.bake.cage_object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->camera, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->world, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->set, IDWALK_CB_NEVER_SELF);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->clip, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->r.bake.cage_object, IDWALK_CB_NOP);
if (scene->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree));
}
if (scene->ed) {
- SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data));
}
/* This pointer can be NULL during old files reading, better be safe than sorry. */
if (scene->master_collection != NULL) {
- BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection));
}
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER);
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- BKE_LIB_FOREACHID_PROCESS(
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
}
- scene_foreach_layer_collection(data, &view_layer->layer_collections);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, scene_foreach_layer_collection(data, &view_layer->layer_collections));
LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) {
if (fmc->script) {
- BKE_LIB_FOREACHID_PROCESS(data, fmc->script, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fmc->script, IDWALK_CB_NOP);
}
}
LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) {
if (fls->group) {
- BKE_LIB_FOREACHID_PROCESS(data, fls->group, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->group, IDWALK_CB_USER);
}
if (fls->linestyle) {
- BKE_LIB_FOREACHID_PROCESS(data, fls->linestyle, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->linestyle, IDWALK_CB_USER);
}
}
}
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
- BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP);
- IDP_foreach_property(
- marker->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ IDP_foreach_property(marker->prop,
+ IDP_TYPE_FILTER_ID,
+ BKE_lib_query_idpropertiesForeachIDLink_callback,
+ data));
}
ToolSettings *toolsett = scene->toolsettings;
if (toolsett) {
- scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett));
}
if (scene->rigidbody_world) {
- BKE_rigidbody_world_id_loop(
- scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data,
+ BKE_rigidbody_world_id_loop(
+ scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data));
}
}
@@ -1853,7 +1925,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
if (!is_subprocess) {
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */
- BKE_libblock_relink_to_newid(&sce_copy->id);
+ BKE_libblock_relink_to_newid(bmain, &sce_copy->id);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 0474c2b81cb..69e926caeae 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -93,13 +93,17 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet *
{
if (ads != NULL) {
BKE_LIB_FOREACHID_PROCESS_ID(data, ads->source, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, ads->filter_grp, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ads->filter_grp, IDWALK_CB_NOP);
}
}
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area)
{
- BKE_LIB_FOREACHID_PROCESS(data, area->full, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP);
/* TODO: this should be moved to a callback in `SpaceType`, defined in each editor's own code.
* Will be for a later round of cleanup though... */
@@ -107,24 +111,21 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
switch (sl->spacetype) {
case SPACE_VIEW3D: {
View3D *v3d = (View3D *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, v3d->camera, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, v3d->ob_center, IDWALK_CB_NOP);
-
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP);
if (v3d->localvd) {
- BKE_LIB_FOREACHID_PROCESS(data, v3d->localvd->camera, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP);
}
break;
}
case SPACE_GRAPH: {
SpaceGraph *sipo = (SpaceGraph *)sl;
-
- screen_foreach_id_dopesheet(data, sipo->ads);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
+ screen_foreach_id_dopesheet(data, sipo->ads));
break;
}
case SPACE_PROPERTIES: {
SpaceProperties *sbuts = (SpaceProperties *)sl;
-
BKE_LIB_FOREACHID_PROCESS_ID(data, sbuts->pinid, IDWALK_CB_NOP);
break;
}
@@ -132,48 +133,41 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
break;
case SPACE_ACTION: {
SpaceAction *saction = (SpaceAction *)sl;
-
screen_foreach_id_dopesheet(data, &saction->ads);
- BKE_LIB_FOREACHID_PROCESS(data, saction->action, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP);
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = (SpaceImage *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, sima->image, IDWALK_CB_USER_ONE);
- BKE_LIB_FOREACHID_PROCESS(data, sima->mask_info.mask, IDWALK_CB_USER_ONE);
- BKE_LIB_FOREACHID_PROCESS(data, sima->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER);
break;
}
case SPACE_SEQ: {
SpaceSeq *sseq = (SpaceSeq *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, sseq->gpd, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER);
break;
}
case SPACE_NLA: {
SpaceNla *snla = (SpaceNla *)sl;
-
- screen_foreach_id_dopesheet(data, snla->ads);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
+ screen_foreach_id_dopesheet(data, snla->ads));
break;
}
case SPACE_TEXT: {
SpaceText *st = (SpaceText *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, st->text, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP);
break;
}
case SPACE_SCRIPT: {
SpaceScript *scpt = (SpaceScript *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, scpt->script, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP);
break;
}
case SPACE_OUTLINER: {
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
-
BKE_LIB_FOREACHID_PROCESS_ID(data, space_outliner->search_tse.id, IDWALK_CB_NOP);
-
if (space_outliner->treestore != NULL) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
@@ -187,26 +181,24 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
}
case SPACE_NODE: {
SpaceNode *snode = (SpaceNode *)sl;
-
const bool is_private_nodetree = snode->id != NULL &&
ntreeFromID(snode->id) == snode->nodetree;
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP);
-
- BKE_LIB_FOREACHID_PROCESS(
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE);
LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
- BKE_LIB_FOREACHID_PROCESS(data,
- path->nodetree,
- is_private_nodetree ? IDWALK_CB_EMBEDDED :
- IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data,
+ path->nodetree,
+ is_private_nodetree ? IDWALK_CB_EMBEDDED :
+ IDWALK_CB_USER_ONE);
}
else {
- BKE_LIB_FOREACHID_PROCESS(data, path->nodetree, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE);
}
if (path->nodetree == NULL) {
@@ -214,22 +206,20 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
}
}
- BKE_LIB_FOREACHID_PROCESS(data, snode->edittree, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
break;
}
case SPACE_CLIP: {
SpaceClip *sclip = (SpaceClip *)sl;
-
- BKE_LIB_FOREACHID_PROCESS(data, sclip->clip, IDWALK_CB_USER_ONE);
- BKE_LIB_FOREACHID_PROCESS(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE);
break;
}
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
-
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
- BKE_LIB_FOREACHID_PROCESS(
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP);
}
}
@@ -243,12 +233,13 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
static void screen_foreach_id(ID *id, LibraryForeachIDData *data)
{
- if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) {
- bScreen *screen = (bScreen *)id;
+ if ((BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) == 0) {
+ return;
+ }
+ bScreen *screen = (bScreen *)id;
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- BKE_screen_foreach_id_screen_area(data, area);
- }
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_screen_foreach_id_screen_area(data, area));
}
}
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 1d297b3ced9..98e7405bde6 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -103,7 +103,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
Simulation *simulation = (Simulation *)id;
if (simulation->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree));
}
}
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index b361f31cc30..230ff9d6da0 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -50,7 +50,7 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data)
{
Speaker *speaker = (Speaker *)id;
- BKE_LIB_FOREACHID_PROCESS(data, speaker->sound, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, speaker->sound, IDWALK_CB_USER);
}
static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address)
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index 663c1951ba3..bbe4e0aab7b 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -486,6 +486,12 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
prev_length = length;
}
+ /* Zero lengths or float innacuracies can cause invalid values, or simply
+ * skip some, so set the values that weren't completed in the main loop. */
+ for (const int i : IndexRange(i_sample, samples_size - i_sample)) {
+ samples[i] = float(samples_size);
+ }
+
if (!is_cyclic_) {
/* In rare cases this can prevent overflow of the stored index. */
samples.last() = lengths.size();
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index d5f7647f07a..0811e6cb675 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -142,9 +142,10 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data)
Tex *texture = (Tex *)id;
if (texture->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree));
}
- BKE_LIB_FOREACHID_PROCESS(data, texture->ima, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER);
}
static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address)
@@ -233,8 +234,8 @@ IDTypeInfo IDType_ID_TE = {
/* Utils for all IDs using those texture slots. */
void BKE_texture_mtex_foreach_id(LibraryForeachIDData *data, MTex *mtex)
{
- BKE_LIB_FOREACHID_PROCESS(data, mtex->object, IDWALK_CB_NOP);
- BKE_LIB_FOREACHID_PROCESS(data, mtex->tex, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->tex, IDWALK_CB_USER);
}
/* ****************** Mapping ******************* */
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 0b9ef5c537d..a72b5268e1d 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -36,6 +36,7 @@
#include "BLI_math.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BLI_string_ref.hh"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
@@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"};
using blender::float3;
using blender::float4x4;
using blender::IndexRange;
+using blender::StringRef;
#ifdef WITH_OPENVDB
# include <atomic>
@@ -556,7 +558,7 @@ static void volume_foreach_id(ID *id, LibraryForeachIDData *data)
{
Volume *volume = (Volume *)id;
for (int i = 0; i < volume->totcol; i++) {
- BKE_LIB_FOREACHID_PROCESS(data, volume->mat[i], IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, volume->mat[i], IDWALK_CB_USER);
}
}
@@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType
#endif
}
+#ifdef WITH_OPENVDB
+VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume,
+ const StringRef name,
+ openvdb::GridBase::Ptr vdb_grid)
+{
+ VolumeGridVector &grids = *volume.runtime.grids;
+ BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr);
+ BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN);
+
+ vdb_grid->setName(name);
+ grids.emplace_back(vdb_grid);
+ return &grids.back();
+}
+#endif
+
void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc
index e9d6eea4614..6e465b2fdf0 100644
--- a/source/blender/blenkernel/intern/volume_to_mesh.cc
+++ b/source/blender/blenkernel/intern/volume_to_mesh.cc
@@ -121,46 +121,66 @@ struct VolumeToMeshOp {
}
};
-static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts,
- Span<openvdb::Vec3I> tris,
- Span<openvdb::Vec4I> quads)
+/**
+ * Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure.
+ * This can be used to add mesh data from a grid into an existing mesh rather than merging multiple
+ * meshes later on.
+ */
+void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
+ const Span<openvdb::Vec3I> vdb_tris,
+ const Span<openvdb::Vec4I> vdb_quads,
+ const int vert_offset,
+ const int poly_offset,
+ const int loop_offset,
+ MutableSpan<MVert> verts,
+ MutableSpan<MPoly> polys,
+ MutableSpan<MLoop> loops)
{
- const int tot_loops = 3 * tris.size() + 4 * quads.size();
- const int tot_polys = tris.size() + quads.size();
-
- Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys);
-
/* Write vertices. */
- for (const int i : verts.index_range()) {
- const blender::float3 co = blender::float3(verts[i].asV());
- copy_v3_v3(mesh->mvert[i].co, co);
+ for (const int i : vdb_verts.index_range()) {
+ const blender::float3 co = blender::float3(vdb_verts[i].asV());
+ copy_v3_v3(verts[vert_offset + i].co, co);
}
/* Write triangles. */
- for (const int i : tris.index_range()) {
- mesh->mpoly[i].loopstart = 3 * i;
- mesh->mpoly[i].totloop = 3;
+ for (const int i : vdb_tris.index_range()) {
+ polys[poly_offset + i].loopstart = loop_offset + 3 * i;
+ polys[poly_offset + i].totloop = 3;
for (int j = 0; j < 3; j++) {
/* Reverse vertex order to get correct normals. */
- mesh->mloop[3 * i + j].v = tris[i][2 - j];
+ loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j];
}
}
/* Write quads. */
- const int poly_offset = tris.size();
- const int loop_offset = tris.size() * 3;
- for (const int i : quads.index_range()) {
- mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i;
- mesh->mpoly[poly_offset + i].totloop = 4;
+ const int quad_offset = poly_offset + vdb_tris.size();
+ const int quad_loop_offset = loop_offset + vdb_tris.size() * 3;
+ for (const int i : vdb_quads.index_range()) {
+ polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i;
+ polys[quad_offset + i].totloop = 4;
for (int j = 0; j < 4; j++) {
/* Reverse vertex order to get correct normals. */
- mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j];
+ loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j];
}
}
+}
- BKE_mesh_calc_edges(mesh, false, false);
- BKE_mesh_normals_tag_dirty(mesh);
- return mesh;
+/**
+ * Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and
+ * triangle indices.
+ */
+bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
+ const VolumeToMeshResolution &resolution,
+ const float threshold,
+ const float adaptivity)
+{
+ const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
+
+ VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
+ if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
+ return {};
+ }
+ return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)};
}
Mesh *volume_to_mesh(const openvdb::GridBase &grid,
@@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid,
const float threshold,
const float adaptivity)
{
- const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
+ const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data(
+ grid, resolution, threshold, adaptivity);
+
+ const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size();
+ const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size();
+ Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys);
+
+ fill_mesh_from_openvdb_data(mesh_data.verts,
+ mesh_data.tris,
+ mesh_data.quads,
+ 0,
+ 0,
+ 0,
+ {mesh->mvert, mesh->totvert},
+ {mesh->mpoly, mesh->totpoly},
+ {mesh->mloop, mesh->totloop});
- VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
- if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
- return nullptr;
- }
+ BKE_mesh_calc_edges(mesh, false, false);
+ BKE_mesh_normals_tag_dirty(mesh);
- return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads);
+ return mesh;
}
#endif /* WITH_OPENVDB */
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index 3c168a6c7b2..6269cfc4349 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -82,7 +82,7 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data)
WorkSpace *workspace = (WorkSpace *)id;
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
- BKE_LIB_FOREACHID_PROCESS(data, layout->screen, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER);
}
}
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index fe03c5b817a..2f0a282a298 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -131,7 +131,8 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data)
if (world->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
+ data, BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree));
}
}
diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh
new file mode 100644
index 00000000000..7b8aa03b807
--- /dev/null
+++ b/source/blender/blenlib/BLI_serialize.hh
@@ -0,0 +1,329 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * An abstraction layer for serialization formats.
+ *
+ * Allowing to read/write data to a serialization format like JSON.
+ *
+ *
+ *
+ * # Supported data types
+ *
+ * The abstraction layer has a limited set of data types it supports.
+ * There are specific classes that builds up the data structure that
+ * can be (de)serialized.
+ *
+ * - StringValue: for strings
+ * - IntValue: for integer values
+ * - DoubleValue: for double precision floating point numbers
+ * - BooleanValue: for boolean values
+ * - ArrayValue: An array of any supported value.
+ * - ObjectValue: A key value pair where keys are std::string.
+ * - NullValue: for null values.
+ *
+ * # Basic usage
+ *
+ * ## Serializing
+ *
+ * - Construct a structure that needs to be serialized using the `*Value` classes.
+ * - Construct the formatter you want to use
+ * - Invoke the formatter.serialize method passing an output stream and the value.
+ *
+ * The next example would format an integer value (42) as JSON the result will
+ * be stored inside `out`.
+ *
+ * \code{.cc}
+ * JsonFormatter json;
+ * std::stringstream out;
+ * IntValue test_value(42);
+ * json.serialize(out, test_value);
+ * \endcode
+ *
+ * ## Deserializing
+ *
+ * \code{.cc}
+ * std::stringstream is("42");
+ * JsonFormatter json;
+ * std::unique_ptr<Value> value = json.deserialize(is);
+ * \endcode
+ *
+ * # Adding a new formatter
+ *
+ * To add a new formatter a new sub-class of `Formatter` must be created and the
+ * `serialize`/`deserialize` methods should be implemented.
+ *
+ */
+
+#include <ostream>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+namespace blender::io::serialize {
+
+/**
+ * Enumeration containing all sub-classes of Value. It is used as for type checking.
+ *
+ * \see #Value::type()
+ */
+enum class eValueType {
+ String,
+ Int,
+ Array,
+ Null,
+ Boolean,
+ Double,
+ Object,
+};
+
+class Value;
+class StringValue;
+class ObjectValue;
+template<typename T, eValueType V> class PrimitiveValue;
+using IntValue = PrimitiveValue<int64_t, eValueType::Int>;
+using DoubleValue = PrimitiveValue<double, eValueType::Double>;
+using BooleanValue = PrimitiveValue<bool, eValueType::Boolean>;
+
+template<typename Container, typename ContainerItem, eValueType V> class ContainerValue;
+/* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can
+ * be created by calling `create_lookup`. */
+using ArrayValue =
+ ContainerValue<Vector<std::shared_ptr<Value>>, std::shared_ptr<Value>, eValueType::Array>;
+
+/**
+ * Class containing a (de)serializable value.
+ *
+ * To serialize from or to a specific format the Value will be used as an intermediate container
+ * holding the values. Value class is abstract. There are concrete classes to for different data
+ * types.
+ *
+ * - `StringValue`: contains a string.
+ * - `IntValue`: contains an integer.
+ * - `ArrayValue`: contains an array of elements. Elements don't need to be the same type.
+ * - `NullValue`: represents nothing (null pointer or optional).
+ * - `BooleanValue`: contains a boolean (true/false).
+ * - `DoubleValue`: contains a double precision floating point number.
+ * - `ObjectValue`: represents an object (key value pairs where keys are strings and values can be
+ * of different types.
+ *
+ */
+class Value {
+ private:
+ eValueType type_;
+
+ protected:
+ Value() = delete;
+ explicit Value(eValueType type) : type_(type)
+ {
+ }
+
+ public:
+ virtual ~Value() = default;
+ const eValueType type() const
+ {
+ return type_;
+ }
+
+ /**
+ * Casts to a StringValue.
+ * Will return nullptr when it is a different type.
+ */
+ const StringValue *as_string_value() const;
+
+ /**
+ * Casts to an IntValue.
+ * Will return nullptr when it is a different type.
+ */
+ const IntValue *as_int_value() const;
+
+ /**
+ * Casts to a DoubleValue.
+ * Will return nullptr when it is a different type.
+ */
+ const DoubleValue *as_double_value() const;
+
+ /**
+ * Casts to a BooleanValue.
+ * Will return nullptr when it is a different type.
+ */
+ const BooleanValue *as_boolean_value() const;
+
+ /**
+ * Casts to an ArrayValue.
+ * Will return nullptr when it is a different type.
+ */
+ const ArrayValue *as_array_value() const;
+
+ /**
+ * Casts to an ObjectValue.
+ * Will return nullptr when it is a different type.
+ */
+ const ObjectValue *as_object_value() const;
+};
+
+/**
+ * For generating value types that represent types that are typically known processor data types.
+ */
+template<
+ /** Wrapped c/cpp data type that is used to store the value. */
+ typename T,
+ /** Value type of the class. */
+ eValueType V>
+class PrimitiveValue : public Value {
+ private:
+ T inner_value_{};
+
+ public:
+ explicit PrimitiveValue(const T value) : Value(V), inner_value_(value)
+ {
+ }
+
+ const T value() const
+ {
+ return inner_value_;
+ }
+};
+
+class NullValue : public Value {
+ public:
+ NullValue() : Value(eValueType::Null)
+ {
+ }
+};
+
+class StringValue : public Value {
+ private:
+ std::string string_;
+
+ public:
+ StringValue(const StringRef string) : Value(eValueType::String), string_(string)
+ {
+ }
+
+ const std::string &value() const
+ {
+ return string_;
+ }
+};
+
+/**
+ * Template for arrays and objects.
+ *
+ * Both ArrayValue and ObjectValue store their values in an array.
+ */
+template<
+ /** The container type where the elements are stored in. */
+ typename Container,
+
+ /** Type of the data inside the container. */
+ typename ContainerItem,
+
+ /** ValueType representing the value (object/array). */
+ eValueType V>
+class ContainerValue : public Value {
+ public:
+ using Items = Container;
+ using Item = ContainerItem;
+
+ private:
+ Container inner_value_;
+
+ public:
+ ContainerValue() : Value(V)
+ {
+ }
+
+ const Container &elements() const
+ {
+ return inner_value_;
+ }
+
+ Container &elements()
+ {
+ return inner_value_;
+ }
+};
+
+/**
+ * Internal storage type for ObjectValue.
+ *
+ * The elements are stored as an key value pair. The value is a shared pointer so it can be shared
+ * when using `ObjectValue::create_lookup`.
+ */
+using ObjectElementType = std::pair<std::string, std::shared_ptr<Value>>;
+
+/**
+ * Object is a key-value container where the key must be a std::string.
+ * Internally it is stored in a blender::Vector to ensure the order of keys.
+ */
+class ObjectValue
+ : public ContainerValue<Vector<ObjectElementType>, ObjectElementType, eValueType::Object> {
+ public:
+ using LookupValue = std::shared_ptr<Value>;
+ using Lookup = Map<std::string, LookupValue>;
+
+ /**
+ * Return a lookup map to quickly lookup by key.
+ *
+ * The lookup is owned by the caller.
+ */
+ const Lookup create_lookup() const
+ {
+ Lookup result;
+ for (const Item &item : elements()) {
+ result.add_as(item.first, item.second);
+ }
+ return result;
+ }
+};
+
+/**
+ * Interface for any provided Formatter.
+ */
+class Formatter {
+ public:
+ virtual ~Formatter() = default;
+
+ /** Serialize the value to the given stream. */
+ virtual void serialize(std::ostream &os, const Value &value) = 0;
+
+ /** Deserialize the stream. */
+ virtual std::unique_ptr<Value> deserialize(std::istream &is) = 0;
+};
+
+/**
+ * Formatter to (de)serialize a JSON formatted stream.
+ */
+class JsonFormatter : public Formatter {
+ public:
+ /**
+ * The indentation level to use.
+ * Typically number of chars. Set to 0 to not use indentation.
+ */
+ int8_t indentation_len = 0;
+
+ public:
+ void serialize(std::ostream &os, const Value &value) override;
+ std::unique_ptr<Value> deserialize(std::istream &is) override;
+};
+
+} // namespace blender::io::serialize
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index c01052f0111..7db984aef5c 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -27,6 +27,7 @@ set(INC
../../../intern/guardedalloc
../../../intern/numaapi/include
../../../extern/wcwidth
+ ../../../extern/json/include
)
set(INC_SYS
@@ -126,6 +127,7 @@ set(SRC
intern/scanfill.c
intern/scanfill_utils.c
intern/session_uuid.c
+ intern/serialize.cc
intern/smallhash.c
intern/sort.c
intern/sort_utils.c
@@ -282,6 +284,7 @@ set(SRC
BLI_session_uuid.h
BLI_set.hh
BLI_set_slots.hh
+ BLI_serialize.hh
BLI_simd.h
BLI_smallhash.h
BLI_sort.h
@@ -364,6 +367,10 @@ if(WITH_GMP)
endif()
if(WIN32)
+ if (WITH_BLENDER_THUMBNAILER)
+ # Needed for querying the thumbnailer .dll in winstuff.c
+ add_definitions(-DWITH_BLENDER_THUMBNAILER)
+ endif()
list(APPEND INC
../../../intern/utfconv
)
@@ -448,6 +455,7 @@ if(WITH_GTESTS)
tests/BLI_span_test.cc
tests/BLI_stack_cxx_test.cc
tests/BLI_stack_test.cc
+ tests/BLI_serialize_test.cc
tests/BLI_string_ref_test.cc
tests/BLI_string_search_test.cc
tests/BLI_string_test.cc
diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc
index 5fa2746d07f..4259237af6e 100644
--- a/source/blender/blenlib/intern/noise.cc
+++ b/source/blender/blenlib/intern/noise.cc
@@ -1528,9 +1528,15 @@ void voronoi_f1(
targetPosition = pointPosition;
}
}
- *r_distance = minDistance;
- *r_color = hash_float_to_float3(cellPosition + targetOffset);
- *r_w = targetPosition + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = minDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ }
+ if (r_w != nullptr) {
+ *r_w = targetPosition + cellPosition;
+ }
}
void voronoi_smooth_f1(const float w,
@@ -1555,14 +1561,26 @@ void voronoi_smooth_f1(const float w,
0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
float correctionFactor = smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
- correctionFactor /= 1.0f + 3.0f * smoothness;
- const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
- smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
- smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+ if (r_color != nullptr || r_w != nullptr) {
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ if (r_color != nullptr) {
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ }
+ if (r_w != nullptr) {
+ smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+ }
+ }
+ }
+ if (r_distance != nullptr) {
+ *r_distance = smoothDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = smoothColor;
+ }
+ if (r_w != nullptr) {
+ *r_w = cellPosition + smoothPosition;
}
- *r_distance = smoothDistance;
- *r_color = smoothColor;
- *r_w = cellPosition + smoothPosition;
}
void voronoi_f2(
@@ -1596,9 +1614,15 @@ void voronoi_f2(
positionF2 = pointPosition;
}
}
- *r_distance = distanceF2;
- *r_color = hash_float_to_float3(cellPosition + offsetF2);
- *r_w = positionF2 + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = distanceF2;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ }
+ if (r_w != nullptr) {
+ *r_w = positionF2 + cellPosition;
+ }
}
void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance)
@@ -1706,9 +1730,15 @@ void voronoi_f1(const float2 coord,
}
}
}
- *r_distance = minDistance;
- *r_color = hash_float_to_float3(cellPosition + targetOffset);
- *r_position = targetPosition + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = minDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ }
+ if (r_position != nullptr) {
+ *r_position = targetPosition + cellPosition;
+ }
}
void voronoi_smooth_f1(const float2 coord,
@@ -1737,15 +1767,28 @@ void voronoi_smooth_f1(const float2 coord,
0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
float correctionFactor = smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
- correctionFactor /= 1.0f + 3.0f * smoothness;
- const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
- smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
- smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor;
+ if (r_color != nullptr || r_position != nullptr) {
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ if (r_color != nullptr) {
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ }
+ if (r_position != nullptr) {
+ smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) -
+ correctionFactor;
+ }
+ }
}
}
- *r_distance = smoothDistance;
- *r_color = smoothColor;
- *r_position = cellPosition + smoothPosition;
+ if (r_distance != nullptr) {
+ *r_distance = smoothDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = smoothColor;
+ }
+ if (r_position != nullptr) {
+ *r_position = cellPosition + smoothPosition;
+ }
}
void voronoi_f2(const float2 coord,
@@ -1787,9 +1830,15 @@ void voronoi_f2(const float2 coord,
}
}
}
- *r_distance = distanceF2;
- *r_color = hash_float_to_float3(cellPosition + offsetF2);
- *r_position = positionF2 + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = distanceF2;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ }
+ if (r_position != nullptr) {
+ *r_position = positionF2 + cellPosition;
+ }
}
void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance)
@@ -1928,9 +1977,15 @@ void voronoi_f1(const float3 coord,
}
}
}
- *r_distance = minDistance;
- *r_color = hash_float_to_float3(cellPosition + targetOffset);
- *r_position = targetPosition + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = minDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ }
+ if (r_position != nullptr) {
+ *r_position = targetPosition + cellPosition;
+ }
}
void voronoi_smooth_f1(const float3 coord,
@@ -1960,16 +2015,29 @@ void voronoi_smooth_f1(const float3 coord,
0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
float correctionFactor = smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
- correctionFactor /= 1.0f + 3.0f * smoothness;
- const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
- smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
- smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor;
+ if (r_color != nullptr || r_position != nullptr) {
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ if (r_color != nullptr) {
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ }
+ if (r_position != nullptr) {
+ smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) -
+ correctionFactor;
+ }
+ }
}
}
}
- *r_distance = smoothDistance;
- *r_color = smoothColor;
- *r_position = cellPosition + smoothPosition;
+ if (r_distance != nullptr) {
+ *r_distance = smoothDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = smoothColor;
+ }
+ if (r_position != nullptr) {
+ *r_position = cellPosition + smoothPosition;
+ }
}
void voronoi_f2(const float3 coord,
@@ -2013,9 +2081,15 @@ void voronoi_f2(const float3 coord,
}
}
}
- *r_distance = distanceF2;
- *r_color = hash_float_to_float3(cellPosition + offsetF2);
- *r_position = positionF2 + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = distanceF2;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ }
+ if (r_position != nullptr) {
+ *r_position = positionF2 + cellPosition;
+ }
}
void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance)
@@ -2166,9 +2240,15 @@ void voronoi_f1(const float4 coord,
}
}
}
- *r_distance = minDistance;
- *r_color = hash_float_to_float3(cellPosition + targetOffset);
- *r_position = targetPosition + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = minDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ }
+ if (r_position != nullptr) {
+ *r_position = targetPosition + cellPosition;
+ }
}
void voronoi_smooth_f1(const float4 coord,
@@ -2200,18 +2280,30 @@ void voronoi_smooth_f1(const float4 coord,
0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
float correctionFactor = smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
- correctionFactor /= 1.0f + 3.0f * smoothness;
- const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
- smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
- smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) -
- correctionFactor;
+ if (r_color != nullptr || r_position != nullptr) {
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ if (r_color != nullptr) {
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ }
+ if (r_position != nullptr) {
+ smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) -
+ correctionFactor;
+ }
+ }
}
}
}
}
- *r_distance = smoothDistance;
- *r_color = smoothColor;
- *r_position = cellPosition + smoothPosition;
+ if (r_distance != nullptr) {
+ *r_distance = smoothDistance;
+ }
+ if (r_color != nullptr) {
+ *r_color = smoothColor;
+ }
+ if (r_position != nullptr) {
+ *r_position = cellPosition + smoothPosition;
+ }
}
void voronoi_f2(const float4 coord,
@@ -2258,9 +2350,15 @@ void voronoi_f2(const float4 coord,
}
}
}
- *r_distance = distanceF2;
- *r_color = hash_float_to_float3(cellPosition + offsetF2);
- *r_position = positionF2 + cellPosition;
+ if (r_distance != nullptr) {
+ *r_distance = distanceF2;
+ }
+ if (r_color != nullptr) {
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ }
+ if (r_position != nullptr) {
+ *r_position = positionF2 + cellPosition;
+ }
}
void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance)
diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc
new file mode 100644
index 00000000000..52aff140e3e
--- /dev/null
+++ b/source/blender/blenlib/intern/serialize.cc
@@ -0,0 +1,216 @@
+#include "BLI_serialize.hh"
+
+#include "json.hpp"
+
+namespace blender::io::serialize {
+
+const StringValue *Value::as_string_value() const
+{
+ if (type_ != eValueType::String) {
+ return nullptr;
+ }
+ return static_cast<const StringValue *>(this);
+}
+
+const IntValue *Value::as_int_value() const
+{
+ if (type_ != eValueType::Int) {
+ return nullptr;
+ }
+ return static_cast<const IntValue *>(this);
+}
+
+const DoubleValue *Value::as_double_value() const
+{
+ if (type_ != eValueType::Double) {
+ return nullptr;
+ }
+ return static_cast<const DoubleValue *>(this);
+}
+
+const BooleanValue *Value::as_boolean_value() const
+{
+ if (type_ != eValueType::Boolean) {
+ return nullptr;
+ }
+ return static_cast<const BooleanValue *>(this);
+}
+
+const ArrayValue *Value::as_array_value() const
+{
+ if (type_ != eValueType::Array) {
+ return nullptr;
+ }
+ return static_cast<const ArrayValue *>(this);
+}
+
+const ObjectValue *Value::as_object_value() const
+{
+ if (type_ != eValueType::Object) {
+ return nullptr;
+ }
+ return static_cast<const ObjectValue *>(this);
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const Value &value);
+static void convert_to_json(nlohmann::ordered_json &j, const ArrayValue &value)
+{
+ const ArrayValue::Items &items = value.elements();
+ /* Create a json array to store the elements. If this isn't done and items is empty it would
+ * return use a null value, in stead of an empty array. */
+ j = "[]"_json;
+ for (const ArrayValue::Item &item_value : items) {
+ nlohmann::ordered_json json_item;
+ convert_to_json(json_item, *item_value);
+ j.push_back(json_item);
+ }
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const ObjectValue &value)
+{
+ const ObjectValue::Items &attributes = value.elements();
+ /* Create a json object to store the attributes. If this isn't done and attributes is empty it
+ * would return use a null value, in stead of an empty object. */
+ j = "{}"_json;
+ for (const ObjectValue::Item &attribute : attributes) {
+ nlohmann::ordered_json json_item;
+ convert_to_json(json_item, *attribute.second);
+ j[attribute.first] = json_item;
+ }
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const Value &value)
+{
+ switch (value.type()) {
+ case eValueType::String: {
+ j = value.as_string_value()->value();
+ break;
+ }
+
+ case eValueType::Int: {
+ j = value.as_int_value()->value();
+ break;
+ }
+
+ case eValueType::Array: {
+ const ArrayValue &array = *value.as_array_value();
+ convert_to_json(j, array);
+ break;
+ }
+
+ case eValueType::Object: {
+ const ObjectValue &object = *value.as_object_value();
+ convert_to_json(j, object);
+ break;
+ }
+
+ case eValueType::Null: {
+ j = nullptr;
+ break;
+ }
+
+ case eValueType::Boolean: {
+ j = value.as_boolean_value()->value();
+ break;
+ }
+
+ case eValueType::Double: {
+ j = value.as_double_value()->value();
+ }
+ }
+}
+
+static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j);
+static std::unique_ptr<ArrayValue> convert_from_json_to_array(const nlohmann::ordered_json &j)
+{
+ std::unique_ptr<ArrayValue> array = std::make_unique<ArrayValue>();
+ ArrayValue::Items &elements = array->elements();
+ for (auto element : j.items()) {
+ nlohmann::ordered_json element_json = element.value();
+ std::unique_ptr<Value> value = convert_from_json(element_json);
+ elements.append_as(value.release());
+ }
+ return array;
+}
+
+static std::unique_ptr<ObjectValue> convert_from_json_to_object(const nlohmann::ordered_json &j)
+{
+ std::unique_ptr<ObjectValue> object = std::make_unique<ObjectValue>();
+ ObjectValue::Items &elements = object->elements();
+ for (auto element : j.items()) {
+ std::string key = element.key();
+ nlohmann::ordered_json element_json = element.value();
+ std::unique_ptr<Value> value = convert_from_json(element_json);
+ elements.append_as(std::pair(key, value.release()));
+ }
+ return object;
+}
+
+static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j)
+{
+ switch (j.type()) {
+ case nlohmann::json::value_t::array: {
+ return convert_from_json_to_array(j);
+ }
+
+ case nlohmann::json::value_t::object: {
+ return convert_from_json_to_object(j);
+ }
+
+ case nlohmann::json::value_t::string: {
+ std::string value = j;
+ return std::make_unique<StringValue>(value);
+ }
+
+ case nlohmann::json::value_t::null: {
+ return std::make_unique<NullValue>();
+ }
+
+ case nlohmann::json::value_t::boolean: {
+ return std::make_unique<BooleanValue>(j);
+ }
+ case nlohmann::json::value_t::number_integer:
+ case nlohmann::json::value_t::number_unsigned: {
+ return std::make_unique<IntValue>(j);
+ }
+
+ case nlohmann::json::value_t::number_float: {
+ return std::make_unique<DoubleValue>(j);
+ }
+
+ case nlohmann::json::value_t::binary:
+ case nlohmann::json::value_t::discarded:
+ /*
+ * Binary data isn't supported.
+ * Discarded is an internal type of nlohmann.
+ *
+ * Assert in case we need to parse them.
+ */
+ BLI_assert_unreachable();
+ return std::make_unique<NullValue>();
+ }
+
+ BLI_assert_unreachable();
+ return std::make_unique<NullValue>();
+}
+
+void JsonFormatter::serialize(std::ostream &os, const Value &value)
+{
+ nlohmann::ordered_json j;
+ convert_to_json(j, value);
+ if (indentation_len) {
+ os << j.dump(indentation_len);
+ }
+ else {
+ os << j.dump();
+ }
+}
+
+std::unique_ptr<Value> JsonFormatter::deserialize(std::istream &is)
+{
+ nlohmann::ordered_json j;
+ is >> j;
+ return convert_from_json(j);
+}
+
+} // namespace blender::io::serialize
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index d5c9c5cd5e6..3001b25bc1e 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -172,12 +172,14 @@ bool BLI_windows_register_blend_extension(const bool background)
return false;
}
+# ifdef WITH_BLENDER_THUMBNAILER
BLI_windows_get_executable_dir(InstallDir);
GetSystemDirectory(SysDir, FILE_MAXDIR);
ThumbHandlerDLL = "BlendThumb.dll";
snprintf(
RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL);
system(RegCmd);
+# endif
RegCloseKey(root);
printf("success (%s)\n", usr_mode ? "user" : "system");
diff --git a/source/blender/blenlib/tests/BLI_serialize_test.cc b/source/blender/blenlib/tests/BLI_serialize_test.cc
new file mode 100644
index 00000000000..6c55a85ca1e
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_serialize_test.cc
@@ -0,0 +1,207 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_serialize.hh"
+
+/* -------------------------------------------------------------------- */
+/* tests */
+
+namespace blender::io::serialize::json::testing {
+
+TEST(serialize, string_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ StringValue test_value("Hello JSON");
+ json.serialize(out, test_value);
+ EXPECT_EQ(out.str(), "\"Hello JSON\"");
+}
+
+static void test_int_to_json(int64_t value, StringRef expected)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ IntValue test_value(value);
+ json.serialize(out, test_value);
+ EXPECT_EQ(out.str(), expected);
+}
+
+TEST(serialize, int_to_json)
+{
+ test_int_to_json(42, "42");
+ test_int_to_json(-42, "-42");
+ test_int_to_json(std::numeric_limits<int32_t>::max(), "2147483647");
+ test_int_to_json(std::numeric_limits<int32_t>::min(), "-2147483648");
+ test_int_to_json(std::numeric_limits<int64_t>::max(), "9223372036854775807");
+ test_int_to_json(std::numeric_limits<int64_t>::min(), "-9223372036854775808");
+}
+
+TEST(serialize, double_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ DoubleValue test_value(42.31);
+ json.serialize(out, test_value);
+ EXPECT_EQ(out.str(), "42.31");
+}
+
+TEST(serialize, null_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ NullValue test_value;
+ json.serialize(out, test_value);
+ EXPECT_EQ(out.str(), "null");
+}
+
+TEST(serialize, false_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ BooleanValue value(false);
+ json.serialize(out, value);
+ EXPECT_EQ(out.str(), "false");
+}
+
+TEST(serialize, true_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ BooleanValue value(true);
+ json.serialize(out, value);
+ EXPECT_EQ(out.str(), "true");
+}
+
+TEST(serialize, array_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ ArrayValue value_array;
+ ArrayValue::Items &array = value_array.elements();
+ array.append_as(new IntValue(42));
+ array.append_as(new StringValue("Hello JSON"));
+ array.append_as(new NullValue);
+ array.append_as(new BooleanValue(false));
+ array.append_as(new BooleanValue(true));
+
+ json.serialize(out, value_array);
+ EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]");
+}
+
+TEST(serialize, object_to_json)
+{
+ JsonFormatter json;
+ std::stringstream out;
+ ObjectValue value_object;
+ ObjectValue::Items &attributes = value_object.elements();
+ attributes.append_as(std::pair(std::string("best_number"), new IntValue(42)));
+
+ json.serialize(out, value_object);
+ EXPECT_EQ(out.str(), "{\"best_number\":42}");
+}
+
+TEST(serialize, json_roundtrip_ordering)
+{
+ const std::string input =
+ "[{\"_id\":\"614ada7c476c472ecbd0ecbb\",\"index\":0,\"guid\":\"d5b81381-cef8-4327-923d-"
+ "41e57ff79326\",\"isActive\":false,\"balance\":\"$2,062.25\",\"picture\":\"http://"
+ "placehold.it/32x32\",\"age\":26,\"eyeColor\":\"brown\",\"name\":\"Geneva "
+ "Vega\",\"gender\":\"female\",\"company\":\"SLOGANAUT\",\"email\":\"genevavega@sloganaut."
+ "com\",\"phone\":\"+1 (993) 432-2805\",\"address\":\"943 Christopher Avenue, Northchase, "
+ "Alabama, 5769\",\"about\":\"Eu cillum qui eu fugiat sit nulla eu duis. Aliqua nulla aliqua "
+ "ea tempor dolor fugiat sint consectetur exercitation ipsum magna ex. Aute laborum esse "
+ "magna nostrud in cillum et mollit proident. Deserunt ex minim adipisicing incididunt "
+ "incididunt dolore velit aliqua.\\r\\n\",\"registered\":\"2014-06-02T06:29:33 "
+ "-02:00\",\"latitude\":-66.003108,\"longitude\":44.038986,\"tags\":[\"exercitation\","
+ "\"laborum\",\"velit\",\"magna\",\"officia\",\"aliqua\",\"laboris\"],\"friends\":[{\"id\":0,"
+ "\"name\":\"Daniel Stuart\"},{\"id\":1,\"name\":\"Jackson "
+ "Velez\"},{\"id\":2,\"name\":\"Browning Boyd\"}],\"greeting\":\"Hello, Geneva Vega! You "
+ "have 8 unread "
+ "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7cf28685063c6722af\","
+ "\"index\":1,\"guid\":\"e157edf3-a86d-4984-b18d-e2fe568a9915\",\"isActive\":false,"
+ "\"balance\":\"$3,550.44\",\"picture\":\"http://placehold.it/"
+ "32x32\",\"age\":40,\"eyeColor\":\"blue\",\"name\":\"Lamb "
+ "Lowe\",\"gender\":\"male\",\"company\":\"PROXSOFT\",\"email\":\"lamblowe@proxsoft.com\","
+ "\"phone\":\"+1 (999) 573-2855\",\"address\":\"632 Rockwell Place, Diaperville, "
+ "Pennsylvania, 5050\",\"about\":\"Anim dolor deserunt esse quis velit adipisicing aute "
+ "nostrud velit minim culpa aute et tempor. Dolor aliqua reprehenderit anim voluptate. "
+ "Consequat proident ut culpa reprehenderit qui. Nisi proident velit cillum voluptate. "
+ "Ullamco id sunt quis aute adipisicing cupidatat consequat "
+ "aliquip.\\r\\n\",\"registered\":\"2014-09-06T06:13:36 "
+ "-02:00\",\"latitude\":-44.550228,\"longitude\":-80.893356,\"tags\":[\"anim\",\"id\","
+ "\"irure\",\"do\",\"officia\",\"irure\",\"Lorem\"],\"friends\":[{\"id\":0,\"name\":"
+ "\"Faulkner Watkins\"},{\"id\":1,\"name\":\"Cecile Schneider\"},{\"id\":2,\"name\":\"Burt "
+ "Lester\"}],\"greeting\":\"Hello, Lamb Lowe! You have 1 unread "
+ "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c235335fc56bc2f78\","
+ "\"index\":2,\"guid\":\"8206bad1-8274-49fd-9223-d727589f22ca\",\"isActive\":false,"
+ "\"balance\":\"$2,548.34\",\"picture\":\"http://placehold.it/"
+ "32x32\",\"age\":37,\"eyeColor\":\"blue\",\"name\":\"Sallie "
+ "Chase\",\"gender\":\"female\",\"company\":\"FLEETMIX\",\"email\":\"salliechase@fleetmix."
+ "com\",\"phone\":\"+1 (953) 453-3388\",\"address\":\"865 Irving Place, Chelsea, Utah, "
+ "9777\",\"about\":\"In magna exercitation incididunt exercitation dolor anim. Consectetur "
+ "dolore commodo elit cillum dolor reprehenderit magna minim et ex labore pariatur. Nulla "
+ "ullamco officia velit in aute proident nostrud. Duis deserunt et labore Lorem aliqua "
+ "eiusmod commodo sunt.\\r\\n\",\"registered\":\"2017-03-16T08:54:53 "
+ "-01:00\",\"latitude\":-78.481939,\"longitude\":-149.820215,\"tags\":[\"Lorem\",\"ipsum\","
+ "\"in\",\"tempor\",\"consectetur\",\"voluptate\",\"elit\"],\"friends\":[{\"id\":0,\"name\":"
+ "\"Gibson Garner\"},{\"id\":1,\"name\":\"Anna Frank\"},{\"id\":2,\"name\":\"Roberson "
+ "Daugherty\"}],\"greeting\":\"Hello, Sallie Chase! You have 7 unread "
+ "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c93b63ecad5f9ba5e\",\"index\":3,"
+ "\"guid\":\"924b02fc-7c27-481a-9941-db3b9403dfe1\",\"isActive\":true,\"balance\":\"$1,633."
+ "60\",\"picture\":\"http://placehold.it/"
+ "32x32\",\"age\":29,\"eyeColor\":\"brown\",\"name\":\"Grace "
+ "Mccall\",\"gender\":\"female\",\"company\":\"PIVITOL\",\"email\":\"gracemccall@pivitol."
+ "com\",\"phone\":\"+1 (964) 541-2514\",\"address\":\"734 Schaefer Street, Topaz, Virginia, "
+ "9137\",\"about\":\"Amet officia magna fugiat ut pariatur fugiat elit culpa voluptate elit "
+ "do proident culpa minim. Commodo do minim reprehenderit ut voluptate ut velit id esse "
+ "consequat. Labore ullamco deserunt irure eiusmod cillum tempor incididunt qui adipisicing "
+ "nostrud pariatur enim aliquip. Excepteur nostrud commodo consectetur esse duis irure "
+ "qui.\\r\\n\",\"registered\":\"2015-04-24T03:55:17 "
+ "-02:00\",\"latitude\":58.801446,\"longitude\":-157.413865,\"tags\":[\"do\",\"ea\",\"eu\","
+ "\"eu\",\"qui\",\"duis\",\"sint\"],\"friends\":[{\"id\":0,\"name\":\"Carrie "
+ "Short\"},{\"id\":1,\"name\":\"Dickerson Barnes\"},{\"id\":2,\"name\":\"Rae "
+ "Rios\"}],\"greeting\":\"Hello, Grace Mccall! You have 5 unread "
+ "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c9caf1353b0e22bbf\",\"index\":4,"
+ "\"guid\":\"e5981ae1-90e4-41c4-9905-161522db700b\",\"isActive\":false,\"balance\":\"$3,660."
+ "34\",\"picture\":\"http://placehold.it/"
+ "32x32\",\"age\":31,\"eyeColor\":\"blue\",\"name\":\"Herring "
+ "Powers\",\"gender\":\"male\",\"company\":\"PYRAMIA\",\"email\":\"herringpowers@pyramia."
+ "com\",\"phone\":\"+1 (981) 541-2829\",\"address\":\"409 Furman Avenue, Waterloo, South "
+ "Carolina, 380\",\"about\":\"In officia culpa aliqua culpa pariatur aliqua mollit ex. Velit "
+ "est Lorem enim magna cillum sunt elit consectetur deserunt ea est consectetur fugiat "
+ "mollit. Aute Lorem excepteur minim esse qui. Id Lorem in tempor et. Nisi aliquip laborum "
+ "magna eu aute.\\r\\n\",\"registered\":\"2018-07-05T07:28:54 "
+ "-02:00\",\"latitude\":51.497405,\"longitude\":-129.422711,\"tags\":[\"eiusmod\",\"et\","
+ "\"nostrud\",\"reprehenderit\",\"Lorem\",\"cillum\",\"nulla\"],\"friends\":[{\"id\":0,"
+ "\"name\":\"Tonia Keith\"},{\"id\":1,\"name\":\"Leanne Rice\"},{\"id\":2,\"name\":\"Craig "
+ "Gregory\"}],\"greeting\":\"Hello, Herring Powers! You have 6 unread "
+ "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c53a3d6da77468f25\","
+ "\"index\":5,\"guid\":\"abb2eec9-c4f0-4a0d-b20a-5c8e50fe88a1\",\"isActive\":true,"
+ "\"balance\":\"$1,481.08\",\"picture\":\"http://placehold.it/"
+ "32x32\",\"age\":31,\"eyeColor\":\"green\",\"name\":\"Lela "
+ "Dillard\",\"gender\":\"female\",\"company\":\"CEMENTION\",\"email\":\"leladillard@"
+ "cemention.com\",\"phone\":\"+1 (856) 456-3657\",\"address\":\"391 Diamond Street, Madaket, "
+ "Ohio, 9337\",\"about\":\"Tempor dolor ullamco esse cillum excepteur. Excepteur aliqua non "
+ "enim anim esse amet cupidatat non. Cillum excepteur occaecat cupidatat elit labore. "
+ "Pariatur ut esse sint elit. Velit sint magna et commodo sit velit labore consectetur irure "
+ "officia proident aliquip. Aliqua dolore ipsum voluptate veniam deserunt amet irure. Cillum "
+ "consequat veniam proident Lorem in anim enim veniam ea "
+ "nulla.\\r\\n\",\"registered\":\"2017-01-11T11:07:22 "
+ "-01:00\",\"latitude\":86.349081,\"longitude\":-179.983754,\"tags\":[\"consequat\","
+ "\"labore\",\"consectetur\",\"dolor\",\"laborum\",\"eiusmod\",\"in\"],\"friends\":[{\"id\":"
+ "0,\"name\":\"Hancock Rivera\"},{\"id\":1,\"name\":\"Chasity "
+ "Oneil\"},{\"id\":2,\"name\":\"Whitaker Barr\"}],\"greeting\":\"Hello, Lela Dillard! You "
+ "have 3 unread messages.\",\"favoriteFruit\":\"strawberry\"}]";
+ std::stringstream is(input);
+
+ JsonFormatter json;
+ std::unique_ptr<Value> value = json.deserialize(is);
+ EXPECT_EQ(value->type(), eValueType::Array);
+
+ std::stringstream out;
+ json.serialize(out, *value);
+ EXPECT_EQ(out.str(), input);
+}
+
+} // namespace blender::io::serialize::json::testing
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 0b69395b4f8..600abcca818 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2211,6 +2211,9 @@ static void direct_link_id_common(
if (id->asset_data) {
BLO_read_data_address(reader, &id->asset_data);
BKE_asset_metadata_read(reader, id->asset_data);
+ /* Restore runtime asset type info. */
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ id->asset_data->local_type_info = id_type->asset_type_info;
}
/* Link direct data of ID properties. */
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index e6247750759..e809d580fd2 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1717,18 +1717,8 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #blo_do_versions_280 in this file.
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ /* Old forgotten versioning code. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) {
/* Paint Brush. This ensure that the brush paints by default. Used during the development and
* patch review of the initial Sculpt Vertex Colors implementation (D5975) */
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
@@ -1748,6 +1738,20 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
}
}
}
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_280 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
/* NOTE: This version patch is intended for versions < 2.52.2,
@@ -5066,17 +5070,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #do_versions_after_linking_280 in this file.
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
+ /* Old forgotten versioning code. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) {
/* Set the cloth wind factor to 1 for old forces. */
if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
@@ -5096,10 +5091,22 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
/* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be
- * edited and this flag should always be set. So we can always execute this. */
+ * edited and this flag should always be set. */
wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
}
+ }
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #do_versions_after_linking_280 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
/* Keep this block, even when empty. */
}
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index d2c722f8be7..def14768ec6 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1985,7 +1985,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
- version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
+ version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density");
}
}
FOREACH_NODETREE_END;
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 8168b917b5e..68faa4c0672 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -535,7 +535,7 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
}
/* Also realize instances for the profile input of the curve to mesh node. */
if (node->type == GEO_NODE_CURVE_TO_MESH) {
- bNodeSocket *profile_socket = node->inputs.last;
+ bNodeSocket *profile_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
add_realize_instances_before_socket(ntree, node, profile_socket);
}
}
@@ -967,7 +967,7 @@ static bool geometry_node_is_293_legacy(const short node_type)
/* Maybe legacy: Special case for grid names? Or finish patch from level set branch to
* generate a mesh for all grids in the volume. */
- case GEO_NODE_VOLUME_TO_MESH:
+ case GEO_NODE_LEGACY_VOLUME_TO_MESH:
return false;
/* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */
@@ -1218,6 +1218,56 @@ static void do_version_bones_roll(ListBase *lb)
}
}
+static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree)
+{
+ /* Add the new Offset socket. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_SET_POSITION) {
+ continue;
+ }
+ if (BLI_listbase_count(&node->inputs) < 4) {
+ /* The offset socket didn't exist in the file yet. */
+ return;
+ }
+ bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
+ if (old_offset_socket->type == SOCK_VECTOR) {
+ /* Versioning happened already. */
+ return;
+ }
+ /* Change identifier of old socket, so that the there is no name collision. */
+ STRNCPY(old_offset_socket->identifier, "Offset_old");
+ nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_VECTOR, PROP_TRANSLATION, "Offset", "Offset");
+ }
+
+ /* Relink links that were connected to Position while Offset was enabled. */
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tonode->type != GEO_NODE_SET_POSITION) {
+ continue;
+ }
+ if (!STREQ(link->tosock->identifier, "Position")) {
+ continue;
+ }
+ bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3);
+ /* This assumes that the offset is not linked to something else. That seems to be a reasonable
+ * assumption, because the node is probably only ever used in one or the other mode. */
+ const bool offset_enabled =
+ ((bNodeSocketValueBoolean *)old_offset_socket->default_value)->value;
+ if (offset_enabled) {
+ /* Relink to new offset socket. */
+ link->tosock = old_offset_socket->next;
+ }
+ }
+
+ /* Remove old Offset socket. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_SET_POSITION) {
+ continue;
+ }
+ bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
+ nodeRemoveSocket(ntree, node, old_offset_socket);
+ }
+}
+
/* NOLINTNEXTLINE: readability-function-size */
void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
@@ -2021,6 +2071,79 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) {
+ LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
+ wm->xr.session_settings.base_scale = 1.0f;
+ wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION |
+ V3D_OFSDRAW_XR_SHOW_CONTROLLERS |
+ V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS);
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 40)) {
+ /* Update the `idnames` for renamed geometry and function nodes. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString");
+ version_geometry_nodes_set_position_node_offset(ntree);
+ version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh");
+ }
+
+ /* Add storage to viewer node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == GEO_NODE_VIEWER) {
+ if (node->storage == NULL) {
+ NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(
+ sizeof(NodeGeometryViewer), __func__);
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+ }
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_input_socket_name(
+ ntree, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Geometry", "Mesh");
+ version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points");
+ version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume");
+ version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh");
+ version_node_socket_name(ntree, GEO_NODE_RESAMPLE_CURVE, "Geometry", "Curve");
+ version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_CURVE, "Geometry", "Curve");
+ version_node_socket_name(ntree, GEO_NODE_SET_CURVE_RADIUS, "Geometry", "Curve");
+ version_node_socket_name(ntree, GEO_NODE_SET_CURVE_TILT, "Geometry", "Curve");
+ version_node_socket_name(ntree, GEO_NODE_SET_CURVE_HANDLES, "Geometry", "Curve");
+ version_node_socket_name(ntree, GEO_NODE_TRANSLATE_INSTANCES, "Geometry", "Instances");
+ version_node_socket_name(ntree, GEO_NODE_ROTATE_INSTANCES, "Geometry", "Instances");
+ version_node_socket_name(ntree, GEO_NODE_SCALE_INSTANCES, "Geometry", "Instances");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry", "Mesh");
+ version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 1", "Mesh 1");
+ version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 2", "Mesh 2");
+ version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_MESH, "Geometry", "Mesh");
+ version_node_socket_name(ntree, GEO_NODE_TRIANGULATE, "Geometry", "Mesh");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CONE, "Geometry", "Mesh");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CUBE, "Geometry", "Mesh");
+ version_node_output_socket_name(
+ ntree, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Geometry", "Mesh");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Geometry", "Mesh");
+ version_node_output_socket_name(
+ ntree, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Geometry", "Mesh");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Geometry", "Mesh");
+ version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_LINE, "Geometry", "Mesh");
+ version_node_output_socket_name(
+ ntree, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "Geometry", "Mesh");
+ version_node_socket_name(ntree, GEO_NODE_SET_POINT_RADIUS, "Geometry", "Points");
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -2030,15 +2153,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*
* \note Keep this message at the bottom of the function.
*/
-
{
- /* Update the `idnames` for renamed geometry and function nodes. */
- LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
- if (ntree->type != NTREE_GEOMETRY) {
- continue;
- }
- version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString");
- }
/* Keep this block, even when empty. */
}
}
diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc
index 6c4996ba9b2..ecc944defba 100644
--- a/source/blender/blenloader/intern/versioning_common.cc
+++ b/source/blender/blenloader/intern/versioning_common.cc
@@ -87,6 +87,18 @@ ID *do_versions_rename_id(Main *bmain,
return id;
}
+static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name)
+{
+ LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
+ if (STREQ(socket->name, old_name)) {
+ BLI_strncpy(socket->name, new_name, sizeof(socket->name));
+ }
+ if (STREQ(socket->identifier, old_name)) {
+ BLI_strncpy(socket->identifier, new_name, sizeof(socket->name));
+ }
+ }
+}
+
void version_node_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
@@ -94,22 +106,32 @@ void version_node_socket_name(bNodeTree *ntree,
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == node_type) {
- LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
- if (STREQ(socket->name, old_name)) {
- BLI_strncpy(socket->name, new_name, sizeof(socket->name));
- }
- if (STREQ(socket->identifier, old_name)) {
- BLI_strncpy(socket->identifier, new_name, sizeof(socket->name));
- }
- }
- LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
- if (STREQ(socket->name, old_name)) {
- BLI_strncpy(socket->name, new_name, sizeof(socket->name));
- }
- if (STREQ(socket->identifier, old_name)) {
- BLI_strncpy(socket->identifier, new_name, sizeof(socket->name));
- }
- }
+ change_node_socket_name(&node->inputs, old_name, new_name);
+ change_node_socket_name(&node->outputs, old_name, new_name);
+ }
+ }
+}
+
+void version_node_input_socket_name(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ change_node_socket_name(&node->inputs, old_name, new_name);
+ }
+ }
+}
+
+void version_node_output_socket_name(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ change_node_socket_name(&node->outputs, old_name, new_name);
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h
index 1826182be21..8697e8e2639 100644
--- a/source/blender/blenloader/intern/versioning_common.h
+++ b/source/blender/blenloader/intern/versioning_common.h
@@ -43,6 +43,14 @@ void version_node_socket_name(struct bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name);
+void version_node_input_socket_name(struct bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name);
+void version_node_output_socket_name(struct bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name);
void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name);
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 170e6be715a..4234570af6c 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -27,9 +27,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
-#ifdef WITH_INTERNATIONAL
-# include "BLT_translation.h"
-#endif
+#include "BLT_translation.h"
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
@@ -60,10 +58,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
{
#define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver)
- if (!USER_VERSION_ATLEAST(280, 20)) {
- memcpy(btheme, &U_theme_default, sizeof(*btheme));
- }
-
#define FROM_DEFAULT_V4_UCHAR(member) copy_v4_v4_uchar(btheme->member, U_theme_default.member)
if (!USER_VERSION_ATLEAST(280, 25)) {
@@ -314,6 +308,15 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
btheme->space_node.dash_alpha = 0.5f;
}
+ if (!USER_VERSION_ATLEAST(300, 39)) {
+ FROM_DEFAULT_V4_UCHAR(space_node.grid);
+ btheme->space_node.grid_levels = 7;
+ }
+
+ if (!USER_VERSION_ATLEAST(300, 40)) {
+ memcpy(btheme, &U_theme_default, sizeof(*btheme));
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -923,6 +926,24 @@ void blo_do_versions_userdef(UserDef *userdef)
userdef->dupflag |= USER_DUP_SPEAKER;
}
+ if (!USER_VERSION_ATLEAST(300, 40)) {
+ /* Rename the default asset library from "Default" to "User Library" */
+ LISTBASE_FOREACH (bUserAssetLibrary *, asset_library, &userdef->asset_libraries) {
+ if (STREQ(asset_library->name, DATA_("Default"))) {
+ BKE_preferences_asset_library_name_set(
+ userdef, asset_library, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME);
+ }
+ }
+ }
+
+ if (!USER_VERSION_ATLEAST(300, 40)) {
+ LISTBASE_FOREACH (uiStyle *, style, &userdef->uistyles) {
+ const int default_title_points = 11; /* UI_DEFAULT_TITLE_POINTS */
+ style->paneltitle.points = default_title_points;
+ style->grouplabel.points = default_title_points;
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index baf83234354..0baf994d978 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -61,6 +61,7 @@ set(SRC
intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
intern/mesh_extractors/extract_mesh_ibo_points.cc
intern/mesh_extractors/extract_mesh_ibo_tris.cc
+ intern/mesh_extractors/extract_mesh_vbo_attributes.cc
intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
intern/mesh_extractors/extract_mesh_vbo_edit_data.cc
intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index b07e86000fd..7f9e37f58d5 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -463,9 +463,14 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
case OB_LIGHTPROBE:
OVERLAY_lightprobe_cache_populate(vedata, ob);
break;
- case OB_LATTICE:
- OVERLAY_lattice_cache_populate(vedata, ob);
+ case OB_LATTICE: {
+ /* Unlike the other types above, lattices actually have a bounding box defined, so hide the
+ * lattice wires if only the boundingbox is requested. */
+ if (ob->dt > OB_BOUNDBOX) {
+ OVERLAY_lattice_cache_populate(vedata, ob);
+ }
break;
+ }
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c
index 31c8ed9d664..4a551c4dec5 100644
--- a/source/blender/draw/engines/overlay/overlay_grid.c
+++ b/source/blender/draw/engines/overlay/overlay_grid.c
@@ -200,6 +200,15 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
shd->grid_distance = dist / 2.0f;
ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps);
+
+ if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) {
+ /* The calculations for the grid parameters assume that the view matrix has no scale component,
+ * which may not be correct if the user is "shrunk" or "enlarged" by zooming in or out.
+ * Therefore, we need to compensate the values here. */
+ float viewinvscale = len_v3(
+ viewinv[0]); /* Assumption is uniform scaling (all column vectors are of same length). */
+ shd->grid_distance *= viewinvscale;
+ }
}
void OVERLAY_grid_cache_init(OVERLAY_Data *vedata)
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index a680cc0d6b7..ba42cdf66e7 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -24,6 +24,10 @@
struct TaskGraph;
+#include "DNA_customdata_types.h"
+
+#include "BKE_attribute.h"
+
#include "GPU_batch.h"
#include "GPU_index_buffer.h"
#include "GPU_vertex_buffer.h"
@@ -56,7 +60,6 @@ typedef struct DRW_MeshCDMask {
uint32_t uv : 8;
uint32_t tan : 8;
uint32_t vcol : 8;
- uint32_t sculpt_vcol : 8;
uint32_t orco : 1;
uint32_t tan_orco : 1;
uint32_t sculpt_overlays : 1;
@@ -64,10 +67,10 @@ typedef struct DRW_MeshCDMask {
* modifiers could remove it. (see T68857) */
uint32_t edit_uv : 1;
} DRW_MeshCDMask;
-/* Keep `DRW_MeshCDMask` struct within an `uint64_t`.
+/* Keep `DRW_MeshCDMask` struct within an `uint32_t`.
* bit-wise and atomic operations are used to compare and update the struct.
* See `mesh_cd_layers_type_*` functions. */
-BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits")
+BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits")
typedef enum eMRIterType {
MR_ITER_LOOPTRI = 1 << 0,
MR_ITER_POLY = 1 << 1,
@@ -76,6 +79,17 @@ typedef enum eMRIterType {
} eMRIterType;
ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT)
+typedef struct DRW_AttributeRequest {
+ CustomDataType cd_type;
+ int layer_index;
+ AttributeDomain domain;
+} DRW_AttributeRequest;
+
+typedef struct DRW_MeshAttributes {
+ DRW_AttributeRequest requests[GPU_MAX_ATTR];
+ int num_requests;
+} DRW_MeshAttributes;
+
typedef enum eMRDataType {
MR_DATA_NONE = 0,
MR_DATA_POLY_NOR = 1 << 1,
@@ -133,6 +147,7 @@ typedef struct MeshBufferList {
GPUVertBuf *edge_idx; /* extend */
GPUVertBuf *poly_idx;
GPUVertBuf *fdot_idx;
+ GPUVertBuf *attr[GPU_MAX_ATTR];
} vbo;
/* Index Buffers:
* Only need to be updated when topology changes. */
@@ -285,6 +300,8 @@ typedef struct MeshBatchCache {
DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
+ DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time;
+
int lastmatch;
/* Valid only if edge_detection is up to date. */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 06c449fe590..f3b72503907 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -650,6 +650,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
EXTRACT_ADD_REQUESTED(vbo, vert_idx);
EXTRACT_ADD_REQUESTED(vbo, fdot_idx);
EXTRACT_ADD_REQUESTED(vbo, skin_roots);
+ for (int i = 0; i < GPU_MAX_ATTR; i++) {
+ EXTRACT_ADD_REQUESTED(vbo, attr[i]);
+ }
EXTRACT_ADD_REQUESTED(ibo, tris);
if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) {
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 18664498d00..12c19c671ab 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -41,6 +41,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_attribute.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
@@ -121,6 +122,8 @@
# define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5)
# define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6)
# define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7)
+# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20)
+# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21);
# define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__)
# define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__)
@@ -192,6 +195,21 @@ static const DRWBatchFlag g_buffer_deps[] = {
[BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges),
[BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces),
[BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots),
+ [BUFFER_INDEX(vbo.attr) + 0] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 1] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 2] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 3] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 4] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 5] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 6] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 7] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 8] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 9] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 10] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 11] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 12] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 13] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
+ [BUFFER_INDEX(vbo.attr) + 14] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG,
[BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface,
surface_weights,
@@ -240,12 +258,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch
/* Return true is all layers in _b_ are inside _a_. */
BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b)
{
- return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b);
+ return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b);
}
BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b)
{
- return *((uint64_t *)&a) == *((uint64_t *)&b);
+ return *((uint32_t *)&a) == *((uint32_t *)&b);
}
BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b)
@@ -253,12 +271,11 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b)
uint32_t *a_p = (uint32_t *)a;
uint32_t *b_p = (uint32_t *)&b;
atomic_fetch_and_or_uint32(a_p, *b_p);
- atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1));
}
BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a)
{
- *((uint64_t *)a) = 0;
+ *((uint32_t *)a) = 0;
}
BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me)
@@ -271,6 +288,95 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c
cd_used->edit_uv = 1;
}
+/** \name DRW_MeshAttributes
+ *
+ * Utilities for handling requested attributes.
+ * \{ */
+
+/* Return true if the given DRW_AttributeRequest is already in the requests. */
+static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req)
+{
+ for (int i = 0; i < requests->num_requests; i++) {
+ const DRW_AttributeRequest src_req = requests->requests[i];
+ if (src_req.domain != req.domain) {
+ continue;
+ }
+ if (src_req.layer_index != req.layer_index) {
+ continue;
+ }
+ if (src_req.cd_type != req.cd_type) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests,
+ DRW_MeshAttributes *dst_requests)
+{
+ for (int i = 0; i < src_requests->num_requests; i++) {
+ if (dst_requests->num_requests == GPU_MAX_ATTR) {
+ return;
+ }
+
+ if (has_request(dst_requests, src_requests->requests[i])) {
+ continue;
+ }
+
+ dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i];
+ dst_requests->num_requests += 1;
+ }
+}
+
+static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes)
+{
+ memset(attributes, 0, sizeof(DRW_MeshAttributes));
+}
+
+static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst,
+ const DRW_MeshAttributes *src,
+ ThreadMutex *mesh_render_mutex)
+{
+ BLI_mutex_lock(mesh_render_mutex);
+ mesh_attrs_merge_requests(src, dst);
+ BLI_mutex_unlock(mesh_render_mutex);
+}
+
+/* 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++) {
+ if (!has_request(a, b->requests[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs,
+ CustomDataType type,
+ int layer,
+ AttributeDomain domain)
+{
+ if (attrs->num_requests >= GPU_MAX_ATTR) {
+ return;
+ }
+
+ DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests];
+ req->cd_type = type;
+ req->layer_index = layer;
+ req->domain = domain;
+ attrs->num_requests += 1;
+}
+
+/** \} */
+
BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
{
switch ((eMeshWrapperType)me->runtime.wrapper_type) {
@@ -286,6 +392,36 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
return &me->ldata;
}
+BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->pdata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->pdata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->pdata;
+}
+
+BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->edata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->edata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->edata;
+}
+
BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
{
switch ((eMeshWrapperType)me->runtime.wrapper_type) {
@@ -321,14 +457,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd
}
}
-static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used)
{
const Mesh *me_final = editmesh_final_or_this(me);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR);
if (layer != -1) {
- cd_used->sculpt_vcol |= (1 << layer);
+ drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT);
}
}
@@ -343,13 +479,45 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c
}
}
+static bool custom_data_match_attribute(const CustomData *custom_data,
+ const char *name,
+ int *r_layer_index,
+ int *r_type)
+{
+ const int possible_attribute_types[6] = {
+ CD_PROP_BOOL,
+ CD_PROP_INT32,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT2,
+ CD_PROP_FLOAT3,
+ CD_PROP_COLOR,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) {
+ const int attr_type = possible_attribute_types[i];
+ int layer_index = CustomData_get_named_layer(custom_data, attr_type, name);
+ if (layer_index == -1) {
+ continue;
+ }
+
+ *r_layer_index = layer_index;
+ *r_type = attr_type;
+ return true;
+ }
+
+ return false;
+}
+
static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
struct GPUMaterial **gpumat_array,
- int gpumat_array_len)
+ int gpumat_array_len,
+ DRW_MeshAttributes *attributes)
{
const Mesh *me_final = editmesh_final_or_this(me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+ const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
+ const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final);
/* See: DM_vertex_attributes_from_gpu for similar logic */
DRW_MeshCDMask cd_used;
@@ -363,6 +531,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
const char *name = gpu_attr->name;
int type = gpu_attr->type;
int layer = -1;
+ /* ATTR_DOMAIN_NUM is standard for "invalid value". */
+ AttributeDomain domain = ATTR_DOMAIN_NUM;
if (type == CD_AUTO_FROM_NAME) {
/* We need to deduct what exact layer is used.
@@ -374,13 +544,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
type = CD_MTFACE;
if (layer == -1) {
- if (U.experimental.use_sculpt_vertex_colors) {
- layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
- type = CD_PROP_COLOR;
- }
- }
-
- if (layer == -1) {
layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
type = CD_MCOL;
}
@@ -392,6 +555,27 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
}
#endif
if (layer == -1) {
+ /* Try to match a generic attribute, we use the first attribute domain with a
+ * matching name. */
+ if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_POINT;
+ }
+ else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_CORNER;
+ }
+ else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_FACE;
+ }
+ else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_EDGE;
+ }
+ else {
+ layer = -1;
+ domain = ATTR_DOMAIN_NUM;
+ }
+ }
+
+ if (layer == -1) {
continue;
}
}
@@ -432,31 +616,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
}
break;
}
- case CD_PROP_COLOR: {
- /* Sculpt Vertex Colors */
- bool use_mloop_cols = false;
- if (layer == -1) {
- layer = (name[0] != '\0') ?
- CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) :
- CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR);
- /* Fallback to Vertex Color data */
- if (layer == -1) {
- layer = (name[0] != '\0') ?
- CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) :
- CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL);
- use_mloop_cols = true;
- }
- }
- if (layer != -1) {
- if (use_mloop_cols) {
- cd_used.vcol |= (1 << layer);
- }
- else {
- cd_used.sculpt_vcol |= (1 << layer);
- }
- }
- break;
- }
case CD_MCOL: {
/* Vertex Color Data */
if (layer == -1) {
@@ -473,6 +632,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
cd_used.orco = 1;
break;
}
+ case CD_PROP_BOOL:
+ case CD_PROP_INT32:
+ case CD_PROP_FLOAT:
+ case CD_PROP_FLOAT2:
+ case CD_PROP_FLOAT3:
+ case CD_PROP_COLOR: {
+ if (layer != -1 && domain != ATTR_DOMAIN_NUM) {
+ drw_mesh_attributes_add_request(attributes, type, layer, domain);
+ }
+ break;
+ }
}
}
}
@@ -935,14 +1105,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me)
{
- DRW_MeshCDMask cd_needed;
- mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_vcol_layer(me, &cd_needed);
+ DRW_MeshAttributes attrs_needed;
+ drw_mesh_attributes_clear(&attrs_needed);
+ mesh_cd_calc_active_vcol_layer(me, &attrs_needed);
- BLI_assert(cd_needed.sculpt_vcol != 0 &&
+ BLI_assert(attrs_needed.num_requests != 0 &&
"No MPropCol layer available in Sculpt, but batches requested anyway!");
- mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
+ drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex);
}
GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me)
@@ -1015,11 +1185,16 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me,
uint gpumat_array_len)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len);
+ DRW_MeshAttributes attrs_needed;
+ drw_mesh_attributes_clear(&attrs_needed);
+ DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(
+ me, gpumat_array, gpumat_array_len, &attrs_needed);
BLI_assert(gpumat_array_len == cache->mat_len);
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
+ ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex;
+ drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex);
mesh_batch_cache_request_surface_batches(cache);
return cache->surface_per_mat;
}
@@ -1296,11 +1471,25 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime)
cache->lastmatch = ctime;
}
+ if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) {
+ cache->lastmatch = ctime;
+ }
+
if (ctime - cache->lastmatch > U.vbotimeout) {
mesh_batch_cache_discard_shaded_tri(cache);
}
mesh_cd_layers_type_clear(&cache->cd_used_over_time);
+ drw_mesh_attributes_clear(&cache->attr_used_over_time);
+}
+
+static void drw_add_attributes_vbo(GPUBatch *batch,
+ MeshBufferList *mbuflist,
+ DRW_MeshAttributes *attr_used)
+{
+ for (int i = 0; i < attr_used->num_requests; i++) {
+ DRW_vbo_request(batch, &mbuflist->vbo.attr[i]);
+ }
}
#ifdef DEBUG
@@ -1409,12 +1598,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
}
}
+ ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex;
+
/* Verify that all surface batches have needed attribute layers.
*/
/* TODO(fclem): We could be a bit smarter here and only do it per
* material. */
bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed);
- if (cd_overlap == false) {
+ bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed);
+ if (cd_overlap == false || attr_overlap == false) {
FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv);
@@ -1430,11 +1622,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data);
}
- if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) ||
- ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) !=
- cache->cd_needed.sculpt_vcol)) {
+ if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
}
+ if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) {
+ for (int i = 0; i < GPU_MAX_ATTR; i++) {
+ GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]);
+ }
+ }
}
/* We can't discard batches at this point as they have been
* referenced for drawing. Just clear them in place. */
@@ -1445,9 +1640,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
cache->batch_ready &= ~(MBC_SURFACE);
mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed);
+ drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex);
}
mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed);
mesh_cd_layers_type_clear(&cache->cd_needed);
+
+ drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex);
+ drw_mesh_attributes_clear(&cache->attr_needed);
}
if (batch_requested & MBC_EDITUV) {
@@ -1506,7 +1705,27 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
MeshBufferList *mbuflist = &cache->final.buff;
/* Initialize batches and request VBO's & IBO's. */
- MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol);
+ MDEPS_ASSERT(surface,
+ ibo.tris,
+ vbo.lnor,
+ vbo.pos_nor,
+ vbo.uv,
+ vbo.vcol,
+ vbo.attr[0],
+ vbo.attr[1],
+ vbo.attr[2],
+ vbo.attr[3],
+ vbo.attr[4],
+ vbo.attr[5],
+ vbo.attr[6],
+ vbo.attr[7],
+ vbo.attr[8],
+ vbo.attr[9],
+ vbo.attr[10],
+ vbo.attr[11],
+ vbo.attr[12],
+ vbo.attr[13],
+ vbo.attr[14]);
if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) {
DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris);
/* Order matters. First ones override latest VBO's attributes. */
@@ -1515,9 +1734,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.uv != 0) {
DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv);
}
- if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
+ if (cache->cd_used.vcol != 0) {
DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol);
}
+ drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used);
}
MDEPS_ASSERT(all_verts, vbo.pos_nor);
if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) {
@@ -1580,8 +1800,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
}
/* Per Material */
- MDEPS_ASSERT_FLAG(
- SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
+ MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG,
+ vbo.lnor,
+ vbo.pos_nor,
+ vbo.uv,
+ vbo.tan,
+ vbo.vcol,
+ vbo.orco,
+ vbo.attr[0],
+ vbo.attr[1],
+ vbo.attr[2],
+ vbo.attr[3],
+ vbo.attr[4],
+ vbo.attr[5],
+ vbo.attr[6],
+ vbo.attr[7],
+ vbo.attr[8],
+ vbo.attr[9],
+ vbo.attr[10],
+ vbo.attr[11],
+ vbo.attr[12],
+ vbo.attr[13],
+ vbo.attr[14]);
MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG);
for (int i = 0; i < cache->mat_len; i++) {
if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) {
@@ -1595,12 +1835,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan);
}
- if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
+ if (cache->cd_used.vcol != 0) {
DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol);
}
if (cache->cd_used.orco != 0) {
DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco);
}
+ drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used);
}
}
@@ -1751,6 +1992,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle);
MDEPS_ASSERT_MAP(vbo.fdots_uv);
MDEPS_ASSERT_MAP(vbo.fdots_edituv_data);
+ for (int i = 0; i < GPU_MAX_ATTR; i++) {
+ MDEPS_ASSERT_MAP(vbo.attr[i]);
+ }
MDEPS_ASSERT_MAP(ibo.tris);
MDEPS_ASSERT_MAP(ibo.lines);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index f96bd474aec..5d3e3db866f 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -1684,10 +1684,12 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox,
bsphere->center[0] = farcenter[0] * z / e;
bsphere->center[1] = farcenter[1] * z / e;
bsphere->center[2] = z;
- bsphere->radius = len_v3v3(bsphere->center, farpoint);
- /* Transform to world space. */
- mul_m4_v3(viewinv, bsphere->center);
+ /* For XR, the view matrix may contain a scale factor. Then, transforming only the center
+ * into world space after calculating the radius will result in incorrect behavior. */
+ mul_m4_v3(viewinv, bsphere->center); /* Transform to world space. */
+ mul_m4_v3(viewinv, farpoint);
+ bsphere->radius = len_v3v3(bsphere->center, farpoint);
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
index d9f397fd8b8..d1ffef4fe92 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h
@@ -328,6 +328,7 @@ extern const MeshExtract extract_poly_idx;
extern const MeshExtract extract_edge_idx;
extern const MeshExtract extract_vert_idx;
extern const MeshExtract extract_fdot_idx;
+extern const MeshExtract extract_attr[GPU_MAX_ATTR];
#ifdef __cplusplus
}
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
new file mode 100644
index 00000000000..9edefe32fbc
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
@@ -0,0 +1,398 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include <functional>
+
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+#include "BLI_float4.hh"
+#include "BLI_string.h"
+
+#include "BKE_attribute.h"
+
+#include "extract_mesh.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Attributes
+ * \{ */
+
+static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain)
+{
+ switch (domain) {
+ default: {
+ return nullptr;
+ }
+ case ATTR_DOMAIN_POINT: {
+ return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ }
+ case ATTR_DOMAIN_FACE: {
+ return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata;
+ }
+ }
+}
+
+/* Utility to convert from the type used in the attributes to the types for the VBO.
+ * This is mostly used to promote integers and booleans to floats, as other types (float, float2,
+ * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4
+ * in the shader.
+ */
+template<typename AttributeType, typename VBOType> struct attribute_type_converter {
+ static VBOType convert_value(AttributeType value)
+ {
+ if constexpr (std::is_same_v<AttributeType, VBOType>) {
+ return value;
+ }
+
+ /* This should only concern bools which are converted to floats. */
+ return static_cast<VBOType>(value);
+ }
+};
+
+/* Similar to the one in #extract_mesh_vcol_vbo.cc */
+struct gpuMeshCol {
+ ushort r, g, b, a;
+};
+
+template<> struct attribute_type_converter<MPropCol, gpuMeshCol> {
+ static gpuMeshCol convert_value(MPropCol value)
+ {
+ gpuMeshCol result;
+ result.r = unit_float_to_ushort_clamp(value.color[0]);
+ result.g = unit_float_to_ushort_clamp(value.color[1]);
+ result.b = unit_float_to_ushort_clamp(value.color[2]);
+ result.a = unit_float_to_ushort_clamp(value.color[3]);
+ return result;
+ }
+};
+
+/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */
+static uint gpu_component_size_for_attribute_type(CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_BOOL:
+ case CD_PROP_INT32:
+ case CD_PROP_FLOAT: {
+ /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See
+ * comment #extract_attr_init. */
+ return 3;
+ }
+ case CD_PROP_FLOAT2: {
+ return 2;
+ }
+ case CD_PROP_FLOAT3: {
+ return 3;
+ }
+ case CD_PROP_COLOR: {
+ return 4;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
+static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_INT32: {
+ return GPU_FETCH_INT_TO_FLOAT;
+ }
+ case CD_PROP_COLOR: {
+ return GPU_FETCH_INT_TO_FLOAT_UNIT;
+ }
+ default: {
+ return GPU_FETCH_FLOAT;
+ }
+ }
+}
+
+static GPUVertCompType get_comp_type_for_type(CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_INT32: {
+ return GPU_COMP_I32;
+ }
+ case CD_PROP_COLOR: {
+ return GPU_COMP_U16;
+ }
+ default: {
+ return GPU_COMP_F32;
+ }
+ }
+}
+
+static void init_vbo_for_attribute(const MeshRenderData *mr,
+ GPUVertBuf *vbo,
+ const DRW_AttributeRequest &request)
+{
+ GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type);
+ GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type);
+ const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type);
+ /* We should not be here if the attribute type is not supported. */
+ BLI_assert(comp_size != 0);
+
+ const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain);
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(
+ custom_data, request.cd_type, request.layer_index);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* Attributes use auto-name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+ GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode);
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, static_cast<uint32_t>(mr->loop_len));
+}
+
+template<typename AttributeType, typename VBOType>
+static void fill_vertbuf_with_attribute(const MeshRenderData *mr,
+ VBOType *vbo_data,
+ const DRW_AttributeRequest &request)
+{
+ const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain);
+ BLI_assert(custom_data);
+ const int layer_index = request.layer_index;
+
+ const MPoly *mpoly = mr->mpoly;
+ const MLoop *mloop = mr->mloop;
+
+ const AttributeType *attr_data = static_cast<AttributeType *>(
+ CustomData_get_layer_n(custom_data, request.cd_type, layer_index));
+
+ using converter = attribute_type_converter<AttributeType, VBOType>;
+
+ switch (request.domain) {
+ default: {
+ BLI_assert(false);
+ break;
+ }
+ case ATTR_DOMAIN_POINT: {
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) {
+ *vbo_data = converter::convert_value(attr_data[mloop->v]);
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) {
+ *vbo_data = converter::convert_value(attr_data[ml_index]);
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) {
+ *vbo_data = converter::convert_value(attr_data[mloop->e]);
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ const MPoly &poly = mpoly[mp_index];
+ const VBOType value = converter::convert_value(attr_data[mp_index]);
+ for (int l = 0; l < poly.totloop; l++) {
+ *vbo_data++ = value;
+ }
+ }
+ break;
+ }
+ }
+}
+
+template<typename AttributeType, typename VBOType>
+static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr,
+ VBOType *&vbo_data,
+ const DRW_AttributeRequest &request)
+{
+ const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain);
+ BLI_assert(custom_data);
+ const int layer_index = request.layer_index;
+
+ int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index);
+
+ using converter = attribute_type_converter<AttributeType, VBOType>;
+
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const AttributeType *attr_data = nullptr;
+ if (request.domain == ATTR_DOMAIN_POINT) {
+ attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs));
+ }
+ else if (request.domain == ATTR_DOMAIN_CORNER) {
+ attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs));
+ }
+ else if (request.domain == ATTR_DOMAIN_FACE) {
+ attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs));
+ }
+ else if (request.domain == ATTR_DOMAIN_EDGE) {
+ attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs));
+ }
+ else {
+ BLI_assert(false);
+ continue;
+ }
+ *vbo_data = converter::convert_value(*attr_data);
+ vbo_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+}
+
+template<typename AttributeType, typename VBOType = AttributeType>
+static void extract_attr_generic(const MeshRenderData *mr,
+ GPUVertBuf *vbo,
+ const DRW_AttributeRequest &request)
+{
+ VBOType *vbo_data = static_cast<VBOType *>(GPU_vertbuf_get_data(vbo));
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ fill_vertbuf_with_attribute_bm<AttributeType>(mr, vbo_data, request);
+ }
+ else {
+ fill_vertbuf_with_attribute<AttributeType>(mr, vbo_data, request);
+ }
+}
+
+static void extract_attr_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data),
+ int index)
+{
+ const DRW_MeshAttributes *attrs_used = &cache->attr_used;
+ const DRW_AttributeRequest &request = attrs_used->requests[index];
+
+ GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
+
+ init_vbo_for_attribute(mr, vbo, request);
+
+ /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by
+ * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the
+ * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar
+ * texture as for volume attribute, so we can control the conversion ourselves. */
+ switch (request.cd_type) {
+ case CD_PROP_BOOL: {
+ extract_attr_generic<bool, float3>(mr, vbo, request);
+ break;
+ }
+ case CD_PROP_INT32: {
+ extract_attr_generic<int32_t, float3>(mr, vbo, request);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ extract_attr_generic<float, float3>(mr, vbo, request);
+ break;
+ }
+ case CD_PROP_FLOAT2: {
+ extract_attr_generic<float2>(mr, vbo, request);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ extract_attr_generic<float3>(mr, vbo, request);
+ break;
+ }
+ case CD_PROP_COLOR: {
+ extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request);
+ break;
+ }
+ default: {
+ BLI_assert(false);
+ }
+ }
+}
+
+/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to
+ * extract. The overall API does not allow us to pass this in a convenient way. */
+#define EXTRACT_INIT_WRAPPER(index) \
+ static void extract_attr_init##index( \
+ const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \
+ { \
+ extract_attr_init(mr, cache, buf, tls_data, index); \
+ }
+
+EXTRACT_INIT_WRAPPER(0)
+EXTRACT_INIT_WRAPPER(1)
+EXTRACT_INIT_WRAPPER(2)
+EXTRACT_INIT_WRAPPER(3)
+EXTRACT_INIT_WRAPPER(4)
+EXTRACT_INIT_WRAPPER(5)
+EXTRACT_INIT_WRAPPER(6)
+EXTRACT_INIT_WRAPPER(7)
+EXTRACT_INIT_WRAPPER(8)
+EXTRACT_INIT_WRAPPER(9)
+EXTRACT_INIT_WRAPPER(10)
+EXTRACT_INIT_WRAPPER(11)
+EXTRACT_INIT_WRAPPER(12)
+EXTRACT_INIT_WRAPPER(13)
+EXTRACT_INIT_WRAPPER(14)
+
+template<int index> constexpr MeshExtract create_extractor_attr(ExtractInitFn fn)
+{
+ MeshExtract extractor = {nullptr};
+ extractor.init = fn;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = 0;
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+#define CREATE_EXTRACTOR_ATTR(index) \
+ blender::draw::create_extractor_attr<index>(blender::draw::extract_attr_init##index)
+
+const MeshExtract extract_attr[GPU_MAX_ATTR] = {
+ CREATE_EXTRACTOR_ATTR(0),
+ CREATE_EXTRACTOR_ATTR(1),
+ CREATE_EXTRACTOR_ATTR(2),
+ CREATE_EXTRACTOR_ATTR(3),
+ CREATE_EXTRACTOR_ATTR(4),
+ CREATE_EXTRACTOR_ATTR(5),
+ CREATE_EXTRACTOR_ATTR(6),
+ CREATE_EXTRACTOR_ATTR(7),
+ CREATE_EXTRACTOR_ATTR(8),
+ CREATE_EXTRACTOR_ATTR(9),
+ CREATE_EXTRACTOR_ATTR(10),
+ CREATE_EXTRACTOR_ATTR(11),
+ CREATE_EXTRACTOR_ATTR(12),
+ CREATE_EXTRACTOR_ATTR(13),
+ CREATE_EXTRACTOR_ATTR(14),
+};
+}
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 2c7770c8e72..f8878eb2617 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
@@ -43,9 +43,7 @@ static void extract_vcol_init(const MeshRenderData *mr,
GPU_vertformat_deinterleave(&format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
uint32_t vcol_layers = cache->cd_used.vcol;
- uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
@@ -64,42 +62,14 @@ static void extract_vcol_init(const MeshRenderData *mr,
}
/* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
- CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
+ /* We only do `vcols` that are not overridden by `uvs`. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
GPU_vertformat_alias_add(&format, attr_name);
}
}
}
- /* Sculpt Vertex Colors */
- if (U.experimental.use_sculpt_vertex_colors) {
- for (int i = 0; i < 8; i++) {
- if (svcol_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "c");
- }
- if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "ac");
- }
- /* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs`. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- }
- }
- }
- }
-
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -108,7 +78,6 @@ static void extract_vcol_init(const MeshRenderData *mr,
};
gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
- MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP);
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
@@ -139,35 +108,6 @@ static void extract_vcol_init(const MeshRenderData *mr,
}
}
}
-
- if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
- vcol_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
- vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
- }
- }
- }
}
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index afbd9b2c92d..69fabd004cc 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -51,6 +51,7 @@
#include "BKE_mask.h"
#include "BKE_nla.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -2522,10 +2523,10 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Find / Set Filter Operator ******************** */
+/* ****************** Select Filter Textbox Operator ******************** */
/* XXX: make this generic? */
-static bool animchannels_find_poll(bContext *C)
+static bool animchannels_select_filter_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
@@ -2537,64 +2538,62 @@ static bool animchannels_find_poll(bContext *C)
return ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA);
}
-/* find_invoke() - Get initial channels */
-static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int animchannels_select_filter_invoke(struct bContext *C,
+ struct wmOperator *op,
+ const struct wmEvent *UNUSED(event))
{
- bAnimContext ac;
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region_ctx = CTX_wm_region(C);
+ ARegion *region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS);
- /* get editor data */
- if (ANIM_animdata_get_context(C, &ac) == 0) {
- return OPERATOR_CANCELLED;
+ CTX_wm_region_set(C, region_channels);
+
+ /* Show the channel region if it's hidden. This means that direct activation of the input field
+ * is impossible, as it may not exist yet. For that reason, the actual activation is deferred to
+ * the modal callback function; by the time it runs, the screen has been redrawn and the UI
+ * element is there to activate. */
+ if (region_channels->flag & RGN_FLAG_HIDDEN) {
+ ED_region_toggle_hidden(C, region_channels);
+ ED_region_tag_redraw(region_channels);
}
- /* set initial filter text, and enable filter */
- RNA_string_set(op->ptr, "query", ac.ads->searchstr);
+ WM_event_add_modal_handler(C, op);
- /* defer to popup */
- return WM_operator_props_popup(C, op, event);
+ CTX_wm_region_set(C, region_ctx);
+ return OPERATOR_RUNNING_MODAL;
}
-/* find_exec() - Called to set the value */
-static int animchannels_find_exec(bContext *C, wmOperator *op)
+static int animchannels_select_filter_modal(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(event))
{
bAnimContext ac;
-
- /* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
- /* update filter text */
- RNA_string_get(op->ptr, "query", ac.ads->searchstr);
-
- /* redraw */
- WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+ ARegion *region = CTX_wm_region(C);
+ if (UI_textbutton_activate_rna(C, region, ac.ads, "filter_text")) {
+ /* Redraw to make sure it shows the cursor after activating */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+ }
return OPERATOR_FINISHED;
}
-static void ANIM_OT_channels_find(wmOperatorType *ot)
+static void ANIM_OT_channels_select_filter(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Find Channels";
- ot->idname = "ANIM_OT_channels_find";
- ot->description = "Filter the set of channels shown to only include those with matching names";
+ ot->name = "Filter Channels";
+ ot->idname = "ANIM_OT_channels_select_filter";
+ ot->description =
+ "Start entering text which filters the set of channels shown to only include those with "
+ "matching names";
/* callbacks */
- ot->invoke = animchannels_find_invoke;
- ot->exec = animchannels_find_exec;
- ot->poll = animchannels_find_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- ot->prop = RNA_def_string(ot->srna,
- "query",
- "Query",
- sizeof(((bDopeSheet *)NULL)->searchstr),
- "",
- "Text to search for in channel names");
+ ot->invoke = animchannels_select_filter_invoke;
+ ot->modal = animchannels_select_filter_modal;
+ ot->poll = animchannels_select_filter_poll;
}
/* ********************** Select All Operator *********************** */
@@ -3563,7 +3562,7 @@ void ED_operatortypes_animchannels(void)
WM_operatortype_append(ANIM_OT_channel_select_keys);
WM_operatortype_append(ANIM_OT_channels_rename);
- WM_operatortype_append(ANIM_OT_channels_find);
+ WM_operatortype_append(ANIM_OT_channels_select_filter);
WM_operatortype_append(ANIM_OT_channels_setting_enable);
WM_operatortype_append(ANIM_OT_channels_setting_disable);
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index bd799c00373..937385f9ffa 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -673,11 +673,7 @@ static EditBone *get_nearest_editbonepoint(
}
if (use_cycle) {
- static int last_mval[2] = {-100, -100};
- if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) {
- use_cycle = false;
- }
- copy_v2_v2_int(last_mval, vc->mval);
+ use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
}
const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle);
diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh
index 8b8fc4d3574..8da8fc0d6c9 100644
--- a/source/blender/editors/asset/ED_asset_catalog.hh
+++ b/source/blender/editors/asset/ED_asset_catalog.hh
@@ -37,3 +37,6 @@ void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogI
void ED_asset_catalog_rename(AssetLibrary *library,
blender::bke::CatalogID catalog_id,
blender::StringRefNull new_name);
+void ED_asset_catalog_move(AssetLibrary *library,
+ blender::bke::CatalogID src_catalog_id,
+ blender::bke::CatalogID dst_parent_catalog_id);
diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h
index bab1d1bf8a5..8e6a8e11d69 100644
--- a/source/blender/editors/asset/ED_asset_mark_clear.h
+++ b/source/blender/editors/asset/ED_asset_mark_clear.h
@@ -26,6 +26,7 @@ extern "C" {
struct ID;
struct bContext;
+struct Main;
/**
* Mark the datablock as asset.
@@ -52,6 +53,8 @@ void ED_asset_generate_preview(const struct bContext *C, struct ID *id);
* \return whether the asset metadata was actually removed; false when the ID was not an asset. */
bool ED_asset_clear_id(struct ID *id);
+void ED_assets_pre_save(struct Main *bmain);
+
bool ED_asset_can_mark_single_from_context(const struct bContext *C);
#ifdef __cplusplus
diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h
index 5629ae189c0..36cbb4591e9 100644
--- a/source/blender/editors/asset/ED_asset_type.h
+++ b/source/blender/editors/asset/ED_asset_type.h
@@ -29,7 +29,8 @@ extern "C" {
struct ID;
bool ED_asset_type_id_is_non_experimental(const struct ID *id);
-#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS (FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO)
+#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \
+ (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO)
/**
* Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset,
@@ -51,7 +52,7 @@ int64_t ED_asset_types_supported_as_filter_flags(void);
* strings with this (not all UI code supports dynamic strings nicely).
* Should start with a consonant, so usages can prefix it with "a" (not "an").
*/
-#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Pose Action, or World"
+#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World"
#ifdef __cplusplus
}
diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc
index dae960cbb0a..8e1e5be2e47 100644
--- a/source/blender/editors/asset/intern/asset_catalog.cc
+++ b/source/blender/editors/asset/intern/asset_catalog.cc
@@ -107,17 +107,44 @@ void ED_asset_catalog_rename(::AssetLibrary *library,
AssetCatalog *catalog = catalog_service->find_catalog(catalog_id);
- AssetCatalogPath new_path = catalog->path.parent();
- new_path = new_path / StringRef(new_name);
+ const AssetCatalogPath new_path = catalog->path.parent() / StringRef(new_name);
+ const AssetCatalogPath clean_new_path = new_path.cleanup();
- if (new_path == catalog->path) {
+ if (new_path == catalog->path || clean_new_path == catalog->path) {
/* Nothing changed, so don't bother renaming for nothing. */
return;
}
catalog_service->undo_push();
catalog_service->tag_has_unsaved_changes(catalog);
- catalog_service->update_catalog_path(catalog_id, new_path);
+ catalog_service->update_catalog_path(catalog_id, clean_new_path);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+}
+
+void ED_asset_catalog_move(::AssetLibrary *library,
+ const CatalogID src_catalog_id,
+ const CatalogID dst_parent_catalog_id)
+{
+ bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library);
+ if (!catalog_service) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id);
+ AssetCatalog *dst_catalog = catalog_service->find_catalog(dst_parent_catalog_id);
+
+ const AssetCatalogPath new_path = dst_catalog->path / StringRef(src_catalog->path.name());
+ const AssetCatalogPath clean_new_path = new_path.cleanup();
+
+ if (new_path == src_catalog->path || clean_new_path == src_catalog->path) {
+ /* Nothing changed, so don't bother renaming for nothing. */
+ return;
+ }
+
+ catalog_service->undo_push();
+ catalog_service->tag_has_unsaved_changes(src_catalog);
+ catalog_service->update_catalog_path(src_catalog_id, clean_new_path);
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
}
diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc
index 4be7376a1c3..eb254dcd28b 100644
--- a/source/blender/editors/asset/intern/asset_mark_clear.cc
+++ b/source/blender/editors/asset/intern/asset_mark_clear.cc
@@ -25,7 +25,9 @@
#include "BKE_asset.h"
#include "BKE_context.h"
+#include "BKE_idtype.h"
#include "BKE_lib_id.h"
+#include "BKE_main.h"
#include "BLO_readfile.h"
@@ -52,7 +54,9 @@ bool ED_asset_mark_id(ID *id)
id_fake_user_set(id);
+ const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id);
id->asset_data = BKE_asset_metadata_create();
+ id->asset_data->local_type_info = id_type_info->asset_type_info;
/* Important for asset storage to update properly! */
ED_assetlist_storage_tag_main_data_dirty();
@@ -79,6 +83,21 @@ bool ED_asset_clear_id(ID *id)
return true;
}
+void ED_assets_pre_save(struct Main *bmain)
+{
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!id->asset_data || !id->asset_data->local_type_info) {
+ continue;
+ }
+
+ if (id->asset_data->local_type_info->pre_save_fn) {
+ id->asset_data->local_type_info->pre_save_fn(id, id->asset_data);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
bool ED_asset_can_mark_single_from_context(const bContext *C)
{
/* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index e2ae3b3893b..d2fd8ab88a4 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -427,7 +427,12 @@ static int asset_catalog_new_exec(bContext *C, wmOperator *op)
struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
- ED_asset_catalog_add(asset_library, "Catalog", parent_path);
+ blender::bke::AssetCatalog *new_catalog = ED_asset_catalog_add(
+ asset_library, "Catalog", parent_path);
+
+ if (sfile) {
+ ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id);
+ }
MEM_freeN(parent_path);
@@ -554,7 +559,7 @@ static bool asset_catalog_redo_poll(bContext *C)
static void ASSET_OT_catalog_redo(struct wmOperatorType *ot)
{
/* identifiers */
- ot->name = "redo Catalog Edits";
+ ot->name = "Redo Catalog Edits";
ot->description = "Redo the last undone edit to the asset catalogs";
ot->idname = "ASSET_OT_catalog_redo";
diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc
index cdff538a712..028c0cb9ffc 100644
--- a/source/blender/editors/asset/intern/asset_type.cc
+++ b/source/blender/editors/asset/intern/asset_type.cc
@@ -30,7 +30,7 @@ bool ED_asset_type_id_is_non_experimental(const ID *id)
{
/* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and
* #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */
- return ELEM(GS(id->name), ID_MA, ID_AC, ID_WO);
+ return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO);
}
bool ED_asset_type_is_supported(const ID *id)
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index 07117c0153b..aed58e31798 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -220,7 +220,7 @@ static void cage3d_draw_circle_wire(const float r[3],
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", line_width * U.pixelsize);
- imm_draw_cube_wire_3d(pos, (float[3]){0}, r);
+ imm_draw_cube_wire_3d(pos, (const float[3]){0}, r);
#if 0
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index 33532bd0549..93ee6ec2d81 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -225,8 +225,10 @@ static void snap_gizmo_setup(wmGizmo *gz)
gz->flag |= WM_GIZMO_NO_TOOLTIP;
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
snap_gizmo->snap_state = ED_view3d_cursor_snap_active();
- snap_gizmo->snap_state->draw_point = true;
- snap_gizmo->snap_state->draw_plane = false;
+ if (snap_gizmo->snap_state) {
+ snap_gizmo->snap_state->draw_point = true;
+ snap_gizmo->snap_state->draw_plane = false;
+ }
rgba_float_to_uchar(snap_gizmo->snap_state->color_point, gz->color);
}
@@ -284,7 +286,9 @@ static int snap_gizmo_invoke(bContext *UNUSED(C),
static void snap_gizmo_free(wmGizmo *gz)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state);
+ if (snap_gizmo->snap_state) {
+ ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state);
+ }
}
static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 3beabaf2d1d..68b6e44371c 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -23,6 +23,8 @@
#pragma once
+#include "DNA_uuid_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -145,7 +147,9 @@ bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile);
struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile);
-/* Activate the file that corresponds to the given ID.
+void ED_fileselect_activate_asset_catalog(const struct SpaceFile *sfile, bUUID catalog_id);
+
+/* Activate and select the file that corresponds to the given ID.
* Pass deferred=true to wait for the next refresh before activating. */
void ED_fileselect_activate_by_id(struct SpaceFile *sfile,
struct ID *asset_id,
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 1d51a3e77cf..e68617f7867 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -49,7 +49,7 @@ typedef enum {
NODE_RIGHT = 8,
} NodeBorder;
-#define NODE_GRID_STEPS 5
+#define NODE_GRID_STEP_SIZE 10
#define NODE_EDGE_PAN_INSIDE_PAD 2
#define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */
#define NODE_EDGE_PAN_SPEED_RAMP 1
@@ -64,7 +64,6 @@ void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]);
int ED_node_tree_path_length(struct SpaceNode *snode);
void ED_node_tree_path_get(struct SpaceNode *snode, char *value);
-void ED_node_tree_path_get_fixedbuf(struct SpaceNode *snode, char *value, int max_length);
void ED_node_tree_start(struct SpaceNode *snode,
struct bNodeTree *ntree,
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 083d167c573..2a557f1abd3 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -122,6 +122,8 @@ void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChil
struct Object *ob_parent_recurse,
int mode);
+void ED_object_xform_array_m4(struct Object **objects, uint objects_len, const float matrix[4][4]);
+
/* object_ops.c */
void ED_operatortypes_object(void);
void ED_operatormacros_object(void);
@@ -202,7 +204,7 @@ void ED_object_parent(struct Object *ob,
const char *substr);
char *ED_object_ot_drop_named_material_tooltip(struct bContext *C,
struct PointerRNA *properties,
- const struct wmEvent *event);
+ const int mval[2]);
/* bitflags for enter/exit editmode */
enum {
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index b90c7f27c57..08b6c02a8d0 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -317,6 +317,7 @@ bool ED_operator_regionactive(struct bContext *C);
bool ED_operator_scene(struct bContext *C);
bool ED_operator_scene_editable(struct bContext *C);
bool ED_operator_objectmode(struct bContext *C);
+bool ED_operator_objectmode_poll_msg(struct bContext *C);
bool ED_operator_view3d_active(struct bContext *C);
bool ED_operator_region_view3d_active(struct bContext *C);
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index 7002db163b6..62d1dfbf0b1 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -44,6 +44,7 @@ typedef enum {
SNAP_NOT_SELECTED = 1,
SNAP_NOT_ACTIVE = 2,
SNAP_ONLY_ACTIVE = 3,
+ SNAP_SELECTABLE = 4,
} eSnapSelect;
typedef enum {
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 67c470a005f..6d20044d8cf 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -276,12 +276,15 @@ typedef struct V3DSnapCursorState {
eV3DPlaceOrient plane_orient;
uchar color_line[4];
uchar color_point[4];
+ uchar color_box[4];
float *prevpoint;
+ float box_dimensions[3];
short snap_elem_force; /* If zero, use scene settings. */
short plane_axis;
bool use_plane_axis_auto;
bool draw_point;
bool draw_plane;
+ bool draw_box;
} V3DSnapCursorState;
void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state);
@@ -293,7 +296,6 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state,
const struct bContext *C,
const int x,
const int y);
-
struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene);
void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d,
const float loc_prev[3],
@@ -302,7 +304,6 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d,
const uchar color_line[4],
const uchar color_point[4],
const short snap_elem_type);
-void ED_view3d_cursor_snap_exit(void);
/* view3d_iterators.c */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 67d034f4ab6..725c9921d13 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -37,6 +37,7 @@ extern "C" {
struct ARegion;
struct AssetFilterSettings;
struct AssetHandle;
+struct AssetMetaData;
struct AutoComplete;
struct EnumPropertyItem;
struct FileDirEntry;
@@ -246,7 +247,7 @@ enum {
#define UI_DEFAULT_TEXT_POINTS 11
/* Larger size used for title text. */
-#define UI_DEFAULT_TITLE_POINTS 12
+#define UI_DEFAULT_TITLE_POINTS 11
#define UI_PANEL_WIDTH 340
#define UI_COMPACT_PANEL_WIDTH 160
@@ -423,10 +424,6 @@ typedef enum eButGradientType {
* Functions to draw various shapes, taking theme settings into account.
* Used for code that draws its own UI style elements. */
-void UI_draw_anti_tria(
- float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]);
-void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]);
-
void UI_draw_roundbox_corner_set(int type);
void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4]);
void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4]);
@@ -437,12 +434,6 @@ void UI_draw_roundbox_3ub_alpha(const struct rctf *rect,
unsigned char alpha);
void UI_draw_roundbox_3fv_alpha(
const struct rctf *rect, bool filled, float rad, const float col[3], float alpha);
-void UI_draw_roundbox_shade_x(const struct rctf *rect,
- bool filled,
- float rad,
- float shadetop,
- float shadedown,
- const float col[4]);
void UI_draw_roundbox_4fv_ex(const struct rctf *rect,
const float inner1[4],
const float inner2[4],
@@ -785,6 +776,7 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id);
void UI_but_drag_set_asset(uiBut *but,
const struct AssetHandle *asset,
const char *path,
+ struct AssetMetaData *metadata,
int import_type, /* eFileAssetImportType */
int icon,
struct ImBuf *imb,
@@ -796,7 +788,8 @@ void UI_but_drag_set_value(uiBut *but);
void UI_but_drag_set_image(
uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free);
-bool UI_but_active_drop_name(struct bContext *C);
+uiBut *UI_but_active_drop_name_button(const struct bContext *C);
+bool UI_but_active_drop_name(const struct bContext *C);
bool UI_but_active_drop_color(struct bContext *C);
void UI_but_flag_enable(uiBut *but, int flag);
@@ -2687,7 +2680,12 @@ void UI_fontstyle_draw_simple_backdrop(const struct uiFontStyle *fs,
const float col_fg[4],
const float col_bg[4]);
-int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str);
+int UI_fontstyle_string_width(const struct uiFontStyle *fs,
+ const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2);
+int UI_fontstyle_string_width_with_block_aspect(const struct uiFontStyle *fs,
+ const char *str,
+ const float aspect) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2);
int UI_fontstyle_height_max(const struct uiFontStyle *fs);
void UI_draw_icon_tri(float x, float y, char dir, const float[4]);
@@ -2780,12 +2778,12 @@ void UI_interface_tag_script_reload(void);
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag);
+bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_);
+bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
+ const struct wmDrag *drag,
+ const char **r_disabled_hint);
+char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag);
bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags);
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item,
- const struct bContext *C,
- const struct wmDrag *drag,
- const struct wmEvent *event);
bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle);
void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle);
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
index 5edccfa8c88..b14ee6c4a59 100644
--- a/source/blender/editors/include/UI_interface.hh
+++ b/source/blender/editors/include/UI_interface.hh
@@ -23,15 +23,40 @@
#include <memory>
#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "UI_resources.h"
namespace blender::nodes::geometry_nodes_eval_log {
struct GeometryAttributeInfo;
}
struct uiBlock;
+struct StructRNA;
+struct uiSearchItems;
+
namespace blender::ui {
+
class AbstractTreeView;
+/**
+ * An item in a breadcrumb-like context. Currently this struct is very simple, but more
+ * could be added to it in the future, to support interactivity or tooltips, for example.
+ */
+struct ContextPathItem {
+ /* Text to display in the UI. */
+ std::string name;
+ /* #BIFIconID */
+ int icon;
+};
+
+void context_path_add_generic(Vector<ContextPathItem> &path,
+ StructRNA &rna_type,
+ void *ptr,
+ const BIFIconID icon_override = ICON_NONE);
+
+void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path);
+
void attribute_search_add_items(
StringRefNull str,
const bool is_output,
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index b1ec22c57a6..0d18eedeac9 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -48,6 +48,8 @@ namespace blender::ui {
class AbstractTreeView;
class AbstractTreeViewItem;
+class AbstractTreeViewItemDropController;
+class AbstractTreeViewItemDragController;
/* ---------------------------------------------------------------------- */
/** \name Tree-View Item Container
@@ -242,17 +244,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* arguments for checking if the item is currently in an active state.
*/
virtual void is_active(IsActiveFn is_active_fn);
- virtual bool on_drop(const wmDrag &drag);
- virtual bool can_drop(const wmDrag &drag) const;
- /**
- * Custom text to display when dragging over a tree item. Should explain what happens when
- * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop()
- * returns true, so the implementing override doesn't have to check that again.
- * The returned value must be a translated string.
- */
- virtual std::string drop_tooltip(const bContext &C,
- const wmDrag &drag,
- const wmEvent &event) const;
+
/**
* Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if
* another item is already being renamed.
@@ -282,6 +274,20 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
virtual bool matches(const AbstractTreeViewItem &other) const;
+ /**
+ * If an item wants to support being dragged, it has to return a drag controller here.
+ * That is an object implementing #AbstractTreeViewItemDragController.
+ */
+ virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const;
+ /**
+ * If an item wants to support dropping data into it, it has to return a drop controller here.
+ * That is an object implementing #AbstractTreeViewItemDropController.
+ *
+ * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop
+ * controller around currently. So it can not contain persistent state.
+ */
+ virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const;
+
void begin_renaming();
void end_renaming();
@@ -344,6 +350,62 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
/** \} */
/* ---------------------------------------------------------------------- */
+/** \name Drag 'n Drop
+ * \{ */
+
+/**
+ * Class to enable dragging a tree-item. An item can return a drop controller for itself via a
+ * custom implementation of #AbstractTreeViewItem::create_drag_controller().
+ */
+class AbstractTreeViewItemDragController {
+ public:
+ virtual ~AbstractTreeViewItemDragController() = default;
+
+ virtual int get_drag_type() const = 0;
+ virtual void *create_drag_data() const = 0;
+};
+
+/**
+ * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this
+ * item. An item can return a drop controller for itself via a custom implementation of
+ * #AbstractTreeViewItem::create_drop_controller().
+ */
+class AbstractTreeViewItemDropController {
+ protected:
+ AbstractTreeView &tree_view_;
+
+ public:
+ AbstractTreeViewItemDropController(AbstractTreeView &tree_view);
+ virtual ~AbstractTreeViewItemDropController() = default;
+
+ /**
+ * Check if the data dragged with \a drag can be dropped on the item this controller is for.
+ * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping
+ * isn't possible on this item. Shouldn't be done too aggressively, e.g.
+ * don't set this if the drag-type can't be dropped here; only if it can
+ * but there's another reason it can't be dropped.
+ * Can assume this is a non-null pointer.
+ */
+ virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
+ /**
+ * Custom text to display when dragging over a tree item. Should explain what happens when
+ * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop()
+ * returns true, so the implementing override doesn't have to check that again.
+ * The returned value must be a translated string.
+ */
+ virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
+ /**
+ * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this
+ * controller is for.
+ */
+ virtual bool on_drop(const wmDrag &drag) = 0;
+
+ template<class TreeViewType> inline TreeViewType &tree_view() const;
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
/** \name Predefined Tree-View Item Types
*
* Common, Basic Tree-View Item Types.
@@ -357,9 +419,10 @@ class BasicTreeViewItem : public AbstractTreeViewItem {
using ActivateFn = std::function<void(BasicTreeViewItem &new_active)>;
BIFIconID icon;
- BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE);
+ explicit BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE);
void build_row(uiLayout &row) override;
+ void add_label(uiLayout &layout, StringRefNull label_override = "");
void on_activate(ActivateFn fn);
protected:
@@ -390,4 +453,11 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args)
add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
}
+template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const
+{
+ static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value,
+ "Type must derive from and implement the AbstractTreeView interface");
+ return static_cast<TreeViewType &>(tree_view_);
+}
+
} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 13895879f01..122e5a7d839 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -147,6 +147,10 @@ void UI_view2d_view_restore(const struct bContext *C);
/* grid drawing */
void UI_view2d_multi_grid_draw(
const struct View2D *v2d, int colorid, float step, int level_size, int totlevels);
+void UI_view2d_dot_grid_draw(const struct View2D *v2d,
+ int grid_color_id,
+ float step,
+ int grid_levels);
void UI_view2d_draw_lines_y__values(const struct View2D *v2d);
void UI_view2d_draw_lines_x__values(const struct View2D *v2d);
@@ -311,6 +315,9 @@ typedef struct View2DEdgePanData {
/** View2d we're operating in. */
struct View2D *v2d;
+ /** Panning should only start once being in the inside rect once (e.g. adding nodes can happen
+ * outside). */
+ bool enabled;
/** Inside distance in UI units from the edge of the region within which to start panning. */
float inside_pad;
/** Outside distance in UI units from the edge of the region at which to stop panning. */
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index b2659f5ed52..84172c7efce 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -43,6 +43,7 @@ set(SRC
interface_anim.c
interface_button_group.c
interface_context_menu.c
+ interface_context_path.cc
interface_draw.c
interface_dropboxes.cc
interface_eyedropper.c
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 5068969946a..62c84ed38ff 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -6231,12 +6231,13 @@ void UI_but_drag_set_id(uiBut *but, ID *id)
void UI_but_drag_set_asset(uiBut *but,
const AssetHandle *asset,
const char *path,
+ struct AssetMetaData *metadata,
int import_type,
int icon,
struct ImBuf *imb,
float scale)
{
- wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, path, import_type);
+ wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type);
/* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the
* #wmDropBox.
diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc
new file mode 100644
index 00000000000..b0f8d186afa
--- /dev/null
+++ b/source/blender/editors/interface/interface_context_path.cc
@@ -0,0 +1,85 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "BLI_vector.hh"
+
+#include "BKE_screen.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_interface.hh"
+#include "UI_resources.h"
+
+#include "WM_api.h"
+
+namespace blender::ui {
+
+void context_path_add_generic(Vector<ContextPathItem> &path,
+ StructRNA &rna_type,
+ void *ptr,
+ const BIFIconID icon_override)
+{
+ /* Add the null check here to make calling functions less verbose. */
+ if (!ptr) {
+ return;
+ }
+
+ PointerRNA rna_ptr;
+ RNA_pointer_create(nullptr, &rna_type, ptr, &rna_ptr);
+ char name[128];
+ RNA_struct_name_get_alloc(&rna_ptr, name, sizeof(name), nullptr);
+
+ /* Use a blank icon by default to check whether to retrieve it automatically from the type. */
+ const BIFIconID icon = icon_override == ICON_NONE ?
+ static_cast<BIFIconID>(RNA_struct_ui_icon(rna_ptr.type)) :
+ icon_override;
+
+ path.append({name, static_cast<int>(icon)});
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Breadcrumb Template
+ * \{ */
+
+void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path)
+{
+ uiLayout *row = uiLayoutRow(&layout, true);
+ uiLayoutSetAlignment(&layout, UI_LAYOUT_ALIGN_LEFT);
+
+ for (const int i : context_path.index_range()) {
+ uiLayout *sub_row = uiLayoutRow(row, true);
+ uiLayoutSetAlignment(sub_row, UI_LAYOUT_ALIGN_LEFT);
+
+ if (i > 0) {
+ uiItemL(sub_row, "", ICON_RIGHTARROW_THIN);
+ }
+ uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon);
+ }
+}
+
+} // namespace blender::ui
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 6cb0fcd499c..e45a5fc61c6 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -178,35 +178,6 @@ void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float
UI_draw_roundbox_4fv_ex(rect, (filled) ? col : NULL, NULL, 1.0f, col, U.pixelsize, rad);
}
-/* linear horizontal shade within button or in outline */
-/* view2d scrollers use it */
-void UI_draw_roundbox_shade_x(
- const rctf *rect, bool filled, float rad, float shadetop, float shadedown, const float col[4])
-{
- float inner1[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- float inner2[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- float outline[4];
-
- if (filled) {
- inner1[0] = min_ff(1.0f, col[0] + shadetop);
- inner1[1] = min_ff(1.0f, col[1] + shadetop);
- inner1[2] = min_ff(1.0f, col[2] + shadetop);
- inner1[3] = 1.0f;
- inner2[0] = max_ff(0.0f, col[0] + shadedown);
- inner2[1] = max_ff(0.0f, col[1] + shadedown);
- inner2[2] = max_ff(0.0f, col[2] + shadedown);
- inner2[3] = 1.0f;
- }
-
- /* TODO: non-filled box don't have gradients. Just use middle color. */
- outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f);
- outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f);
- outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f);
- outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f);
-
- UI_draw_roundbox_4fv_ex(rect, inner1, inner2, 1.0f, outline, U.pixelsize, rad);
-}
-
void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4])
{
const int ofs_y = 4 * U.pixelsize;
diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc
index 62250a34cf4..81a1354cbe7 100644
--- a/source/blender/editors/interface/interface_dropboxes.cc
+++ b/source/blender/editors/interface/interface_dropboxes.cc
@@ -22,6 +22,10 @@
#include "DNA_space_types.h"
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
#include "WM_api.h"
#include "UI_interface.h"
@@ -35,24 +39,43 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve
return false;
}
- return UI_tree_view_item_can_drop(hovered_tree_item, drag);
+ if (drag->free_disabled_info) {
+ MEM_SAFE_FREE(drag->disabled_info);
+ }
+
+ drag->free_disabled_info = false;
+ return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->disabled_info);
}
static char *ui_tree_view_drop_tooltip(bContext *C,
wmDrag *drag,
- const wmEvent *event,
+ const int xy[2],
wmDropBox *UNUSED(drop))
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region,
- event->xy);
+ const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy);
if (!hovered_tree_item) {
return nullptr;
}
- return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event);
+ return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag);
}
+/* ---------------------------------------------------------------------- */
+
+static bool ui_drop_name_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
+{
+ return UI_but_active_drop_name(C) && (drag->type == WM_DRAG_ID);
+}
+
+static void ui_drop_name_copy(wmDrag *drag, wmDropBox *drop)
+{
+ const ID *id = WM_drag_get_local_ID(drag, 0);
+ RNA_string_set(drop->ptr, "string", id->name + 2);
+}
+
+/* ---------------------------------------------------------------------- */
+
void ED_dropboxes_ui()
{
ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0);
@@ -63,4 +86,10 @@ void ED_dropboxes_ui()
nullptr,
nullptr,
ui_tree_view_drop_tooltip);
+ WM_dropbox_add(lb,
+ "UI_OT_drop_name",
+ ui_drop_name_poll,
+ ui_drop_name_copy,
+ WM_drag_free_imported_drag_ID,
+ nullptr);
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 52ab13e5cd0..51ebe5399b3 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -2145,6 +2145,12 @@ static bool ui_but_drag_init(bContext *C,
return false;
}
}
+ else if (but->type == UI_BTYPE_TREEROW) {
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
+ if (tree_row_but->tree_item) {
+ UI_tree_view_item_drag_start(C, tree_row_but->tree_item);
+ }
+ }
else {
wmDrag *drag = WM_event_start_drag(
C,
@@ -2437,39 +2443,6 @@ static void ui_apply_but(
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Button Drop Event
- * \{ */
-
-/* only call if event type is EVT_DROP */
-static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data)
-{
- ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */
-
- LISTBASE_FOREACH (wmDrag *, wmd, drags) {
- /* TODO: asset dropping. */
- if (wmd->type == WM_DRAG_ID) {
- /* align these types with UI_but_active_drop_name */
- if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
- ID *id = WM_drag_get_local_ID(wmd, 0);
-
- button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
-
- ui_textedit_string_set(but, data, id->name + 2);
-
- if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) {
- but->changed = true;
- ui_searchbox_update(C, data->searchbox, but, true);
- }
-
- button_activate_state(C, but, BUTTON_STATE_EXIT);
- }
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Button Copy & Paste
* \{ */
@@ -2666,15 +2639,9 @@ static void ui_but_copy_text(uiBut *but, char *output, int output_len_max)
static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
{
- button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
- ui_textedit_string_set(but, but->active, buf_paste);
-
- if (but->type == UI_BTYPE_SEARCH_MENU) {
- but->changed = true;
- ui_searchbox_update(C, data->searchbox, but, true);
- }
-
- button_activate_state(C, but, BUTTON_STATE_EXIT);
+ BLI_assert(but->active == data);
+ UNUSED_VARS_NDEBUG(data);
+ ui_but_set_string_interactive(C, but, buf_paste);
}
static void ui_but_copy_colorband(uiBut *but)
@@ -3018,6 +2985,24 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR],
/** \name Button Text Selection/Editing
* \{ */
+/**
+ * Use handling code to set a string for the button. Handles the case where the string is set for a
+ * search button while the search menu is open, so the results are updated accordingly.
+ * This is basically the same as pasting the string into the button.
+ */
+void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value)
+{
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ ui_textedit_string_set(but, but->active, value);
+
+ if (but->type == UI_BTYPE_SEARCH_MENU && but->active) {
+ but->changed = true;
+ ui_searchbox_update(C, but->active->searchbox, but, true);
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+}
+
void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but)
{
if (!but->active) {
@@ -4821,19 +4806,33 @@ static int ui_do_but_TREEROW(bContext *C,
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE) {
- if (event->val == KM_CLICK) {
- button_activate_state(C, but, BUTTON_STATE_EXIT);
- return WM_UI_HANDLER_BREAK;
- }
- if (event->val == KM_DBL_CLICK) {
- data->cancel = true;
+ switch (event->val) {
+ case KM_PRESS:
+ /* Extra icons have priority, don't mess with them. */
+ if (ui_but_extra_operator_icon_mouse_over_get(but, data, event)) {
+ return WM_UI_HANDLER_BREAK;
+ }
+ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+ data->dragstartx = event->xy[0];
+ data->dragstarty = event->xy[1];
+ return WM_UI_HANDLER_CONTINUE;
- UI_tree_view_item_begin_rename(tree_row_but->tree_item);
- ED_region_tag_redraw(CTX_wm_region(C));
- return WM_UI_HANDLER_BREAK;
+ case KM_CLICK:
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+
+ case KM_DBL_CLICK:
+ data->cancel = true;
+ UI_tree_view_item_begin_rename(tree_row_but->tree_item);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ return WM_UI_HANDLER_BREAK;
}
}
}
+ else if (data->state == BUTTON_STATE_WAIT_DRAG) {
+ /* Let "default" button handling take care of the drag logic. */
+ return ui_do_but_EXIT(C, but, data, event);
+ }
return WM_UI_HANDLER_CONTINUE;
}
@@ -7929,7 +7928,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
/* Only hard-coded stuff here, button interactions with configurable
* keymaps are handled using operators (see #ED_keymap_ui). */
- if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) {
+ 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) &&
@@ -7978,11 +7977,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
return WM_UI_HANDLER_BREAK;
}
- /* handle drop */
- if (event->type == EVT_DROP) {
- ui_but_drop(C, event, but, data);
- }
-
if ((data->state == BUTTON_STATE_HIGHLIGHT) &&
ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, EVT_PADENTER, EVT_RETKEY) &&
(event->val == KM_RELEASE) &&
@@ -10648,7 +10642,8 @@ static int ui_handle_menu_event(bContext *C,
menu->menuretval = UI_RETURN_OUT;
}
}
- else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) {
+ else if (saferct && !BLI_rctf_isect_pt(
+ &saferct->parent, (float)event->xy[0], (float)event->xy[1])) {
if (block->flag & UI_BLOCK_OUT_1) {
menu->menuretval = UI_RETURN_OK;
}
@@ -10779,7 +10774,7 @@ static int ui_handle_menu_event(bContext *C,
}
#endif
- /* Don't handle double click events, rehandle as regular press/release. */
+ /* Don't handle double click events, re-handle as regular press/release. */
if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) {
return retval;
}
@@ -11695,20 +11690,25 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen)
}
}
-/* returns true if highlighted button allows drop of names */
-/* called in region context */
-bool UI_but_active_drop_name(bContext *C)
+uiBut *UI_but_active_drop_name_button(const bContext *C)
{
ARegion *region = CTX_wm_region(C);
uiBut *but = ui_region_find_active_but(region);
if (but) {
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
- return true;
+ return but;
}
}
- return false;
+ return NULL;
+}
+
+/* returns true if highlighted button allows drop of names */
+/* called in region context */
+bool UI_but_active_drop_name(const bContext *C)
+{
+ return UI_but_active_drop_name_button(C) != NULL;
}
bool UI_but_active_drop_color(bContext *C)
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 0826157b5e6..f766bb1465f 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -680,6 +680,7 @@ extern bool ui_but_string_eval_number(struct bContext *C,
extern int ui_but_string_get_max_length(uiBut *but);
/* Clear & exit the active button's string. */
extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL();
+extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value);
extern uiBut *ui_but_drag_multi_edit_get(uiBut *but);
void ui_def_but_icon(uiBut *but, const int icon, const int flag);
@@ -1031,7 +1032,6 @@ enum {
struct GPUBatch *ui_batch_roundbox_widget_get(void);
struct GPUBatch *ui_batch_roundbox_shadow_get(void);
-void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]);
void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect);
void ui_draw_popover_back(struct ARegion *region,
struct uiStyle *style,
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index e54b261facd..25ba0e13487 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -351,12 +351,16 @@ static int ui_text_icon_width_ex(uiLayout *layout,
if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) {
layout->item.flag |= UI_ITEM_FIXED_SIZE;
}
- const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
+
float margin = pad_factor->text;
if (icon) {
margin += pad_factor->icon;
}
- return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin);
+
+ const float aspect = layout->root->block->aspect;
+ const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
+ return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) +
+ (int)ceilf(unit_x * margin);
}
return unit_x * 10;
}
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 1a1d52b0425..c962a1107ae 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1864,6 +1864,39 @@ static void UI_OT_drop_color(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Drop Name Operator
+ * \{ */
+
+static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ uiBut *but = UI_but_active_drop_name_button(C);
+ char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL);
+
+ if (str) {
+ ui_but_set_string_interactive(C, but, str);
+ MEM_freeN(str);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_drop_name(wmOperatorType *ot)
+{
+ ot->name = "Drop Name";
+ ot->idname = "UI_OT_drop_name";
+ ot->description = "Drop name to button";
+
+ ot->poll = ED_operator_regionactive;
+ ot->invoke = drop_name_invoke;
+ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ RNA_def_string(
+ ot->srna, "string", NULL, 0, "String", "The string value to drop into the button");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name UI List Search Operator
* \{ */
@@ -2025,6 +2058,7 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_copy_to_selected_button);
WM_operatortype_append(UI_OT_jump_to_target_button);
WM_operatortype_append(UI_OT_drop_color);
+ WM_operatortype_append(UI_OT_drop_name);
#ifdef WITH_PYTHON
WM_operatortype_append(UI_OT_editsource);
WM_operatortype_append(UI_OT_edittranslation_init);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index a22351eea7e..072362492d8 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -1550,7 +1550,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
}
BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
- BLF_color3ubv(fontid, theme_col_text);
+ BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text);
BLF_draw(fontid, category_id_draw, category_draw_len);
GPU_blend(GPU_BLEND_NONE);
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 6b1ff92a855..92a9f14c77d 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -376,6 +376,37 @@ int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str)
return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
}
+/**
+ * Return the width of `str` with the spacing & kerning of `fs` with `aspect`
+ * (representing #uiBlock.aspect) applied.
+ *
+ * When calculating text width, the UI layout logic calculate widths without scale,
+ * only applying scale when drawing. This causes problems for fonts since kerning at
+ * smaller sizes often makes them wider than a scaled down version of the larger text.
+ * Resolve this by calculating the text at the on-screen size,
+ * returning the result scaled back to 1:1. See T92361.
+ */
+int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs,
+ const char *str,
+ const float aspect)
+{
+ uiFontStyle fs_buf;
+ if (aspect != 1.0f) {
+ fs_buf = *fs;
+ ui_fontscale(&fs_buf.points, aspect);
+ fs = &fs_buf;
+ }
+
+ int width = UI_fontstyle_string_width(fs, str);
+
+ if (aspect != 1.0f) {
+ /* While in most cases rounding up isn't important, it can make a difference
+ * with small fonts (3px or less), zooming out in the node-editor for e.g. */
+ width = (int)ceilf(width * aspect);
+ }
+ return width;
+}
+
int UI_fontstyle_height_max(const uiFontStyle *fs)
{
UI_fontstyle_set(fs);
diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
index f27b37a27de..d3ce7ebc3db 100644
--- a/source/blender/editors/interface/interface_template_asset_view.cc
+++ b/source/blender/editors/interface/interface_template_asset_view.cc
@@ -70,6 +70,7 @@ static void asset_view_item_but_drag_set(uiBut *but,
UI_but_drag_set_asset(but,
asset_handle,
BLI_strdup(blend_path),
+ ED_asset_handle_get_metadata(asset_handle),
FILE_ASSET_IMPORT_APPEND,
ED_asset_handle_get_preview_icon_id(asset_handle),
imbuf,
diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc
index 0157d0b66a3..85a6147432b 100644
--- a/source/blender/editors/interface/interface_template_attribute_search.cc
+++ b/source/blender/editors/interface/interface_template_attribute_search.cc
@@ -80,9 +80,10 @@ void attribute_search_add_items(StringRefNull str,
break;
}
}
- if (!contained && is_output) {
+ if (!contained) {
dummy_info.name = str;
- UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_ADD, 0, 0);
+ UI_search_item_add(
+ seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0);
}
}
@@ -122,4 +123,4 @@ void attribute_search_add_items(StringRefNull str,
BLI_string_search_free(search);
}
-} // namespace blender::ui \ No newline at end of file
+} // namespace blender::ui
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index e9acc65ed37..3e9042d29a0 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -518,7 +518,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void)
/** \name Draw Triangle Arrow
* \{ */
-void UI_draw_anti_tria(
+static void draw_anti_tria(
float x1, float y1, float x2, float y2, float x3, float y3, const float color[4])
{
const float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}};
@@ -559,66 +559,31 @@ void UI_draw_icon_tri(float x, float y, char dir, const float color[4])
const float f7 = 0.25 * U.widget_unit;
if (dir == 'h') {
- UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color);
+ draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color);
}
else if (dir == 't') {
- UI_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color);
+ draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color);
}
else { /* 'v' = vertical, down. */
- UI_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color);
+ draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color);
}
}
/* triangle 'icon' inside rect */
-void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
+static void draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
{
if (dir == 'h') {
const float half = 0.5f * BLI_rctf_size_y(rect);
- UI_draw_anti_tria(
+ draw_anti_tria(
rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color);
}
else {
const float half = 0.5f * BLI_rctf_size_x(rect);
- UI_draw_anti_tria(
+ draw_anti_tria(
rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color);
}
}
-void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
-{
- float draw_color[4];
-
- copy_v4_v4(draw_color, color);
- draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- const uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- immUniformColor4fv(draw_color);
-
- /* for each AA step */
- for (int j = 0; j < WIDGET_AA_JITTER; j++) {
- immBegin(GPU_PRIM_TRI_FAN, length);
- immVertex2f(pos, tri_array[0][0], tri_array[0][1]);
- immVertex2f(pos, tri_array[1][0], tri_array[1][1]);
-
- /* We jitter only the middle of the fan, the extremes are pinned. */
- for (int i = 2; i < length - 1; i++) {
- immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]);
- }
-
- immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]);
- immEnd();
- }
-
- immUnbindProgram();
-
- GPU_blend(GPU_BLEND_NONE);
-}
-
static void widget_init(uiWidgetBase *wtb)
{
wtb->totvert = wtb->halfwayvert = 0;
@@ -1494,7 +1459,7 @@ static void widget_draw_submenu_tria(const uiBut *but,
GPU_blend(GPU_BLEND_ALPHA);
UI_widgetbase_draw_cache_flush();
GPU_blend(GPU_BLEND_NONE);
- ui_draw_anti_tria_rect(&tria_rect, 'h', col);
+ draw_anti_tria_rect(&tria_rect, 'h', col);
}
static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
@@ -3721,7 +3686,7 @@ static void widget_datasetrow(
static void widget_nodesocket(
uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
{
- const int radi = 5;
+ const int radi = 0.25f * BLI_rcti_size_y(rect);
uiWidgetBase wtb;
widget_init(&wtb);
@@ -4632,6 +4597,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
switch (but->type) {
case UI_BTYPE_LABEL:
wt = widget_type(UI_WTYPE_ICON_LABEL);
+ if (!(but->flag & UI_HAS_ICON)) {
+ but->drawflag |= UI_BUT_NO_TEXT_PADDING;
+ }
break;
default:
wt = widget_type(UI_WTYPE_ICON);
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index ad7c6332ee9..aece2e58f1e 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -252,10 +252,11 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
case TH_HEADER_ACTIVE:
cp = ts->header;
+ const int factor = 5;
/* Lighten the header color when editor is active. */
- header_active[0] = cp[0] > 245 ? cp[0] - 10 : cp[0] + 10;
- header_active[1] = cp[1] > 245 ? cp[1] - 10 : cp[1] + 10;
- header_active[2] = cp[2] > 245 ? cp[2] - 10 : cp[2] + 10;
+ header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor;
+ header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor;
+ header_active[2] = cp[2] > 245 ? cp[2] - factor : cp[2] + factor;
header_active[3] = cp[3];
cp = header_active;
break;
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
index 88aa362deb5..c08fa51d5a5 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/tree_view.cc
@@ -29,6 +29,7 @@
#include "UI_interface.h"
+#include "WM_api.h"
#include "WM_types.h"
#include "UI_tree_view.hh"
@@ -354,22 +355,18 @@ void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn)
is_active_fn_ = is_active_fn;
}
-bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/)
+std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller()
+ const
{
- /* Do nothing by default. */
- return false;
-}
-
-bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const
-{
- return false;
+ /* There's no drag controller (and hence no drag support) by default. */
+ return nullptr;
}
-std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/,
- const wmDrag & /*drag*/,
- const wmEvent & /*event*/) const
+std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller()
+ const
{
- return TIP_("Drop into/onto tree item");
+ /* There's no drop controller (and hence no drop support) by default. */
+ return nullptr;
}
bool AbstractTreeViewItem::can_rename() const
@@ -553,6 +550,12 @@ void AbstractTreeViewItem::change_state_delayed()
activate();
}
}
+/* ---------------------------------------------------------------------- */
+
+AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view)
+ : tree_view_(tree_view)
+{
+}
/* ---------------------------------------------------------------------- */
@@ -646,7 +649,18 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic
void BasicTreeViewItem::build_row(uiLayout &row)
{
- uiItemL(&row, label_.c_str(), icon);
+ add_label(row);
+}
+
+void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override)
+{
+ const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override;
+
+ /* Some padding for labels without collapse chevron and no icon. Looks weird without. */
+ if (icon == ICON_NONE && !is_collapsible()) {
+ uiItemS_ex(&layout, 0.8f);
+ }
+ uiItemL(&layout, label.c_str(), icon);
}
void BasicTreeViewItem::on_activate()
@@ -680,19 +694,53 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
return a.matches_including_parents(b);
}
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag)
+/**
+ * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't
+ * support dragging, i.e. it won't create a drag-controller upon request.
+ * \return True if dragging started successfully, otherwise false.
+ */
+bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_)
+{
+ const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
+ const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller =
+ item.create_drag_controller();
+ if (!drag_controller) {
+ return false;
+ }
+
+ WM_event_start_drag(C,
+ ICON_NONE,
+ drag_controller->get_drag_type(),
+ drag_controller->create_drag_data(),
+ 0,
+ WM_DRAG_FREE_DATA);
+ return true;
+}
+
+bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
+ const wmDrag *drag,
+ const char **r_disabled_hint)
{
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return item.can_drop(*drag);
+ const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return false;
+ }
+
+ return drop_controller->can_drop(*drag, r_disabled_hint);
}
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_,
- const bContext *C,
- const wmDrag *drag,
- const wmEvent *event)
+char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag)
{
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str());
+ const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return nullptr;
+ }
+
+ return BLI_strdup(drop_controller->drop_tooltip(*drag).c_str());
}
/**
@@ -702,10 +750,13 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_,
bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags)
{
AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_);
+ std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ const char *disabled_hint_dummy = nullptr;
LISTBASE_FOREACH (const wmDrag *, drag, drags) {
- if (item.can_drop(*drag)) {
- return item.on_drop(*drag);
+ if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) {
+ return drop_controller->on_drop(*drag);
}
}
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index ca96fde9810..eea6512f0f8 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -32,6 +32,7 @@
#include "DNA_userdef_types.h"
#include "BLI_array.h"
+#include "BLI_easing.h"
#include "BLI_link_utils.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -166,7 +167,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
scroll = view2d_scroll_mapped(v2d->scroll);
- /* scrollers are based off regionsize
+ /* Scrollers are based off region-size:
* - they can only be on one to two edges of the region they define
* - if they overlap, they must not occupy the corners (which are reserved for other widgets)
*/
@@ -1291,6 +1292,114 @@ void UI_view2d_multi_grid_draw(
immUnbindProgram();
}
+static void grid_axis_start_and_count(
+ const float step, const float min, const float max, float *r_start, int *r_count)
+{
+ *r_start = min;
+ if (*r_start < 0.0f) {
+ *r_start += -(float)fmod(min, step);
+ }
+ else {
+ *r_start += step - (float)fabs(fmod(min, step));
+ }
+
+ if (*r_start > max) {
+ *r_count = 0;
+ }
+ else {
+ *r_count = (max - *r_start) / step + 1;
+ }
+}
+
+typedef struct DotGridLevelInfo {
+ /* The factor applied to the #min_step argument. This could be easily computed in runtime,
+ * but seeing it together with the other values is helpful. */
+ float step_factor;
+ /* The normalized zoom level at which the grid level starts to fade in.
+ * At lower zoom levels, the points will not be visible and the level will be skipped. */
+ float fade_in_start_zoom;
+ /* The normalized zoom level at which the grid finishes fading in.
+ * At higher zoom levels, the points will be opaque. */
+ float fade_in_end_zoom;
+} DotGridLevelInfo;
+
+static const DotGridLevelInfo level_info[9] = {
+ {6.4f, -0.1f, 0.01f},
+ {3.2f, 0.0f, 0.025f},
+ {1.6f, 0.025f, 0.15f},
+ {0.8f, 0.05f, 0.2f},
+ {0.4f, 0.1f, 0.25f},
+ {0.2f, 0.125f, 0.3f},
+ {0.1f, 0.25f, 0.5f},
+ {0.05f, 0.7f, 0.9f},
+ {0.025f, 0.6f, 0.9f},
+};
+
+/**
+ * Draw a multi-level grid of dots, with a dynamic number of levels based on the fading.
+ *
+ * \param grid_color_id: The theme color used for the points. Faded dynamically based on zoom.
+ * \param min_step: The base size of the grid. At different zoom levels, the visible grid may have
+ * a larger step size.
+ * \param grid_levels: The maximum grid depth. Larger grid levels will subdivide the grid more.
+ */
+void UI_view2d_dot_grid_draw(const View2D *v2d,
+ const int grid_color_id,
+ const float min_step,
+ const int grid_levels)
+{
+ BLI_assert(grid_levels > 0 && grid_levels < 10);
+ const float zoom_x = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
+ const float zoom_normalized = (zoom_x - v2d->minzoom) / (v2d->maxzoom - v2d->minzoom);
+
+ GPUVertFormat *format = immVertexFormat();
+ const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ GPU_point_size(3.0f * UI_DPI_FAC);
+
+ float color[4];
+ UI_GetThemeColor3fv(grid_color_id, color);
+
+ for (int level = 0; level < grid_levels; level++) {
+ const DotGridLevelInfo *info = &level_info[level];
+ const float step = min_step * info->step_factor * U.widget_unit;
+
+ const float alpha_factor = (zoom_normalized - info->fade_in_start_zoom) /
+ (info->fade_in_end_zoom - info->fade_in_start_zoom);
+ color[3] = clamp_f(BLI_easing_cubic_ease_in_out(alpha_factor, 0.0f, 1.0f, 1.0f), 0.0f, 1.0f);
+ if (color[3] == 0.0f) {
+ break;
+ }
+
+ int count_x;
+ float start_x;
+ grid_axis_start_and_count(step, v2d->cur.xmin, v2d->cur.xmax, &start_x, &count_x);
+ int count_y;
+ float start_y;
+ grid_axis_start_and_count(step, v2d->cur.ymin, v2d->cur.ymax, &start_y, &count_y);
+ if (count_x == 0 || count_y == 0) {
+ continue;
+ }
+
+ immBegin(GPU_PRIM_POINTS, count_x * count_y);
+
+ /* Theoretically drawing on top of lower grid levels could be avoided, but it would also
+ * increase the complexity of this loop, which isn't worth the time at the moment. */
+ for (int i_y = 0; i_y < count_y; i_y++) {
+ const float y = start_y + step * i_y;
+ for (int i_x = 0; i_x < count_x; i_x++) {
+ const float x = start_x + step * i_x;
+ immAttr4fv(color_id, color);
+ immVertex2f(pos, x, y);
+ }
+ }
+
+ immEnd();
+ }
+
+ immUnbindProgram();
+}
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c
index a49666ebbd3..8d8b9a4fe48 100644
--- a/source/blender/editors/interface/view2d_edge_pan.c
+++ b/source/blender/editors/interface/view2d_edge_pan.c
@@ -92,6 +92,8 @@ void UI_view2d_edge_pan_init(bContext *C,
vpd->delay = delay;
vpd->zoom_influence = zoom_influence;
+ vpd->enabled = false;
+
/* Calculate translation factor, based on size of view. */
const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1);
const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1);
@@ -227,9 +229,16 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[
BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit);
BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit);
+ /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start
+ * disabled). */
+ if (BLI_rcti_isect_pt_v(&inside_rect, xy)) {
+ /* We are inside once, can start. */
+ vpd->enabled = true;
+ }
+
int pan_dir_x = 0;
int pan_dir_y = 0;
- if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy)) {
+ if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) {
/* Find whether the mouse is beyond X and Y edges. */
if (xy[0] > inside_rect.xmax) {
pan_dir_x = 1;
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index d073f5f2ba4..b712cfc24ed 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -184,8 +184,6 @@ typedef struct KnifeMeasureData {
float cage[3];
float mval[2];
bool is_stored;
- float corr_prev_cage[3]; /* "knife_start_cut" updates prev.cage breaking angle calculations,
- * store correct version. */
} KnifeMeasureData;
typedef struct KnifeUndoFrame {
@@ -242,6 +240,7 @@ typedef struct KnifeTool_OpData {
BLI_mempool *kverts;
BLI_mempool *kedges;
+ bool no_cuts; /* A cut has not been made yet. */
BLI_Stack *undostack;
BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */
@@ -496,7 +495,7 @@ static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
const int distance_precision = 4;
/* Calculate distance and convert to string. */
- const float cut_len = len_v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage);
+ const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage);
UnitSettings *unit = &kcd->scene->unit;
if (unit->system == USER_UNIT_NONE) {
@@ -703,7 +702,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
else {
tempkfv = tempkfe->v2;
}
- angle = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, tempkfv->cageco);
+ angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco);
if (angle < min_angle) {
min_angle = angle;
kfe = tempkfe;
@@ -717,7 +716,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP);
knifetool_draw_angle(kcd,
- kcd->mdata.corr_prev_cage,
+ kcd->prev.cage,
kcd->curr.cage,
end,
kcd->prev.mval,
@@ -730,11 +729,11 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
kfe = kcd->curr.edge;
/* Check for most recent cut (if cage is part of previous cut). */
- if (!compare_v3v3(kfe->v1->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG) &&
- !compare_v3v3(kfe->v2->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG)) {
+ if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) &&
+ !compare_v3v3(kfe->v2->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG)) {
/* Determine acute angle. */
- float angle1 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v1->cageco);
- float angle2 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v2->cageco);
+ float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco);
+ float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco);
float angle;
float *end;
@@ -751,14 +750,8 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
float end_ss[2];
ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP);
- knifetool_draw_angle(kcd,
- kcd->mdata.corr_prev_cage,
- kcd->curr.cage,
- end,
- kcd->prev.mval,
- kcd->curr.mval,
- end_ss,
- angle);
+ knifetool_draw_angle(
+ kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle);
}
}
@@ -852,10 +845,10 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle);
}
else if (kcd->mdata.is_stored && !kcd->prev.is_space) {
- float angle = angle_v3v3v3(kcd->curr.cage, kcd->mdata.corr_prev_cage, kcd->mdata.cage);
+ float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage);
knifetool_draw_angle(kcd,
kcd->curr.cage,
- kcd->mdata.corr_prev_cage,
+ kcd->prev.cage,
kcd->mdata.cage,
kcd->curr.mval,
kcd->prev.mval,
@@ -1468,7 +1461,10 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd,
ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs);
}
-static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3])
+/* No longer used, but may be useful in the future. */
+static void UNUSED_FUNCTION(knifetool_recast_cageco)(KnifeTool_OpData *kcd,
+ float mval[3],
+ float r_cage[3])
{
float origin[3];
float origin_ofs[3];
@@ -2396,7 +2392,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
}
/* Save values for angle drawing calculations. */
- copy_v3_v3(kcd->mdata.cage, kcd->mdata.corr_prev_cage);
+ copy_v3_v3(kcd->mdata.cage, kcd->prev.cage);
copy_v2_v2(kcd->mdata.mval, kcd->prev.mval);
kcd->mdata.is_stored = true;
@@ -4094,6 +4090,8 @@ static void knifetool_init(bContext *C,
knife_init_colors(&kcd->colors);
}
+ kcd->no_cuts = true;
+
kcd->axis_string[0] = ' ';
kcd->axis_string[1] = '\0';
@@ -4506,12 +4504,23 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
handled = true;
break;
case KNF_MODAL_NEW_CUT:
+ /* If no cuts have been made, exit.
+ * Preserves right click cancel workflow which most tools use,
+ * but stops accidentally deleting entire cuts with right click.
+ */
+ if (kcd->no_cuts) {
+ ED_region_tag_redraw(kcd->region);
+ knifetool_exit(op);
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_CANCELLED;
+ }
ED_region_tag_redraw(kcd->region);
knife_finish_cut(kcd);
kcd->mode = MODE_IDLE;
handled = true;
break;
case KNF_MODAL_ADD_CUT:
+ kcd->no_cuts = false;
knife_recalc_ortho(kcd);
/* Get the value of the event which triggered this one. */
@@ -4525,16 +4534,6 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
kcd->init = kcd->curr;
}
- /* Preserve correct prev.cage for angle drawing calculations. */
- if (kcd->prev.edge == NULL && kcd->prev.vert == NULL) {
- /* "knife_start_cut" moves prev.cage so needs to be recalculated. */
- /* Only occurs if prev was started on a face. */
- knifetool_recast_cageco(kcd, kcd->prev.mval, kcd->mdata.corr_prev_cage);
- }
- else {
- copy_v3_v3(kcd->mdata.corr_prev_cage, kcd->prev.cage);
- }
-
/* Freehand drawing is incompatible with cut-through. */
if (kcd->cut_through == false) {
kcd->is_drag_hold = true;
@@ -4811,7 +4810,7 @@ void MESH_OT_knife_tool(wmOperatorType *ot)
"Occlude Geometry",
"Only cut the front most geometry");
RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry");
- RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts through geometry");
+ RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry");
RNA_def_enum(ot->srna,
"visible_measurements",
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 2fcf8fa6f8f..e0768bcff24 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -884,9 +884,8 @@ static bool unified_findnearest(ViewContext *vc,
BMFace **r_efa)
{
BMEditMesh *em = vc->em;
- static short mval_prev[2] = {-1, -1};
- /* only cycle while the mouse remains still */
- const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1]));
+
+ const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
const float dist_init = ED_view3d_select_dist_px();
/* since edges select lines, we give dots advantage of ~20 pix */
const float dist_margin = (dist_init / 2);
@@ -988,9 +987,6 @@ static bool unified_findnearest(ViewContext *vc,
}
}
- mval_prev[0] = vc->mval[0];
- mval_prev[1] = vc->mval[1];
-
/* Only one element type will be non-null. */
BLI_assert(((hit.v.ele != NULL) + (hit.e.ele != NULL) + (hit.f.ele != NULL)) <= 1);
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 114f540b614..d22ae5bc804 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1323,6 +1323,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front");
const bool use_lights = RNA_boolean_get(op->ptr, "use_lights");
const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order");
+ const float stroke_depth_offset = RNA_float_get(op->ptr, "stroke_depth_offset");
ushort local_view_bits;
float loc[3], rot[3];
@@ -1454,6 +1455,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
if (stroke_depth_order == GP_DRAWMODE_3D) {
gpd->draw_mode = GP_DRAWMODE_3D;
}
+ md->stroke_depth_offset = stroke_depth_offset;
}
break;
@@ -1491,9 +1493,10 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op)
uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE);
uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE);
bool in_front = RNA_boolean_get(op->ptr, "use_in_front");
- uiLayout *row = uiLayoutRow(layout, false);
- uiLayoutSetActive(row, !in_front);
- uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE);
+ 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);
}
}
@@ -1532,9 +1535,18 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
RNA_def_boolean(ot->srna,
"use_in_front",
- false,
- "In Front",
+ true,
+ "Show In Front",
"Show line art grease pencil in front of everything");
+ RNA_def_float(ot->srna,
+ "stroke_depth_offset",
+ 0.05f,
+ 0.0f,
+ FLT_MAX,
+ "Stroke Offset",
+ "Stroke offset for the line art modifier",
+ 0.0f,
+ 0.5f);
RNA_def_boolean(
ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object");
RNA_def_enum(
@@ -1543,7 +1555,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
rna_enum_gpencil_add_stroke_depth_order_items,
GP_DRAWMODE_3D,
"Stroke Depth Order",
- "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'");
+ "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front')");
}
/** \} */
@@ -2133,7 +2145,7 @@ static void copy_object_set_idnew(bContext *C)
Main *bmain = CTX_data_main(C);
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
- BKE_libblock_relink_to_newid(&ob->id);
+ BKE_libblock_relink_to_newid(bmain, &ob->id);
}
CTX_DATA_END;
@@ -2366,7 +2378,7 @@ static void make_object_duplilist_real(bContext *C,
Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob);
/* Remap new object to itself, and clear again newid pointer of orig object. */
- BKE_libblock_relink_to_newid(&ob_dst->id);
+ BKE_libblock_relink_to_newid(bmain, &ob_dst->id);
DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY);
@@ -3363,7 +3375,7 @@ Base *ED_object_add_duplicate(
ob = basen->object;
/* link own references to the newly duplicated data T26816. */
- BKE_libblock_relink_to_newid(&ob->id);
+ BKE_libblock_relink_to_newid(bmain, &ob->id);
/* DAG_relations_tag_update(bmain); */ /* caller must do */
@@ -3469,19 +3481,6 @@ void OBJECT_OT_duplicate(wmOperatorType *ot)
* Use for drag & drop.
* \{ */
-static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob)
-{
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
- if (!base) {
- LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
- BKE_collection_object_add(bmain, layer_collection->collection, ob);
- base = BKE_view_layer_base_find(view_layer, ob);
- }
-
- return base;
-}
-
static int object_add_named_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -3489,8 +3488,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
Base *basen;
Object *ob;
- const bool duplicate = RNA_boolean_get(op->ptr, "duplicate");
- const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked");
+ const bool linked = RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
char name[MAX_ID_NAME - 2];
@@ -3504,41 +3502,26 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
- if (duplicate) {
- basen = object_add_duplicate_internal(
- bmain,
- scene,
- view_layer,
- ob,
- dupflag,
- /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this
- * function will only work if the object is already linked in the view layer, which is not
- * the case here. So we have to do the new-ID relinking ourselves
- * (#copy_object_set_idnew()).
- */
- LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID);
- }
- else {
- /* basen is actually not a new base in this case. */
- basen = object_add_ensure_in_view_layer(bmain, view_layer, ob);
- }
+ basen = object_add_duplicate_internal(
+ bmain,
+ scene,
+ view_layer,
+ ob,
+ dupflag,
+ /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this
+ * function will only work if the object is already linked in the view layer, which is not
+ * the case here. So we have to do the new-ID relinking ourselves
+ * (#copy_object_set_idnew()).
+ */
+ LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID);
if (basen == NULL) {
- BKE_report(op->reports,
- RPT_ERROR,
- duplicate ? "Object could not be duplicated" :
- "Object could not be linked to the view layer");
+ BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
return OPERATOR_CANCELLED;
}
basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT;
- int mval[2];
- if (object_add_drop_xy_get(C, op, &mval)) {
- ED_object_location_from_view(C, basen->object->loc);
- ED_view3d_cursor3d_position(C, mval, false, basen->object->loc);
- }
-
/* 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);
@@ -3556,44 +3539,164 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
ED_outliner_select_sync_from_object_tag(C);
+ PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix");
+ if (RNA_property_is_set(op->ptr, prop_matrix)) {
+ Object *ob_add = basen->object;
+ RNA_property_float_get_array(op->ptr, prop_matrix, &ob_add->obmat[0][0]);
+ BKE_object_apply_mat4(ob_add, ob_add->obmat, true, true);
+
+ DEG_id_tag_update(&ob_add->id, ID_RECALC_TRANSFORM);
+ }
+ else {
+ int mval[2];
+ if (object_add_drop_xy_get(C, op, &mval)) {
+ ED_object_location_from_view(C, basen->object->loc);
+ ED_view3d_cursor3d_position(C, mval, false, basen->object->loc);
+ }
+ }
+
return OPERATOR_FINISHED;
}
void OBJECT_OT_add_named(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Add Named Object";
+ ot->name = "Add Object";
ot->description = "Add named object";
ot->idname = "OBJECT_OT_add_named";
/* api callbacks */
ot->invoke = object_add_drop_xy_generic_invoke;
ot->exec = object_add_named_exec;
- ot->poll = ED_operator_objectmode;
+ ot->poll = ED_operator_objectmode_poll_msg;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
-
- prop = RNA_def_boolean(
- ot->srna,
- "duplicate",
- true,
- "Duplicate",
- "Create a duplicate of the object. If not set, only ensures the object is linked into the "
- "active view layer, positions and selects/activates it (deselecting others)");
- RNA_def_property_flag(prop, PROP_HIDDEN);
-
RNA_def_boolean(ot->srna,
"linked",
false,
"Linked",
- "Duplicate object but not object data, linking to the original data (ignored if "
- "'duplicate' is false)");
+ "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");
+ 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);
+
+ object_add_drop_xy_props(ot);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform Object to Mouse Operator
+ * \{ */
+
+/**
+ * Alternate behavior for dropping an asset that positions the appended object(s).
+ */
+static int object_transform_to_mouse_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob;
+
+ if (RNA_struct_property_is_set(op->ptr, "name")) {
+ char name[MAX_ID_NAME - 2];
+ RNA_string_get(op->ptr, "name", name);
+ ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
+ }
+ else {
+ ob = OBACT(view_layer);
+ }
+
+ if (ob == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Object not found");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Don't transform a linked object. There's just nothing to do here in this case, so return
+ * #OPERATOR_FINISHED. */
+ if (ID_IS_LINKED(ob)) {
+ return OPERATOR_FINISHED;
+ }
+
+ /* Ensure the locations are updated so snap reads the evaluated active location. */
+ CTX_data_ensure_evaluated_depsgraph(C);
+
+ PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix");
+ if (RNA_property_is_set(op->ptr, prop_matrix)) {
+ uint objects_len;
+ Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0});
+
+ float matrix[4][4];
+ RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]);
+
+ float mat_src_unit[4][4];
+ float mat_dst_unit[4][4];
+ float final_delta[4][4];
+
+ normalize_m4_m4(mat_src_unit, ob->obmat);
+ normalize_m4_m4(mat_dst_unit, matrix);
+ invert_m4(mat_src_unit);
+ mul_m4_m4m4(final_delta, mat_dst_unit, mat_src_unit);
+
+ ED_object_xform_array_m4(objects, objects_len, final_delta);
+
+ MEM_freeN(objects);
+ }
+ else {
+ int mval[2];
+ if (object_add_drop_xy_get(C, op, &mval)) {
+ float cursor[3];
+ ED_object_location_from_view(C, cursor);
+ ED_view3d_cursor3d_position(C, mval, false, cursor);
+
+ /* Use the active objects location since this is the ID which the user selected to drop.
+ *
+ * This transforms all selected objects, so that dropping a single object which links in
+ * other objects will have their relative transformation preserved.
+ * For example a child/parent relationship or other objects used with a boolean modifier.
+ *
+ * The caller is responsible for ensuring the selection state gives useful results.
+ * Link/append does this using #FILE_AUTOSELECT. */
+ ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_transform_to_mouse(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Place Object Under Mouse";
+ ot->description = "Snap selected item(s) to the mouse location";
+ ot->idname = "OBJECT_OT_transform_to_mouse";
+
+ /* api callbacks */
+ ot->invoke = object_add_drop_xy_generic_invoke;
+ ot->exec = object_transform_to_mouse_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ PropertyRNA *prop;
+ RNA_def_string(ot->srna,
+ "name",
+ NULL,
+ 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);
+
object_add_drop_xy_props(ot);
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index ea9a2de090b..fe07ecef438 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -106,6 +106,7 @@ void OBJECT_OT_select_same_collection(struct wmOperatorType *ot);
/* object_add.c */
void OBJECT_OT_add(struct wmOperatorType *ot);
void OBJECT_OT_add_named(struct wmOperatorType *ot);
+void OBJECT_OT_transform_to_mouse(struct wmOperatorType *ot);
void OBJECT_OT_metaball_add(struct wmOperatorType *ot);
void OBJECT_OT_text_add(struct wmOperatorType *ot);
void OBJECT_OT_armature_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index b3bf2c64a91..b171da42522 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -112,6 +112,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_volume_import);
WM_operatortype_append(OBJECT_OT_add);
WM_operatortype_append(OBJECT_OT_add_named);
+ WM_operatortype_append(OBJECT_OT_transform_to_mouse);
WM_operatortype_append(OBJECT_OT_effector_add);
WM_operatortype_append(OBJECT_OT_collection_instance_add);
WM_operatortype_append(OBJECT_OT_data_instance_add);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index d81143d6081..acd3f058554 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1685,18 +1685,20 @@ static bool single_data_needs_duplication(ID *id)
return (id != NULL && (id->us > 1 || ID_IS_LINKED(id)));
}
-static void libblock_relink_collection(Collection *collection, const bool do_collection)
+static void libblock_relink_collection(Main *bmain,
+ Collection *collection,
+ const bool do_collection)
{
if (do_collection) {
- BKE_libblock_relink_to_newid(&collection->id);
+ BKE_libblock_relink_to_newid(bmain, &collection->id);
}
for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) {
- BKE_libblock_relink_to_newid(&cob->ob->id);
+ BKE_libblock_relink_to_newid(bmain, &cob->ob->id);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- libblock_relink_collection(child->collection, true);
+ libblock_relink_collection(bmain, child->collection, true);
}
}
@@ -1766,10 +1768,10 @@ static void single_object_users(
single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true);
/* Will also handle the master collection. */
- BKE_libblock_relink_to_newid(&scene->id);
+ BKE_libblock_relink_to_newid(bmain, &scene->id);
/* Collection and object pointers in collections */
- libblock_relink_collection(scene->master_collection, false);
+ libblock_relink_collection(bmain, scene->master_collection, false);
/* We also have to handle runtime things in UI. */
if (v3d) {
@@ -2589,10 +2591,10 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot)
char *ED_object_ot_drop_named_material_tooltip(bContext *C,
PointerRNA *properties,
- const wmEvent *event)
+ const int mval[2])
{
int mat_slot = 0;
- Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot);
+ Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot);
if (ob == NULL) {
return BLI_strdup("");
}
@@ -2642,8 +2644,10 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_FINISHED;
}
-/* used for dropbox */
-/* assigns to object under cursor, only first material slot */
+/**
+ * Used for drop-box.
+ * Assigns to object under cursor, only first material slot.
+ */
void OBJECT_OT_drop_named_material(wmOperatorType *ot)
{
/* identifiers */
diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c
index 66390f6f165..c7dfe911ce7 100644
--- a/source/blender/editors/object/object_utils.c
+++ b/source/blender/editors/object/object_utils.c
@@ -36,6 +36,7 @@
#include "BKE_armature.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "DEG_depsgraph_query.h"
@@ -430,3 +431,70 @@ void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xd
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform Object Array
+ *
+ * Low level object transform function, transforming objects by `matrix`.
+ * Simple alternative to full transform logic.
+ * \{ */
+
+static bool object_parent_in_set(GSet *objects_set, Object *ob)
+{
+ for (Object *parent = ob->parent; parent; parent = parent->parent) {
+ if (BLI_gset_lookup(objects_set, parent)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ED_object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4])
+{
+ /* Filter out objects that have parents in `objects_set`. */
+ {
+ GSet *objects_set = BLI_gset_ptr_new_ex(__func__, objects_len);
+ for (uint i = 0; i < objects_len; i++) {
+ BLI_gset_add(objects_set, objects[i]);
+ }
+ for (uint i = 0; i < objects_len;) {
+ if (object_parent_in_set(objects_set, objects[i])) {
+ objects[i] = objects[--objects_len];
+ }
+ else {
+ i++;
+ }
+ }
+ BLI_gset_free(objects_set, NULL);
+ }
+
+ /* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */
+ bool is_translation_only;
+ {
+ float test_m4_a[4][4], test_m4_b[4][4];
+ unit_m4(test_m4_a);
+ copy_m4_m4(test_m4_b, matrix);
+ zero_v3(test_m4_b[3]);
+ is_translation_only = equals_m4m4(test_m4_a, test_m4_b);
+ }
+
+ if (is_translation_only) {
+ for (uint i = 0; i < objects_len; i++) {
+ Object *ob = objects[i];
+ add_v3_v3(ob->loc, matrix[3]);
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+ }
+ }
+ else {
+ for (uint i = 0; i < objects_len; i++) {
+ float m4[4][4];
+ Object *ob = objects[i];
+ BKE_object_to_mat4(ob, m4);
+ mul_m4_m4m4(m4, matrix, m4);
+ BKE_object_apply_mat4(ob, m4, true, true);
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 3ac6dca3044..367d72b0ad7 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -1235,9 +1235,15 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op)
const bool use_active = RNA_boolean_get(op->ptr, "use_active");
Scene *scene = CTX_data_scene(C);
Object *ob_from = ED_object_active_context(C);
- ParticleSystem *psys_from =
- use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data :
- NULL;
+
+ ParticleSystem *psys_from = NULL;
+ if (use_active) {
+ psys_from = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data;
+ if (psys_from == NULL) {
+ /* Particle System context pointer is only valid in the Properties Editor. */
+ psys_from = psys_get_current(ob_from);
+ }
+ }
int changed_tot = 0;
int fail = 0;
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index b69a563166a..80c14371c16 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1744,7 +1744,7 @@ static void ed_default_handlers(
if (flag & ED_KEYMAP_TOOL) {
if (flag & ED_KEYMAP_GIZMO) {
WM_event_add_keymap_handler_dynamic(
- &region->handlers, WM_event_get_keymap_from_toolsystem_fallback, area);
+ &region->handlers, WM_event_get_keymap_from_toolsystem_with_gizmos, area);
}
else {
WM_event_add_keymap_handler_dynamic(
@@ -1940,7 +1940,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
rcti window_rect;
WM_window_rect_calc(win, &window_rect);
- /* set typedefinitions */
+ /* Set type-definitions. */
area->type = BKE_spacetype_from_id(area->spacetype);
if (area->type == NULL) {
@@ -3027,7 +3027,7 @@ void ED_region_panels_layout_ex(const bContext *C,
search_filter);
}
- /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */
+ /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */
if (has_instanced_panel) {
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->type == NULL) {
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 841792d5f2d..fa0cfd16817 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -935,6 +935,10 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
}
}
}
+
+ /* Ensure test-motion values are never shared between regions. */
+ const bool use_cycle = !WM_cursor_test_motion_and_update((const int[2]){-1, -1});
+ UNUSED_VARS(use_cycle);
}
/* Cursors, for time being set always on edges,
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index e5fbcbb0b6c..66140cba9c6 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -219,6 +219,20 @@ bool ED_operator_objectmode(bContext *C)
return true;
}
+/**
+ * Same as #ED_operator_objectmode() but additionally sets a "disabled hint". That is, a message
+ * to be displayed to the user explaining why the operator can't be used in current context.
+ */
+bool ED_operator_objectmode_poll_msg(bContext *C)
+{
+ if (!ED_operator_objectmode(C)) {
+ CTX_wm_operator_poll_msg_set(C, "Only supported in object mode");
+ return false;
+ }
+
+ return true;
+}
+
static bool ed_spacetype_test(bContext *C, int type)
{
if (ED_operator_areaactive(C)) {
@@ -1283,7 +1297,7 @@ static ScrEdge *screen_area_edge_from_cursor(const bContext *C,
*
* callbacks:
*
- * invoke() gets called on shift+lmb drag in action-zone
+ * invoke() gets called on Shift-LMB drag in action-zone
* exec() execute without any user interaction, based on properties
* call init(), add handler
*
@@ -2078,7 +2092,7 @@ typedef struct sAreaSplitData {
int bigger, smaller; /* constraints for moving new edge */
int delta; /* delta move edge */
int origmin, origsize; /* to calculate fac, for property storage */
- int previewmode; /* draw previewline, then split */
+ int previewmode; /* draw preview-line, then split. */
void *draw_callback; /* call `screen_draw_split_preview` */
bool do_snap;
@@ -2628,8 +2642,8 @@ static int area_max_regionsize(ScrArea *area, ARegion *scalear, AZEdge edge)
dist = BLI_rcti_size_y(&area->totrct);
}
- /* subtractwidth of regions on opposite side
- * prevents dragging regions into other opposite regions */
+ /* Subtract the width of regions on opposite side
+ * prevents dragging regions into other opposite regions. */
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region == scalear) {
continue;
@@ -3082,12 +3096,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
float cfra = (float)(CFRA);
- /* init binarytree-list for getting keyframes */
+ /* Initialize binary-tree-list for getting keyframes. */
struct AnimKeylist *keylist = ED_keylist_create();
- /* seed up dummy dopesheet context with flags to perform necessary filtering */
+ /* Speed up dummy dope-sheet context with flags to perform necessary filtering. */
if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
- /* only selected channels are included */
+ /* Only selected channels are included. */
ads.filterflag |= ADS_FILTER_ONLYSEL;
}
@@ -4206,7 +4220,7 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Region Context Menu Operator (Header/Footer/Navbar)
+/** \name Region Context Menu Operator (Header/Footer/Navigation-Bar)
* \{ */
static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
@@ -5058,6 +5072,18 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC;
int sizey = 520 * UI_DPI_FAC;
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section");
+ if (prop && RNA_property_is_set(op->ptr, prop)) {
+ /* Set active section via RNA, so it can fail properly. */
+
+ PointerRNA pref_ptr;
+ RNA_pointer_create(NULL, &RNA_Preferences, &U, &pref_ptr);
+ PropertyRNA *active_section_prop = RNA_struct_find_property(&pref_ptr, "active_section");
+
+ RNA_property_enum_set(&pref_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop));
+ RNA_property_update(C, &pref_ptr, active_section_prop);
+ }
+
/* changes context! */
if (WM_window_open(C,
IFACE_("Blender Preferences"),
@@ -5091,14 +5117,24 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
- ot->name = "Show Preferences";
+ ot->name = "Open Preferences...";
ot->description = "Edit user preferences and system settings";
ot->idname = "SCREEN_OT_userpref_show";
/* api callbacks */
ot->exec = userpref_show_exec;
ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
+
+ prop = RNA_def_enum(ot->srna,
+ "section",
+ rna_enum_preference_section_items,
+ 0,
+ "",
+ "Section to activate in the Preferences");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 4e0dcfe8e3c..fede01a614b 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -295,7 +295,7 @@ static uint vpaint_blend(const VPaint *vp,
uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i);
- /* if no accumulate, clip color adding with colorig & orig alpha */
+ /* If no accumulate, clip color adding with `color_orig` & `color_test`. */
if (!brush_use_accumulate(vp)) {
uint color_test, a;
char *cp, *ct, *co;
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
index c305a11daf4..e6b76e05e16 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -60,6 +60,7 @@ class AssetCatalogTreeView : public ui::AbstractTreeView {
SpaceFile &space_file_;
friend class AssetCatalogTreeViewItem;
+ friend class AssetCatalogDropController;
public:
AssetCatalogTreeView(::AssetLibrary *library,
@@ -86,25 +87,52 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
public:
AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item);
- static bool has_droppable_item(const wmDrag &drag);
- static bool drop_into_catalog(const AssetCatalogTreeView &tree_view,
- const wmDrag &drag,
- CatalogID catalog_id,
- StringRefNull simple_name = "");
-
void on_activate() override;
void build_row(uiLayout &row) override;
void build_context_menu(bContext &C, uiLayout &column) const override;
- bool can_drop(const wmDrag &drag) const override;
- std::string drop_tooltip(const bContext &C,
- const wmDrag &drag,
- const wmEvent &event) const override;
- bool on_drop(const wmDrag &drag) override;
-
bool can_rename() const override;
bool rename(StringRefNull new_name) override;
+
+ /** Add drag support for catalog items. */
+ std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override;
+ /** Add dropping support for catalog items. */
+ std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+};
+
+class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController {
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ explicit AssetCatalogDragController(AssetCatalogTreeItem &catalog_item);
+
+ int get_drag_type() const override;
+ void *create_drag_data() const override;
+};
+
+class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController {
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item);
+
+ bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
+ std::string drop_tooltip(const wmDrag &drag) const override;
+ bool on_drop(const wmDrag &drag) override;
+
+ ::AssetLibrary &get_asset_library() const;
+
+ static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint);
+ static bool drop_assets_into_catalog(const AssetCatalogTreeView &tree_view,
+ const wmDrag &drag,
+ CatalogID catalog_id,
+ StringRefNull simple_name = "");
+
+ private:
+ bool drop_asset_catalog_into_catalog(const wmDrag &drag);
+ std::string drop_tooltip_asset_list(const wmDrag &drag) const;
+ std::string drop_tooltip_asset_catalog(const wmDrag &drag) const;
};
/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level
@@ -118,11 +146,15 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
- bool can_drop(const wmDrag &drag) const override;
- std::string drop_tooltip(const bContext &C,
- const wmDrag &drag,
- const wmEvent &event) const override;
- bool on_drop(const wmDrag &drag) override;
+ struct DropController : public ui::AbstractTreeViewItemDropController {
+ DropController(AssetCatalogTreeView &tree_view);
+
+ bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
+ std::string drop_tooltip(const wmDrag &drag) const override;
+ bool on_drop(const wmDrag &drag) override;
+ };
+
+ std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
};
/* ---------------------------------------------------------------------- */
@@ -219,12 +251,8 @@ void AssetCatalogTreeViewItem::on_activate()
void AssetCatalogTreeViewItem::build_row(uiLayout &row)
{
- if (catalog_item_.has_unsaved_changes()) {
- uiItemL(&row, (label_ + "*").c_str(), icon);
- }
- else {
- uiItemL(&row, label_.c_str(), icon);
- }
+ const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_;
+ add_label(row, label_override);
if (!is_hovered()) {
return;
@@ -275,31 +303,80 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column)
UI_menutype_draw(&C, mt, &column);
}
-bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag)
+bool AssetCatalogTreeViewItem::can_rename() const
{
- const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+ return true;
+}
- /* There needs to be at least one asset from the current file. */
- LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) {
- if (!asset_item->is_external) {
- return true;
- }
+bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
+{
+ /* Important to keep state. */
+ BasicTreeViewItem::rename(new_name);
+
+ const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
+ get_tree_view());
+ ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name);
+ return true;
+}
+
+std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem::
+ create_drop_controller() const
+{
+ return std::make_unique<AssetCatalogDropController>(
+ static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
+}
+
+std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem::
+ create_drag_controller() const
+{
+ return std::make_unique<AssetCatalogDragController>(catalog_item_);
+}
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view,
+ AssetCatalogTreeItem &catalog_item)
+ : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item)
+{
+}
+
+bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const
+{
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ /* Always supported. */
+ return true;
+ }
+ if (drag.type == WM_DRAG_ASSET_LIST) {
+ return has_droppable_asset(drag, r_disabled_hint);
}
return false;
}
-bool AssetCatalogTreeViewItem::can_drop(const wmDrag &drag) const
+std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const
{
- if (drag.type != WM_DRAG_ASSET_LIST) {
- return false;
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ return drop_tooltip_asset_catalog(drag);
}
- return has_droppable_item(drag);
+ return drop_tooltip_asset_list(drag);
}
-std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/,
- const wmDrag &drag,
- const wmEvent & /*event*/) const
+std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const
{
+ BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
+
+ const ::AssetLibrary *asset_library = tree_view<AssetCatalogTreeView>().asset_library_;
+ bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(asset_library);
+ wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag);
+ AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id);
+
+ return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " +
+ IFACE_("into") + " '" + catalog_item_.get_name() + "'";
+}
+
+std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_LIST);
+
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
@@ -312,11 +389,34 @@ std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/,
")";
}
-bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tree_view,
- const wmDrag &drag,
- CatalogID catalog_id,
- StringRefNull simple_name)
+bool AssetCatalogDropController::on_drop(const wmDrag &drag)
{
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ return drop_asset_catalog_into_catalog(drag);
+ }
+ return drop_assets_into_catalog(tree_view<AssetCatalogTreeView>(),
+ drag,
+ catalog_item_.get_catalog_id(),
+ catalog_item_.get_simple_name());
+}
+
+bool AssetCatalogDropController::drop_asset_catalog_into_catalog(const wmDrag &drag)
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
+ wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag);
+ ED_asset_catalog_move(
+ &get_asset_library(), catalog_drag->drag_catalog_id, catalog_item_.get_catalog_id());
+
+ WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr);
+ return true;
+}
+
+bool AssetCatalogDropController::drop_assets_into_catalog(const AssetCatalogTreeView &tree_view,
+ const wmDrag &drag,
+ CatalogID catalog_id,
+ StringRefNull simple_name)
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_LIST);
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
if (!asset_drags) {
return false;
@@ -339,28 +439,46 @@ bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tre
return true;
}
-bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag)
+bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag,
+ const char **r_disabled_hint)
{
- const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
- get_tree_view());
- return drop_into_catalog(
- tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name());
+ const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+
+ *r_disabled_hint = nullptr;
+ /* There needs to be at least one asset from the current file. */
+ LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) {
+ if (!asset_item->is_external) {
+ return true;
+ }
+ }
+
+ *r_disabled_hint = "Only assets from this current file can be moved between catalogs";
+ return false;
}
-bool AssetCatalogTreeViewItem::can_rename() const
+::AssetLibrary &AssetCatalogDropController::get_asset_library() const
{
- return true;
+ return *tree_view<AssetCatalogTreeView>().asset_library_;
}
-bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeItem &catalog_item)
+ : catalog_item_(catalog_item)
{
- /* Important to keep state. */
- BasicTreeViewItem::rename(new_name);
+}
- const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
- get_tree_view());
- ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name);
- return true;
+int AssetCatalogDragController::get_drag_type() const
+{
+ return WM_DRAG_ASSET_CATALOG;
+}
+
+void *AssetCatalogDragController::create_drag_data() const
+{
+ wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog),
+ __func__);
+ drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id();
+ return drag_catalog;
}
/* ---------------------------------------------------------------------- */
@@ -382,17 +500,29 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
/* ---------------------------------------------------------------------- */
-bool AssetCatalogTreeViewUnassignedItem::can_drop(const wmDrag &drag) const
+std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem::
+ create_drop_controller() const
+{
+ return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>(
+ static_cast<AssetCatalogTreeView &>(get_tree_view()));
+}
+
+AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view)
+ : ui::AbstractTreeViewItemDropController(tree_view)
+{
+}
+
+bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop(
+ const wmDrag &drag, const char **r_disabled_hint) const
{
if (drag.type != WM_DRAG_ASSET_LIST) {
return false;
}
- return AssetCatalogTreeViewItem::has_droppable_item(drag);
+ return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint);
}
-std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /*C*/,
- const wmDrag &drag,
- const wmEvent & /*event*/) const
+std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip(
+ const wmDrag &drag) const
{
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
@@ -401,16 +531,17 @@ std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /*
TIP_("Move asset out of any catalog");
}
-bool AssetCatalogTreeViewUnassignedItem::on_drop(const wmDrag &drag)
+bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag)
{
- const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
- get_tree_view());
/* Assign to nil catalog ID. */
- return AssetCatalogTreeViewItem::drop_into_catalog(tree_view, drag, CatalogID{});
+ return AssetCatalogDropController::drop_assets_into_catalog(
+ tree_view<AssetCatalogTreeView>(), drag, CatalogID{});
}
} // namespace blender::ed::asset_browser
+/* ---------------------------------------------------------------------- */
+
namespace blender::ed::asset_browser {
class AssetCatalogFilterSettings {
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 24b24eb81dd..2e2f0c146d6 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -190,6 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile,
UI_but_drag_set_asset(but,
&(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
+ file->asset_data,
asset_params->import_type,
icon,
preview_image,
@@ -242,8 +243,9 @@ static void file_draw_string(int sx,
}
/**
- * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on
- * completion.
+ * \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last
+ * line. This is the cursor position on completion to allow drawing more text
+ * behind that.
*/
static void file_draw_string_multiline(int sx,
int sy,
@@ -515,6 +517,7 @@ static void file_draw_preview(const SpaceFile *sfile,
UI_but_drag_set_asset(but,
&(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
+ file->asset_data,
asset_params->import_type,
icon,
imb,
@@ -1064,7 +1067,9 @@ void file_draw_list(const bContext *C, ARegion *region)
layout->curr_size = params->thumbnail_size;
}
-static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region)
+static void file_draw_invalid_library_hint(const bContext *C,
+ const SpaceFile *sfile,
+ ARegion *region)
{
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
@@ -1072,9 +1077,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
uchar text_col[4];
- uchar text_alert_col[4];
UI_GetThemeColor4ubv(TH_TEXT, text_col);
- UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col);
const View2D *v2d = &region->v2d;
const int pad = sfile->layout->tile_border_x;
@@ -1085,23 +1088,42 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
int sy = v2d->tot.ymax;
{
- const char *message = TIP_("Library not found");
- const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path);
- char *draw_string = alloca(draw_string_str_len);
- BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path);
- file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy);
+ const char *message = TIP_("Path to asset library does not exist:");
+ file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy);
+
+ sy -= line_height;
+ file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col);
}
- /* Next line, but separate it a bit further. */
- sy -= line_height;
+ /* Separate a bit further. */
+ sy -= line_height * 2.2f;
{
UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
const char *suggestion = TIP_(
- "Set up the library or edit libraries in the Preferences, File Paths section");
+ "Asset Libraries are local directories that can contain .blend files with assets inside.\n"
+ "Manage Asset Libraries from the File Paths section in Preferences.");
file_draw_string_multiline(
- sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL);
+ sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy);
+
+ uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
+ uiBut *but = uiDefIconTextButO(block,
+ UI_BTYPE_BUT,
+ "SCREEN_OT_userpref_show",
+ WM_OP_INVOKE_DEFAULT,
+ ICON_PREFERENCES,
+ NULL,
+ sx + UI_UNIT_X,
+ sy - line_height - UI_UNIT_Y * 1.2f,
+ UI_UNIT_X * 8,
+ UI_UNIT_Y,
+ NULL);
+ PointerRNA *but_opptr = UI_but_operator_ptr_get(but);
+ RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS);
+
+ UI_block_end(C, block);
+ UI_block_draw(C, block);
}
}
@@ -1109,7 +1131,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
* Draw a string hint if the file list is invalid.
* \return true if the list is invalid and a hint was drawn.
*/
-bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
+bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region)
{
FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
/* Only for asset browser. */
@@ -1122,7 +1144,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
return false;
}
- file_draw_invalid_library_hint(sfile, region);
+ file_draw_invalid_library_hint(C, sfile, region);
return true;
}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index ba08777e4e2..4be5d6d8008 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -47,7 +47,7 @@ struct View2D;
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);
-bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region);
+bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region);
void file_draw_check_ex(bContext *C, struct ScrArea *area);
void file_draw_check(bContext *C);
@@ -79,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot);
void FILE_OT_previous(struct wmOperatorType *ot);
void FILE_OT_next(struct wmOperatorType *ot);
void FILE_OT_refresh(struct wmOperatorType *ot);
+void FILE_OT_asset_library_refresh(struct wmOperatorType *ot);
void FILE_OT_filenum(struct wmOperatorType *ot);
void FILE_OT_delete(struct wmOperatorType *ot);
void FILE_OT_rename(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index f647e1d4e4f..4eb10e65867 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1950,8 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_refresh_exec;
- /* Operator works for file or asset browsing */
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Refresh Asset Library Operator
+ * \{ */
+
+static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ ED_fileselect_clear(wm, sfile);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void FILE_OT_asset_library_refresh(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Refresh Asset Library";
+ ot->description = "Reread assets and asset catalogs from the asset library on disk";
+ ot->idname = "FILE_OT_asset_library_refresh";
+
+ /* api callbacks */
+ ot->exec = file_asset_library_refresh_exec;
+ ot->poll = ED_operator_asset_browsing_active;
}
/** \} */
diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c
index 51d0581d6a4..0e468718a04 100644
--- a/source/blender/editors/space_file/file_panels.c
+++ b/source/blender/editors/space_file/file_panels.c
@@ -247,7 +247,7 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane
uiItemR(row, &params_ptr, "asset_library_ref", 0, "", ICON_NONE);
if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) {
- uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_refresh");
+ uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh");
}
uiItemS(col);
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index d329a8809c7..a73fa2b9740 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -817,88 +817,85 @@ static bool is_filtered_hidden(const char *filename,
return false;
}
-static bool is_filtered_file(FileListInternEntry *file,
- const char *UNUSED(root),
- FileListFilter *filter)
+/**
+ * Apply the filter string as file path matching pattern.
+ * \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file_relpath(const FileListInternEntry *file, const FileListFilter *filter)
{
- bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
+ if (filter->filter_search[0] == '\0') {
+ return true;
+ }
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if (filter->filter && (filter->flags & FLF_DO_FILTER)) {
- if (file->typeflag & FILE_TYPE_DIR) {
- if (file->typeflag &
- (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
- if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
- is_filtered = false;
- }
- }
- else {
- if (!(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
- }
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0;
+}
+
+/** \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter)
+{
+ if (is_filtered_hidden(file->relpath, filter, file)) {
+ return false;
+ }
+
+ if (FILENAME_IS_CURRPAR(file->relpath)) {
+ return false;
+ }
+
+ /* We only check for types if some type are enabled in filtering. */
+ if (filter->filter && (filter->flags & FLF_DO_FILTER)) {
+ if (file->typeflag & FILE_TYPE_DIR) {
+ if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ return false;
}
}
else {
- if (!(file->typeflag & filter->filter)) {
- is_filtered = false;
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
+ return false;
}
}
}
- /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
- if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
- is_filtered = false;
+ else {
+ if (!(file->typeflag & filter->filter)) {
+ return false;
}
}
}
+ return true;
+}
- return is_filtered;
+/** \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file(FileListInternEntry *file,
+ const char *UNUSED(root),
+ FileListFilter *filter)
+{
+ return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter);
}
-static bool is_filtered_id_file(const FileListInternEntry *file,
- const char *id_group,
- const char *name,
- const FileListFilter *filter)
+static bool is_filtered_id_file_type(const FileListInternEntry *file,
+ const char *id_group,
+ const char *name,
+ const FileListFilter *filter)
{
- bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
- if (file->typeflag & FILE_TYPE_DIR) {
- if (file->typeflag &
- (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
- if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
- is_filtered = false;
- }
- }
- else {
- if (!(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
- }
- }
- }
- if (is_filtered && id_group) {
- if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
- is_filtered = false;
- }
- else {
- uint64_t filter_id = groupname_to_filter_id(id_group);
- if (!(filter_id & filter->filter_id)) {
- is_filtered = false;
- }
- }
+ if (!is_filtered_file_type(file, filter)) {
+ return false;
+ }
+
+ /* We only check for types if some type are enabled in filtering. */
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
+ if (id_group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ return false;
}
- }
- /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
- if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
- is_filtered = false;
+
+ uint64_t filter_id = groupname_to_filter_id(id_group);
+ if (!(filter_id & filter->filter_id)) {
+ return false;
}
}
}
- return is_filtered;
+ return true;
}
/**
@@ -921,40 +918,100 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte
file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library);
}
+/**
+ * Copy a string from source to `dest`, but prefix and suffix it with a single space.
+ * Assumes `dest` has at least space enough for the two spaces.
+ */
+static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size)
+{
+ BLI_assert(dest_size > 2);
+ const size_t source_length = BLI_strncpy_rlen(dest + 1, source, dest_size - 2);
+ dest[0] = ' ';
+ dest[source_length + 1] = ' ';
+ dest[source_length + 2] = '\0';
+}
+
+/**
+ * Return whether at least one tag matches the search filter.
+ * Tags are searched as "entire words", so instead of searching for "tag" in the
+ * filter string, this function searches for " tag ". Assumes the search filter
+ * starts and ends with a space.
+ *
+ * Here the tags on the asset are written in set notation:
+ *
+ * `asset_tag_matches_filter(" some tags ", {"some", "blue"})` -> true
+ * `asset_tag_matches_filter(" some tags ", {"som", "tag"})` -> false
+ * `asset_tag_matches_filter(" some tags ", {})` -> false
+ */
+static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data)
+{
+ LISTBASE_FOREACH (const AssetTag *, asset_tag, &asset_data->tags) {
+ char tag_name[MAX_NAME + 2]; /* sizeof(AssetTag::name) + 2 */
+ tag_copy_with_spaces(tag_name, asset_tag->name, sizeof(tag_name));
+ if (BLI_strcasestr(filter_search, tag_name) != NULL) {
+ return true;
+ }
+ }
+ return false;
+}
+
static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter)
{
+ const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file);
+
/* Not used yet for the asset view template. */
- if (!filter->asset_catalog_filter) {
+ if (filter->asset_catalog_filter && !file_is_asset_visible_in_catalog_filter_settings(
+ filter->asset_catalog_filter, asset_data)) {
+ return false;
+ }
+
+ if (filter->filter_search[0] == '\0') {
+ /* If there is no filter text, everything matches. */
return true;
}
- const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file);
- return file_is_asset_visible_in_catalog_filter_settings(filter->asset_catalog_filter,
- asset_data);
+ /* filter->filter_search contains "*the search text*". */
+ char filter_search[66]; /* sizeof(FileListFilter::filter_search) */
+ const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search);
+
+ /* When doing a name comparison, get rid of the leading/trailing asterisks. */
+ filter_search[string_length - 1] = '\0';
+ if (BLI_strcasestr(file->name, filter_search + 1) != NULL) {
+ return true;
+ }
+
+ /* Replace the asterisks with spaces, so that we can do matching on " sometag "; that way
+ * an artist searching for "redder" doesn't result in a match for the tag "red". */
+ filter_search[string_length - 1] = ' ';
+ filter_search[0] = ' ';
+
+ return asset_tag_matches_filter(filter_search, asset_data);
}
-static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_lib_type(FileListInternEntry *file,
+ const char *root,
+ FileListFilter *filter)
{
- bool is_filtered;
char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
BLI_join_dirfile(path, sizeof(path), root, file->relpath);
if (BLO_library_path_explode(path, dir, &group, &name)) {
- is_filtered = is_filtered_id_file(file, group, name, filter);
- }
- else {
- is_filtered = is_filtered_file(file, root, filter);
+ return is_filtered_id_file_type(file, group, name, filter);
}
+ return is_filtered_file_type(file, filter);
+}
- return is_filtered;
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+{
+ return is_filtered_lib_type(file, root, filter) && is_filtered_file_relpath(file, filter);
}
static bool is_filtered_asset_library(FileListInternEntry *file,
const char *root,
FileListFilter *filter)
{
- return is_filtered_lib(file, root, filter) && is_filtered_asset(file, filter);
+ return is_filtered_lib_type(file, root, filter) && is_filtered_asset(file, filter);
}
static bool is_filtered_main(FileListInternEntry *file,
@@ -969,7 +1026,7 @@ static bool is_filtered_main_assets(FileListInternEntry *file,
FileListFilter *filter)
{
/* "Filtered" means *not* being filtered out... So return true if the file should be visible. */
- return is_filtered_id_file(file, file->relpath, file->name, filter) &&
+ return is_filtered_id_file_type(file, file->relpath, file->name, filter) &&
is_filtered_asset(file, filter);
}
@@ -1854,6 +1911,7 @@ static void filelist_clear_asset_library(FileList *filelist)
{
/* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */
filelist->asset_library = NULL;
+ file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter);
}
void filelist_clear_ex(struct FileList *filelist,
@@ -1953,7 +2011,6 @@ void filelist_free(struct FileList *filelist)
filelist->selection_state = NULL;
}
- file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter);
MEM_SAFE_FREE(filelist->asset_library_ref);
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 6ab7e4eeecf..ce76fd65a86 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -486,6 +486,18 @@ struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
return filelist_file_get_id(file);
}
+void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID catalog_id)
+{
+ if (!ED_fileselect_is_asset_browser(sfile)) {
+ return;
+ }
+
+ FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
+ params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
+ params->catalog_id = catalog_id;
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
+}
+
static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data)
{
ID *asset_id = (ID *)custom_data;
@@ -517,14 +529,12 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def
const FileDirEntry *file = filelist_file_ex(files, file_index, false);
if (filelist_file_get_id(file) != asset_id) {
- filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
continue;
}
params->active_file = file_index;
filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
-
- /* Keep looping to deselect the other files. */
+ break;
}
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL);
@@ -984,6 +994,8 @@ static void file_attribute_columns_init(const FileSelectParams *params, FileLayo
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
{
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ /* Request a slightly more compact layout for asset browsing. */
+ const bool compact = ED_fileselect_is_asset_browser(sfile);
FileLayout *layout = NULL;
View2D *v2d = &region->v2d;
int numfiles;
@@ -1003,12 +1015,13 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
layout->textheight = textheight;
if (params->display == FILE_IMGDISPLAY) {
+ const float pad_fac = compact ? 0.15f : 0.3f;
layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
- layout->tile_border_x = 0.3f * UI_UNIT_X;
- layout->tile_border_y = 0.3f * UI_UNIT_X;
- layout->prv_border_x = 0.3f * UI_UNIT_X;
- layout->prv_border_y = 0.3f * UI_UNIT_Y;
+ layout->tile_border_x = pad_fac * UI_UNIT_X;
+ layout->tile_border_y = pad_fac * UI_UNIT_X;
+ layout->prv_border_x = pad_fac * UI_UNIT_X;
+ layout->prv_border_y = pad_fac * UI_UNIT_Y;
layout->tile_w = layout->prv_w + 2 * layout->prv_border_x;
layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight;
layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index a875b7a2c12..b115c63a569 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -659,7 +659,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
file_highlight_set(sfile, region, event->xy[0], event->xy[1]);
}
- if (!file_draw_hint_if_invalid(sfile, region)) {
+ if (!file_draw_hint_if_invalid(C, sfile, region)) {
file_draw_list(C, region);
}
@@ -688,6 +688,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_previous);
WM_operatortype_append(FILE_OT_next);
WM_operatortype_append(FILE_OT_refresh);
+ WM_operatortype_append(FILE_OT_asset_library_refresh);
WM_operatortype_append(FILE_OT_bookmark_add);
WM_operatortype_append(FILE_OT_bookmark_delete);
WM_operatortype_append(FILE_OT_bookmark_cleanup);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 4694d8652f6..bf2d20cf4c9 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -323,12 +323,19 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin
{
const float yheight = ymaxc - yminc;
- immUniformColor3f(0.7f, 0.7f, 0.7f);
-
/* draw with AA'd line */
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
+ /* Fully opaque line on selected strips. */
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* TODO: Use theme setting. */
+ immUniformColor3f(1.0f, 1.0f, 1.0f);
+ }
+ else {
+ immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
+ }
+
/* influence -------------------------- */
if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0);
@@ -501,7 +508,7 @@ static void nla_draw_strip(SpaceNla *snla,
/* strip is in normal track */
UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */
- UI_draw_roundbox_shade_x(
+ UI_draw_roundbox_4fv(
&(const rctf){
.xmin = strip->start,
.xmax = strip->end,
@@ -509,9 +516,7 @@ static void nla_draw_strip(SpaceNla *snla,
.ymax = ymaxc,
},
true,
- 0.0,
- 0.5,
- 0.1,
+ 0.0f,
color);
/* restore current vertex format & program (roundbox trashes it) */
@@ -545,11 +550,9 @@ static void nla_draw_strip(SpaceNla *snla,
/* draw strip outline
* - color used here is to indicate active vs non-active
*/
- if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
+ if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT)) {
/* strip should appear 'sunken', so draw a light border around it */
- color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */
- color[1] = 1.0f;
- color[2] = 0.9f;
+ color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */
}
else {
/* strip should appear to stand out, so draw a dark border around it */
@@ -566,7 +569,7 @@ static void nla_draw_strip(SpaceNla *snla,
}
else {
/* non-muted - draw solid, rounded outline */
- UI_draw_roundbox_shade_x(
+ UI_draw_roundbox_4fv(
&(const rctf){
.xmin = strip->start,
.xmax = strip->end,
@@ -574,9 +577,7 @@ static void nla_draw_strip(SpaceNla *snla,
.ymax = ymaxc,
},
false,
- 0.0,
- 0.0,
- 0.1,
+ 0.0f,
color);
/* restore current vertex format & program (roundbox trashes it) */
@@ -661,7 +662,7 @@ static void nla_draw_strip_text(AnimData *adt,
}
/* set text color - if colors (see above) are light, draw black text, otherwise draw white */
- if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) {
+ if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_TWEAKUSER)) {
col[0] = col[1] = col[2] = 0;
}
else {
@@ -805,29 +806,6 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
immRectf(
pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
- /* draw 'embossed' lines above and below the strip for effect */
- /* white base-lines */
- GPU_line_width(2.0f);
- immUniformColor4f(1.0f, 1.0f, 1.0f, 0.3f);
- immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
- immEnd();
-
- /* black top-lines */
- GPU_line_width(1.0f);
- immUniformColor3f(0.0f, 0.0f, 0.0f);
- immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP);
- immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
- immEnd();
-
- /* TODO: these lines but better --^ */
-
immUnbindProgram();
/* draw keyframes in the action */
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 80d3b43bf6b..600309c2c86 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -40,6 +40,7 @@ set(INC
set(SRC
drawnode.cc
node_add.cc
+ node_context_path.cc
node_draw.cc
node_edit.cc
node_geometry_attribute_search.cc
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 8a63a1f3505..24f5decacdf 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -3619,7 +3619,39 @@ static void std_node_socket_draw(
break;
}
case SOCK_IMAGE: {
- uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
+ if (node_tree->type == NTREE_GEOMETRY) {
+ if (text[0] == '\0') {
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "default_value",
+ "image.new",
+ "image.open",
+ nullptr,
+ 0,
+ ICON_NONE,
+ nullptr);
+ }
+ else {
+ /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */
+ uiLayout *row = uiLayoutSplit(layout, 0.3f, false);
+ uiItemL(row, text, 0);
+ uiTemplateID(row,
+ C,
+ ptr,
+ "default_value",
+ "image.new",
+ "image.open",
+ nullptr,
+ 0,
+ ICON_NONE,
+ nullptr);
+ }
+ }
+ else {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ }
break;
}
case SOCK_COLLECTION: {
@@ -4297,6 +4329,25 @@ void node_draw_link_bezier(const View2D *v2d,
UI_GetThemeColor4fv(th_col2, colors[2]);
}
+ /* 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) {
+ 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) {
+ interp_v3_v3v3(colors[1], colors[1], color_selected, alpha);
+ }
+ if (is_tonode_selected) {
+ interp_v3_v3v3(colors[2], colors[2], color_selected, alpha);
+ }
+ }
+ }
+
if (g_batch_link.enabled && !highlighted) {
/* Add link to batch. */
nodelink_batch_add_link(snode,
@@ -4370,15 +4421,6 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
else if (link->flag & NODE_LINK_MUTED) {
th_col1 = th_col2 = TH_REDALERT;
}
- else {
- /* Regular link, highlight if connected to selected node. */
- if (link->fromnode && link->fromnode->flag & SELECT) {
- th_col1 = TH_EDGE_SELECT;
- }
- if (link->tonode && link->tonode->flag & SELECT) {
- th_col2 = TH_EDGE_SELECT;
- }
- }
}
else {
/* Invalid link. */
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 7b6ca5e6e61..cb66d0dbd2b 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -758,7 +758,7 @@ static bool node_add_file_poll(bContext *C)
{
const SpaceNode *snode = CTX_wm_space_node(C);
return ED_operator_node_editable(C) &&
- ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT);
+ ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY);
}
static int node_add_file_exec(bContext *C, wmOperator *op)
@@ -784,6 +784,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
case NTREE_COMPOSIT:
type = CMP_NODE_IMAGE;
break;
+ case NTREE_GEOMETRY:
+ type = GEO_NODE_IMAGE_TEXTURE;
+ break;
default:
return OPERATOR_CANCELLED;
}
@@ -797,7 +800,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- node->id = (ID *)ima;
+ if (type == GEO_NODE_IMAGE_TEXTURE) {
+ bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value;
+ socket_value->value = ima;
+ }
+ else {
+ node->id = (ID *)ima;
+ }
/* When adding new image file via drag-drop we need to load imbuf in order
* to get proper image source.
diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc
new file mode 100644
index 00000000000..a0ff7f3ce25
--- /dev/null
+++ b/source/blender/editors/space_node/node_context_path.cc
@@ -0,0 +1,184 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spnode
+ * \brief Node breadcrumbs drawing
+ */
+
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "BKE_context.h"
+#include "BKE_material.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BKE_screen.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_interface.hh"
+#include "UI_resources.h"
+
+#include "UI_interface.hh"
+
+#include "node_intern.h"
+
+struct Mesh;
+struct Curve;
+struct Light;
+struct World;
+struct Material;
+
+namespace blender::ed::space_node {
+
+static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Object &object)
+{
+ if (object.type == OB_MESH && object.data) {
+ Mesh *mesh = (Mesh *)object.data;
+ ui::context_path_add_generic(path, RNA_Mesh, mesh);
+ }
+ if (object.type == OB_LAMP && object.data) {
+ 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) {
+ Curve *curve = (Curve *)object.data;
+ ui::context_path_add_generic(path, RNA_Curve, curve);
+ }
+}
+
+static void context_path_add_node_tree_and_node_groups(const SpaceNode &snode,
+ Vector<ui::ContextPathItem> &path,
+ const bool skip_base = false)
+{
+ Vector<const bNodeTreePath *> tree_path = snode.treepath;
+ for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(int(skip_base))) {
+ ui::context_path_add_generic(path, RNA_NodeTree, path_item->nodetree, ICON_NODETREE);
+ }
+}
+
+static void get_context_path_node_shader(const bContext &C,
+ SpaceNode &snode,
+ Vector<ui::ContextPathItem> &path)
+{
+ if (snode.flag & SNODE_PIN) {
+ if (snode.shaderfrom == SNODE_SHADER_WORLD) {
+ Scene *scene = CTX_data_scene(&C);
+ ui::context_path_add_generic(path, RNA_Scene, scene);
+ if (scene != nullptr) {
+ World *world = scene->world;
+ ui::context_path_add_generic(path, RNA_World, world);
+ }
+ /* Skip the base node tree here, because the world contains a node tree already. */
+ context_path_add_node_tree_and_node_groups(snode, path, true);
+ }
+ else {
+ context_path_add_node_tree_and_node_groups(snode, path);
+ }
+ }
+ else {
+ Object *object = CTX_data_active_object(&C);
+ if (snode.shaderfrom == SNODE_SHADER_OBJECT && object != nullptr) {
+ ui::context_path_add_generic(path, RNA_Object, object);
+ if (!(object->matbits && object->matbits[object->actcol - 1])) {
+ context_path_add_object_data(path, *object);
+ }
+ Material *material = BKE_object_material_get(object, object->actcol);
+ ui::context_path_add_generic(path, RNA_Material, material);
+ }
+ else if (snode.shaderfrom == SNODE_SHADER_WORLD) {
+ Scene *scene = CTX_data_scene(&C);
+ ui::context_path_add_generic(path, RNA_Scene, scene);
+ if (scene != nullptr) {
+ World *world = scene->world;
+ ui::context_path_add_generic(path, RNA_World, world);
+ }
+ }
+#ifdef WITH_FREESTYLE
+ else if (snode.shaderfrom == SNODE_SHADER_LINESTYLE) {
+ ViewLayer *viewlayer = CTX_data_view_layer(&C);
+ FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(viewlayer);
+ ui::context_path_add_generic(path, RNA_ViewLayer, viewlayer);
+ Material *mat = BKE_object_material_get(object, object->actcol);
+ ui::context_path_add_generic(path, RNA_Material, mat);
+ }
+#endif
+ context_path_add_node_tree_and_node_groups(snode, path, true);
+ }
+}
+
+static void get_context_path_node_compositor(const bContext &C,
+ SpaceNode &snode,
+ Vector<ui::ContextPathItem> &path)
+{
+ if (snode.flag & SNODE_PIN) {
+ context_path_add_node_tree_and_node_groups(snode, path);
+ }
+ else {
+ Scene *scene = CTX_data_scene(&C);
+ ui::context_path_add_generic(path, RNA_Scene, scene);
+ context_path_add_node_tree_and_node_groups(snode, path);
+ }
+}
+
+static void get_context_path_node_geometry(const bContext &C,
+ SpaceNode &snode,
+ Vector<ui::ContextPathItem> &path)
+{
+ if (snode.flag & SNODE_PIN) {
+ context_path_add_node_tree_and_node_groups(snode, path);
+ }
+ else {
+ Object *object = CTX_data_active_object(&C);
+ ui::context_path_add_generic(path, RNA_Object, object);
+ ModifierData *modifier = BKE_object_active_modifier(object);
+ ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER);
+ context_path_add_node_tree_and_node_groups(snode, path);
+ }
+}
+
+Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C)
+{
+ SpaceNode *snode = CTX_wm_space_node(&C);
+ if (snode == nullptr) {
+ return {};
+ }
+
+ Vector<ui::ContextPathItem> context_path;
+
+ if (snode->edittree->type == NTREE_GEOMETRY) {
+ get_context_path_node_geometry(C, *snode, context_path);
+ }
+ else if (snode->edittree->type == NTREE_SHADER) {
+ get_context_path_node_shader(C, *snode, context_path);
+ }
+ else if (snode->edittree->type == NTREE_COMPOSIT) {
+ get_context_path_node_compositor(C, *snode, context_path);
+ }
+
+ return context_path;
+}
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 97655080192..a6496294f96 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -71,8 +71,10 @@
#include "ED_gpencil.h"
#include "ED_node.h"
+#include "ED_screen.h"
#include "ED_space_api.h"
+#include "UI_interface.hh"
#include "UI_resources.h"
#include "UI_view2d.h"
@@ -728,7 +730,7 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) {
- node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1);
+ node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE);
}
GPU_blend(GPU_BLEND_NONE);
@@ -807,12 +809,10 @@ static void node_socket_outline_color_get(const bool selected,
float r_outline_color[4])
{
if (selected) {
- UI_GetThemeColor4fv(TH_TEXT_HI, r_outline_color);
- r_outline_color[3] = 0.9f;
+ UI_GetThemeColor4fv(TH_ACTIVE, r_outline_color);
}
else {
- copy_v4_fl(r_outline_color, 0.0f);
- r_outline_color[3] = 0.6f;
+ UI_GetThemeColor4fv(TH_WIRE, r_outline_color);
}
/* Until there is a better place for per socket color,
@@ -832,11 +832,6 @@ void node_socket_color_get(
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color);
-
- bNode *node = (bNode *)node_ptr->data;
- if (node->flag & NODE_MUTED) {
- r_color[3] *= 0.25f;
- }
}
struct SocketTooltipData {
@@ -854,60 +849,7 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
const GPointer value = value_log.value();
const CPPType &type = *value.type();
- if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) {
- const CPPType &base_type = field_type->field_type();
- BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer);
- const GField &field = field_type->get_gfield(value.get());
- if (field.node().depends_on_input()) {
- if (base_type.is<int>()) {
- ss << TIP_("Integer Field");
- }
- else if (base_type.is<float>()) {
- ss << TIP_("Float Field");
- }
- else if (base_type.is<blender::float3>()) {
- ss << TIP_("Vector Field");
- }
- else if (base_type.is<bool>()) {
- ss << TIP_("Boolean Field");
- }
- else if (base_type.is<std::string>()) {
- ss << TIP_("String Field");
- }
- ss << TIP_(" based on:\n");
-
- /* Use vector set to deduplicate inputs. */
- VectorSet<std::reference_wrapper<const FieldInput>> field_inputs;
- field.node().foreach_field_input(
- [&](const FieldInput &field_input) { field_inputs.add(field_input); });
- for (const FieldInput &field_input : field_inputs) {
- ss << "\u2022 " << field_input.socket_inspection_name();
- if (field_input != field_inputs.as_span().last().get()) {
- ss << ".\n";
- }
- }
- }
- else {
- blender::fn::evaluate_constant_field(field, buffer);
- if (base_type.is<int>()) {
- ss << *(int *)buffer << TIP_(" (Integer)");
- }
- else if (base_type.is<float>()) {
- ss << *(float *)buffer << TIP_(" (Float)");
- }
- else if (base_type.is<blender::float3>()) {
- ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
- }
- else if (base_type.is<bool>()) {
- ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
- }
- else if (base_type.is<std::string>()) {
- ss << *(std::string *)buffer << TIP_(" (String)");
- }
- base_type.destruct(buffer);
- }
- }
- else if (type.is<Object *>()) {
+ if (type.is<Object *>()) {
id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB);
}
else if (type.is<Material *>()) {
@@ -924,6 +866,71 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
}
}
+static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log,
+ std::stringstream &ss)
+{
+ const CPPType &type = value_log.type();
+ const GField &field = value_log.field();
+ const Span<std::string> input_tooltips = value_log.input_tooltips();
+
+ if (input_tooltips.is_empty()) {
+ if (field) {
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+ blender::fn::evaluate_constant_field(field, buffer);
+ if (type.is<int>()) {
+ ss << *(int *)buffer << TIP_(" (Integer)");
+ }
+ else if (type.is<float>()) {
+ ss << *(float *)buffer << TIP_(" (Float)");
+ }
+ else if (type.is<blender::float3>()) {
+ ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
+ }
+ else if (type.is<bool>()) {
+ ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+ }
+ else if (type.is<std::string>()) {
+ ss << *(std::string *)buffer << TIP_(" (String)");
+ }
+ type.destruct(buffer);
+ }
+ else {
+ /* Constant values should always be logged. */
+ BLI_assert_unreachable();
+ ss << "Value has not been logged";
+ }
+ }
+ else {
+ if (type.is<int>()) {
+ ss << TIP_("Integer field");
+ }
+ else if (type.is<float>()) {
+ ss << TIP_("Float field");
+ }
+ else if (type.is<blender::float3>()) {
+ ss << TIP_("Vector field");
+ }
+ else if (type.is<bool>()) {
+ ss << TIP_("Boolean field");
+ }
+ else if (type.is<std::string>()) {
+ ss << TIP_("String field");
+ }
+ else if (type.is<blender::ColorGeometry4f>()) {
+ ss << TIP_("Color field");
+ }
+ ss << TIP_(" based on:\n");
+
+ for (const int i : input_tooltips.index_range()) {
+ const blender::StringRef tooltip = input_tooltips[i];
+ ss << "\u2022 " << tooltip;
+ if (i < input_tooltips.size() - 1) {
+ ss << ".\n";
+ }
+ }
+ }
+}
+
static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log,
std::stringstream &ss)
{
@@ -1015,6 +1022,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C,
dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
create_inspection_string_for_generic_value(*generic_value_log, ss);
}
+ if (const geo_log::GFieldValueLog *gfield_value_log =
+ dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) {
+ create_inspection_string_for_gfield(*gfield_value_log, ss);
+ }
else if (const geo_log::GeometryValueLog *geo_value_log =
dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) {
create_inspection_string_for_geometry(*geo_value_log, ss);
@@ -1156,7 +1167,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", 0.7f);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Single point. */
@@ -1301,7 +1312,7 @@ 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", 0.7f);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Set handle size. */
@@ -1595,24 +1606,13 @@ static void node_draw_basis(const bContext *C,
/* Shadow. */
node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
+ rctf *rct = &node->totr;
float color[4];
int color_id = node_get_colorid(node);
- if (node->flag & NODE_MUTED) {
- /* Muted nodes are semi-transparent and colorless. */
- UI_GetThemeColor3fv(TH_NODE, color);
- color[3] = 0.25f;
- }
- else {
- /* Opaque headers for regular nodes. */
- UI_GetThemeColor3fv(color_id, color);
- color[3] = 1.0f;
- }
GPU_line_width(1.0f);
- rctf *rct = &node->totr;
- UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
-
+ /* Header. */
{
const rctf rect = {
rct->xmin,
@@ -1620,7 +1620,19 @@ static void node_draw_basis(const bContext *C,
rct->ymax - NODE_DY,
rct->ymax,
};
- UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color);
+
+ float color_header[4];
+
+ /* Muted nodes get a mix of the background with the node color. */
+ if (node->flag & NODE_MUTED) {
+ UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header);
+ }
+ else {
+ UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color_header);
+ }
+
+ UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
+ UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_header);
}
/* Show/hide icons. */
@@ -1703,31 +1715,28 @@ static void node_draw_basis(const bContext *C,
UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
}
- /* Open/close entirely. */
+ /* Collapse/expand icon. */
{
- int but_size = U.widget_unit * 0.8f;
- /* XXX button uses a custom triangle draw below, so make it invisible without icon. */
+ const int but_size = U.widget_unit * 0.8f;
UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
- uiBut *but = uiDefBut(node->block,
- UI_BTYPE_BUT_TOGGLE,
- 0,
- "",
- rct->xmin + 0.35f * U.widget_unit,
- rct->ymax - NODE_DY / 2.2f - but_size / 2,
- but_size,
- but_size,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- "");
+
+ uiBut *but = uiDefIconBut(node->block,
+ UI_BTYPE_BUT_TOGGLE,
+ 0,
+ ICON_DOWNARROW_HLT,
+ rct->xmin + (NODE_MARGIN_X / 3),
+ rct->ymax - NODE_DY / 2.2f - but_size / 2,
+ but_size,
+ but_size,
+ nullptr,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ "");
+
UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
UI_block_emboss_set(node->block, UI_EMBOSS);
-
- UI_GetThemeColor4fv(TH_TEXT, color);
- /* Custom draw function for this button. */
- UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, rct->ymax - NODE_DY / 2.2f, 'v', color);
}
char showname[128];
@@ -1737,7 +1746,7 @@ static void node_draw_basis(const bContext *C,
UI_BTYPE_LABEL,
0,
showname,
- (int)(rct->xmin + NODE_MARGIN_X),
+ (int)(rct->xmin + NODE_MARGIN_X + 0.4f),
(int)(rct->ymax - NODE_DY),
(short)(iconofs - rct->xmin - (18.0f * U.dpi_fac)),
(short)NODE_DY,
@@ -1751,49 +1760,96 @@ static void node_draw_basis(const bContext *C,
UI_but_flag_enable(but, UI_BUT_INACTIVE);
}
+ /* Wire across the node when muted/disabled. */
+ if (node->flag & NODE_MUTED) {
+ node_draw_mute_line(v2d, snode, node);
+ }
+
/* Body. */
- if (nodeTypeUndefined(node)) {
+ const float outline_width = 1.0f;
+ {
/* Use warning color to indicate undefined types. */
- UI_GetThemeColor4fv(TH_REDALERT, color);
- }
- else if (node->flag & NODE_MUTED) {
- /* Muted nodes are semi-transparent and colorless. */
- UI_GetThemeColor4fv(TH_NODE, color);
- }
- else if (node->flag & NODE_CUSTOM_COLOR) {
- rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f);
- }
- else {
- UI_GetThemeColor4fv(TH_NODE, color);
- }
+ if (nodeTypeUndefined(node)) {
+ UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color);
+ }
+ /* Muted nodes get a mix of the background with the node color. */
+ else if (node->flag & NODE_MUTED) {
+ UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color);
+ }
+ else if (node->flag & NODE_CUSTOM_COLOR) {
+ rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f);
+ }
+ else {
+ UI_GetThemeColor4fv(TH_NODE, color);
+ }
- if (node->flag & NODE_MUTED) {
- color[3] = 0.5f;
+ /* Draw selected nodes fully opaque. */
+ if (node->flag & SELECT) {
+ color[3] = 1.0f;
+ }
+
+ /* Draw muted nodes slightly transparent so the wires inside are visible. */
+ if (node->flag & NODE_MUTED) {
+ color[3] -= 0.2f;
+ }
+
+ const rctf rect = {
+ rct->xmin,
+ rct->xmax,
+ rct->ymin,
+ rct->ymax - (NODE_DY + outline_width),
+ };
+
+ UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
+ UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color);
}
+ /* Header underline. */
{
- UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
+ float color_underline[4];
+
+ if (node->flag & NODE_MUTED) {
+ UI_GetThemeColor4fv(TH_WIRE, color_underline);
+ }
+ else {
+ UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline);
+ }
+
const rctf rect = {
rct->xmin,
rct->xmax,
- rct->ymin,
+ rct->ymax - (NODE_DY + outline_width),
rct->ymax - NODE_DY,
};
- UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color);
+
+ UI_draw_roundbox_corner_set(UI_CNR_NONE);
+ UI_draw_roundbox_4fv(&rect, true, 0.0f, color_underline);
}
- /* Outline active and selected emphasis. */
- if (node->flag & SELECT) {
- UI_GetThemeColorShadeAlpha4fv(
- (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color);
+ /* Outline. */
+ {
+ const rctf rect = {
+ rct->xmin - outline_width,
+ rct->xmax + outline_width,
+ rct->ymin - outline_width,
+ rct->ymax + outline_width,
+ };
- UI_draw_roundbox_corner_set(UI_CNR_ALL);
- UI_draw_roundbox_aa(rct, false, BASIS_RAD, color);
- }
+ /* Color the outline according to active, selected, or undefined status. */
+ float color_outline[4];
- /* Disable lines. */
- if (node->flag & NODE_MUTED) {
- node_draw_mute_line(v2d, snode, node);
+ if (node->flag & SELECT) {
+ UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
+ }
+ else if (nodeTypeUndefined(node)) {
+ UI_GetThemeColor4fv(TH_REDALERT, color_outline);
+ }
+ else {
+ UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
+ }
+
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline);
}
node_draw_sockets(v2d, C, ntree, node, true, false);
@@ -1828,46 +1884,45 @@ static void node_draw_hidden(const bContext *C,
float scale;
UI_view2d_scale_get(v2d, &scale, nullptr);
+ const int color_id = node_get_colorid(node);
+
/* Shadow. */
node_draw_shadow(snode, node, hiddenrad, 1.0f);
- /* Body. */
- float color[4];
- int color_id = node_get_colorid(node);
+ /* Wire across the node when muted/disabled. */
if (node->flag & NODE_MUTED) {
- /* Muted nodes are semi-transparent and colorless. */
- UI_GetThemeColor4fv(TH_NODE, color);
- color[3] = 0.25f;
- }
- else {
- UI_GetThemeColor4fv(color_id, color);
+ node_draw_mute_line(v2d, snode, node);
}
- UI_draw_roundbox_aa(rct, true, hiddenrad, color);
-
- /* Outline active and selected emphasis. */
- if (node->flag & SELECT) {
- UI_GetThemeColorShadeAlpha4fv(
- (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color);
-
- UI_draw_roundbox_aa(rct, false, hiddenrad, color);
- }
+ /* Body. */
+ float color[4];
+ {
+ if (nodeTypeUndefined(node)) {
+ /* Use warning color to indicate undefined types. */
+ UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color);
+ }
+ else if (node->flag & NODE_MUTED) {
+ /* Muted nodes get a mix of the background with the node color. */
+ UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color);
+ }
+ else if (node->flag & NODE_CUSTOM_COLOR) {
+ rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f);
+ }
+ else {
+ UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color);
+ }
- /* Custom color inline. */
- if (node->flag & NODE_CUSTOM_COLOR) {
- GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_smooth(true);
+ /* Draw selected nodes fully opaque. */
+ if (node->flag & SELECT) {
+ color[3] = 1.0f;
+ }
- const rctf rect = {
- rct->xmin + 1,
- rct->xmax - 1,
- rct->ymin + 1,
- rct->ymax - 1,
- };
- UI_draw_roundbox_3fv_alpha(&rect, false, hiddenrad, node->color, 1.0f);
+ /* Draw muted nodes slightly transparent so the wires inside are visible. */
+ if (node->flag & NODE_MUTED) {
+ color[3] -= 0.2f;
+ }
- GPU_line_smooth(false);
- GPU_blend(GPU_BLEND_NONE);
+ UI_draw_roundbox_4fv(rct, true, hiddenrad, color);
}
/* Title. */
@@ -1878,36 +1933,28 @@ static void node_draw_hidden(const bContext *C,
UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
}
- /* Open / collapse icon. */
+ /* Collapse/expand icon. */
{
- int but_size = U.widget_unit * 0.8f;
- /* XXX button uses a custom triangle draw below, so make it invisible without icon */
+ const int but_size = U.widget_unit * 1.0f;
UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
- uiBut *but = uiDefBut(node->block,
- UI_BTYPE_BUT_TOGGLE,
- 0,
- "",
- rct->xmin + 0.35f * U.widget_unit,
- centy - but_size / 2,
- but_size,
- but_size,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- "");
- UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
- UI_block_emboss_set(node->block, UI_EMBOSS);
- UI_GetThemeColor4fv(TH_TEXT, color);
- /* Custom draw function for this button. */
- UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, centy, 'h', color);
- }
+ uiBut *but = uiDefIconBut(node->block,
+ UI_BTYPE_BUT_TOGGLE,
+ 0,
+ ICON_RIGHTARROW,
+ rct->xmin + (NODE_MARGIN_X / 3),
+ centy - but_size / 2,
+ but_size,
+ but_size,
+ nullptr,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ "");
- /* Disable lines. */
- if (node->flag & NODE_MUTED) {
- node_draw_mute_line(v2d, snode, node);
+ UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
+ UI_block_emboss_set(node->block, UI_EMBOSS);
}
char showname[128];
@@ -1927,15 +1974,44 @@ static void node_draw_hidden(const bContext *C,
0,
0,
"");
+
+ /* Outline. */
+ {
+ const float outline_width = 1.0f;
+ const rctf rect = {
+ rct->xmin - outline_width,
+ rct->xmax + outline_width,
+ rct->ymin - outline_width,
+ rct->ymax + outline_width,
+ };
+
+ /* Color the outline according to active, selected, or undefined status. */
+ float color_outline[4];
+
+ if (node->flag & SELECT) {
+ UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
+ }
+ else if (nodeTypeUndefined(node)) {
+ UI_GetThemeColor4fv(TH_REDALERT, color_outline);
+ }
+ else {
+ UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
+ }
+
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&rect, false, hiddenrad, color_outline);
+ }
+
if (node->flag & NODE_MUTED) {
UI_but_flag_enable(but, UI_BUT_INACTIVE);
}
/* Scale widget thing. */
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_blend(GPU_BLEND_ALPHA);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformThemeColorShade(color_id, -10);
+ immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180);
float dx = 10.0f;
immBegin(GPU_PRIM_LINES, 4);
@@ -1946,7 +2022,7 @@ static void node_draw_hidden(const bContext *C,
immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f);
immEnd();
- immUniformThemeColorShade(color_id, 30);
+ immUniformThemeColorShadeAlpha(TH_TEXT, 0, -180);
dx -= snode->runtime->aspect;
immBegin(GPU_PRIM_LINES, 4);
@@ -1958,6 +2034,7 @@ static void node_draw_hidden(const bContext *C,
immEnd();
immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
node_draw_sockets(v2d, C, ntree, node, true, false);
@@ -2142,15 +2219,34 @@ void node_draw_nodetree(const bContext *C,
}
}
-/* Draw tree path info in lower left corner. */
-static void draw_tree_path(SpaceNode *snode)
+/* Draw the breadcrumb on the bottom of the editor. */
+static void draw_tree_path(const bContext &C, ARegion &region)
{
- char info[256];
+ using namespace blender;
+
+ GPU_matrix_push_projection();
+ wmOrtho2_region_pixelspace(&region);
+
+ const rcti *rect = ED_region_visible_rect(&region);
+
+ const uiStyle *style = UI_style_get_dpi();
+ const float padding_x = 16 * UI_DPI_FAC;
+ const int x = rect->xmin + padding_x;
+ const int y = region.winy - UI_UNIT_Y * 0.6f;
+ const int width = BLI_rcti_size_x(rect) - 2 * padding_x;
- ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info));
+ uiBlock *block = UI_block_begin(&C, &region, __func__, UI_EMBOSS_NONE);
+ uiLayout *layout = UI_block_layout(
+ block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y, width, 1, 0, style);
- UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
- BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info));
+ Vector<ui::ContextPathItem> context_path = ed::space_node::context_path_for_space_node(C);
+ ui::template_breadcrumbs(*layout, context_path);
+
+ UI_block_layout_resolve(block, nullptr, nullptr);
+ UI_block_end(&C, block);
+ UI_block_draw(&C, block);
+
+ GPU_matrix_pop_projection();
}
static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float center[2])
@@ -2227,8 +2323,6 @@ void node_draw_space(const bContext *C, ARegion *region)
snode->runtime->cursor[0] /= UI_DPI_FAC;
snode->runtime->cursor[1] /= UI_DPI_FAC;
- int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE);
-
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
/* Only set once. */
@@ -2237,6 +2331,9 @@ void node_draw_space(const bContext *C, ARegion *region)
/* Nodes. */
snode_set_context(C);
+ const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE);
+ UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels);
+
/* Draw parent node trees. */
if (snode->treepath.last) {
bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
@@ -2264,9 +2361,6 @@ void node_draw_space(const bContext *C, ARegion *region)
if (ntree) {
snode_setup_v2d(snode, region, center);
- /* Grid. */
- UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels);
-
/* Backdrop. */
draw_nodespace_back_pix(C, region, snode, path->parent_key);
@@ -2305,8 +2399,6 @@ void node_draw_space(const bContext *C, ARegion *region)
}
}
else {
- /* Default grid. */
- UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels);
/* Backdrop. */
draw_nodespace_back_pix(C, region, snode, NODE_INSTANCE_KEY_NONE);
@@ -2324,8 +2416,10 @@ void node_draw_space(const bContext *C, ARegion *region)
}
}
- /* Tree path info. */
- draw_tree_path(snode);
+ /* Draw context path. */
+ if (snode->edittree) {
+ draw_tree_path(*C, *region);
+ }
/* Scrollers. */
UI_view2d_scrollers_draw(v2d, nullptr);
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index f069038cc09..c0d50e753ff 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -332,7 +332,7 @@ extern const char *node_context_dir[];
#define NODE_SOCKDY (0.1f * U.widget_unit)
#define NODE_WIDTH(node) (node->width * UI_DPI_FAC)
#define NODE_HEIGHT(node) (node->height * UI_DPI_FAC)
-#define NODE_MARGIN_X (1.10f * U.widget_unit)
+#define NODE_MARGIN_X (1.2f * U.widget_unit)
#define NODE_SOCKSIZE (0.25f * U.widget_unit)
#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
@@ -341,3 +341,11 @@ extern const char *node_context_dir[];
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+# include "BLI_vector.hh"
+# include "UI_interface.hh"
+namespace blender::ed::space_node {
+Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C);
+}
+#endif \ No newline at end of file
diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c
index df4f63af20b..0c54da65e9c 100644
--- a/source/blender/editors/space_node/node_ops.c
+++ b/source/blender/editors/space_node/node_ops.c
@@ -156,6 +156,7 @@ void ED_operatormacros_node(void)
OPTYPE_UNDO | OPTYPE_REGISTER);
mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(mot->ptr, "remove_on_cancel", true);
+ RNA_boolean_set(mot->ptr, "view2d_edge_pan", true);
WM_operatortype_macro_define(ot, "NODE_OT_attach");
WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index b69e7e98bca..76aad684b4c 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -57,8 +57,12 @@
#include "BLT_translation.h"
+#include "NOD_node_tree_ref.hh"
+
#include "node_intern.h" /* own include */
+using namespace blender::nodes::node_tree_ref_types;
+
/* -------------------------------------------------------------------- */
/** \name Relations Helpers
* \{ */
@@ -612,160 +616,282 @@ static void snode_autoconnect(Main *bmain,
/** \name Link Viewer Operator
* \{ */
-static int node_link_viewer(const bContext *C, bNode *tonode)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
+namespace blender::ed::nodes::viewer_linking {
- /* context check */
- if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) {
- return OPERATOR_CANCELLED;
+/* Depending on the node tree type, different socket types are supported by viewer nodes. */
+static bool socket_can_be_viewed(const OutputSocketRef &socket)
+{
+ if (nodeSocketIsHidden(socket.bsocket())) {
+ return false;
}
- if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
- return OPERATOR_CANCELLED;
+ if (socket.idname() == "NodeSocketVirtual") {
+ return false;
}
+ if (socket.tree().btree()->type != NTREE_GEOMETRY) {
+ return true;
+ }
+ return ELEM(socket.typeinfo()->type,
+ SOCK_GEOMETRY,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_RGBA);
+}
- /* get viewer */
- bNode *viewer_node = nullptr;
- LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
- if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
- if (node->flag & NODE_DO_OUTPUT) {
- viewer_node = node;
- break;
- }
- }
+static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
+{
+ switch (socket_type) {
+ case SOCK_FLOAT:
+ return CD_PROP_FLOAT;
+ case SOCK_INT:
+ return CD_PROP_INT32;
+ case SOCK_VECTOR:
+ return CD_PROP_FLOAT3;
+ case SOCK_BOOLEAN:
+ return CD_PROP_BOOL;
+ case SOCK_RGBA:
+ return CD_PROP_COLOR;
+ default:
+ /* Fallback. */
+ return CD_AUTO_FROM_NAME;
}
- /* no viewer, we make one active */
- if (viewer_node == nullptr) {
- LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
- if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
- node->flag |= NODE_DO_OUTPUT;
- viewer_node = node;
- break;
+}
+
+/**
+ * Find the socket to link to in a viewer node.
+ */
+static bNodeSocket *node_link_viewer_get_socket(bNodeTree *ntree,
+ bNode *viewer_node,
+ bNodeSocket *src_socket)
+{
+ if (viewer_node->type != GEO_NODE_VIEWER) {
+ /* In viewer nodes in the compositor, only the first input should be linked to. */
+ return (bNodeSocket *)viewer_node->inputs.first;
+ }
+ /* For the geometry nodes viewer, find the socket with the correct type. */
+ LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node->inputs) {
+ if (viewer_socket->type == src_socket->type) {
+ if (viewer_socket->type == SOCK_GEOMETRY) {
+ return viewer_socket;
}
+ NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node->storage;
+ const CustomDataType data_type = socket_type_to_custom_data_type(
+ (eNodeSocketDatatype)src_socket->type);
+ BLI_assert(data_type != CD_AUTO_FROM_NAME);
+ storage->data_type = data_type;
+ nodeUpdate(ntree, viewer_node);
+ return viewer_socket;
}
}
+ return nullptr;
+}
- bNodeSocket *sock = nullptr;
- bNodeLink *link = nullptr;
+static bool is_viewer_node(const NodeRef &node)
+{
+ return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
+}
- /* try to find an already connected socket to cycle to the next */
- if (viewer_node) {
- link = nullptr;
+static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree)
+{
+ Vector<const NodeRef *> viewer_nodes;
+ for (const NodeRef *node : tree.nodes()) {
+ if (is_viewer_node(*node)) {
+ viewer_nodes.append(node);
+ }
+ }
+ return viewer_nodes;
+}
- for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
- if (link->tonode == viewer_node && link->fromnode == tonode) {
- if (link->tosock == viewer_node->inputs.first) {
- break;
- }
- }
+static bool is_viewer_socket_in_viewer(const InputSocketRef &socket)
+{
+ const NodeRef &node = socket.node();
+ BLI_assert(is_viewer_node(node));
+ if (node.typeinfo()->type == GEO_NODE_VIEWER) {
+ return true;
+ }
+ return socket.index() == 0;
+}
+
+static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node)
+{
+ for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) {
+ if (&target_socket->node() != &viewer_node) {
+ continue;
+ }
+ if (!target_socket->is_available()) {
+ continue;
+ }
+ if (is_viewer_socket_in_viewer(*target_socket)) {
+ return true;
}
- if (link) {
- /* unlink existing connection */
- sock = link->fromsock;
- nodeRemLink(snode->edittree, link);
+ }
+ return false;
+}
- /* find a socket after the previously connected socket */
- if (ED_node_is_geometry(snode)) {
- /* Geometry nodes viewer only supports geometry sockets for now. */
- for (sock = sock->next; sock; sock = sock->next) {
- if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
- break;
- }
- }
- }
- else {
- for (sock = sock->next; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock)) {
- break;
- }
- }
+static int get_default_viewer_type(const bContext *C)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER;
+}
+
+static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node)
+{
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
+ if (link->tonode == &viewer_node) {
+ if (link->tosock->flag & SOCK_UNAVAIL) {
+ nodeRemLink(&btree, link);
}
}
}
+}
- if (tonode) {
- /* Find a selected socket that overrides the socket to connect to */
- if (ED_node_is_geometry(snode)) {
- /* Geometry nodes viewer only supports geometry sockets for now. */
- LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
- if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
- sock = sock2;
- break;
- }
- }
+static const NodeRef *get_existing_viewer(const NodeTreeRef &tree)
+{
+ Vector<const NodeRef *> viewer_nodes = find_viewer_nodes(tree);
+
+ /* Check if there is already an active viewer node that should be used. */
+ for (const NodeRef *viewer_node : viewer_nodes) {
+ if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) {
+ return viewer_node;
}
- else {
- LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
- if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
- sock = sock2;
- break;
- }
- }
+ }
+
+ /* If no active but non-active viewers exist, make one active. */
+ if (!viewer_nodes.is_empty()) {
+ viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT;
+ return viewer_nodes[0];
+ }
+ return nullptr;
+}
+
+static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node,
+ const NodeRef &node_to_view)
+{
+ /* Check if any of the output sockets is selected, which is the case when the user just clicked
+ * on the socket. */
+ for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
+ if (output_socket->bsocket()->flag & SELECT) {
+ return output_socket;
}
}
- /* find a socket starting from the first socket */
- if (!sock) {
- if (ED_node_is_geometry(snode)) {
- /* Geometry nodes viewer only supports geometry sockets for now. */
- for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
- if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
- break;
- }
+ const OutputSocketRef *last_socket_linked_to_viewer = nullptr;
+ if (active_viewer_node != nullptr) {
+ for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
+ if (!socket_can_be_viewed(*output_socket)) {
+ continue;
}
- }
- else {
- for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock)) {
- break;
- }
+ if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
+ last_socket_linked_to_viewer = output_socket;
}
}
}
-
- if (sock) {
- /* add a new viewer if none exists yet */
- if (!viewer_node) {
- /* XXX location is a quick hack, just place it next to the linked socket */
- const int viewer_type = ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER;
- viewer_node = node_add_node(C, nullptr, viewer_type, sock->locx + 100, sock->locy);
- if (!viewer_node) {
- return OPERATOR_CANCELLED;
+ if (last_socket_linked_to_viewer == nullptr) {
+ /* If no output is connected to a viewer, use the first output that can be viewed. */
+ for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
+ if (socket_can_be_viewed(*output_socket)) {
+ return output_socket;
}
-
- link = nullptr;
}
- else {
- /* get link to viewer */
- for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
- if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
- break;
- }
+ }
+ else {
+ /* Pick the next socket to be linked to the viewer. */
+ const int tot_outputs = node_to_view.outputs().size();
+ for (const int offset : IndexRange(1, tot_outputs - 1)) {
+ const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
+ const OutputSocketRef &output_socket = node_to_view.output(index);
+ if (!socket_can_be_viewed(output_socket)) {
+ continue;
+ }
+ if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
+ continue;
}
+ return &output_socket;
}
+ }
+ return nullptr;
+}
- if (link == nullptr) {
- nodeAddLink(
- snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first);
- }
- else {
- link->fromnode = tonode;
- link->fromsock = sock;
- /* make sure the dependency sorting is updated */
- snode->edittree->update |= NTREE_UPDATE_LINKS;
+static int link_socket_to_viewer(const bContext *C,
+ bNode *viewer_bnode,
+ bNode *bnode_to_view,
+ bNodeSocket *bsocket_to_view)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *btree = snode->edittree;
+
+ if (viewer_bnode == nullptr) {
+ /* Create a new viewer node if none exists. */
+ const int viewer_type = get_default_viewer_type(C);
+ viewer_bnode = node_add_node(
+ C, nullptr, viewer_type, bsocket_to_view->locx + 100, bsocket_to_view->locy);
+ if (viewer_bnode == nullptr) {
+ return OPERATOR_CANCELLED;
}
- if (ED_node_is_geometry(snode)) {
- ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node);
+ }
+
+ bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view);
+ if (viewer_bsocket == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bNodeLink *link_to_change = nullptr;
+ LISTBASE_FOREACH (bNodeLink *, link, &btree->links) {
+ if (link->tosock == viewer_bsocket) {
+ link_to_change = link;
+ break;
}
+ }
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
- snode_update(snode, viewer_node);
- DEG_id_tag_update(&snode->edittree->id, 0);
+ if (link_to_change == nullptr) {
+ nodeAddLink(btree, bnode_to_view, bsocket_to_view, viewer_bnode, viewer_bsocket);
+ }
+ else {
+ link_to_change->fromnode = bnode_to_view;
+ link_to_change->fromsock = bsocket_to_view;
+ btree->update |= NTREE_UPDATE_LINKS;
}
+ remove_links_to_unavailable_viewer_sockets(*btree, *viewer_bnode);
+
+ if (btree->type == NTREE_GEOMETRY) {
+ ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_bnode);
+ }
+
+ ntreeUpdateTree(CTX_data_main(C), btree);
+ snode_update(snode, viewer_bnode);
+ DEG_id_tag_update(&btree->id, 0);
+
return OPERATOR_FINISHED;
}
+static int node_link_viewer(const bContext *C, bNode *bnode_to_view)
+{
+ if (bnode_to_view == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *btree = snode->edittree;
+
+ const NodeTreeRef tree{btree};
+ const NodeRef &node_to_view = *tree.find_node(*bnode_to_view);
+ const NodeRef *active_viewer_node = get_existing_viewer(tree);
+
+ const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node,
+ node_to_view);
+ if (socket_to_view == nullptr) {
+ return OPERATOR_FINISHED;
+ }
+
+ bNodeSocket *bsocket_to_view = socket_to_view->bsocket();
+ bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr;
+ return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view);
+}
+
+} // namespace blender::ed::nodes::viewer_linking
+
static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -777,7 +903,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (node_link_viewer(C, node) == OPERATOR_CANCELLED) {
+ if (blender::ed::nodes::viewer_linking::node_link_viewer(C, node) == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index bd2559c4d4d..0b5d7cdda82 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -92,9 +92,6 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
snode->id = id;
snode->from = from;
- snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS;
- snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS;
-
ED_node_set_active_viewer_key(snode);
WM_main_add_notifier(NC_SCENE | ND_NODES, NULL);
@@ -204,27 +201,6 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value)
}
}
-void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_length)
-{
- int size;
-
- value[0] = '\0';
- int i = 0;
- LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
- if (i == 0) {
- size = BLI_strncpy_rlen(value, path->display_name, max_length);
- }
- else {
- size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name);
- }
- max_length -= size;
- if (max_length <= 0) {
- break;
- }
- value += size;
- }
-}
-
void ED_node_set_active_viewer_key(SpaceNode *snode)
{
bNodeTreePath *path = snode->treepath.last;
@@ -259,6 +235,8 @@ static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(s
snode->spacetype = SPACE_NODE;
snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA;
+ snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS;
+ snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS;
/* backdrop */
snode->zoom = 1.0f;
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index a82f516b125..a391d032d7e 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -868,7 +868,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
static char *datastack_drop_tooltip(bContext *UNUSED(C),
wmDrag *drag,
- const wmEvent *UNUSED(event),
+ const int UNUSED(xy[2]),
struct wmDropBox *UNUSED(drop))
{
StackDropData *drop_data = drag->poin;
@@ -1201,11 +1201,13 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *collection_drop_tooltip(bContext *C,
wmDrag *drag,
- const wmEvent *event,
+ const int UNUSED(xy[2]),
wmDropBox *UNUSED(drop))
{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL;
CollectionDrop data;
- if (!event->shift && collection_drop_init(C, drag, event, &data)) {
+ if (event && !event->shift && collection_drop_init(C, drag, event, &data)) {
TreeElement *te = data.te;
if (!data.from || event->ctrl) {
return BLI_strdup(TIP_("Link inside Collection"));
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 75bdc5dbac6..ae2b1870884 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -197,11 +197,24 @@ static void get_element_operation_type(
static TreeElement *get_target_element(SpaceOutliner *space_outliner)
{
TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
- BLI_assert(te);
return te;
}
+static bool outliner_operation_tree_element_poll(bContext *C)
+{
+ if (!ED_operator_outliner_active(C)) {
+ return false;
+ }
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+ TreeElement *te = get_target_element(space_outliner);
+ if (te == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
static void unlink_action_fn(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -1426,7 +1439,7 @@ static void outliner_do_data_operation(
}
}
-static Base *outline_batch_delete_hierarchy(
+static Base *outliner_batch_delete_hierarchy(
ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
{
Base *child_base, *base_next;
@@ -1444,7 +1457,7 @@ static Base *outline_batch_delete_hierarchy(
/* pass */
}
if (parent) {
- base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base);
+ base_next = outliner_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base);
}
}
@@ -1497,7 +1510,7 @@ static void object_batch_delete_hierarchy_fn(bContext *C,
ED_object_editmode_exit(C, EM_FREEDATA);
}
- outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base);
+ outliner_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base);
}
}
@@ -1868,6 +1881,10 @@ static bool outliner_id_operation_item_poll(bContext *C,
PropertyRNA *UNUSED(prop),
const int enum_value)
{
+ if (!outliner_operation_tree_element_poll(C)) {
+ return false;
+ }
+
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
TreeElement *te = get_target_element(space_outliner);
TreeStoreElem *tselem = TREESTORE(te);
@@ -2254,7 +2271,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = outliner_id_operation_exec;
- ot->poll = ED_operator_outliner_active;
+ ot->poll = outliner_operation_tree_element_poll;
ot->flag = 0;
@@ -2361,7 +2378,7 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = outliner_lib_operation_exec;
- ot->poll = ED_operator_outliner_active;
+ ot->poll = outliner_operation_tree_element_poll;
ot->prop = RNA_def_enum(
ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", "");
@@ -2420,14 +2437,8 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
-
bAction *act;
- /* check for invalid states */
- if (space_outliner == NULL) {
- return OPERATOR_CANCELLED;
- }
-
TreeElement *te = get_target_element(space_outliner);
get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
@@ -2482,7 +2493,7 @@ void OUTLINER_OT_action_set(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_enum_search_invoke;
ot->exec = outliner_action_set_exec;
- ot->poll = ED_operator_outliner_active;
+ ot->poll = outliner_operation_tree_element_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2531,12 +2542,6 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
-
- /* check for invalid states */
- if (space_outliner == NULL) {
- return OPERATOR_CANCELLED;
- }
-
TreeElement *te = get_target_element(space_outliner);
get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
@@ -2722,12 +2727,6 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
-
- /* check for invalid states */
- if (space_outliner == NULL) {
- return OPERATOR_CANCELLED;
- }
-
TreeElement *te = get_target_element(space_outliner);
get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
@@ -2806,6 +2805,13 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C,
return DummyRNA_DEFAULT_items;
}
+ TreeElement *te = get_target_element(space_outliner);
+ if (te == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
+ TreeStoreElem *tselem = TREESTORE(te);
+
static const EnumPropertyItem optype_sel_and_hide[] = {
{OL_DOP_SELECT, "SELECT", 0, "Select", ""},
{OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
@@ -2816,9 +2822,6 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C,
static const EnumPropertyItem optype_sel_linked[] = {
{OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}};
- TreeElement *te = get_target_element(space_outliner);
- TreeStoreElem *tselem = TREESTORE(te);
-
if (tselem->type == TSE_RNA_STRUCT) {
return optype_sel_linked;
}
@@ -2835,7 +2838,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = outliner_data_operation_exec;
- ot->poll = ED_operator_outliner_active;
+ ot->poll = outliner_operation_tree_element_poll;
ot->flag = 0;
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 2a29125af19..8c70f4e3f7a 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -1709,16 +1709,24 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
- Editing *ed = SEQ_editing_get(scene);
+ ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene));
SEQ_prefetch_stop(scene);
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ const bool is_preview = sequencer_view_preview_poll(C);
+ if (is_preview) {
+ SEQ_query_rendered_strips_to_tag(seqbasep, scene->r.cfra, 0);
+ }
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbasep) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (seq->flag & SELECT) {
- SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq);
+ SEQ_edit_flag_for_removal(scene, seqbasep, seq);
}
}
- SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep);
+ SEQ_edit_remove_flagged_sequences(scene, seqbasep);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
DEG_relations_tag_update(bmain);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index b4271ebd812..8a8a24f08ff 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -25,6 +25,8 @@
#include <stdlib.h>
#include <string.h>
+#include "MEM_guardedalloc.h"
+
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -414,9 +416,17 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
Editing *ed = SEQ_editing_get(scene);
Sequence *seq;
+ const bool is_preview = sequencer_view_preview_poll(C);
+ if (is_preview) {
+ SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0);
+ }
+
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (seq->flag & SEQ_ALLSEL) {
action = SEL_DESELECT;
break;
@@ -425,6 +435,9 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
}
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
switch (action) {
case SEL_SELECT:
seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
@@ -481,7 +494,15 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
Editing *ed = SEQ_editing_get(scene);
Sequence *seq;
+ const bool is_preview = sequencer_view_preview_poll(C);
+ if (is_preview) {
+ SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0);
+ }
+
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (seq->flag & SELECT) {
seq->flag &= ~SEQ_ALLSEL;
}
@@ -635,11 +656,51 @@ static void sequencer_select_linked_handle(const bContext *C,
}
}
-/* Check if click happened on image which belongs to strip. If multiple strips are found, loop
- * through them in order. */
-static Sequence *seq_select_seq_from_preview(const bContext *C,
- const int mval[2],
- const bool center)
+/** Collect sequencer that are candidates for being selected. */
+struct SeqSelect_Link {
+ struct SeqSelect_Link *next, *prev;
+ Sequence *seq;
+ /** Only use for center selection. */
+ float center_dist_sq;
+};
+
+static int seq_sort_for_depth_select(const void *a, const void *b)
+{
+ const struct SeqSelect_Link *slink_a = a;
+ const struct SeqSelect_Link *slink_b = b;
+
+ /* Exactly overlapping strips, sort by machine (so the top-most is first). */
+ if (slink_a->seq->machine < slink_b->seq->machine) {
+ return 1;
+ }
+ if (slink_a->seq->machine > slink_b->seq->machine) {
+ return -1;
+ }
+ return 0;
+}
+
+static int seq_sort_for_center_select(const void *a, const void *b)
+{
+ const struct SeqSelect_Link *slink_a = a;
+ const struct SeqSelect_Link *slink_b = b;
+ if (slink_a->center_dist_sq > slink_b->center_dist_sq) {
+ return 1;
+ }
+ if (slink_a->center_dist_sq < slink_b->center_dist_sq) {
+ return -1;
+ }
+
+ /* Exactly overlapping strips, use depth. */
+ return seq_sort_for_depth_select(a, b);
+}
+
+/**
+ * Check if click happened on image which belongs to strip.
+ * If multiple strips are found, loop through them in order
+ * (depth (top-most first) or closest to mouse when `center` is true).
+ */
+static Sequence *seq_select_seq_from_preview(
+ const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
@@ -650,70 +711,82 @@ static Sequence *seq_select_seq_from_preview(const bContext *C,
float mouseco_view[2];
UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]);
+ /* Always update the coordinates (check extended after). */
+ const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle);
+
SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown);
/* Allow strips this far from the closest center to be included.
* This allows cycling over center points which are near enough
* to overlapping from the users perspective. */
- const float center_threshold_cycle_px = 5.0f;
- const float center_dist_sq_eps = square_f(center_threshold_cycle_px * U.pixelsize);
+ const float center_dist_sq_max = square_f(75.0f * U.pixelsize);
const float center_scale_px[2] = {
UI_view2d_scale_get_x(v2d),
UI_view2d_scale_get_y(v2d),
};
- float center_co_best[2] = {0.0f};
-
- if (center) {
- Sequence *seq_best = NULL;
- float center_dist_sq_best = 0.0f;
-
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, strips) {
- float co[2];
- SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co);
- const float center_dist_sq_test = len_squared_v2v2(co, mouseco_view);
- if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) {
- seq_best = seq;
- center_dist_sq_best = center_dist_sq_test;
- copy_v2_v2(center_co_best, co);
- }
- }
- }
+ struct SeqSelect_Link *slink_active = NULL;
+ Sequence *seq_active = SEQ_select_active_get(scene);
ListBase strips_ordered = {NULL};
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
bool isect = false;
+ float center_dist_sq_test = 0.0f;
if (center) {
/* Detect overlapping center points (scaled by the zoom level). */
float co[2];
SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co);
- sub_v2_v2(co, center_co_best);
+ sub_v2_v2(co, mouseco_view);
mul_v2_v2(co, center_scale_px);
- isect = len_squared_v2(co) <= center_dist_sq_eps;
+ center_dist_sq_test = len_squared_v2(co);
+ isect = center_dist_sq_test <= center_dist_sq_max;
+ if (isect) {
+ /* Use an active strip penalty for "center" selection when cycle is enabled. */
+ if (use_cycle && (seq == seq_active) && (seq_active->flag & SELECT)) {
+ center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize));
+ }
+ }
}
else {
isect = seq_point_image_isect(scene, seq, mouseco_view);
}
if (isect) {
- BLI_remlink(seqbase, seq);
- BLI_addtail(&strips_ordered, seq);
+ struct SeqSelect_Link *slink = MEM_callocN(sizeof(*slink), __func__);
+ slink->seq = seq;
+ slink->center_dist_sq = center_dist_sq_test;
+ BLI_addtail(&strips_ordered, slink);
+
+ if (seq == seq_active) {
+ slink_active = slink;
+ }
}
}
SEQ_collection_free(strips);
- SEQ_sort(&strips_ordered);
- Sequence *seq_active = SEQ_select_active_get(scene);
- Sequence *seq_select = strips_ordered.first;
- LISTBASE_FOREACH (Sequence *, seq_iter, &strips_ordered) {
- if (seq_iter == seq_active && seq_iter->next != NULL) {
- seq_select = seq_iter->next;
- break;
+ BLI_listbase_sort(&strips_ordered,
+ center ? seq_sort_for_center_select : seq_sort_for_depth_select);
+
+ struct SeqSelect_Link *slink_select = strips_ordered.first;
+ Sequence *seq_select = NULL;
+ if (slink_select != NULL) {
+ /* Only use special behavior for the active strip when it's selected. */
+ if ((center == false) && slink_active && (seq_active->flag & SELECT)) {
+ if (use_cycle) {
+ if (slink_active->next) {
+ slink_select = slink_active->next;
+ }
+ }
+ else {
+ /* Match object selection behavior: keep the current active item unless cycle is enabled.
+ * Clicking again in the same location will cycle away from the active object. */
+ slink_select = slink_active;
+ }
}
+ seq_select = slink_select->seq;
}
- BLI_movelisttolist(seqbase, &strips_ordered);
+ BLI_freelistN(&strips_ordered);
return seq_select;
}
@@ -759,7 +832,7 @@ static void sequencer_select_strip_impl(const Editing *ed,
action = 0;
}
else {
- if ((seq->flag & SELECT) == 0 || is_active) {
+ if (!((seq->flag & SELECT) && is_active)) {
action = 1;
}
else if (toggle) {
@@ -812,7 +885,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
int handle_clicked = SEQ_SIDE_NONE;
Sequence *seq = NULL;
if (region->regiontype == RGN_TYPE_PREVIEW) {
- seq = seq_select_seq_from_preview(C, mval, center);
+ seq = seq_select_seq_from_preview(C, mval, toggle, extend, center);
}
else {
seq = find_nearest_seq(scene, v2d, &handle_clicked, mval);
@@ -821,7 +894,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
/* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap,
* therefore both properties can be true at the same time. */
if (seq && RNA_boolean_get(op->ptr, "linked_time")) {
- if (!extend) {
+ if (!extend && !toggle) {
ED_sequencer_deselect_all(scene);
}
sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle);
@@ -833,7 +906,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
/* Select left, right or overlapping the current frame. */
if (RNA_boolean_get(op->ptr, "side_of_frame")) {
- if (!extend) {
+ if (!extend && !toggle) {
ED_sequencer_deselect_all(scene);
}
sequencer_select_side_of_frame(C, v2d, mval, scene);
@@ -843,7 +916,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
/* On Alt selection, select the strip and bordering handles. */
if (seq && RNA_boolean_get(op->ptr, "linked_handle")) {
- if (!extend) {
+ if (!extend && !toggle) {
ED_sequencer_deselect_all(scene);
}
sequencer_select_linked_handle(C, seq, handle_clicked);
@@ -1694,11 +1767,17 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = {
#define SEQ_CHANNEL_CHECK(_seq, _chan) (ELEM((_chan), 0, (_seq)->machine))
-static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel)
+static bool select_grouped_type(ListBase *seqbasep,
+ const bool is_preview,
+ Sequence *actseq,
+ const int channel)
{
bool changed = false;
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbasep) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
seq->flag |= SELECT;
changed = true;
@@ -1708,12 +1787,18 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel
return changed;
}
-static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel)
+static bool select_grouped_type_basic(ListBase *seqbase,
+ const bool is_preview,
+ Sequence *actseq,
+ const int channel)
{
bool changed = false;
const bool is_sound = SEQ_IS_SOUND(actseq);
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
seq->flag |= SELECT;
changed = true;
@@ -1723,12 +1808,18 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c
return changed;
}
-static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel)
+static bool select_grouped_type_effect(ListBase *seqbase,
+ const bool is_preview,
+ Sequence *actseq,
+ const int channel)
{
bool changed = false;
const bool is_effect = SEQ_IS_EFFECT(actseq);
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) &&
(is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) {
seq->flag |= SELECT;
@@ -1739,7 +1830,10 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int
return changed;
}
-static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel)
+static bool select_grouped_data(ListBase *seqbase,
+ const bool is_preview,
+ Sequence *actseq,
+ const int channel)
{
bool changed = false;
const char *dir = actseq->strip ? actseq->strip->dir : NULL;
@@ -1749,7 +1843,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
if (SEQ_HAS_PATH(actseq) && dir) {
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
STREQ(seq->strip->dir, dir)) {
seq->flag |= SELECT;
@@ -1759,7 +1856,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
else if (actseq->type == SEQ_TYPE_SCENE) {
Scene *sce = actseq->scene;
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
seq->flag |= SELECT;
changed = true;
@@ -1768,7 +1865,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
MovieClip *clip = actseq->clip;
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP &&
seq->clip == clip) {
seq->flag |= SELECT;
@@ -1778,7 +1875,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
else if (actseq->type == SEQ_TYPE_MASK) {
struct Mask *mask = actseq->mask;
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
seq->flag |= SELECT;
changed = true;
@@ -1789,7 +1886,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
return changed;
}
-static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel)
+static bool select_grouped_effect(ListBase *seqbase,
+ const bool is_preview,
+ Sequence *actseq,
+ const int channel)
{
bool changed = false;
bool effects[SEQ_TYPE_MAX + 1];
@@ -1798,14 +1898,20 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
effects[i] = false;
}
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) {
effects[seq->type] = true;
}
}
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
if (seq->seq1) {
seq->seq1->flag |= SELECT;
@@ -1823,11 +1929,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
return changed;
}
-static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
+static bool select_grouped_time_overlap(ListBase *seqbase, const bool is_preview, Sequence *actseq)
{
bool changed = false;
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) {
seq->flag |= SELECT;
changed = true;
@@ -1856,12 +1965,11 @@ static void query_lower_channel_strips(Sequence *seq_reference,
/* Select all strips within time range and with lower channel of initial selection. Then select
* effect chains of these strips. */
-static bool select_grouped_effect_link(Editing *ed,
+static bool select_grouped_effect_link(ListBase *seqbase,
+ const bool is_preview,
Sequence *UNUSED(actseq),
const int UNUSED(channel))
{
- ListBase *seqbase = SEQ_active_seqbase_get(ed);
-
/* Get collection of strips. */
SeqCollection *collection = SEQ_query_selected_strips(seqbase);
const int selected_strip_count = BLI_gset_len(collection->set);
@@ -1874,6 +1982,9 @@ static bool select_grouped_effect_link(Editing *ed,
/* Actual logic. */
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (is_preview && (seq->tmp_tag == false)) {
+ continue;
+ }
seq->flag |= SELECT;
}
@@ -1889,9 +2000,17 @@ static bool select_grouped_effect_link(Editing *ed,
static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- Editing *ed = SEQ_editing_get(scene);
+ ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
Sequence *actseq = SEQ_select_active_get(scene);
+ const bool is_preview = sequencer_view_preview_poll(C);
+ if (is_preview) {
+ SEQ_query_rendered_strips_to_tag(seqbase, scene->r.cfra, 0);
+ if (actseq && actseq->tmp_tag == false) {
+ actseq = NULL;
+ }
+ }
+
if (actseq == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active sequence!");
return OPERATOR_CANCELLED;
@@ -1904,7 +2023,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
bool changed = false;
if (!extend) {
- LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
seq->flag &= ~SELECT;
changed = true;
}
@@ -1912,25 +2031,25 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
switch (type) {
case SEQ_SELECT_GROUP_TYPE:
- changed |= select_grouped_type(ed, actseq, channel);
+ changed |= select_grouped_type(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_TYPE_BASIC:
- changed |= select_grouped_type_basic(ed, actseq, channel);
+ changed |= select_grouped_type_basic(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_TYPE_EFFECT:
- changed |= select_grouped_type_effect(ed, actseq, channel);
+ changed |= select_grouped_type_effect(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_DATA:
- changed |= select_grouped_data(ed, actseq, channel);
+ changed |= select_grouped_data(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_EFFECT:
- changed |= select_grouped_effect(ed, actseq, channel);
+ changed |= select_grouped_effect(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_EFFECT_LINK:
- changed |= select_grouped_effect_link(ed, actseq, channel);
+ changed |= select_grouped_effect_link(seqbase, is_preview, actseq, channel);
break;
case SEQ_SELECT_GROUP_OVERLAP:
- changed |= select_grouped_time_overlap(ed, actseq);
+ changed |= select_grouped_time_overlap(seqbase, is_preview, actseq);
break;
default:
BLI_assert(0);
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 79593b0bbb0..2d2e7de7135 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -92,7 +92,14 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
- SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box);
+ SEQ_timeline_init_boundbox(scene, &box);
+ MetaStack *ms = SEQ_meta_stack_active_get(ed);
+ /* Use meta strip range instead of scene. */
+ if (ms != NULL) {
+ box.xmin = ms->disp_range[0] - 1;
+ box.xmax = ms->disp_range[1] + 1;
+ }
+ SEQ_timeline_expand_boundbox(SEQ_active_seqbase_get(ed), &box);
UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index e903feeec1b..91fe1bc01b7 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -35,6 +35,7 @@ set(INC
set(SRC
space_spreadsheet.cc
+ spreadsheet_cache.cc
spreadsheet_column.cc
spreadsheet_context.cc
spreadsheet_data_source.cc
@@ -47,6 +48,7 @@ set(SRC
spreadsheet_row_filter.cc
spreadsheet_row_filter_ui.cc
+ spreadsheet_cache.hh
spreadsheet_cell_value.hh
spreadsheet_column.hh
spreadsheet_column_values.hh
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index a82648aeee0..73e0be76466 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- MEM_SAFE_FREE(sspreadsheet->runtime);
+ delete sspreadsheet->runtime;
LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
spreadsheet_row_filter_free(row_filter);
@@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first;
if (sspreadsheet->runtime == nullptr) {
- sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN(
- sizeof(SpaceSpreadsheet_Runtime), __func__);
+ sspreadsheet->runtime = new SpaceSpreadsheet_Runtime();
}
}
@@ -138,7 +137,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
{
const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl;
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
- sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
+ sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime);
BLI_listbase_clear(&sspreadsheet_new->row_filters);
LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) {
@@ -294,16 +293,39 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C)
return {};
}
-static float get_column_width(const ColumnValues &values)
+static float get_default_column_width(const ColumnValues &values)
{
- if (values.default_width > 0) {
+ if (values.default_width > 0.0f) {
return values.default_width;
}
+ static const float float_width = 3;
+ switch (values.type()) {
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ return 2.0f;
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ return float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT:
+ return float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT2:
+ return 2.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT3:
+ return 3.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ return 4.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ return 8.0f;
+ }
+ return float_width;
+}
+
+static float get_column_width(const ColumnValues &values)
+{
+ float data_width = get_default_column_width(values);
const int fontid = UI_style_get()->widget.uifont_id;
BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
const StringRefNull name = values.name();
const float name_width = BLF_width(fontid, name.data(), name.size());
- return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
+ return std::max<float>(name_width / UI_UNIT_X + 1.0f, data_width);
}
static float get_column_width_in_pixels(const ColumnValues &values)
@@ -339,21 +361,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source)
}
}
- data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
- std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
- if (values) {
- if (used_ids.add(column_id)) {
- SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
- SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
- BLI_addtail(&columns, new_column);
- }
- }
- });
+ data_source.foreach_default_column_ids(
+ [&](const SpreadsheetColumnID &column_id, const bool is_extra) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
+ if (values) {
+ if (used_ids.add(column_id)) {
+ SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
+ if (is_extra) {
+ BLI_addhead(&columns, new_column);
+ }
+ else {
+ BLI_addtail(&columns, new_column);
+ }
+ }
+ }
+ });
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ sspreadsheet->runtime->cache.set_all_unused();
spreadsheet_update_context_path(C);
std::unique_ptr<DataSource> data_source = get_data_source(C);
@@ -394,6 +423,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
ED_region_tag_redraw(footer);
ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI);
ED_region_tag_redraw(sidebar);
+
+ /* Free all cache items that have not been used. */
+ sspreadsheet->runtime->cache.remove_all_unused();
}
static void spreadsheet_main_region_listener(const wmRegionListenerParams *params)
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
new file mode 100644
index 00000000000..2a399e018b6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "spreadsheet_cache.hh"
+
+namespace blender::ed::spreadsheet {
+
+void SpreadsheetCache::add(std::unique_ptr<Key> key, std::unique_ptr<Value> value)
+{
+ key->is_used = true;
+ cache_map_.add_overwrite(*key, std::move(value));
+ keys_.append(std::move(key));
+}
+
+SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key)
+{
+ std::unique_ptr<Value> *value = cache_map_.lookup_ptr(key);
+ if (value == nullptr) {
+ return nullptr;
+ }
+ const Key &stored_cache_key = cache_map_.lookup_key(key);
+ stored_cache_key.is_used = true;
+ return value->get();
+}
+
+SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add(
+ std::unique_ptr<Key> key, FunctionRef<std::unique_ptr<Value>()> create_value)
+{
+ Value *value = this->lookup(*key);
+ if (value != nullptr) {
+ return *value;
+ }
+ std::unique_ptr<Value> new_value = create_value();
+ value = new_value.get();
+ this->add(std::move(key), std::move(new_value));
+ return *value;
+}
+
+void SpreadsheetCache::set_all_unused()
+{
+ for (std::unique_ptr<Key> &key : keys_) {
+ key->is_used = false;
+ }
+}
+
+void SpreadsheetCache::remove_all_unused()
+{
+ /* First remove the keys from the map and free the values. */
+ for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) {
+ const Key &key = *it;
+ if (!key.is_used) {
+ cache_map_.remove(it);
+ }
+ }
+ /* Then free the keys. */
+ for (int i = 0; i < keys_.size();) {
+ if (keys_[i]->is_used) {
+ i++;
+ }
+ else {
+ keys_.remove_and_reorder(i);
+ }
+ }
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
new file mode 100644
index 00000000000..d370bdab5c1
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include "BLI_function_ref.hh"
+#include "BLI_map.hh"
+#include "BLI_vector.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * A generic cache for the spreadsheet. Different data sources can cache custom data using custom
+ * keys.
+ *
+ * Elements are removed from the cache when they are not used during a redraw.
+ */
+class SpreadsheetCache {
+ public:
+ class Key {
+ public:
+ virtual ~Key() = default;
+
+ mutable bool is_used = false;
+
+ virtual uint64_t hash() const = 0;
+
+ friend bool operator==(const Key &a, const Key &b)
+ {
+ return a.is_equal_to(b);
+ }
+
+ private:
+ virtual bool is_equal_to(const Key &other) const = 0;
+ };
+
+ class Value {
+ public:
+ virtual ~Value() = default;
+ };
+
+ private:
+ Vector<std::unique_ptr<Key>> keys_;
+ Map<std::reference_wrapper<const Key>, std::unique_ptr<Value>> cache_map_;
+
+ public:
+ /* Adding or looking up a key tags it as being used, so that it won't be removed. */
+ void add(std::unique_ptr<Key> key, std::unique_ptr<Value> value);
+ Value *lookup(const Key &key);
+ Value &lookup_or_add(std::unique_ptr<Key> key,
+ FunctionRef<std::unique_ptr<Value>()> create_value);
+
+ void set_all_unused();
+ void remove_all_unused();
+
+ template<typename T> T &lookup_or_add(std::unique_ptr<Key> key)
+ {
+ return dynamic_cast<T &>(
+ this->lookup_or_add(std::move(key), []() { return std::make_unique<T>(); }));
+ }
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
index 68370cf6a44..877651d6530 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -97,9 +97,4 @@ std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColu
return column_values;
}
-static constexpr float default_float_column_width = 3;
-static constexpr float default_float2_column_width = 2 * default_float_column_width;
-static constexpr float default_float3_column_width = 3 * default_float_column_width;
-static constexpr float default_color_column_width = 4 * default_float_column_width;
-
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
index c38e765caee..e55a7cae6a6 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -373,6 +373,21 @@ void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreads
BLI_addtail(&sspreadsheet->context_path, context);
}
+static bScreen *find_screen_to_search_for_context(wmWindow *window,
+ SpaceSpreadsheet *current_space)
+{
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) {
+ /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */
+ ScrArea *main_area = (ScrArea *)screen->areabase.first;
+ SpaceLink *sl = (SpaceLink *)main_area->spacedata.first;
+ if (sl == (SpaceLink *)current_space) {
+ return main_area->full;
+ }
+ }
+ return screen;
+}
+
void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet)
{
ED_spreadsheet_context_path_clear(sspreadsheet);
@@ -385,9 +400,12 @@ void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspr
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
- bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl == nullptr) {
+ continue;
+ }
if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
if (snode->edittree != nullptr) {
@@ -466,9 +484,12 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet *
}
LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
- bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl == nullptr) {
+ continue;
+ }
if (sl->spacetype != SPACE_NODE) {
continue;
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index 2ea7fb5809f..873735c81e5 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -36,8 +36,12 @@ class DataSource {
* Calls the callback with all the column ids that should be displayed as long as the user does
* not manually add or remove columns. The column id can be stack allocated. Therefore, the
* callback should not keep a reference to it (and copy it instead).
+ *
+ * The `is_extra` argument indicates that this column is special and should be drawn as the first
+ * column. (This can be made a bit more generic in the future when necessary.)
*/
- virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ virtual void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
UNUSED_VARS(fn);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 78d9f61d8d5..c1d345d1861 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -33,18 +33,99 @@
#include "NOD_geometry_nodes_eval_log.hh"
+#include "FN_field_cpp_type.hh"
+
#include "bmesh.h"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
+using blender::fn::GField;
namespace blender::ed::spreadsheet {
+static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type(
+ const fn::CPPType &type)
+{
+ if (type.is<bool>()) {
+ return SPREADSHEET_VALUE_TYPE_BOOL;
+ }
+ if (type.is<int>()) {
+ return SPREADSHEET_VALUE_TYPE_INT32;
+ }
+ if (type.is<float>()) {
+ return SPREADSHEET_VALUE_TYPE_FLOAT;
+ }
+ if (type.is<float2>()) {
+ return SPREADSHEET_VALUE_TYPE_FLOAT2;
+ }
+ if (type.is<float3>()) {
+ return SPREADSHEET_VALUE_TYPE_FLOAT3;
+ }
+ if (type.is<ColorGeometry4f>()) {
+ return SPREADSHEET_VALUE_TYPE_COLOR;
+ }
+ return std::nullopt;
+}
+
+void ExtraColumns::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
+{
+ for (const auto &item : columns_.items()) {
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)item.key.c_str();
+ fn(column_id, true);
+ }
+}
+
+std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ const fn::GSpan *values = columns_.lookup_ptr(column_id.name);
+ if (values == nullptr) {
+ return {};
+ }
+ eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type());
+ return column_values_from_function(column_type,
+ column_id.name,
+ values->size(),
+ [column_type, values](int index, CellValue &r_cell_value) {
+ const void *value = (*values)[index];
+ switch (column_type) {
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ r_cell_value.value_bool = *(const bool *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ r_cell_value.value_int = *(const int *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT:
+ r_cell_value.value_float = *(const float *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT2:
+ r_cell_value.value_float2 = *(const float2 *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT3:
+ r_cell_value.value_float3 = *(const float3 *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ r_cell_value.value_color = *(
+ const ColorGeometry4f *)value;
+ break;
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ break;
+ }
+ });
+}
+
void GeometryDataSource::foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
+ if (component_->attribute_domain_size(domain_) == 0) {
+ return;
+ }
+
+ extra_columns_.foreach_default_column_ids(fn);
component_->attribute_foreach(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
@@ -55,7 +136,7 @@ void GeometryDataSource::foreach_default_column_ids(
}
SpreadsheetColumnID column_id;
column_id.name = (char *)attribute_id.name().data();
- fn(column_id);
+ fn(column_id, false);
return true;
});
}
@@ -63,8 +144,17 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
+ if (component_->attribute_domain_size(domain_) == 0) {
+ return {};
+ }
+
std::lock_guard lock{mutex_};
+ std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id);
+ if (extra_column_values) {
+ return extra_column_values;
+ }
+
bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
if (!attribute) {
return {};
@@ -86,14 +176,16 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
r_cell_value.value_float = value;
});
case CD_PROP_INT32:
- return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- int value;
- varray->get(index, &value);
- r_cell_value.value_int = value;
- });
+ return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_INT32,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ int value;
+ varray->get(index, &value);
+ r_cell_value.value_int = value;
+ },
+ STREQ(column_id.name, "id") ? 5.5f : 0.0f);
case CD_PROP_BOOL:
return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL,
column_id.name,
@@ -104,40 +196,34 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
r_cell_value.value_bool = value;
});
case CD_PROP_FLOAT2: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT2,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- float2 value;
- varray->get(index, &value);
- r_cell_value.value_float2 = value;
- },
- default_float2_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float2 value;
+ varray->get(index, &value);
+ r_cell_value.value_float2 = value;
+ });
}
case CD_PROP_FLOAT3: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- float3 value;
- varray->get(index, &value);
- r_cell_value.value_float3 = value;
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float3 value;
+ varray->get(index, &value);
+ r_cell_value.value_float3 = value;
+ });
}
case CD_PROP_COLOR: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_COLOR,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- ColorGeometry4f value;
- varray->get(index, &value);
- r_cell_value.value_color = value;
- },
- default_color_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ ColorGeometry4f value;
+ varray->get(index, &value);
+ r_cell_value.value_color = value;
+ });
}
default:
break;
@@ -293,18 +379,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included)
}
void InstancesDataSource::foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
if (component_->instances_amount() == 0) {
return;
}
+ extra_columns_.foreach_default_column_ids(fn);
+
SpreadsheetColumnID column_id;
column_id.name = (char *)"Name";
- fn(column_id);
- for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
+ fn(column_id, false);
+ for (const char *name : {"Position", "Rotation", "Scale", "id"}) {
column_id.name = (char *)name;
- fn(column_id);
+ fn(column_id, false);
}
}
@@ -315,6 +403,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
return {};
}
+ std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id);
+ if (extra_column_values) {
+ return extra_column_values;
+ }
+
const int size = this->tot_rows();
if (STREQ(column_id.name, "Name")) {
Span<int> reference_handles = component_->instance_reference_handles();
@@ -346,7 +439,6 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
}
}
});
- values->default_width = 8.0f;
return values;
}
Span<float4x4> transforms = component_->instance_transforms();
@@ -357,38 +449,35 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].translation();
- },
- default_float3_column_width);
+ });
}
if (STREQ(column_id.name, "Rotation")) {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- size,
- [transforms](int index, CellValue &r_cell_value) {
- r_cell_value.value_float3 = transforms[index].to_euler();
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].to_euler();
+ });
}
if (STREQ(column_id.name, "Scale")) {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- size,
- [transforms](int index, CellValue &r_cell_value) {
- r_cell_value.value_float3 = transforms[index].scale();
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].scale();
+ });
}
Span<int> ids = component_->instance_ids();
- if (STREQ(column_id.name, "ID")) {
- /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_INT32,
- column_id.name,
- size,
- [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
- 5.5f);
+ if (!ids.is_empty()) {
+ if (STREQ(column_id.name, "id")) {
+ /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
+ return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_INT32,
+ column_id.name,
+ size,
+ [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
+ 5.5f);
+ }
}
return {};
}
@@ -469,6 +558,36 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
return geometry_set;
}
+static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
+ Map<std::string, GField> &r_fields)
+{
+ if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ return;
+ }
+ if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) {
+ /* No viewer is currently referenced by the context path. */
+ return;
+ }
+ const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(
+ *sspreadsheet);
+ if (node_log == nullptr) {
+ return;
+ }
+ for (const geo_log::SocketLog &socket_log : node_log->input_logs()) {
+ const geo_log::ValueLog *value_log = socket_log.value();
+ if (value_log == nullptr) {
+ continue;
+ }
+ if (const geo_log::GFieldValueLog *field_value_log =
+ dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) {
+ const GField &field = field_value_log->field();
+ if (field) {
+ r_fields.add("Viewer", std::move(field));
+ }
+ }
+ }
+}
+
static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@@ -481,6 +600,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec
return GEO_COMPONENT_TYPE_MESH;
}
+class GeometryComponentCacheKey : public SpreadsheetCache::Key {
+ public:
+ /* Use the pointer to the geometry component as a key to detect when the geometry changed. */
+ const GeometryComponent *component;
+
+ GeometryComponentCacheKey(const GeometryComponent &component) : component(&component)
+ {
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash(this->component);
+ }
+
+ bool is_equal_to(const Key &other) const override
+ {
+ if (const GeometryComponentCacheKey *other_geo =
+ dynamic_cast<const GeometryComponentCacheKey *>(&other)) {
+ return this->component == other_geo->component;
+ }
+ return false;
+ }
+};
+
+class GeometryComponentCacheValue : public SpreadsheetCache::Value {
+ public:
+ /* Stores the result of fields evaluated on a geometry component. Without this, fields would have
+ * to be reevaluated on every redraw. */
+ Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays;
+};
+
+static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
+ const GeometryComponent &component,
+ ExtraColumns &r_extra_columns)
+{
+ Map<std::string, GField> fields_to_show;
+ find_fields_to_evaluate(sspreadsheet, fields_to_show);
+
+ GeometryComponentCacheValue &cache =
+ sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>(
+ std::make_unique<GeometryComponentCacheKey>(component));
+
+ const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const int domain_size = component.attribute_domain_size(domain);
+ for (const auto &item : fields_to_show.items()) {
+ StringRef name = item.key;
+ const GField &field = item.value;
+
+ /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
+ fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
+ fn::GArray<> evaluated_array(field.cpp_type(), domain_size);
+
+ bke::GeometryComponentFieldContext field_context{component, domain};
+ fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ field_evaluator.add_with_destination(field, evaluated_array);
+ field_evaluator.evaluate();
+ return evaluated_array;
+ });
+
+ r_extra_columns.add(std::move(name), evaluated_array.as_span());
+ }
+}
+
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@@ -493,10 +675,15 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
return {};
}
+ const GeometryComponent &component = *geometry_set.get_component_for_read(component_type);
+ ExtraColumns extra_columns;
+ add_fields_as_extra_columns(sspreadsheet, component, extra_columns);
+
if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
- return std::make_unique<InstancesDataSource>(geometry_set);
+ return std::make_unique<InstancesDataSource>(geometry_set, std::move(extra_columns));
}
- return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain);
+ return std::make_unique<GeometryDataSource>(
+ object_eval, geometry_set, component_type, domain, std::move(extra_columns));
}
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index d1b5dc6845e..6c88a94f585 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -28,12 +28,34 @@ struct bContext;
namespace blender::ed::spreadsheet {
+/**
+ * Contains additional named columns that should be displayed that are not stored on the geometry
+ * directly. This is used for displaying the evaluated fields connected to a viewer node.
+ */
+class ExtraColumns {
+ private:
+ /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */
+ Map<std::string, fn::GSpan> columns_;
+
+ public:
+ void add(std::string name, fn::GSpan data)
+ {
+ columns_.add(std::move(name), data);
+ }
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const;
+
+ std::unique_ptr<ColumnValues> get_column_values(const SpreadsheetColumnID &column_id) const;
+};
+
class GeometryDataSource : public DataSource {
private:
Object *object_eval_;
const GeometrySet geometry_set_;
const GeometryComponent *component_;
AttributeDomain domain_;
+ ExtraColumns extra_columns_;
/* Some data is computed on the fly only when it is requested. Computing it does not change the
* logical state of this data source. Therefore, the corresponding methods are const and need to
@@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource {
GeometryDataSource(Object *object_eval,
GeometrySet geometry_set,
const GeometryComponentType component_type,
- const AttributeDomain domain)
+ const AttributeDomain domain,
+ ExtraColumns extra_columns)
: object_eval_(object_eval),
geometry_set_(std::move(geometry_set)),
component_(geometry_set_.get_component_for_read(component_type)),
- domain_(domain)
+ domain_(domain),
+ extra_columns_(std::move(extra_columns))
{
}
@@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource {
void apply_selection_filter(MutableSpan<bool> rows_included) const;
void foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
@@ -73,16 +97,18 @@ class GeometryDataSource : public DataSource {
class InstancesDataSource : public DataSource {
const GeometrySet geometry_set_;
const InstancesComponent *component_;
+ ExtraColumns extra_columns_;
public:
- InstancesDataSource(GeometrySet geometry_set)
+ InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns)
: geometry_set_(std::move(geometry_set)),
- component_(geometry_set_.get_component_for_read<InstancesComponent>())
+ component_(geometry_set_.get_component_for_read<InstancesComponent>()),
+ extra_columns_(std::move(extra_columns))
{
}
void foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
index 8be5283fd63..8b050c2e69b 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
@@ -17,12 +17,24 @@
#pragma once
#include "BKE_geometry_set.hh"
+#include "spreadsheet_cache.hh"
-typedef struct SpaceSpreadsheet_Runtime {
- int visible_rows;
- int tot_rows;
- int tot_columns;
-} SpaceSpreadsheet_Runtime;
+struct SpaceSpreadsheet_Runtime {
+ public:
+ int visible_rows = 0;
+ int tot_rows = 0;
+ int tot_columns = 0;
+
+ blender::ed::spreadsheet::SpreadsheetCache cache;
+
+ SpaceSpreadsheet_Runtime() = default;
+
+ /* The cache is not copied currently. */
+ SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other)
+ : visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns)
+ {
+ }
+};
struct bContext;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index 1a5eac53306..355899be279 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
const int real_index = spreadsheet_layout_.row_indices[row_index];
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
CellValue cell_value;
- column.get_value(real_index, cell_value);
+ if (real_index < column.size()) {
+ column.get_value(real_index, cell_value);
+ }
if (cell_value.value_int.has_value()) {
const int value = *cell_value.value_int;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 7999018a6b6..6acf51aec6e 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -40,12 +40,14 @@
#include "BLT_translation.h"
+#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_mesh.h"
@@ -55,6 +57,7 @@
#include "BKE_workspace.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -82,6 +85,7 @@
#endif
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "view3d_intern.h" /* own include */
@@ -515,10 +519,74 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C,
return WM_drag_is_ID_type(drag, id_type);
}
+static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag)
+{
+ V3DSnapCursorState *state = drop->draw_data;
+ if (state) {
+ return;
+ }
+
+ /* Don't use the snap cursor when linking the object. Object transform isn't editable then and
+ * would be reset on reload. */
+ if (WM_drag_asset_will_import_linked(drag)) {
+ return;
+ }
+
+ state = drop->draw_data = ED_view3d_cursor_snap_active();
+ state->draw_plane = true;
+
+ float dimensions[3] = {0.0f};
+ if (drag->type == WM_DRAG_ID) {
+ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
+ BKE_object_dimensions_get(ob, dimensions);
+ }
+ else {
+ struct AssetMetaData *meta_data = WM_drag_get_asset_meta_data(drag, ID_OB);
+ IDProperty *dimensions_prop = BKE_asset_metadata_idprop_find(meta_data, "dimensions");
+ if (dimensions_prop) {
+ copy_v3_v3(dimensions, IDP_Array(dimensions_prop));
+ }
+ }
+
+ if (!is_zero_v3(dimensions)) {
+ mul_v3_v3fl(state->box_dimensions, dimensions, 0.5f);
+ UI_GetThemeColor4ubv(TH_GIZMO_PRIMARY, state->color_box);
+ state->draw_box = true;
+ }
+}
+
+static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag))
+{
+ V3DSnapCursorState *state = drop->draw_data;
+ if (state) {
+ ED_view3d_cursor_snap_deactive(state);
+ drop->draw_data = NULL;
+ }
+}
+
static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB);
}
+static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event)
+{
+ if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * \note the term local here refers to not being an external asset,
+ * poll will succeed for linked library objects.
+ */
+static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event)
+{
+ if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) {
+ return false;
+ }
+ return true;
+}
static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
@@ -532,12 +600,17 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *view3d_mat_drop_tooltip(bContext *C,
wmDrag *drag,
- const wmEvent *event,
+ const int xy[2],
struct wmDropBox *drop)
{
const char *name = WM_drag_get_item_name(drag);
+ ARegion *region = CTX_wm_region(C);
RNA_string_set(drop->ptr, "name", name);
- return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event);
+ int mval[2] = {
+ xy[0] - region->winrct.xmin,
+ xy[1] - region->winrct.ymin,
+ };
+ return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval);
}
static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
@@ -556,7 +629,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven
static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C),
wmDrag *UNUSED(drag),
- const wmEvent *UNUSED(event),
+ const int UNUSED(xy[2]),
wmDropBox *UNUSED(drop))
{
return BLI_strdup(TIP_("Create object instance from object-data"));
@@ -626,14 +699,85 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C),
return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME);
}
-static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
+static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state,
+ Object *ob,
+ float obmat_final[4][4])
{
- ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB);
+ V3DSnapCursorData *snap_data;
+ snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0);
+ BLI_assert(snap_state->draw_box || snap_state->draw_plane);
+ copy_m4_m3(obmat_final, snap_data->plane_omat);
+ copy_v3_v3(obmat_final[3], snap_data->loc);
+
+ float scale[3];
+ mat4_to_size(scale, ob->obmat);
+ rescale_m4(obmat_final, scale);
+
+ BoundBox *bb = BKE_object_boundbox_get(ob);
+ if (bb) {
+ float offset[3];
+ BKE_boundbox_calc_center_aabb(bb, offset);
+ offset[2] = bb->vec[0][2];
+ mul_mat3_m4_v3(obmat_final, offset);
+ sub_v3_v3(obmat_final[3], offset);
+ }
+}
+
+static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop)
+{
+ ID *id = WM_drag_get_local_ID(drag, ID_OB);
RNA_string_set(drop->ptr, "name", id->name + 2);
/* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */
- const bool is_imported_id = drag->type == WM_DRAG_ASSET;
- RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id);
+ BLI_assert(drag->type != WM_DRAG_ASSET);
+
+ V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get();
+ float obmat_final[4][4];
+
+ view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final);
+
+ RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]);
+}
+
+static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop)
+{
+ /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append,
+ * using auto-select to ensure the new objects are selected.
+ * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler)
+ * can use the context setup here to place the objects. */
+ BLI_assert(drag->type == WM_DRAG_ASSET);
+
+ wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0);
+ bContext *C = asset_drag->evil_C;
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ BKE_view_layer_base_deselect_all(view_layer);
+
+ ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT);
+
+ /* TODO(sergey): Only update relations for the current scene. */
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
+
+ RNA_string_set(drop->ptr, "name", id->name + 2);
+
+ Base *base = BKE_view_layer_base_find(view_layer, (Object *)id);
+ if (base != NULL) {
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+ WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene);
+ }
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ ED_outliner_select_sync_from_object_tag(C);
+
+ V3DSnapCursorState *snap_state = drop->draw_data;
+ if (snap_state) {
+ float obmat_final[4][4];
+
+ view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final);
+
+ RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]);
+ }
}
static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
@@ -698,12 +842,31 @@ static void view3d_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb,
- "OBJECT_OT_add_named",
- view3d_ob_drop_poll,
- view3d_ob_drop_copy,
- WM_drag_free_imported_drag_ID,
- NULL);
+ struct wmDropBox *drop;
+ drop = WM_dropbox_add(lb,
+ "OBJECT_OT_add_named",
+ view3d_ob_drop_poll_local_id,
+ view3d_ob_drop_copy_local_id,
+ WM_drag_free_imported_drag_ID,
+ NULL);
+
+ drop->draw = WM_drag_draw_item_name_fn;
+ drop->draw_activate = view3d_ob_drop_draw_activate;
+ drop->draw_deactivate = view3d_ob_drop_draw_deactivate;
+ drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */
+
+ drop = WM_dropbox_add(lb,
+ "OBJECT_OT_transform_to_mouse",
+ view3d_ob_drop_poll_external_asset,
+ view3d_ob_drop_copy_external_asset,
+ WM_drag_free_imported_drag_ID,
+ NULL);
+
+ drop->draw = WM_drag_draw_item_name_fn;
+ drop->draw_activate = view3d_ob_drop_draw_activate;
+ drop->draw_deactivate = view3d_ob_drop_draw_deactivate;
+ drop->opcontext = WM_OP_INVOKE_DEFAULT;
+
WM_dropbox_add(lb,
"OBJECT_OT_drop_named_material",
view3d_mat_drop_poll,
@@ -1616,6 +1779,7 @@ static void space_view3d_refresh(const bContext *C, ScrArea *area)
const char *view3d_context_dir[] = {
"active_object",
+ "selected_ids",
NULL,
};
@@ -1626,8 +1790,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, view3d_context_dir);
+ return CTX_RESULT_OK;
}
- else if (CTX_data_equals(member, "active_object")) {
+ if (CTX_data_equals(member, "active_object")) {
/* In most cases the active object is the `view_layer->basact->object`.
* For the 3D view however it can be NULL when hidden.
*
@@ -1651,13 +1816,21 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
}
}
- return 1;
+ return CTX_RESULT_OK;
}
- else {
- return 0; /* not found */
+ if (CTX_data_equals(member, "selected_ids")) {
+ ListBase selected_objects;
+ CTX_data_selected_objects(C, &selected_objects);
+ LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected_objects) {
+ ID *selected_id = object_ptr_link->ptr.owner_id;
+ CTX_data_id_list_add(result, selected_id);
+ }
+ BLI_freelistN(&selected_objects);
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ return CTX_RESULT_OK;
}
- return -1; /* found but not available */
+ return CTX_RESULT_MEMBER_NOT_FOUND;
}
static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index 1cb650910ce..baf61befcba 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -37,6 +37,7 @@
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
@@ -54,27 +55,25 @@
#include "WM_api.h"
-#define STATE_LEN 3
+#define STATE_INTERN_GET(state) \
+ (SnapStateIntern *)((char *)state - offsetof(SnapStateIntern, snap_state))
typedef struct SnapStateIntern {
+ struct SnapStateIntern *next, *prev;
V3DSnapCursorState snap_state;
- float prevpoint_stack[3];
- int state_active_prev;
- bool is_active;
} SnapStateIntern;
typedef struct SnapCursorDataIntern {
V3DSnapCursorState state_default;
- SnapStateIntern state_intern[STATE_LEN];
+ ListBase state_intern;
V3DSnapCursorData snap_data;
- int state_active_len;
- int state_active;
-
struct SnapObjectContext *snap_context_v3d;
const Scene *scene;
short snap_elem_hidden;
+ float prevpoint_stack[3];
+
/* Copy of the parameters of the last event state in order to detect updates. */
struct {
int x;
@@ -94,17 +93,6 @@ typedef struct SnapCursorDataIntern {
bool is_initiated;
} SnapCursorDataIntern;
-static void UNUSED_FUNCTION(v3d_cursor_snap_state_init)(V3DSnapCursorState *state)
-{
- state->prevpoint = NULL;
- state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT);
- state->plane_axis = 2;
- rgba_uchar_args_set(state->color_point, 255, 255, 255, 255);
- rgba_uchar_args_set(state->color_line, 255, 255, 255, 128);
- state->draw_point = true;
- state->draw_plane = false;
-}
static SnapCursorDataIntern g_data_intern = {
.state_default = {.prevpoint = NULL,
.snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE |
@@ -113,8 +101,9 @@ static SnapCursorDataIntern g_data_intern = {
.plane_axis = 2,
.color_point = {255, 255, 255, 255},
.color_line = {255, 255, 255, 128},
- .draw_point = true,
- .draw_plane = false}};
+ .color_box = {255, 255, 255, 128},
+ .box_dimensions = {1.0f, 1.0f, 1.0f},
+ .draw_point = true}};
/**
* Calculate a 3x3 orientation matrix from the surface under the cursor.
@@ -373,6 +362,24 @@ static void v3d_cursor_plane_draw(const RegionView3D *rv3d,
}
}
+static void cursor_box_draw(const float dimensions[3], uchar color[4])
+{
+ GPUVertFormat *format = immVertexFormat();
+ const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_line_smooth(true);
+ GPU_line_width(1.0f);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformColor4ubv(color);
+ imm_draw_cube_corners_3d(pos_id, (const float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f);
+ immUnbindProgram();
+
+ GPU_line_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+}
+
void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d,
const float loc_prev[3],
const float loc_curr[3],
@@ -601,7 +608,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
ushort snap_elements = v3d_cursor_snap_elements(state, scene);
data_intern->snap_elem_hidden = 0;
- const bool draw_plane = state->draw_plane;
+ const bool draw_plane = state->draw_plane || state->draw_box;
if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) {
data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
snap_elements |= SCE_SNAP_MODE_FACE;
@@ -674,6 +681,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
}
if (draw_plane) {
+ RegionView3D *rv3d = region->regiondata;
bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE);
if (orient_surface) {
copy_m3_m4(omat, obmat);
@@ -686,7 +694,6 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
ED_transform_calc_orientation_from_type_ex(
scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat);
- RegionView3D *rv3d = region->regiondata;
if (state->use_plane_axis_auto) {
mat3_align_axis_to_v3(omat, state->plane_axis, rv3d->viewinv[2]);
}
@@ -699,6 +706,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
orthogonalize_m3(omat, state->plane_axis);
if (orient_surface) {
+ if (dot_v3v3(rv3d->viewinv[2], face_nor) < 0.0f) {
+ negate_v3(face_nor);
+ }
v3d_cursor_poject_surface_normal(face_nor, obmat, omat);
}
}
@@ -755,16 +765,12 @@ static bool v3d_cursor_snap_pool_fn(bContext *C)
return false;
}
- ARegion *region = CTX_wm_region(C);
- if (region->regiontype != RGN_TYPE_WINDOW) {
- return false;
- }
-
ScrArea *area = CTX_wm_area(C);
if (area->spacetype != SPACE_VIEW3D) {
return false;
}
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
RegionView3D *rv3d = region->regiondata;
if (rv3d->rflag & RV3D_NAVIGATING) {
/* Don't draw the cursor while navigating. It can be distracting. */
@@ -781,7 +787,8 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
V3DSnapCursorData *snap_data = &data_intern->snap_data;
wmWindowManager *wm = CTX_wm_manager(C);
- ARegion *region = CTX_wm_region(C);
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
x -= region->winrct.xmin;
y -= region->winrct.ymin;
if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) {
@@ -791,7 +798,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y);
}
- const bool draw_plane = state->draw_plane;
+ const bool draw_plane = state->draw_plane || state->draw_box;
if (!snap_data->snap_elem && !draw_plane) {
return;
}
@@ -802,8 +809,6 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
- GPU_blend(GPU_BLEND_ALPHA);
-
float matrix[4][4];
if (draw_plane) {
copy_m4_m3(matrix, snap_data->plane_omat);
@@ -812,7 +817,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix);
}
- if (snap_data->snap_elem && state->draw_point) {
+ if (snap_data->snap_elem && (state->draw_point || state->draw_box)) {
const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ?
state->prevpoint :
NULL;
@@ -829,7 +834,10 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
snap_data->snap_elem);
}
- GPU_blend(GPU_BLEND_NONE);
+ if (state->draw_box) {
+ GPU_matrix_mul(matrix);
+ cursor_box_draw(state->box_dimensions, state->color_box);
+ }
/* Restore matrix. */
wmWindowViewport(CTX_wm_window(C));
@@ -839,10 +847,11 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void)
{
- if (!g_data_intern.state_active_len) {
+ SnapCursorDataIntern *data_intern = &g_data_intern;
+ if (BLI_listbase_is_empty(&data_intern->state_intern)) {
return &g_data_intern.state_default;
}
- return (V3DSnapCursorState *)&g_data_intern.state_intern[g_data_intern.state_active];
+ return &((SnapStateIntern *)data_intern->state_intern.last)->snap_state;
}
static void v3d_cursor_snap_activate(void)
@@ -872,20 +881,16 @@ static void v3d_cursor_snap_activate(void)
static void v3d_cursor_snap_free(void)
{
SnapCursorDataIntern *data_intern = &g_data_intern;
- if (data_intern->handle && G_MAIN->wm.first) {
- WM_paint_cursor_end(data_intern->handle);
+ if (data_intern->handle) {
+ if (G_MAIN->wm.first) {
+ WM_paint_cursor_end(data_intern->handle);
+ }
data_intern->handle = NULL;
}
if (data_intern->snap_context_v3d) {
ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d);
data_intern->snap_context_v3d = NULL;
}
-
- for (SnapStateIntern *state_intern = data_intern->state_intern;
- state_intern < &data_intern->state_intern[STATE_LEN];
- state_intern++) {
- state_intern->is_active = false;
- }
}
void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state)
@@ -896,56 +901,41 @@ void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state)
V3DSnapCursorState *ED_view3d_cursor_snap_active(void)
{
SnapCursorDataIntern *data_intern = &g_data_intern;
- if (!data_intern->state_active_len) {
+ if (!data_intern->handle) {
v3d_cursor_snap_activate();
}
- data_intern->state_active_len++;
- for (int i = 0; i < STATE_LEN; i++) {
- SnapStateIntern *state_intern = &g_data_intern.state_intern[i];
- if (!state_intern->is_active) {
- state_intern->snap_state = g_data_intern.state_default;
- state_intern->is_active = true;
- state_intern->state_active_prev = data_intern->state_active;
- data_intern->state_active = i;
- return (V3DSnapCursorState *)state_intern;
- }
- }
+ SnapStateIntern *state_intern = MEM_mallocN(sizeof(*state_intern), __func__);
+ state_intern->snap_state = g_data_intern.state_default;
+ BLI_addtail(&g_data_intern.state_intern, state_intern);
- BLI_assert(false);
- data_intern->state_active_len--;
- return NULL;
+ return (V3DSnapCursorState *)&state_intern->snap_state;
}
void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state)
{
SnapCursorDataIntern *data_intern = &g_data_intern;
- if (!data_intern->state_active_len) {
- BLI_assert(false);
- return;
- }
-
- SnapStateIntern *state_intern = (SnapStateIntern *)state;
- if (!state_intern->is_active) {
+ if (BLI_listbase_is_empty(&data_intern->state_intern)) {
return;
}
- state_intern->is_active = false;
- data_intern->state_active_len--;
- if (!data_intern->state_active_len) {
+ SnapStateIntern *state_intern = STATE_INTERN_GET(state);
+ BLI_remlink(&data_intern->state_intern, state_intern);
+ MEM_freeN(state_intern);
+ if (BLI_listbase_is_empty(&data_intern->state_intern)) {
v3d_cursor_snap_free();
}
- else {
- data_intern->state_active = state_intern->state_active_prev;
- }
}
void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3])
{
- SnapStateIntern *state_intern = (SnapStateIntern *)state;
+ SnapCursorDataIntern *data_intern = &g_data_intern;
+ if (!state) {
+ state = ED_view3d_cursor_snap_state_get();
+ }
if (prev_point) {
- copy_v3_v3(state_intern->prevpoint_stack, prev_point);
- state->prevpoint = state_intern->prevpoint_stack;
+ copy_v3_v3(data_intern->prevpoint_stack, prev_point);
+ state->prevpoint = data_intern->prevpoint_stack;
}
else {
state->prevpoint = NULL;
@@ -958,12 +948,13 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state,
const int y)
{
SnapCursorDataIntern *data_intern = &g_data_intern;
- if (C && data_intern->state_active_len) {
+ if (C) {
wmWindowManager *wm = CTX_wm_manager(C);
if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = DEG_get_input_scene(depsgraph);
- ARegion *region = CTX_wm_region(C);
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
View3D *v3d = CTX_wm_view3d(C);
if (!state) {
@@ -982,8 +973,3 @@ struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(Scene *scene)
v3d_cursor_snap_context_ensure(scene);
return data_intern->snap_context_v3d;
}
-
-void ED_view3d_cursor_snap_exit(void)
-{
- v3d_cursor_snap_free();
-}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index fe347e89600..fceb6553cab 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -347,6 +347,8 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm,
(wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) !=
0,
V3D_XR_SHOW_CUSTOM_OVERLAYS);
+ /* Hide navigation gizmo since it gets distorted if the view matrix has a scale factor. */
+ v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE;
/* Reset overridden View3D data. */
v3d->lens = lens_old;
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 7fe97705765..572fc8e3156 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -742,16 +742,19 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
- ipd->snap_state = ED_view3d_cursor_snap_active();
- ipd->snap_state->draw_point = true;
- ipd->snap_state->draw_plane = true;
+ V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active();
+ if (snap_state_new) {
+ ipd->snap_state = snap_state = snap_state_new;
+ }
+ snap_state->draw_point = true;
+ snap_state->draw_plane = true;
ipd->is_snap_found =
view3d_interactive_add_calc_snap(
C, event, ipd->co_src, ipd->matrix_orient, &ipd->use_snap, &ipd->is_snap_invert) != 0;
- ipd->snap_state->draw_plane = false;
- ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src);
+ snap_state->draw_plane = false;
+ ED_view3d_cursor_snap_prevpoint_set(snap_state, ipd->co_src);
ipd->orient_axis = plane_axis;
for (int i = 0; i < 2; i++) {
@@ -1515,10 +1518,12 @@ static void preview_plane_free_fn(void *customdata)
static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
{
V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active();
- snap_state->draw_plane = true;
+ if (snap_state) {
+ snap_state->draw_plane = true;
- gzgroup->customdata = snap_state;
- gzgroup->customdata_free = preview_plane_free_fn;
+ gzgroup->customdata = snap_state;
+ gzgroup->customdata_free = preview_plane_free_fn;
+ }
}
void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt)
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 07f1f8a753c..18820039c7f 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2047,19 +2047,16 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
bool enumerate,
bool *r_do_nearest)
{
- static int last_mval[2] = {-100, -100};
bool do_nearest = false;
View3D *v3d = vc->v3d;
/* define if we use solid nearest select or not */
if (use_cycle) {
+ /* Update the coordinates (even if the return value isn't used). */
+ const bool has_motion = WM_cursor_test_motion_and_update(mval);
if (!XRAY_ACTIVE(v3d)) {
- do_nearest = true;
- if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
- do_nearest = false;
- }
+ do_nearest = has_motion;
}
- copy_v2_v2_int(last_mval, mval);
}
else {
if (!XRAY_ACTIVE(v3d)) {
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 55ec6652495..67b61ed77d8 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -615,7 +615,7 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op)
const float *snap_target_global = scene->cursor.location;
const int pivot_point = scene->toolsettings->transform_pivot_point;
- if (snap_selected_to_location(C, snap_target_global, pivot_point, use_offset, true)) {
+ if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index f5da7c14a88..46a664f10fa 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1730,7 +1730,12 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S
if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) {
View3DShading *xr_shading = &wm->xr.session_settings.shading;
/* Flags that shouldn't be overridden by the 3D View shading. */
- const int flag_copy = V3D_SHADING_WORLD_ORIENTATION;
+ int flag_copy = 0;
+ if (v3d->shading.type !=
+ OB_SOLID) { /* Don't set V3D_SHADING_WORLD_ORIENTATION for solid shading since it results
+ in distorted lighting when the view matrix has a scale factor. */
+ flag_copy |= V3D_SHADING_WORLD_ORIENTATION;
+ }
BLI_assert(WM_xr_session_exists(&wm->xr));
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index e4c20fa0be1..466c4202dbd 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -646,10 +646,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = area->spacedata.first;
- Object *obedit = CTX_data_edit_object(C);
RegionView3D *rv3d = region->regiondata;
Base *base;
- Object *ob = OBACT(view_layer);
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
@@ -660,6 +658,15 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
(params->orientation_index - 1) :
BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
+ Object *ob = OBACT(view_layer);
+ Object *obedit = OBEDIT_FROM_OBACT(ob);
+ if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
+ Object *obpose = BKE_object_pose_armature_get(ob);
+ if (obpose != NULL) {
+ ob = obpose;
+ }
+ }
+
/* transform widget matrix */
unit_m4(rv3d->twmat);
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index dea8a7c6f03..c779fbe4a33 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -493,6 +493,11 @@ static void iter_snap_objects(SnapObjectContext *sctx,
continue;
}
}
+ else if (snap_select == SNAP_SELECTABLE) {
+ if (!(base->flag & BASE_SELECTABLE)) {
+ continue;
+ }
+ }
Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object);
if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) {
@@ -2308,7 +2313,7 @@ static short snapMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
+ BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (bb &&
!snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) {
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index 2acdf5cfd9c..7bcf6812ce9 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -220,13 +220,13 @@ static void seq_snap_target_points_build(const TransInfo *t,
int content_end = max_ii(seq->startdisp, seq->start + seq->len);
/* Effects and single image strips produce incorrect content length. Skip these strips. */
if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) {
- if (seq->anim_startofs == 0 && seq->startstill == 0) {
- content_start = seq->startdisp;
- }
- if (seq->anim_endofs == 0 && seq->endstill == 0) {
- content_end = seq->enddisp;
- }
+ content_start = seq->startdisp;
+ content_end = seq->enddisp;
}
+
+ CLAMP(content_start, seq->startdisp, seq->enddisp);
+ CLAMP(content_end, seq->startdisp, seq->enddisp);
+
snap_data->target_snap_points[i] = content_start;
snap_data->target_snap_points[i + 1] = content_end;
i += 2;
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index ed5064fdf25..5e0302130af 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -227,9 +227,19 @@ class FieldContext;
* A #FieldNode that represents an input to the entire field-tree.
*/
class FieldInput : public FieldNode {
+ public:
+ /* The order is also used for sorting in socket inspection. */
+ enum class Category {
+ NamedAttribute = 0,
+ Generated = 1,
+ AnonymousAttribute = 2,
+ Unknown,
+ };
+
protected:
const CPPType *type_;
std::string debug_name_;
+ Category category_ = Category::Unknown;
public:
FieldInput(const CPPType &type, std::string debug_name = "");
@@ -245,6 +255,7 @@ class FieldInput : public FieldNode {
virtual std::string socket_inspection_name() const;
blender::StringRef debug_name() const;
const CPPType &cpp_type() const;
+ Category category() const;
const CPPType &output_cpp_type(int output_index) const override;
void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override;
@@ -527,6 +538,11 @@ inline const CPPType &FieldInput::cpp_type() const
return *type_;
}
+inline FieldInput::Category FieldInput::category() const
+{
+ return category_;
+}
+
inline const CPPType &FieldInput::output_cpp_type(int output_index) const
{
BLI_assert(output_index == 0);
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index c6125b65c5e..4de5e71c910 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -477,6 +477,12 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
void evaluate_constant_field(const GField &field, void *r_value)
{
+ if (field.node().depends_on_input()) {
+ const CPPType &type = field.cpp_type();
+ type.copy_construct(type.default_value(), r_value);
+ return;
+ }
+
ResourceScope scope;
FieldContext context;
Vector<const GVArray *> varrays = evaluate_fields(scope, {field}, IndexRange(1), context);
@@ -517,6 +523,7 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
{
+ category_ = Category::Generated;
}
GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 595a0c1cc5e..9ea146c77f2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -156,7 +156,7 @@ float get_modifier_point_weight(MDeformVert *dvert, bool inverse, int def_nr)
MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
weight = dw ? dw->weight : -1.0f;
if ((weight >= 0.0f) && (inverse)) {
- return -1.0f;
+ return 1.0f - weight;
}
if ((weight < 0.0f) && (!inverse)) {
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
index ba33edd6a94..33cc3094a36 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
@@ -255,7 +255,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
BKE_gpencil_frame_active_set(depsgraph, gpd);
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
- return;
+ continue;
}
apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index c5ccf1d8229..fa31aec2b5b 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -160,12 +160,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
LineartCache *local_lc = gpd->runtime.lineart_cache;
if (!gpd->runtime.lineart_cache) {
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
else {
if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) {
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
MOD_lineart_chain_clear_picked_flag(local_lc);
@@ -210,7 +212,8 @@ static void bakeModifier(Main *UNUSED(bmain),
lmd->edge_types_override = lmd->edge_types;
lmd->level_end_override = lmd->level_end;
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
@@ -261,7 +264,13 @@ static void updateDepsgraph(GpencilModifierData *md,
else {
add_this_collection(ctx->scene->master_collection, ctx, mode);
}
- if (ctx->scene->camera) {
+ if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) {
+ DEG_add_object_relation(
+ ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ DEG_add_object_relation(
+ ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
+ }
+ else if (ctx->scene->camera) {
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
DEG_add_object_relation(
@@ -277,6 +286,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP);
walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -382,7 +392,12 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
return;
}
- uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE);
+ uiLayout *row = uiLayoutRowWithHeading(layout, false, IFACE_("Custom Camera"));
+ uiItemR(row, ptr, "use_custom_camera", 0, "", 0);
+ uiLayout *subrow = uiLayoutRow(row, true);
+ uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera"));
+ uiLayoutSetPropSep(subrow, true);
+ uiItemR(subrow, ptr, "source_camera", 0, "", ICON_OBJECT_DATA);
uiLayout *col = uiLayoutColumn(layout, true);
@@ -391,7 +406,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE);
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(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
+ uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
}
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -412,14 +427,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
- PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
- const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ if (!show_in_front) {
+ uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO);
+ }
+
+ layout = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(layout, show_in_front);
uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE);
@@ -447,11 +471,14 @@ static bool anything_showing_through(PointerRNA *ptr)
static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
- PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
uiLayoutSetEnabled(layout, !is_baked);
- uiLayoutSetActive(layout, anything_showing_through(ptr));
+ uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr));
uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE);
}
@@ -654,6 +681,32 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all");
}
+static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ uiLayout *layout = panel->layout;
+
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE);
+
+ if (show_in_front) {
+ uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR);
+ }
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, !show_in_front);
+
+ uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE);
+ uiItemR(
+ col, ptr, "use_offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE);
+}
+
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
@@ -682,6 +735,8 @@ static void panelRegister(ARegionType *region_type)
gpencil_modifier_subpanel_register(
region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
+ region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index fb75b1e99ac..2e55369ea97 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -240,10 +240,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "hardness", 0, NULL, ICON_NONE);
}
else {
- const bool is_normalized = RNA_boolean_get(ptr, "normalize_opacity");
+ const bool is_normalized = RNA_boolean_get(ptr, "use_normalized_opacity");
const bool is_weighted = RNA_boolean_get(ptr, "use_weight_factor");
- uiItemR(layout, ptr, "normalize_opacity", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_normalized_opacity", 0, NULL, ICON_NONE);
const char *text = (is_normalized) ? IFACE_("Strength") : IFACE_("Opacity Factor");
uiLayout *row = uiLayoutRow(layout, true);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index cac700e15f4..233992bbd31 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -191,8 +191,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, ptr, "normalize_thickness", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(ptr, "normalize_thickness")) {
+ uiItemR(layout, ptr, "use_normalized_thickness", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(ptr, "use_normalized_thickness")) {
uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE);
}
else {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index c00f34185dd..d8926a63307 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -182,9 +182,9 @@ typedef struct LineartEdgeChain {
typedef struct LineartEdgeChainItem {
struct LineartEdgeChainItem *next, *prev;
- /** Need z value for fading */
- float pos[3];
- /** For restoring position to 3d space */
+ /** Need z value for fading, w value for image frame clipping. */
+ float pos[4];
+ /** For restoring position to 3d space. */
float gpos[3];
float normal[3];
unsigned char line_type;
@@ -299,6 +299,7 @@ typedef struct LineartRenderBuffer {
bool use_loose_as_contour;
bool use_loose_edge_chain;
bool use_geometry_space_chain;
+ bool use_image_boundary_trimming;
bool filter_face_mark;
bool filter_face_mark_invert;
@@ -311,6 +312,7 @@ typedef struct LineartRenderBuffer {
bool cam_is_persp;
float cam_obmat[4][4];
double camera_pos[3];
+ double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */
double near_clip, far_clip;
float shift_x, shift_y;
float crease_threshold;
@@ -593,15 +595,20 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb);
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
void MOD_lineart_chain_connect(LineartRenderBuffer *rb);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
+void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance);
+void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb,
+ float dist,
+ bool use_custom_camera);
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
struct LineartGpencilModifierData *lmd,
- LineartCache **cached_result);
+ struct LineartCache **cached_result,
+ bool enable_stroke_offset);
struct Scene;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 8935bdd1870..f3110cf87b6 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -126,7 +126,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(eci->pos, fbcoord);
+ copy_v4_v4(eci->pos, fbcoord);
copy_v3_v3(eci->gpos, gpos);
eci->index = index;
copy_v3_v3(eci->normal, normal);
@@ -156,7 +156,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb
eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(eci->pos, fbcoord);
+ copy_v4_v4(eci->pos, fbcoord);
copy_v3_v3(eci->gpos, gpos);
eci->index = index;
copy_v3_v3(eci->normal, normal);
@@ -177,15 +177,15 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
int last_occlusion;
unsigned char last_transparency;
/* Used when converting from double. */
- float use_fbcoord[2];
+ float use_fbcoord[4];
float use_gpos[3];
#define VERT_COORD_TO_FLOAT(a) \
- copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \
+ copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \
copy_v3fl_v3db(use_gpos, (a)->gloc);
#define POS_TO_FLOAT(lpos, gpos) \
- copy_v2fl_v2db(use_fbcoord, lpos); \
+ copy_v3fl_v3db(use_fbcoord, lpos); \
copy_v3fl_v3db(use_gpos, gpos);
LRT_ITER_ALL_LINES_BEGIN
@@ -262,6 +262,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
+ use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
ec,
@@ -287,6 +288,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
+ use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
ec,
@@ -340,6 +342,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
+ use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
ec,
@@ -403,6 +406,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
+ use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency;
POS_TO_FLOAT(lpos, gpos)
@@ -430,6 +434,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
+ use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
ec,
@@ -926,9 +931,9 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
{
- LISTBASE_FOREACH (LineartEdgeChain *, rlc, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
LineartEdgeChainItem *next_eci;
- for (LineartEdgeChainItem *eci = rlc->chain.first; eci; eci = next_eci) {
+ for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) {
next_eci = eci->next;
LineartEdgeChainItem *eci2, *eci3, *eci4;
@@ -944,7 +949,7 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) {
/* And if p4 is on the extension of p1-p2 , we remove p3. */
if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) {
- BLI_remlink(&rlc->chain, eci3);
+ BLI_remlink(&ec->chain, eci3);
next_eci = eci;
}
}
@@ -952,6 +957,116 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
}
}
+static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb,
+ LineartEdgeChainItem *eci_inside,
+ LineartEdgeChainItem *eci_outside)
+{
+ float isec[2];
+ float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f};
+ bool found = false;
+ LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside;
+ if (eci2->pos[0] < -1.0f) {
+ found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0);
+ }
+ if (!found && eci2->pos[0] > 1.0f) {
+ found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0);
+ }
+ if (!found && eci2->pos[1] < -1.0f) {
+ found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0);
+ }
+ if (!found && eci2->pos[1] > 1.0f) {
+ found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0);
+ }
+
+ if (UNLIKELY(!found)) {
+ return NULL;
+ }
+
+ float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ?
+ ratiof(eci1->pos[0], eci2->pos[0], isec[0]) :
+ ratiof(eci1->pos[1], eci2->pos[1], isec[1]);
+ float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]);
+
+ LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool,
+ sizeof(LineartEdgeChainItem));
+ memcpy(eci, eci1, sizeof(LineartEdgeChainItem));
+ interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio);
+ interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio);
+ eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio);
+ eci->next = eci->prev = NULL;
+ return eci;
+}
+
+#define LRT_ECI_INSIDE(eci) \
+ ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \
+ (eci)->pos[1] <= 1.0f)
+
+void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb)
+{
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci;
+ bool is_inside, new_inside;
+ ListBase swap = {0};
+ swap.first = rb->chains.first;
+ swap.last = rb->chains.last;
+
+ rb->chains.last = rb->chains.first = NULL;
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ bool ec_added = false;
+ LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
+ is_inside = LRT_ECI_INSIDE(first_eci) ? true : false;
+ if (!is_inside) {
+ ec->picked = true;
+ }
+ for (eci = first_eci->next; eci; eci = next_eci) {
+ next_eci = eci->next;
+ prev_eci = eci->prev;
+
+ /* We only need to do something if the edge crossed from outside to the inside or from inside
+ * to the outside. */
+ if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) {
+ if (new_inside == false) {
+ /* Stroke goes out. */
+ new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci);
+
+ LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool,
+ sizeof(LineartEdgeChain));
+ memcpy(new_ec, ec, sizeof(LineartEdgeChain));
+ new_ec->chain.first = next_eci;
+ eci->prev = NULL;
+ prev_eci->next = NULL;
+ ec->chain.last = prev_eci;
+ BLI_addtail(&ec->chain, new_eci);
+ BLI_addtail(&rb->chains, ec);
+ ec_added = true;
+ ec = new_ec;
+
+ next_eci = eci->next;
+ is_inside = new_inside;
+ continue;
+ }
+ /* Stroke comes in. */
+ new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci);
+
+ ec->chain.first = eci;
+ eci->prev = NULL;
+
+ BLI_addhead(&ec->chain, new_eci);
+
+ ec_added = false;
+
+ next_eci = eci->next;
+ is_inside = new_inside;
+ continue;
+ }
+ }
+
+ if ((!ec_added) && is_inside) {
+ BLI_addtail(&rb->chains, ec);
+ }
+ }
+}
+
/**
* This should always be the last stage!, see the end of
* #MOD_lineart_chain_split_for_fixed_occlusion().
@@ -1008,3 +1123,45 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol
}
}
}
+
+void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb,
+ float dist,
+ bool use_custom_camera)
+{
+ float dir[3];
+ float cam[3];
+ float view[3];
+ float view_clamp[3];
+ copy_v3fl_v3db(cam, rb->camera_pos);
+ copy_v3fl_v3db(view, rb->view_vector);
+
+ if (use_custom_camera) {
+ copy_v3fl_v3db(cam, rb->camera_pos);
+ }
+ else {
+ copy_v3fl_v3db(cam, rb->active_camera_pos);
+ }
+
+ if (rb->cam_is_persp) {
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ sub_v3_v3v3(dir, cam, eci->gpos);
+ float orig_len = len_v3(dir);
+ normalize_v3(dir);
+ mul_v3_fl(dir, MIN2(dist, orig_len - rb->near_clip));
+ add_v3_v3(eci->gpos, dir);
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ sub_v3_v3v3(dir, cam, eci->gpos);
+ float len_lim = dot_v3v3(view, dir) - rb->near_clip;
+ normalize_v3_v3(view_clamp, view);
+ mul_v3_fl(view_clamp, MIN2(dist, len_lim));
+ add_v3_v3(eci->gpos, view_clamp);
+ }
+ }
+ }
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 6660da79b40..93e9062e910 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -2998,7 +2998,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd)
}
}
-static LineartCache *lineart_init_cache()
+static LineartCache *lineart_init_cache(void)
{
LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache");
return lc;
@@ -3016,6 +3016,8 @@ void MOD_lineart_clear_cache(struct LineartCache **lc)
static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
LineartGpencilModifierData *lmd,
+ Object *camera,
+ Object *active_camera,
LineartCache *lc)
{
LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer");
@@ -3024,10 +3026,10 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
lmd->render_buffer_ptr = rb;
lc->rb_edge_types = lmd->edge_types_override;
- if (!scene || !scene->camera || !lc) {
+ if (!scene || !camera || !lc) {
return NULL;
}
- Camera *c = scene->camera->data;
+ Camera *c = camera->data;
double clipping_offset = 0;
if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) {
@@ -3035,8 +3037,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
clipping_offset = 0.0001;
}
- copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]);
- copy_m4_m4(rb->cam_obmat, scene->camera->obmat);
+ copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]);
+ if (active_camera) {
+ copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]);
+ }
+ copy_m4_m4(rb->cam_obmat, camera->obmat);
rb->cam_is_persp = (c->type == CAM_PERSP);
rb->near_clip = c->clip_start + clipping_offset;
rb->far_clip = c->clip_end - clipping_offset;
@@ -3072,6 +3077,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0;
rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0;
rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0;
+ rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) !=
+ 0;
/* See lineart_edge_from_triangle() for how this option may impact performance. */
rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0;
@@ -4076,11 +4083,13 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
*/
bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
LineartGpencilModifierData *lmd,
- LineartCache **cached_result)
+ LineartCache **cached_result,
+ bool enable_stroke_depth_offset)
{
LineartRenderBuffer *rb;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
int intersections_only = 0; /* Not used right now, but preserve for future. */
+ Object *use_camera;
double t_start;
@@ -4090,14 +4099,24 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
BKE_scene_camera_switch_update(scene);
- if (!scene->camera) {
- return false;
+ if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) {
+ if (!lmd->source_camera ||
+ (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type !=
+ OB_CAMERA) {
+ return false;
+ }
+ }
+ else {
+ if (!scene->camera) {
+ return false;
+ }
+ use_camera = scene->camera;
}
LineartCache *lc = lineart_init_cache();
*cached_result = lc;
- rb = lineart_create_render_buffer(scene, lmd, lc);
+ rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc);
/* Triangle thread testing data size varies depending on the thread count.
* See definition of LineartTriangleThread for details. */
@@ -4117,7 +4136,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* Get view vector before loading geometries, because we detect feature lines there. */
lineart_main_get_view_vector(rb);
lineart_main_load_geometries(
- depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
+ depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
if (!rb->vertex_buffer_pointers.first) {
/* No geometry loaded, return early. */
@@ -4185,10 +4204,19 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50);
}
+ if (rb->use_image_boundary_trimming) {
+ MOD_lineart_chain_clip_at_border(rb);
+ }
+
if (rb->angle_splitting_threshold > FLT_EPSILON) {
MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
}
+ if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) {
+ MOD_lineart_chain_offset_towards_camera(
+ rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA);
+ }
+
/* Finally transfer the result list into cache. */
memcpy(&lc->chains, &rb->chains, sizeof(ListBase));
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
index 988c90483a6..b74499daf6b 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob,
}
LineartCache *local_lc = *lc;
if (!(*lc)) {
- MOD_lineart_compute_feature_lines(dg, lmd, lc);
+ MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
else {
if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) {
- MOD_lineart_compute_feature_lines(dg, lmd, &local_lc);
+ MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
MOD_lineart_chain_clear_picked_flag(local_lc);
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 018e192bf37..911c8cc2e42 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -32,7 +32,7 @@
#include "GPU_shader.h"
#include "GPU_vertex_buffer.h"
-#define GPU_BATCH_VBO_MAX_LEN 6
+#define GPU_BATCH_VBO_MAX_LEN 16
#define GPU_BATCH_INST_VBO_MAX_LEN 2
#define GPU_BATCH_VAO_STATIC_LEN 3
#define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16
@@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag {
GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1),
/** Has been initialized. At least one VBO is set. */
- GPU_BATCH_INIT = (1 << 16),
+ GPU_BATCH_INIT = (1 << 26),
/** Batch is initialized but its VBOs are still being populated. (optional) */
- GPU_BATCH_BUILDING = (1 << 16),
+ GPU_BATCH_BUILDING = (1 << 26),
/** Cached data need to be rebuild. (VAO, PSO, ...) */
- GPU_BATCH_DIRTY = (1 << 17),
+ GPU_BATCH_DIRTY = (1 << 27),
} eGPUBatchFlag;
#define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID
diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h
index 3ea809d59a7..0d3d39839b2 100644
--- a/source/blender/gpu/GPU_immediate_util.h
+++ b/source/blender/gpu/GPU_immediate_util.h
@@ -78,8 +78,12 @@ void imm_draw_box_checker_2d_ex(float x1,
int checker_size);
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2);
-void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]);
-void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]);
+void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]);
+void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]);
+void imm_draw_cube_corners_3d(uint pos,
+ const float center[3],
+ const float aspect[3],
+ const float factor);
void imm_draw_cylinder_fill_normal_3d(
uint pos, uint nor, float base, float top, float height, int slices, int stacks);
diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c
index d18dc862ce7..032974db8d1 100644
--- a/source/blender/gpu/intern/gpu_immediate_util.c
+++ b/source/blender/gpu/intern/gpu_immediate_util.c
@@ -391,12 +391,12 @@ void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2)
imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size);
}
-void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3])
+void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3])
{
float coords[ARRAY_SIZE(cube_coords)][3];
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
- madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect);
+ madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
}
immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2);
@@ -412,12 +412,12 @@ void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3])
immEnd();
}
-void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3])
+void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3])
{
float coords[ARRAY_SIZE(cube_coords)][3];
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
- madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect);
+ madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
}
immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2);
@@ -428,6 +428,36 @@ void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3])
immEnd();
}
+void imm_draw_cube_corners_3d(uint pos,
+ const float center[3],
+ const float aspect[3],
+ const float factor)
+{
+ float coords[ARRAY_SIZE(cube_coords)][3];
+
+ for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
+ madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
+ }
+
+ immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4);
+ for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
+ float vec[3], co[3];
+ sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]);
+ mul_v3_fl(vec, factor);
+
+ copy_v3_v3(co, coords[cube_line_index[i][0]]);
+ immVertex3fv(pos, co);
+ add_v3_v3(co, vec);
+ immVertex3fv(pos, co);
+
+ copy_v3_v3(co, coords[cube_line_index[i][1]]);
+ immVertex3fv(pos, co);
+ sub_v3_v3(co, vec);
+ immVertex3fv(pos, co);
+ }
+ immEnd();
+}
+
/**
* Draw a cylinder. Replacement for gluCylinder.
* _warning_ : Slow, better use it only if you no other choices.
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index f5bdad3e79e..bd604b90b5a 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -56,6 +56,9 @@ typedef struct AssetFilterSettings {
* more than that from the file. So pointers to other IDs or ID data are strictly forbidden.
*/
typedef struct AssetMetaData {
+ /** Runtime type, to reference event callbacks. Only valid for local assets. */
+ struct AssetTypeInfo *local_type_info;
+
/** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */
struct IDProperty *properties;
@@ -72,8 +75,12 @@ typedef struct AssetMetaData {
* #catalog_id is updated. */
char catalog_simple_name[64]; /* MAX_NAME */
+ /** Optional name of the author for display in the UI. Dynamic length. */
+ char *author;
+
/** Optional description of this asset for display in the UI. Dynamic length. */
char *description;
+
/** User defined tags for this asset. The asset manager uses these for filtering, but how they
* function exactly (e.g. how they are registered to provide a list of searchable available tags)
* is up to the asset-engine. */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 11299ae9717..1ad884bee8f 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -319,6 +319,7 @@
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
.chain_smooth_tolerance = 0.2f,\
+ .stroke_depth_offset = 0.05,\
}
#define _DNA_DEFAULT_LengthGpencilModifierData \
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index c7a93080f7c..339714da255 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -978,6 +978,7 @@ typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */,
LRT_GPENCIL_IS_BAKED = (1 << 3),
LRT_GPENCIL_USE_CACHE = (1 << 4),
+ LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5),
} eLineArtGPencilModifierFlags;
typedef enum eLineartGpencilMaskSwitches {
@@ -1004,6 +1005,8 @@ typedef struct LineartGpencilModifierData {
short level_start;
short level_end;
+ struct Object *source_camera;
+
struct Object *source_object;
struct Collection *source_collection;
@@ -1042,7 +1045,6 @@ typedef struct LineartGpencilModifierData {
/** Strength for smoothing jagged chains. */
float chain_smooth_tolerance;
- int _pad1;
/* CPU mode */
float chaining_image_threshold;
@@ -1053,6 +1055,9 @@ typedef struct LineartGpencilModifierData {
/* #eLineArtGPencilModifierFlags, modifier internal state. */
int flags;
+ /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */
+ float stroke_depth_offset;
+
/* Runtime data. */
/* Because we can potentially only compute features lines once per modifier stack (Use Cache), we
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index bdc9bcb6980..d4440592a00 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -49,6 +49,8 @@ typedef enum eLineartMainFlags {
LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14),
LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15),
LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16),
+ LRT_USE_CUSTOM_CAMERA = (1 << 17),
+ LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20),
} eLineartMainFlags;
typedef enum eLineartEdgeFlag {
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 97f14b2195d..3943c063c39 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -127,6 +127,10 @@ typedef struct Mesh_Runtime {
/** Needed in case we need to lazily initialize the mesh. */
CustomData_MeshMasks cd_mask_extra;
+ /** Needed to ensure some thread-safety during render data pre-processing. */
+ void *render_mutex;
+ void *_pad3;
+
} Mesh_Runtime;
typedef struct Mesh {
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index b585cbd6306..d5d2520ddf6 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1581,6 +1581,16 @@ typedef struct NodeGeometrySeparateGeometry {
int8_t domain;
} NodeGeometrySeparateGeometry;
+typedef struct NodeGeometryImageTexture {
+ int interpolation;
+ int extension;
+} NodeGeometryImageTexture;
+
+typedef struct NodeGeometryViewer {
+ /* CustomDataType. */
+ int8_t data_type;
+} NodeGeometryViewer;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 5a88ce7c9f5..e94541fdc7f 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -501,6 +501,9 @@ enum {
/* check if the object type supports materials */
#define OB_TYPE_SUPPORT_MATERIAL(_type) \
(((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME))
+/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */
+#define OB_TYPE_IS_GEOMETRY(_type) \
+ (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, 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))
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index af01bb76680..fc23d3c69a3 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -179,7 +179,9 @@ typedef struct Sequence {
/** Starting and ending points of the strip in the sequence. */
int startdisp, enddisp;
float sat;
- float mul, handsize;
+ float mul;
+ char tmp_tag;
+ char _pad[3];
short anim_preseek; /* UNUSED. */
/** Streamindex for movie or sound files with several streams. */
@@ -250,7 +252,7 @@ typedef struct Sequence {
/* Multiview */
char views_format;
- char _pad[3];
+ char _pad1[3];
struct Stereo3dFormat *stereo3d_format;
struct IDProperty *prop;
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index e24d39b61eb..841edaf8724 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -138,7 +138,14 @@ typedef struct wmWindowManager {
ID id;
/** Separate active from drawable. */
- struct wmWindow *windrawable, *winactive;
+ struct wmWindow *windrawable;
+ /**
+ * \note `CTX_wm_window(C)` is usually preferred.
+ * Avoid relying on this where possible as this may become NULL during when handling
+ * events that close or replace windows (opening a file for e.g.).
+ * While this happens rarely in practice, it can cause difficult to reproduce bugs.
+ */
+ struct wmWindow *winactive;
ListBase windows;
/** Set on file read. */
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index 6f4f7e3e8ae..f7da912f299 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -32,8 +32,8 @@ typedef struct XrSessionSettings {
/** Shading settings, struct shared with 3D-View so settings are the same. */
struct View3DShading shading;
- char _pad[7];
-
+ float base_scale;
+ char _pad[3];
char base_pose_type; /* #eXRSessionBasePoseType */
/** Object to take the location and rotation as base position from. */
Object *base_pose_object;
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index 979d0882dd5..5d83da170b5 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -139,6 +139,40 @@ static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr)
return &asset_data->properties;
}
+static void rna_AssetMetaData_author_get(PointerRNA *ptr, char *value)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ if (asset_data->author) {
+ strcpy(value, asset_data->author);
+ }
+ else {
+ value[0] = '\0';
+ }
+}
+
+static int rna_AssetMetaData_author_length(PointerRNA *ptr)
+{
+ AssetMetaData *asset_data = ptr->data;
+ return asset_data->author ? strlen(asset_data->author) : 0;
+}
+
+static void rna_AssetMetaData_author_set(PointerRNA *ptr, const char *value)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ if (asset_data->author) {
+ MEM_freeN(asset_data->author);
+ }
+
+ if (value[0]) {
+ asset_data->author = BLI_strdup(value);
+ }
+ else {
+ asset_data->author = NULL;
+ }
+}
+
static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value)
{
AssetMetaData *asset_data = ptr->data;
@@ -347,6 +381,14 @@ static void rna_def_asset_data(BlenderRNA *brna)
RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops");
RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */
+ prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE);
+ RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable");
+ RNA_def_property_string_funcs(prop,
+ "rna_AssetMetaData_author_get",
+ "rna_AssetMetaData_author_length",
+ "rna_AssetMetaData_author_set");
+ RNA_def_property_ui_text(prop, "Author", "Name of the creator of the asset");
+
prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable");
RNA_def_property_string_funcs(prop,
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index f1831bca0fe..dbf20896463 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -323,8 +323,10 @@ static void rna_AttributeGroup_next_domain(ID *id,
int(skip)(CollectionPropertyIterator *iter, void *data))
{
do {
- CustomDataLayer *prev_layers = (CustomDataLayer *)iter->internal.array.endptr -
- iter->internal.array.length;
+ CustomDataLayer *prev_layers = (iter->internal.array.endptr == NULL) ?
+ NULL :
+ (CustomDataLayer *)iter->internal.array.endptr -
+ iter->internal.array.length;
CustomData *customdata = BKE_id_attributes_iterator_next_domain(id, prev_layers);
if (customdata == NULL) {
return;
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 631d5822c5e..c4efe5a0ea1 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -1309,7 +1309,7 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
prop, "Custom Curve", "Use a custom curve to define thickness change along the strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "normalize_thickness", PROP_BOOLEAN, PROP_NONE);
+ prop = RNA_def_property(srna, "use_normalized_thickness", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_NORMALIZE);
RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1865,7 +1865,7 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "normalize_opacity", PROP_BOOLEAN, PROP_NONE);
+ prop = RNA_def_property(srna, "use_normalized_opacity", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_NORMALIZE);
RNA_def_property_ui_text(prop, "Uniform Opacity", "Replace the stroke opacity");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_opacity_update");
@@ -3073,6 +3073,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_define_lib_overridable(true);
+ prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CUSTOM_CAMERA);
+ RNA_def_property_ui_text(
+ prop, "Use Custom Camera", "Use custom camera instead of the active camera");
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
RNA_def_property_ui_text(prop,
@@ -3200,6 +3206,30 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
"separate stroke for each overlapping type");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "stroke_depth_offset", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_ui_text(prop,
+ "Stroke Depth Offset",
+ "Move strokes slightly towards the camera to avoid clipping while "
+ "preserve depth for the viewport");
+ RNA_def_property_ui_range(prop, 0.0, 0.5, 0.001, 4);
+ RNA_def_property_range(prop, -0.1, FLT_MAX);
+ RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA);
+ RNA_def_property_ui_text(prop,
+ "Offset Towards Custom Camera",
+ "Offset strokes towards selected camera instead of the active camera");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "source_camera", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(
+ prop, "Camera Object", "Use specified camera object for generating line art");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_lineart_source_type);
RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type");
@@ -3375,6 +3405,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING);
+ RNA_def_property_ui_text(
+ prop,
+ "Image Boundary Trimming",
+ "Trim all edges right at the boundary of image(including overscan region)");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
RNA_define_lib_overridable(false);
}
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index f0508ee5317..6a36ef07dee 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2056,7 +2056,8 @@ static bool switch_type_supported(const EnumPropertyItem *item)
SOCK_OBJECT,
SOCK_COLLECTION,
SOCK_TEXTURE,
- SOCK_MATERIAL);
+ SOCK_MATERIAL,
+ SOCK_IMAGE);
}
static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
@@ -5022,10 +5023,8 @@ static void def_fn_input_bool(StructRNA *srna)
prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1);
- RNA_def_property_ui_text(
- prop, "Boolean", "Input value used for unconnected socket");
+ RNA_def_property_ui_text(prop, "Boolean", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
-
}
static void def_fn_input_int(StructRNA *srna)
@@ -5037,8 +5036,7 @@ static void def_fn_input_int(StructRNA *srna)
prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "integer");
RNA_def_property_int_default(prop, 1);
- RNA_def_property_ui_text(
- prop, "Integer", "Input value used for unconnected socket");
+ RNA_def_property_ui_text(prop, "Integer", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@@ -5416,6 +5414,50 @@ static void def_sh_tex_image(StructRNA *srna)
RNA_def_property_update(prop, 0, "rna_Node_update");
}
+static void def_geo_image_texture(StructRNA *srna)
+{
+ static const EnumPropertyItem fn_tex_prop_interpolation_items[] = {
+ {SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"},
+ {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"},
+ {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", "Cubic interpolation"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem prop_image_extension[] = {
+ {SHD_IMAGE_EXTENSION_REPEAT,
+ "REPEAT",
+ 0,
+ "Repeat",
+ "Cause the image to repeat horizontally and vertically"},
+ {SHD_IMAGE_EXTENSION_EXTEND,
+ "EXTEND",
+ 0,
+ "Extend",
+ "Extend by repeating edge pixels of the image"},
+ {SHD_IMAGE_EXTENSION_CLIP,
+ "CLIP",
+ 0,
+ "Clip",
+ "Clip to image size and set exterior pixels as transparent"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage");
+
+ prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, fn_tex_prop_interpolation_items);
+ RNA_def_property_ui_text(prop, "Interpolation", "Method for smoothing values between pixels");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_image_extension);
+ RNA_def_property_ui_text(
+ prop, "Extension", "How the image is extrapolated past its original bounds");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+}
+
static void def_sh_tex_gradient(StructRNA *srna)
{
static const EnumPropertyItem prop_gradient_type[] = {
@@ -10300,12 +10342,12 @@ static void def_geo_volume_to_mesh(StructRNA *srna)
{VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT,
"VOXEL_AMOUNT",
0,
- "Voxel Amount",
+ "Amount",
"Desired number of voxels along one axis"},
{VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE,
"VOXEL_SIZE",
0,
- "Voxel Size",
+ "Size",
"Desired voxel side length"},
{0, NULL, 0, NULL, NULL},
};
@@ -11035,6 +11077,20 @@ static void def_geo_separate_geometry(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_viewer(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index c91ef25daa8..03976967e9f 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -3074,12 +3074,34 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
PointerRNA *ptr)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
- if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
- sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
- }
- if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE &&
- !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
- sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ switch (sspreadsheet->geometry_component_type) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ if (!ELEM(sspreadsheet->attribute_domain,
+ ATTR_DOMAIN_POINT,
+ ATTR_DOMAIN_EDGE,
+ ATTR_DOMAIN_FACE,
+ ATTR_DOMAIN_CORNER)) {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (!ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ }
+ break;
+ }
}
}
diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c
index 295ecf590bb..c5569683c9c 100644
--- a/source/blender/makesrna/intern/rna_space_api.c
+++ b/source/blender/makesrna/intern/rna_space_api.c
@@ -122,7 +122,8 @@ void RNA_api_space_filebrowser(StructRNA *srna)
PropertyRNA *parm;
func = RNA_def_function(srna, "activate_asset_by_id", "ED_fileselect_activate_by_id");
- RNA_def_function_ui_description(func, "Activate the asset entry that represents the given ID");
+ RNA_def_function_ui_description(
+ func, "Activate and select the asset entry that represents the given ID");
parm = RNA_def_property(func, "id_to_activate", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(parm, "ID");
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 37d2b711b7d..2514a604087 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -2844,7 +2844,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "wire_select", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "edge_select");
- RNA_def_property_array(prop, 3);
+ RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Wire Select", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
@@ -2911,10 +2911,10 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "grid_levels", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "grid_levels");
- RNA_def_property_int_default(prop, 2);
- RNA_def_property_range(prop, 0, 2);
+ RNA_def_property_int_default(prop, 7);
+ RNA_def_property_range(prop, 0, 9);
RNA_def_property_ui_text(
- prop, "Grid Levels", "Amount of grid lines displayed in the background");
+ prop, "Grid Levels", "Number of subdivisions for the dot grid displayed in the background");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "dash_alpha", PROP_FLOAT, PROP_FACTOR);
diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c
index 826e6d21c36..68f11d71de2 100644
--- a/source/blender/makesrna/intern/rna_world.c
+++ b/source/blender/makesrna/intern/rna_world.c
@@ -128,6 +128,7 @@ static void rna_def_lighting(BlenderRNA *brna)
prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "aodist");
+ RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_text(
prop, "Distance", "Length of rays, defines how far away other faces give occlusion effect");
RNA_def_property_update(prop, 0, "rna_World_update");
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index 3705284ca66..dd4cbeac174 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -892,6 +892,71 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *
# endif
}
+static void rna_XrSessionState_nav_location_get(PointerRNA *ptr, float *r_values)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_location_get(xr, r_values);
+# else
+ UNUSED_VARS(ptr);
+ zero_v3(r_values);
+# endif
+}
+
+static void rna_XrSessionState_nav_location_set(PointerRNA *ptr, const float *values)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_location_set(xr, values);
+# else
+ UNUSED_VARS(ptr, values);
+# endif
+}
+
+static void rna_XrSessionState_nav_rotation_get(PointerRNA *ptr, float *r_values)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_rotation_get(xr, r_values);
+# else
+ UNUSED_VARS(ptr);
+ unit_qt(r_values);
+# endif
+}
+
+static void rna_XrSessionState_nav_rotation_set(PointerRNA *ptr, const float *values)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_rotation_set(xr, values);
+# else
+ UNUSED_VARS(ptr, values);
+# endif
+}
+
+static float rna_XrSessionState_nav_scale_get(PointerRNA *ptr)
+{
+ float value;
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_scale_get(xr, &value);
+# else
+ UNUSED_VARS(ptr);
+ value = 1.0f;
+# endif
+ return value;
+}
+
+static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_session_state_nav_scale_set(xr, value);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -1615,6 +1680,13 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
"Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+ prop = RNA_def_property(srna, "base_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Base Scale", "Uniform scale to apply to VR view");
+ RNA_def_property_range(prop, 1e-6f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR);
RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid");
@@ -1675,7 +1747,9 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
"rna_XrSessionSettings_use_absolute_tracking_get",
"rna_XrSessionSettings_use_absolute_tracking_set");
RNA_def_property_ui_text(
- prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime");
+ prop,
+ "Absolute Tracking",
+ "Allow the VR tracking origin to be defined independently of the headset location");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
}
@@ -1981,6 +2055,32 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
"Viewer Pose Rotation",
"Last known rotation of the viewer pose (center between the eyes) in world space");
+ prop = RNA_def_property(srna, "navigation_location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_location_get", "rna_XrSessionState_nav_location_set", NULL);
+ RNA_def_property_ui_text(
+ prop,
+ "Navigation Location",
+ "Location offset to apply to base pose when determining viewer location");
+
+ prop = RNA_def_property(srna, "navigation_rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_rotation_get", "rna_XrSessionState_nav_rotation_set", NULL);
+ RNA_def_property_ui_text(
+ prop,
+ "Navigation Rotation",
+ "Rotation offset to apply to base pose when determining viewer rotation");
+
+ prop = RNA_def_property(srna, "navigation_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(
+ prop, "rna_XrSessionState_nav_scale_get", "rna_XrSessionState_nav_scale_set", NULL);
+ RNA_def_property_ui_text(
+ prop,
+ "Navigation Scale",
+ "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_collection_funcs(prop,
"rna_XrSessionState_actionmaps_begin",
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index a14d582063a..e087b8411f8 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -25,6 +25,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -112,6 +113,31 @@ static void requiredDataMask(Object *UNUSED(ob),
}
}
+typedef struct FluidIsolationData {
+ Depsgraph *depsgraph;
+ Object *object;
+ Mesh *mesh;
+ FluidModifierData *fmd;
+
+ Mesh *result;
+} FluidIsolationData;
+
+#ifdef WITH_FLUID
+static void fluid_modifier_do_isolated(void *userdata)
+{
+ FluidIsolationData *isolation_data = (FluidIsolationData *)userdata;
+
+ Scene *scene = DEG_get_evaluated_scene(isolation_data->depsgraph);
+
+ Mesh *result = BKE_fluid_modifier_do(isolation_data->fmd,
+ isolation_data->depsgraph,
+ scene,
+ isolation_data->object,
+ isolation_data->mesh);
+ isolation_data->result = result ? result : isolation_data->mesh;
+}
+#endif /* WITH_FLUID */
+
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *me)
{
#ifndef WITH_FLUID
@@ -119,16 +145,24 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return me;
#else
FluidModifierData *fmd = (FluidModifierData *)md;
- Mesh *result = NULL;
if (ctx->flag & MOD_APPLY_ORCO) {
return me;
}
- Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
-
- result = BKE_fluid_modifier_do(fmd, ctx->depsgraph, scene, ctx->object, me);
- return result ? result : me;
+ /* Isolate execution of Mantaflow when running from dependency graph. The reason for this is
+ * because Mantaflow uses TBB to parallel its own computation which without isolation will start
+ * stealing tasks from dependency graph. Stealing tasks from the dependency graph might cause
+ * a recursive lock when Python drivers are used (because Mantaflow is interfaced via Python as
+ * well. */
+ FluidIsolationData isolation_data;
+ isolation_data.depsgraph = ctx->depsgraph;
+ isolation_data.object = ctx->object;
+ isolation_data.mesh = me;
+ isolation_data.fmd = fmd;
+ BLI_task_isolate(fluid_modifier_do_isolated, &isolation_data);
+
+ return isolation_data.result;
#endif /* WITH_FLUID */
}
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index e6cc7663c58..c88940c00c2 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -917,6 +917,10 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set,
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
store_field_on_geometry_component(component, attribute_name, domain, field);
}
+ if (geometry_set.has_instances()) {
+ InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>();
+ store_field_on_geometry_component(component, attribute_name, domain, field);
+ }
}
/**
@@ -1424,13 +1428,18 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, true);
+ bool has_output_attribute = false;
if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
if (socket_type_has_attribute_toggle(*socket)) {
+ has_output_attribute = true;
draw_property_for_output_socket(layout, *nmd, ptr, *socket);
}
}
}
+ if (!has_output_attribute) {
+ uiItemL(layout, IFACE_("No group output attributes connected."), ICON_INFO);
+ }
}
static void panelRegister(ARegionType *region_type)
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 69132f6c177..a312872f5d9 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -1455,6 +1455,7 @@ class GeometryNodesEvaluator {
}
void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
this->convert_value(type, required_type, buffer, converted_buffer);
+ type.destruct(buffer);
return {required_type, converted_buffer};
}
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 7a728b9041b..f4ca9f51b1b 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -193,6 +193,8 @@ set(SRC
geometry/nodes/legacy/node_geo_raycast.cc
geometry/nodes/legacy/node_geo_select_by_material.cc
geometry/nodes/legacy/node_geo_subdivision_surface.cc
+ geometry/nodes/legacy/node_geo_volume_to_mesh.cc
+
geometry/nodes/node_geo_attribute_capture.cc
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_statistic.cc
@@ -226,11 +228,13 @@ set(SRC
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_distribute_points_on_faces.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_image_texture.cc
geometry/nodes/node_geo_input_curve_handles.cc
geometry/nodes/node_geo_input_curve_tilt.cc
+ geometry/nodes/node_geo_input_id.cc
geometry/nodes/node_geo_input_index.cc
- geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_input_material_index.cc
+ geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_input_normal.cc
geometry/nodes/node_geo_input_position.cc
geometry/nodes/node_geo_input_radius.cc
@@ -269,8 +273,9 @@ set(SRC
geometry/nodes/node_geo_set_curve_handles.cc
geometry/nodes/node_geo_set_curve_radius.cc
geometry/nodes/node_geo_set_curve_tilt.cc
- geometry/nodes/node_geo_set_material.cc
+ geometry/nodes/node_geo_set_id.cc
geometry/nodes/node_geo_set_material_index.cc
+ geometry/nodes/node_geo_set_material.cc
geometry/nodes/node_geo_set_point_radius.cc
geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_set_shade_smooth.cc
@@ -445,6 +450,7 @@ set(SRC
NOD_shader.h
NOD_socket.h
NOD_socket_declarations.hh
+ NOD_socket_declarations_geometry.hh
NOD_static_types.h
NOD_texture.h
NOD_type_conversions.hh
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index a37dcf28757..ea3458af065 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -29,24 +29,25 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_custom_group(bNodeType *ntype);
-void register_node_type_geo_legacy_attribute_transfer(void);
-void register_node_type_geo_legacy_curve_set_handles(void);
void register_node_type_geo_legacy_attribute_proximity(void);
void register_node_type_geo_legacy_attribute_randomize(void);
+void register_node_type_geo_legacy_attribute_transfer(void);
+void register_node_type_geo_legacy_curve_endpoints(void);
+void register_node_type_geo_legacy_curve_reverse(void);
+void register_node_type_geo_legacy_curve_set_handles(void);
+void register_node_type_geo_legacy_curve_spline_type(void);
+void register_node_type_geo_legacy_curve_subdivide(void);
void register_node_type_geo_legacy_curve_to_points(void);
void register_node_type_geo_legacy_delete_geometry(void);
+void register_node_type_geo_legacy_edge_split(void);
void register_node_type_geo_legacy_material_assign(void);
void register_node_type_geo_legacy_mesh_to_curve(void);
void register_node_type_geo_legacy_points_to_volume(void);
-void register_node_type_geo_legacy_select_by_material(void);
-void register_node_type_geo_legacy_curve_endpoints(void);
-void register_node_type_geo_legacy_curve_spline_type(void);
-void register_node_type_geo_legacy_curve_reverse(void);
+void register_node_type_geo_legacy_raycast(void);
void register_node_type_geo_legacy_select_by_handle_type(void);
-void register_node_type_geo_legacy_curve_subdivide(void);
-void register_node_type_geo_legacy_edge_split(void);
+void register_node_type_geo_legacy_select_by_material(void);
void register_node_type_geo_legacy_subdivision_surface(void);
-void register_node_type_geo_legacy_raycast(void);
+void register_node_type_geo_legacy_volume_to_mesh(void);
void register_node_type_geo_align_rotation_to_vector(void);
void register_node_type_geo_attribute_capture(void);
@@ -94,11 +95,13 @@ void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_image_texture(void);
void register_node_type_geo_input_curve_handles(void);
void register_node_type_geo_input_curve_tilt(void);
+void register_node_type_geo_input_id(void);
void register_node_type_geo_input_index(void);
-void register_node_type_geo_input_material(void);
void register_node_type_geo_input_material_index(void);
+void register_node_type_geo_input_material(void);
void register_node_type_geo_input_normal(void);
void register_node_type_geo_input_position(void);
void register_node_type_geo_input_radius(void);
@@ -145,8 +148,9 @@ void register_node_type_geo_separate_geometry(void);
void register_node_type_geo_set_curve_handles(void);
void register_node_type_geo_set_curve_radius(void);
void register_node_type_geo_set_curve_tilt(void);
-void register_node_type_geo_set_material(void);
+void register_node_type_geo_set_id(void);
void register_node_type_geo_set_material_index(void);
+void register_node_type_geo_set_material(void);
void register_node_type_geo_set_point_radius(void);
void register_node_type_geo_set_position(void);
void register_node_type_geo_set_shade_smooth(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 962e1c3c48f..6e1f21dbae0 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -171,10 +171,16 @@ class GeoNodeExecParams {
this->check_input_access(identifier, &CPPType::get<T>());
#endif
GMutablePointer gvalue = this->extract_input(identifier);
- return gvalue.relocate_out<T>();
+ T value = gvalue.relocate_out<T>();
+ if constexpr (std::is_same_v<T, GeometrySet>) {
+ this->check_input_geometry_set(identifier, value);
+ }
+ return value;
}
}
+ void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const;
+
/**
* Get input as vector for multi input socket with the given identifier.
*
@@ -211,7 +217,11 @@ class GeoNodeExecParams {
#endif
GPointer gvalue = provider_->get_input(identifier);
BLI_assert(gvalue.is_type<T>());
- return *(const T *)gvalue.get();
+ const T &value = *(const T *)gvalue.get();
+ if constexpr (std::is_same_v<T, GeometrySet>) {
+ this->check_input_geometry_set(identifier, value);
+ }
+ return value;
}
}
@@ -335,6 +345,8 @@ class GeoNodeExecParams {
const GeometryComponent &component,
const AttributeDomain default_domain) const;
+ std::string attribute_producer_name() const;
+
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 830a7d4070c..2a118057a03 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -76,6 +76,31 @@ class GenericValueLog : public ValueLog {
}
};
+class GFieldValueLog : public ValueLog {
+ private:
+ fn::GField field_;
+ const fn::CPPType &type_;
+ Vector<std::string> input_tooltips_;
+
+ public:
+ GFieldValueLog(fn::GField field, bool log_full_field);
+
+ const fn::GField &field() const
+ {
+ return field_;
+ }
+
+ Span<std::string> input_tooltips() const
+ {
+ return input_tooltips_;
+ }
+
+ const fn::CPPType &type() const
+ {
+ return type_;
+ }
+};
+
struct GeometryAttributeInfo {
std::string name;
AttributeDomain domain;
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index b6e372470c8..e04dd7f41bd 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -200,6 +200,8 @@ class NodeRef : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
+ StringRefNull label() const;
+ StringRefNull label_or_name() const;
bNodeType *typeinfo() const;
const NodeDeclaration *declaration() const;
@@ -285,7 +287,17 @@ class NodeTreeRef : NonCopyable, NonMovable {
RightToLeft,
};
- Vector<const NodeRef *> toposort(ToposortDirection direction) const;
+ struct ToposortResult {
+ Vector<const NodeRef *> sorted_nodes;
+ /**
+ * There can't be a correct topologycal sort of the nodes when there is a cycle. The nodes will
+ * still be sorted to some degree. The caller has to decide whether it can handle non-perfect
+ * sorts or not.
+ */
+ bool has_cycle = false;
+ };
+
+ ToposortResult toposort(ToposortDirection direction) const;
bNodeTree *btree() const;
StringRefNull name() const;
@@ -575,6 +587,20 @@ inline StringRefNull NodeRef::name() const
return bnode_->name;
}
+inline StringRefNull NodeRef::label() const
+{
+ return bnode_->label;
+}
+
+inline StringRefNull NodeRef::label_or_name() const
+{
+ const StringRefNull label = this->label();
+ if (!label.is_empty()) {
+ return label;
+ }
+ return this->name();
+}
+
inline bNodeType *NodeRef::typeinfo() const
{
return bnode_->typeinfo;
diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh
index d4958f433d6..f7aea212f73 100644
--- a/source/blender/nodes/NOD_socket_declarations.hh
+++ b/source/blender/nodes/NOD_socket_declarations.hh
@@ -212,14 +212,6 @@ class Image : public IDSocketDeclaration {
Image();
};
-class Geometry : public SocketDeclaration {
- public:
- using Builder = SocketDeclarationBuilder<Geometry>;
-
- bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override;
- bool matches(const bNodeSocket &socket) const override;
-};
-
/* -------------------------------------------------------------------- */
/** \name #FloatBuilder Inline Methods
* \{ */
@@ -396,9 +388,7 @@ MAKE_EXTERN_SOCKET_DECLARATION(decl::Vector)
MAKE_EXTERN_SOCKET_DECLARATION(decl::Bool)
MAKE_EXTERN_SOCKET_DECLARATION(decl::Color)
MAKE_EXTERN_SOCKET_DECLARATION(decl::String)
-MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry)
-#undef MAKE_EXTERN_SOCKET_DECLARATION
} // namespace blender::nodes
/** \} */
diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh
new file mode 100644
index 00000000000..1531f82d67d
--- /dev/null
+++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BKE_geometry_set.hh"
+
+#include "NOD_socket_declarations.hh"
+
+namespace blender::nodes::decl {
+
+class GeometryBuilder;
+
+class Geometry : public SocketDeclaration {
+ private:
+ blender::Vector<GeometryComponentType> supported_types_;
+ bool only_realized_data_ = false;
+ bool only_instances_ = false;
+
+ friend GeometryBuilder;
+
+ public:
+ using Builder = GeometryBuilder;
+
+ bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override;
+ bool matches(const bNodeSocket &socket) const override;
+
+ Span<GeometryComponentType> supported_types() const;
+ bool only_realized_data() const;
+ bool only_instances() const;
+};
+
+class GeometryBuilder : public SocketDeclarationBuilder<Geometry> {
+ public:
+ GeometryBuilder &supported_type(GeometryComponentType supported_type);
+ GeometryBuilder &supported_type(blender::Vector<GeometryComponentType> supported_types);
+ GeometryBuilder &only_realized_data(bool value = true);
+ GeometryBuilder &only_instances(bool value = true);
+};
+
+} // namespace blender::nodes::decl
+
+namespace blender::nodes {
+MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry)
+}
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index af14538e6cc..20ad4d359f1 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -321,21 +321,18 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to
DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
-DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
+DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "")
-DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
-DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
@@ -343,22 +340,21 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
-DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
-DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
-DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "")
+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_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
-DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
+DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
+DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
+DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "")
@@ -371,8 +367,8 @@ DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", Inst
DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
+DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
@@ -381,7 +377,6 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
@@ -390,29 +385,38 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN
DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
+DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
+DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "")
+DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "")
DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "")
DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "")
DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "")
DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "")
+DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "")
DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "")
+DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "")
+DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 3a4bf94d256..3f8a7606d94 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -29,7 +29,7 @@ namespace blender::nodes {
static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Color>("Image");
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index ad4b09c69d0..028afad3cf8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -29,10 +29,10 @@ namespace blender::nodes {
static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Bright").min(-100.0f).max(100.0f);
- b.add_input<decl::Float>("Contrast").min(-100.0f).max(100.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f);
+ b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index 440e37fe741..ef8af5f81a6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 0682c66f1e8..095fbef826a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Mask").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index 170fecb251c..4247e81e9b2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_composite_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
- b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index 88d96e1ca4a..5f99bb57768 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -29,7 +29,7 @@ namespace blender::nodes {
static void cmp_node_time_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Fac");
+ b.add_output<decl::Float>(N_("Fac"));
}
} // namespace blender::nodes
@@ -90,11 +90,12 @@ namespace blender::nodes {
static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f});
- b.add_input<decl::Color>("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype(
+ PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index fd959376afe..c1e64065f7e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Exposure").min(-10.0f).max(10.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index a29a001688a..74152a27485 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -29,10 +29,13 @@ namespace blender::nodes {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype(
- PROP_UNSIGNED);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Gamma"))
+ .default_value(1.0f)
+ .min(0.001f)
+ .max(10.0f)
+ .subtype(PROP_UNSIGNED);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc
index 07746918a94..21430035465 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc
@@ -29,16 +29,20 @@ namespace blender::nodes {
static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Saturation")
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Saturation"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(2.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index 39014896a7b..83743bbed18 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.cc b/source/blender/nodes/composite/nodes/node_composite_idMask.cc
index de011dd6274..5121370567c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_idMask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc
@@ -29,8 +29,8 @@ namespace blender::nodes {
static void cmp_node_idmask_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("ID value").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Float>("Alpha");
+ b.add_input<decl::Float>(N_("ID value")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Float>(N_("Alpha"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index 57b7ed36ccd..dabf0452628 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Color");
+ b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Color"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index aaab8dcc874..54064f24e0d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_levels_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
- b.add_output<decl::Float>("Mean");
- b.add_output<decl::Float>("Std Dev");
+ b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_output<decl::Float>(N_("Mean"));
+ b.add_output<decl::Float>(N_("Std Dev"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index 8b415bb8b63..6428fadaa5f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -31,7 +31,7 @@ namespace blender::nodes {
static void cmp_node_mask_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Mask");
+ b.add_output<decl::Float>(N_("Mask"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index ae91212f811..5d63a1b8002 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -30,12 +30,12 @@ namespace blender::nodes {
static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Color>("Image");
- b.add_output<decl::Float>("Alpha");
- b.add_output<decl::Float>("Offset X");
- b.add_output<decl::Float>("Offset Y");
- b.add_output<decl::Float>("Scale");
- b.add_output<decl::Float>("Angle");
+ b.add_output<decl::Color>(N_("Image"));
+ b.add_output<decl::Float>(N_("Alpha"));
+ b.add_output<decl::Float>(N_("Offset X"));
+ b.add_output<decl::Float>(N_("Offset Y"));
+ b.add_output<decl::Float>(N_("Scale"));
+ b.add_output<decl::Float>(N_("Angle"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index e557854c611..49068429a8d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -29,8 +29,8 @@ namespace blender::nodes {
static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 332e56e26b1..abe69d6a756 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -29,7 +29,7 @@ namespace blender::nodes {
static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Color>("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc
index aa719a99b36..83c54069658 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc
@@ -29,11 +29,11 @@ namespace blender::nodes {
static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Float>("H");
- b.add_output<decl::Float>("S");
- b.add_output<decl::Float>("V");
- b.add_output<decl::Float>("A");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("H"));
+ b.add_output<decl::Float>(N_("S"));
+ b.add_output<decl::Float>(N_("V"));
+ b.add_output<decl::Float>(N_("A"));
}
} // namespace blender::nodes
@@ -53,11 +53,11 @@ namespace blender::nodes {
static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("H").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("S").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("V").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc
index b29af1359f5..049e798af0a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc
@@ -28,11 +28,11 @@ namespace blender::nodes {
static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Float>("R");
- b.add_output<decl::Float>("G");
- b.add_output<decl::Float>("B");
- b.add_output<decl::Float>("A");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("R"));
+ b.add_output<decl::Float>(N_("G"));
+ b.add_output<decl::Float>(N_("B"));
+ b.add_output<decl::Float>(N_("A"));
}
} // namespace blender::nodes
@@ -53,11 +53,11 @@ namespace blender::nodes {
static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("R").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("G").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("B").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc
index 526d6b4eb5b..eaf6ba5e9b2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc
@@ -29,11 +29,11 @@ namespace blender::nodes {
static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Float>("Y");
- b.add_output<decl::Float>("Cb");
- b.add_output<decl::Float>("Cr");
- b.add_output<decl::Float>("A");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("Y"));
+ b.add_output<decl::Float>(N_("Cb"));
+ b.add_output<decl::Float>(N_("Cr"));
+ b.add_output<decl::Float>(N_("A"));
}
} // namespace blender::nodes
@@ -60,11 +60,11 @@ namespace blender::nodes {
static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Y").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("Cb").default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>("Cr").default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc
index 4619b0c97f1..bc7710122d1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc
@@ -29,11 +29,11 @@ namespace blender::nodes {
static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Float>("Y");
- b.add_output<decl::Float>("U");
- b.add_output<decl::Float>("V");
- b.add_output<decl::Float>("A");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("Y"));
+ b.add_output<decl::Float>(N_("U"));
+ b.add_output<decl::Float>(N_("V"));
+ b.add_output<decl::Float>(N_("A"));
}
} // namespace blender::nodes
@@ -54,11 +54,11 @@ namespace blender::nodes {
static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Y").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("U").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("V").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 07a7ffcb426..f59ba76f0c5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index eff008b4b41..55ae6a4185e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -29,14 +29,14 @@ namespace blender::nodes {
static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Scale")
+ b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Scale"))
.default_value({1.0f, 1.0f, 1.0f})
.min(-10.0f)
.max(10.0f)
.subtype(PROP_XYZ);
- b.add_output<decl::Float>("Value");
- b.add_output<decl::Color>("Color");
+ b.add_output<decl::Float>(N_("Value"));
+ b.add_output<decl::Color>(N_("Color"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index 85fd240ce2e..33d6f98201c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -27,8 +27,8 @@ namespace blender::nodes {
static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index cb5c9468daa..537f7e661db 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("X");
- b.add_output<decl::Float>("Y");
- b.add_output<decl::Vector>("Speed").subtype(PROP_VELOCITY);
+ b.add_output<decl::Float>(N_("X"));
+ b.add_output<decl::Float>(N_("Y"));
+ b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc
index 9e4f1329fbd..a0ab056e657 100644
--- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>("Image");
- b.add_output<decl::Float>("Alpha");
+ b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Image"));
+ b.add_output<decl::Float>(N_("Alpha"));
}
} // namespace blender::nodes
@@ -60,8 +60,8 @@ namespace blender::nodes {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Color>("Val");
+ b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_output<decl::Color>(N_("Val"));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc
index 5459801bcc7..51214d23472 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -29,7 +29,7 @@ namespace blender::nodes {
static void cmp_node_value_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Value").default_value(0.5f);
+ b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index 7234d4d8eb2..b86ae57f664 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -32,9 +32,9 @@ namespace blender::nodes {
static void cmp_node_viewer_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
- b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
index 7f6f554ba93..d98d49c7273 100644
--- a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
@@ -23,10 +23,10 @@ namespace blender::nodes {
static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Float>("Value");
+ b.add_input<decl::Float>(N_("Min")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Float>(N_("Value"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
index ae41cdfca5a..4088fa24ca7 100644
--- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -28,10 +28,14 @@ namespace blender::nodes {
static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value();
- b.add_input<decl::Float>("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Vector>("Vector").default_value({0.0, 0.0, 1.0});
- b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER);
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value();
+ b.add_input<decl::Float>(N_("Factor"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>(N_("Vector")).default_value({0.0, 0.0, 1.0});
+ b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER);
}
static void fn_node_align_euler_to_vector_layout(uiLayout *layout,
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index 09caf12e425..b44e8d54ff1 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Bool>("Boolean", "Boolean");
- b.add_input<decl::Bool>("Boolean", "Boolean_001");
- b.add_output<decl::Bool>("Boolean");
+ b.add_input<decl::Bool>(N_("Boolean"), "Boolean");
+ b.add_input<decl::Bool>(N_("Boolean"), "Boolean_001");
+ b.add_output<decl::Bool>(N_("Boolean"));
};
static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index bdc4a3c1e02..2e1f2aaeeef 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -31,10 +31,10 @@ namespace blender::nodes {
static void fn_node_float_compare_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f);
- b.add_output<decl::Bool>("Result");
+ b.add_input<decl::Float>(N_("A")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("B")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Epsilon")).default_value(0.001f).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Bool>(N_("Result"));
};
static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 5dccd26158b..e6ec925f945 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -30,8 +30,8 @@ namespace blender::nodes {
static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Float");
- b.add_output<decl::Int>("Integer");
+ b.add_input<decl::Float>(N_("Float"));
+ b.add_output<decl::Int>(N_("Integer"));
};
static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
index 58f8969f1b6..1358bf8a223 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
@@ -25,7 +25,7 @@ namespace blender::nodes {
static void fn_node_input_bool_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Boolean");
+ b.add_output<decl::Bool>(N_("Boolean"));
};
static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
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 b6079835eae..43bb654b776 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc
@@ -23,7 +23,7 @@ namespace blender::nodes {
static void fn_node_input_color_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Color>("Color");
+ b.add_output<decl::Color>(N_("Color"));
};
static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc
index db52d569ac5..ddbb86e2661 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc
@@ -25,7 +25,7 @@ namespace blender::nodes {
static void fn_node_input_int_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Integer");
+ b.add_output<decl::Int>(N_("Integer"));
};
static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
index 11c64d3f694..c61af419e50 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
@@ -20,8 +20,8 @@ namespace blender::nodes {
static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::String>("Line Break");
- b.add_output<decl::String>("Tab");
+ b.add_output<decl::String>(N_("Line Break"));
+ b.add_output<decl::String>(N_("Tab"));
};
class MF_SpecialCharacters : public fn::MultiFunction {
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index 0982096eaea..dd2d1292601 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void fn_node_input_string_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_output<decl::String>("String");
+ b.add_output<decl::String>(N_("String"));
};
static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index f64fd182282..1e5fd186b5a 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -25,7 +25,7 @@ namespace blender::nodes {
static void fn_node_input_vector_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Vector");
+ b.add_output<decl::Vector>(N_("Vector"));
};
static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc
index 53ca77aab0c..d48b9f3461a 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_value.cc
+++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc
@@ -26,29 +26,29 @@ namespace blender::nodes {
static void fn_node_random_value_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Min").supports_field();
- b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field();
- b.add_input<decl::Float>("Min", "Min_001").supports_field();
- b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field();
- b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field();
- b.add_input<decl::Int>("Max", "Max_002")
+ b.add_input<decl::Vector>(N_("Min")).supports_field();
+ b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}).supports_field();
+ b.add_input<decl::Float>(N_("Min"), "Min_001").supports_field();
+ b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f).supports_field();
+ b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>(N_("Max"), "Max_002")
.default_value(100)
.min(-100000)
.max(100000)
.supports_field();
- b.add_input<decl::Float>("Probability")
+ b.add_input<decl::Float>(N_("Probability"))
.min(0.0f)
.max(1.0f)
.default_value(0.5f)
.subtype(PROP_FACTOR)
.supports_field();
- b.add_input<decl::Int>("ID").implicit_field();
- b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field();
+ b.add_input<decl::Int>(N_("ID")).implicit_field();
+ b.add_input<decl::Int>(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field();
- b.add_output<decl::Vector>("Value").dependent_field();
- b.add_output<decl::Float>("Value", "Value_001").dependent_field();
- b.add_output<decl::Int>("Value", "Value_002").dependent_field();
- b.add_output<decl::Bool>("Value", "Value_003").dependent_field();
+ b.add_output<decl::Vector>(N_("Value")).dependent_field();
+ b.add_output<decl::Float>(N_("Value"), "Value_001").dependent_field();
+ b.add_output<decl::Int>(N_("Value"), "Value_002").dependent_field();
+ b.add_output<decl::Bool>(N_("Value"), "Value_003").dependent_field();
}
static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc
index 1ec4979176e..881a3c68e7d 100644
--- a/source/blender/nodes/function/nodes/node_fn_replace_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc
@@ -22,10 +22,11 @@ namespace blender::nodes {
static void fn_node_replace_string_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("String");
- b.add_input<decl::String>("Find").description("The string to find in the input string");
- b.add_input<decl::String>("Replace").description("The string to replace each match with");
- b.add_output<decl::String>("String");
+ b.add_input<decl::String>(N_("String"));
+ b.add_input<decl::String>(N_("Find")).description(N_("The string to find in the input string"));
+ b.add_input<decl::String>(N_("Replace"))
+ .description(N_("The string to replace each match with"));
+ b.add_output<decl::String>(N_("String"));
};
static std::string replace_all(std::string str, const std::string &from, const std::string &to)
diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
index a01cc6b58dd..fc4c3d8221f 100644
--- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
+++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
@@ -29,11 +29,11 @@ namespace blender::nodes {
static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value();
- b.add_input<decl::Vector>("Rotate By").subtype(PROP_EULER);
- b.add_input<decl::Vector>("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
- b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
- b.add_output<decl::Vector>("Rotation");
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value();
+ b.add_input<decl::Vector>(N_("Rotate By")).subtype(PROP_EULER);
+ b.add_input<decl::Vector>(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
+ b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE);
+ b.add_output<decl::Vector>(N_("Rotation"));
};
static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node)
diff --git a/source/blender/nodes/function/nodes/node_fn_slice_string.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc
index 08e17da0d92..5cb753e8f34 100644
--- a/source/blender/nodes/function/nodes/node_fn_slice_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc
@@ -22,10 +22,10 @@ namespace blender::nodes {
static void fn_node_slice_string_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("String");
- b.add_input<decl::Int>("Position");
- b.add_input<decl::Int>("Length").min(0).default_value(10);
- b.add_output<decl::String>("String");
+ b.add_input<decl::String>(N_("String"));
+ b.add_input<decl::Int>(N_("Position"));
+ b.add_input<decl::Int>(N_("Length")).min(0).default_value(10);
+ b.add_output<decl::String>(N_("String"));
};
static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder)
diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc
index d882280b566..63429d35993 100644
--- a/source/blender/nodes/function/nodes/node_fn_string_length.cc
+++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc
@@ -24,8 +24,8 @@ namespace blender::nodes {
static void fn_node_string_length_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("String");
- b.add_output<decl::Int>("Length");
+ b.add_input<decl::String>(N_("String"));
+ b.add_output<decl::Int>(N_("Length"));
};
static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder)
diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
index 112726f98dc..96a56760664 100644
--- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc
@@ -21,9 +21,9 @@ namespace blender::nodes {
static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Value");
- b.add_input<decl::Int>("Decimals").min(0);
- b.add_output<decl::String>("String");
+ b.add_input<decl::Float>(N_("Value"));
+ b.add_input<decl::Int>(N_("Decimals")).min(0);
+ b.add_output<decl::String>(N_("String"));
};
static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder)
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index f382ff6c132..167765fa131 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -32,6 +32,7 @@
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
#include "NOD_socket_declarations.hh"
+#include "NOD_socket_declarations_geometry.hh"
#include "node_util.h"
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
index d0bb906e8af..b92d4704d63 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
@@ -26,18 +26,18 @@ namespace blender::nodes {
static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Float>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Float>(N_("Factor"), "Factor_001")
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001")
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001")
.default_value({0.0, 0.0, 1.0})
.subtype(PROP_ANGLE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
index 2e931a2da98..91ff114a480 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
@@ -24,18 +24,18 @@ namespace blender::nodes {
static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_input<decl::Vector>("Min");
- b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Min", "Min_001");
- b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f);
- b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000);
- b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000);
- b.add_input<decl::Color>("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::Color>("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_input<decl::Vector>(N_("Min"));
+ b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Min"), "Min_001");
+ b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f);
+ b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000);
+ b.add_input<decl::Color>(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::Color>(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
index aa054af3acd..ab4b6aad545 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
@@ -27,10 +27,10 @@ namespace blender::nodes {
static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
index 569d5a824ca..d4c23380b4e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
@@ -23,15 +23,15 @@ namespace blender::nodes {
static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("X");
- b.add_input<decl::Float>("X", "X_001");
- b.add_input<decl::String>("Y");
- b.add_input<decl::Float>("Y", "Y_001");
- b.add_input<decl::String>("Z");
- b.add_input<decl::Float>("Z", "Z_001");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("X"));
+ b.add_input<decl::Float>(N_("X"), "X_001");
+ b.add_input<decl::String>(N_("Y"));
+ b.add_input<decl::Float>(N_("Y"), "Y_001");
+ b.add_input<decl::String>(N_("Z"));
+ b.add_input<decl::Float>(N_("Z"), "Z_001");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_combine_xyz_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
index 0b9708dae14..e4e43a7b724 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
@@ -25,18 +25,18 @@ namespace blender::nodes {
static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::Vector>("A", "A_002");
- b.add_input<decl::Color>("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0});
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::Vector>("B", "B_002");
- b.add_input<decl::Color>("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0});
- b.add_input<decl::Float>("Threshold").default_value(0.01f).min(0.0f);
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::Vector>(N_("A"), "A_002");
+ b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0});
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::Vector>(N_("B"), "B_002");
+ b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0});
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.01f).min(0.0f);
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_compare_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
index a2382aa9d25..dc05fa2c125 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
@@ -23,10 +23,10 @@ namespace blender::nodes {
static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_convert_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
index b9621b4ae92..669ac21436f 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_curve_map_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
index 1458b6df9ba..5cb49dd83d0 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
@@ -23,14 +23,14 @@ namespace blender::nodes {
static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute").is_attribute_name();
- b.add_input<decl::Vector>("Value", "Value");
- b.add_input<decl::Float>("Value", "Value_001");
- b.add_input<decl::Color>("Value", "Value_002");
- b.add_input<decl::Bool>("Value", "Value_003");
- b.add_input<decl::Int>("Value", "Value_004");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute")).is_attribute_name();
+ b.add_input<decl::Vector>(N_("Value"), "Value");
+ b.add_input<decl::Float>(N_("Value"), "Value_001");
+ b.add_input<decl::Color>(N_("Value"), "Value_002");
+ b.add_input<decl::Bool>(N_("Value"), "Value_003");
+ b.add_input<decl::Int>(N_("Value"), "Value_004");
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
index 0ea3bbe1e45..978c75187fe 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
@@ -26,21 +26,21 @@ namespace blender::nodes {
static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_input<decl::Float>("From Min");
- b.add_input<decl::Float>("From Max").default_value(1.0f);
- b.add_input<decl::Float>("To Min");
- b.add_input<decl::Float>("To Max").default_value(1.0f);
- b.add_input<decl::Float>("Steps").default_value(4.0f);
- b.add_input<decl::Vector>("From Min", "From Min_001");
- b.add_input<decl::Vector>("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Vector>("To Min", "To Min_001");
- b.add_input<decl::Vector>("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Vector>("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f});
- b.add_input<decl::Bool>("Clamp");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_input<decl::Float>(N_("From Min"));
+ b.add_input<decl::Float>(N_("From Max")).default_value(1.0f);
+ b.add_input<decl::Float>(N_("To Min"));
+ b.add_input<decl::Float>(N_("To Max")).default_value(1.0f);
+ b.add_input<decl::Float>(N_("Steps")).default_value(4.0f);
+ b.add_input<decl::Vector>(N_("From Min"), "From Min_001");
+ b.add_input<decl::Vector>(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Vector>(N_("To Min"), "To Min_001");
+ b.add_input<decl::Vector>(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Vector>(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f});
+ b.add_input<decl::Bool>(N_("Clamp"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
index efa09215b45..55d35f87cda 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
@@ -29,15 +29,15 @@ namespace blender::nodes {
static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::String>("C");
- b.add_input<decl::Float>("C", "C_001");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::String>(N_("C"));
+ b.add_input<decl::Float>(N_("C"), "C_001");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static bool operation_use_input_c(const NodeMathOperation operation)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
index 74e05cb997d..b4205bc91b7 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
@@ -29,23 +29,23 @@ namespace blender::nodes {
static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Float>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Float>(N_("Factor"), "Factor_001")
.default_value(0.5f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::Vector>("A", "A_002");
- b.add_input<decl::Color>("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::Vector>("B", "B_002");
- b.add_input<decl::Color>("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::Vector>(N_("A"), "A_002");
+ b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::Vector>(N_("B"), "B_002");
+ b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
index 6120118f611..9e3a7984c53 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
@@ -30,11 +30,11 @@ namespace blender::nodes {
static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Target");
- b.add_input<decl::String>("Distance");
- b.add_input<decl::String>("Position");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Target"));
+ b.add_input<decl::String>(N_("Distance"));
+ b.add_input<decl::String>(N_("Position"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_proximity_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
index 2e6ba456725..2901472d661 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
@@ -27,16 +27,16 @@ namespace blender::nodes {
static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::Vector>("Min");
- b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Min", "Min_001");
- b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f);
- b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000);
- b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000);
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::Vector>(N_("Min"));
+ b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Min"), "Min_001");
+ b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f);
+ b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_legacy_attribute_random_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
index 52f97475941..19d6ced6eb6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
@@ -32,11 +32,11 @@ namespace blender::nodes {
static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Texture>("Texture").hide_label();
- b.add_input<decl::String>("Mapping");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Texture>(N_("Texture")).hide_label();
+ b.add_input<decl::String>(N_("Mapping"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static AttributeDomain get_result_domain(const GeometryComponent &component,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
index de0090406c6..809e75e73a3 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
@@ -23,13 +23,13 @@ namespace blender::nodes {
static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001");
- b.add_input<decl::String>("Result X");
- b.add_input<decl::String>("Result Y");
- b.add_input<decl::String>("Result Z");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001");
+ b.add_input<decl::String>(N_("Result X"));
+ b.add_input<decl::String>(N_("Result Y"));
+ b.add_input<decl::String>(N_("Result Z"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_separate_xyz_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
index d7a66dac3ad..3a9cd52661a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
@@ -33,11 +33,11 @@ namespace blender::nodes {
static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Source Geometry");
- b.add_input<decl::String>("Source");
- b.add_input<decl::String>("Destination");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Source Geometry"));
+ b.add_input<decl::String>(N_("Source"));
+ b.add_input<decl::String>(N_("Destination"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_transfer_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
index 59903050f88..4c351846243 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
@@ -30,17 +30,17 @@ namespace blender::nodes {
static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Vector>("A", "A_001");
- b.add_input<decl::String>("B");
- b.add_input<decl::Vector>("B", "B_001");
- b.add_input<decl::Float>("B", "B_002");
- b.add_input<decl::String>("C");
- b.add_input<decl::Vector>("C", "C_001");
- b.add_input<decl::Float>("C", "C_002");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Vector>(N_("A"), "A_001");
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Vector>(N_("B"), "B_001");
+ b.add_input<decl::Float>(N_("B"), "B_002");
+ b.add_input<decl::String>(N_("C"));
+ b.add_input<decl::Vector>(N_("C"), "C_001");
+ b.add_input<decl::Float>(N_("C"), "C_002");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static bool operation_use_input_b(const NodeVectorMathOperation operation)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
index 0c515fa63fb..9ab8ec25fb6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
@@ -25,21 +25,21 @@ namespace blender::nodes {
static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value();
- b.add_input<decl::String>("Center");
- b.add_input<decl::Vector>("Center", "Center_001").subtype(PROP_XYZ);
- b.add_input<decl::String>("Axis");
- b.add_input<decl::Vector>("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ);
- b.add_input<decl::String>("Angle");
- b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE);
- b.add_input<decl::String>("Rotation");
- b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER);
- b.add_input<decl::Bool>("Invert");
- b.add_input<decl::String>("Result");
-
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value();
+ b.add_input<decl::String>(N_("Center"));
+ b.add_input<decl::Vector>(N_("Center"), "Center_001").subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Axis"));
+ b.add_input<decl::Vector>(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Angle"));
+ b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE);
+ b.add_input<decl::String>(N_("Rotation"));
+ b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER);
+ b.add_input<decl::Bool>(N_("Invert"));
+ b.add_input<decl::String>(N_("Result"));
+
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
index 85d1392aa35..8b81008ff34 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Start Points");
- b.add_output<decl::Geometry>("End Points");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Start Points"));
+ b.add_output<decl::Geometry>(N_("End Points"));
}
/**
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
index d1c81333c30..ba76fafe3e6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
@@ -24,9 +24,9 @@ namespace blender::nodes {
static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
index 0d3de7ac5f5..40d827ae141 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
index 339029336d9..4bac9cb976e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
@@ -25,9 +25,9 @@ namespace blender::nodes {
static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_set_handles_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
index 44522e990d9..df53c96e6ca 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
index 61165902028..f9b0a9d128e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
@@ -33,10 +33,10 @@ namespace blender::nodes {
static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Cuts");
- b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Cuts"));
+ b.add_input<decl::Int>(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
index 2936c150376..c171d485a6a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
@@ -30,10 +30,10 @@ namespace blender::nodes {
static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000);
+ b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
index 2d9b4da4c83..1d76a0532a1 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
@@ -47,10 +47,10 @@ namespace blender::nodes {
static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Selection");
- b.add_input<decl::Bool>("Invert");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_input<decl::Bool>(N_("Invert"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
index d7e908edf61..8f2bf05d2b4 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
@@ -26,15 +26,15 @@ namespace blender::nodes {
static void geo_node_edge_split_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Edge Angle").default_value(true);
- b.add_input<decl::Float>("Angle")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Edge Angle")).default_value(true);
+ b.add_input<decl::Float>(N_("Angle"))
.default_value(DEG2RADF(30.0f))
.min(0.0f)
.max(DEG2RADF(180.0f))
.subtype(PROP_ANGLE);
- b.add_input<decl::Bool>("Sharp Edges");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Bool>(N_("Sharp Edges"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_edge_split_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc
index 7d3481c1067..333a17aa4e9 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Material").hide_label(true);
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Material>(N_("Material")).hide_label(true);
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
index 7a27e856cef..9167096fd3d 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
@@ -22,9 +22,9 @@ namespace blender::nodes {
static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Mesh"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
index f30feb48734..210757f986d 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
@@ -42,16 +42,16 @@ namespace blender::nodes {
static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Density Max")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Density Max"))
.default_value(1.0f)
.min(0.0f)
.max(100000.0f)
.subtype(PROP_NONE);
- b.add_input<decl::String>("Density Attribute");
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("Density Attribute"));
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_point_distribute_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
index fb45c22ced4..ffb2a0dd7ac 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
@@ -28,12 +28,12 @@ namespace blender::nodes {
static void geo_node_point_instance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Object>("Object").hide_label();
- b.add_input<decl::Collection>("Collection").hide_label();
- b.add_input<decl::Geometry>("Instance Geometry");
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Object>(N_("Object")).hide_label();
+ b.add_input<decl::Collection>(N_("Collection")).hide_label();
+ b.add_input<decl::Geometry>(N_("Instance Geometry"));
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -184,7 +184,7 @@ static void add_instances_from_component(InstancesComponent &instances,
instances.resize(start_len + domain_size);
MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
- MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+ MutableSpan<int> instance_ids = instances.instance_ids_ensure().slice(start_len, domain_size);
/* Skip all of the randomness handling if there is only a single possible instance
* (anything except for collection mode with "Whole Collection" turned off). */
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
index 60c82360007..54d36dab98d 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
@@ -25,14 +25,16 @@ namespace blender::nodes {
static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Axis");
- b.add_input<decl::Vector>("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
- b.add_input<decl::String>("Angle");
- b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE);
- b.add_input<decl::String>("Rotation");
- b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Axis"));
+ b.add_input<decl::Vector>(N_("Axis"), "Axis_001")
+ .default_value({0.0, 0.0, 1.0})
+ .subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Angle"));
+ b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE);
+ b.add_input<decl::String>(N_("Rotation"));
+ b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
index 99adce149e9..934442ee8a3 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
@@ -25,13 +25,13 @@ namespace blender::nodes {
static void geo_node_point_scale_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Vector>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Vector>(N_("Factor"), "Factor_001")
.default_value({1.0f, 1.0f, 1.0f})
.subtype(PROP_XYZ);
- b.add_input<decl::Float>("Factor", "Factor_002").default_value(1.0f).min(0.0f);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
index 48b6676c1dd..accdaf78439 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
@@ -27,10 +27,10 @@ namespace blender::nodes {
static void geo_node_point_instance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Mask");
- b.add_output<decl::Geometry>("Geometry 1");
- b.add_output<decl::Geometry>("Geometry 2");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Mask"));
+ b.add_output<decl::Geometry>(N_("Geometry 1"));
+ b.add_output<decl::Geometry>(N_("Geometry 2"));
}
template<typename T>
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
index f2fce45c57b..34f7641995f 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
@@ -23,10 +23,10 @@ namespace blender::nodes {
static void geo_node_point_translate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Translation");
- b.add_input<decl::Vector>("Translation", "Translation_001").subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Translation"));
+ b.add_input<decl::Vector>(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
index 68d3f232ce1..cf7f466c2a6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
@@ -32,13 +32,13 @@ namespace blender::nodes {
static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
- b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f);
- b.add_input<decl::String>("Radius");
- b.add_input<decl::Float>("Radius", "Radius_001").default_value(0.5f).min(0.0f);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::String>(N_("Radius"));
+ b.add_input<decl::Float>(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_points_to_volume_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
index 6641e622362..e6a81fc9627 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
@@ -28,23 +28,23 @@ namespace blender::nodes {
static void geo_node_raycast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Target Geometry");
- b.add_input<decl::String>("Ray Direction");
- b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Target Geometry"));
+ b.add_input<decl::String>(N_("Ray Direction"));
+ b.add_input<decl::Vector>(N_("Ray Direction"), "Ray Direction_001")
.default_value({0.0f, 0.0f, 1.0f});
- b.add_input<decl::String>("Ray Length");
- b.add_input<decl::Float>("Ray Length", "Ray Length_001")
+ b.add_input<decl::String>(N_("Ray Length"));
+ b.add_input<decl::Float>(N_("Ray Length"), "Ray Length_001")
.default_value(100.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
- b.add_input<decl::String>("Target Attribute");
- b.add_input<decl::String>("Is Hit");
- b.add_input<decl::String>("Hit Position");
- b.add_input<decl::String>("Hit Normal");
- b.add_input<decl::String>("Hit Distance");
- b.add_input<decl::String>("Hit Attribute");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("Target Attribute"));
+ b.add_input<decl::String>(N_("Is Hit"));
+ b.add_input<decl::String>(N_("Hit Position"));
+ b.add_input<decl::String>(N_("Hit Normal"));
+ b.add_input<decl::String>(N_("Hit Distance"));
+ b.add_input<decl::String>(N_("Hit Attribute"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc
index eabdd2bcd5a..a8d6f33a5fd 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc
@@ -30,10 +30,10 @@ namespace blender::nodes {
static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Material>(N_("Material")).hide_label();
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void select_mesh_by_material(const Mesh &mesh,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
index 101c915eb77..295cd05fd01 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
@@ -27,10 +27,10 @@ namespace blender::nodes {
static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
- b.add_input<decl::Bool>("Use Creases");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_input<decl::Bool>(N_("Use Creases"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_subdivision_surface_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc
new file mode 100644
index 00000000000..39af5bf1fd2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc
@@ -0,0 +1,173 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DEG_depsgraph_query.h"
+#ifdef WITH_OPENVDB
+# include <openvdb/tools/GridTransformer.h>
+# include <openvdb/tools/VolumeToMesh.h>
+#endif
+
+#include "node_geometry_util.hh"
+
+#include "BKE_lib_id.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_volume.h"
+#include "BKE_volume_to_mesh.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Density"));
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f);
+ b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
+}
+
+static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN(
+ sizeof(NodeGeometryVolumeToMesh), __func__);
+ data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
+
+ bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
+ bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
+ STRNCPY(grid_socket_value->value, "density");
+
+ node->storage = data;
+}
+
+static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage;
+
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT);
+ nodeSetSocketAvailability(voxel_size_socket,
+ data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
+}
+
+#ifdef WITH_OPENVDB
+
+static void create_mesh_from_volume(GeometrySet &geometry_set_in,
+ GeometrySet &geometry_set_out,
+ GeoNodeExecParams &params)
+{
+ if (!geometry_set_in.has<VolumeComponent>()) {
+ return;
+ }
+
+ const NodeGeometryVolumeToMesh &storage =
+ *(const NodeGeometryVolumeToMesh *)params.node().storage;
+
+ bke::VolumeToMeshResolution resolution;
+ resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
+ if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
+ resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
+ if (resolution.settings.voxel_amount <= 0.0f) {
+ return;
+ }
+ }
+ else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
+ resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
+ if (resolution.settings.voxel_size <= 0.0f) {
+ return;
+ }
+ }
+
+ const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
+ const Volume *volume = component->get_for_read();
+ if (volume == nullptr) {
+ return;
+ }
+
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
+
+ const std::string grid_name = params.get_input<std::string>("Density");
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
+ if (volume_grid == nullptr) {
+ return;
+ }
+
+ float threshold = params.get_input<float>("Threshold");
+ float adaptivity = params.get_input<float>("Adaptivity");
+
+ const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity);
+ if (mesh == nullptr) {
+ return;
+ }
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
+ dst_component.replace(mesh);
+}
+
+#endif /* WITH_OPENVDB */
+
+static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+#ifdef WITH_OPENVDB
+ create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
+#else
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+#endif
+
+ params.set_output("Geometry", geometry_set_out);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_volume_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare;
+ node_type_storage(
+ &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
+ node_type_size(&ntype, 170, 120, 700);
+ node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init);
+ node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index b7352160f89..5cc8f1476f8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -25,19 +25,19 @@ namespace blender::nodes {
static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Value").supports_field();
- b.add_input<decl::Float>("Value", "Value_001").supports_field();
- b.add_input<decl::Color>("Value", "Value_002").supports_field();
- b.add_input<decl::Bool>("Value", "Value_003").supports_field();
- b.add_input<decl::Int>("Value", "Value_004").supports_field();
-
- b.add_output<decl::Geometry>("Geometry");
- b.add_output<decl::Vector>("Attribute").field_source();
- b.add_output<decl::Float>("Attribute", "Attribute_001").field_source();
- b.add_output<decl::Color>("Attribute", "Attribute_002").field_source();
- b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source();
- b.add_output<decl::Int>("Attribute", "Attribute_004").field_source();
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Vector>(N_("Value")).supports_field();
+ b.add_input<decl::Float>(N_("Value"), "Value_001").supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_003").supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_004").supports_field();
+
+ b.add_output<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Vector>(N_("Attribute")).field_source();
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").field_source();
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").field_source();
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").field_source();
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").field_source();
}
static void geo_node_attribute_capture_layout(uiLayout *layout,
@@ -144,7 +144,7 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
break;
}
- WeakAnonymousAttributeID anonymous_id{"Attribute Capture"};
+ WeakAnonymousAttributeID anonymous_id{"Attribute"};
const CPPType &type = field.cpp_type();
static const Array<GeometryComponentType> types = {
@@ -158,8 +158,8 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
}
});
- GField output_field{
- std::make_shared<bke::AnonymousAttributeFieldInput>(std::move(anonymous_id), type)};
+ GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>(
+ std::move(anonymous_id), type, params.attribute_producer_name())};
switch (data_type) {
case CD_PROP_FLOAT: {
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 f93ef6f1db3..f80b8ccc971 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -20,9 +20,9 @@ namespace blender::nodes {
static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute").multi_input();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute")).multi_input();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void remove_attribute(GeometryComponent &component,
@@ -59,6 +59,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
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);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 1b7d2fe28a1..155bd8c8c28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -28,27 +28,27 @@ namespace blender::nodes {
static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Attribute").hide_value().supports_field();
- b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field();
-
- b.add_output<decl::Float>("Mean");
- b.add_output<decl::Float>("Median");
- b.add_output<decl::Float>("Sum");
- b.add_output<decl::Float>("Min");
- b.add_output<decl::Float>("Max");
- b.add_output<decl::Float>("Range");
- b.add_output<decl::Float>("Standard Deviation");
- b.add_output<decl::Float>("Variance");
-
- b.add_output<decl::Vector>("Mean", "Mean_001");
- b.add_output<decl::Vector>("Median", "Median_001");
- b.add_output<decl::Vector>("Sum", "Sum_001");
- b.add_output<decl::Vector>("Min", "Min_001");
- b.add_output<decl::Vector>("Max", "Max_001");
- b.add_output<decl::Vector>("Range", "Range_001");
- b.add_output<decl::Vector>("Standard Deviation", "Standard Deviation_001");
- b.add_output<decl::Vector>("Variance", "Variance_001");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+
+ b.add_output<decl::Float>(N_("Mean"));
+ b.add_output<decl::Float>(N_("Median"));
+ b.add_output<decl::Float>(N_("Sum"));
+ b.add_output<decl::Float>(N_("Min"));
+ b.add_output<decl::Float>(N_("Max"));
+ b.add_output<decl::Float>(N_("Range"));
+ b.add_output<decl::Float>(N_("Standard Deviation"));
+ b.add_output<decl::Float>(N_("Variance"));
+
+ b.add_output<decl::Vector>(N_("Mean"), "Mean_001");
+ b.add_output<decl::Vector>(N_("Median"), "Median_001");
+ b.add_output<decl::Vector>(N_("Sum"), "Sum_001");
+ b.add_output<decl::Vector>(N_("Min"), "Min_001");
+ b.add_output<decl::Vector>(N_("Max"), "Max_001");
+ b.add_output<decl::Vector>(N_("Range"), "Range_001");
+ b.add_output<decl::Vector>(N_("Standard Deviation"), "Standard Deviation_001");
+ b.add_output<decl::Vector>(N_("Variance"), "Variance_001");
}
static void geo_node_attribute_statistic_layout(uiLayout *layout,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 27e18f16bad..516f07b7ad3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -27,11 +27,13 @@ namespace blender::nodes {
static void geo_node_boolean_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry 1");
- b.add_input<decl::Geometry>("Geometry 2").multi_input();
- b.add_input<decl::Bool>("Self Intersection");
- b.add_input<decl::Bool>("Hole Tolerant");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh 1"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Geometry>(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Self Intersection"));
+ b.add_input<decl::Bool>(N_("Hole Tolerant"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -51,12 +53,12 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
case GEO_NODE_BOOLEAN_UNION:
nodeSetSocketAvailability(geometry_1_socket, false);
nodeSetSocketAvailability(geometry_2_socket, true);
- node_sock_label(geometry_2_socket, N_("Geometry"));
+ node_sock_label(geometry_2_socket, N_("Mesh"));
break;
case GEO_NODE_BOOLEAN_DIFFERENCE:
nodeSetSocketAvailability(geometry_1_socket, true);
nodeSetSocketAvailability(geometry_2_socket, true);
- node_sock_label(geometry_2_socket, N_("Geometry 2"));
+ node_sock_label(geometry_2_socket, N_("Mesh 2"));
break;
}
}
@@ -82,12 +84,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
GeometrySet set_a;
if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
- set_a = params.extract_input<GeometrySet>("Geometry 1");
- if (set_a.has_instances()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("Instances are not supported for the first geometry input, and will not be used"));
- }
+ set_a = params.extract_input<GeometrySet>("Mesh 1");
/* Note that it technically wouldn't be necessary to realize the instances for the first
* geometry input, but the boolean code expects the first shape for the difference operation
* to be a single mesh. */
@@ -101,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
/* The instance transform matrices are owned by the instance group, so we have to
* keep all of them around for use during the boolean operation. */
Vector<bke::GeometryInstanceGroup> set_groups;
- Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2");
+ Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2");
for (const GeometrySet &geometry_set : geometry_sets) {
bke::geometry_set_gather_instances(geometry_set, set_groups);
}
@@ -119,7 +116,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
Mesh *result = blender::meshintersect::direct_mesh_boolean(
meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation);
- params.set_output("Geometry", GeometrySet::create_with_mesh(result));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(result));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index e34b65e6c94..e7c9715934a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -20,10 +20,10 @@ namespace blender::nodes {
static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Bounding Box");
- b.add_output<decl::Vector>("Min");
- b.add_output<decl::Vector>("Max");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Bounding Box"));
+ b.add_output<decl::Vector>(N_("Min"));
+ b.add_output<decl::Vector>(N_("Max"));
}
static void geo_node_bounding_box_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index eca4e3d2d14..f068e621596 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -31,15 +31,15 @@ namespace blender::nodes {
static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Collection>("Collection").hide_label();
- b.add_input<decl::Bool>("Separate Children")
+ b.add_input<decl::Collection>(N_("Collection")).hide_label();
+ b.add_input<decl::Bool>(N_("Separate Children"))
.description(
- "Output each child of the collection as a separate instance, sorted alphabetically");
- b.add_input<decl::Bool>("Reset Children")
+ N_("Output each child of the collection as a separate instance, sorted alphabetically"));
+ b.add_input<decl::Bool>(N_("Reset Children"))
.description(
- "Reset the transforms of every child instance in the output. Only used when Separate "
- "Children is enabled");
- b.add_output<decl::Geometry>("Geometry");
+ N_("Reset the transforms of every child instance in the output. Only used when Separate "
+ "Children is enabled"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -71,6 +71,14 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set_out);
return;
}
+ const Object *self_object = params.self_object();
+ const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection,
+ (Object *)self_object);
+ if (is_recursive) {
+ params.error_message_add(NodeWarningType::Error, "Collection contains current object");
+ params.set_output("Geometry", geometry_set_out);
+ return;
+ }
const bNode &bnode = params.node();
NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
@@ -79,8 +87,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- const Object *self_object = params.self_object();
-
const bool separate_children = params.get_input<bool>("Separate Children");
if (separate_children) {
const bool reset_children = params.get_input<bool>("Reset Children");
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 4377d32210d..3cf682e161c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -32,8 +32,8 @@ namespace blender::nodes {
static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Convex Hull");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Convex Hull"));
}
using bke::GeometryInstanceGroup;
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 ee6cf055ecb..42d88cdb1e7 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
@@ -25,19 +25,20 @@ namespace blender::nodes {
static void geo_node_curve_endpoint_selection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Start Size")
+ b.add_input<decl::Int>(N_("Start Size"))
.min(0)
.default_value(1)
.supports_field()
- .description("The amount of points to select from the start of each spline");
- b.add_input<decl::Int>("End Size")
+ .description(N_("The amount of points to select from the start of each spline"));
+ b.add_input<decl::Int>(N_("End Size"))
.min(0)
.default_value(1)
.supports_field()
- .description("The amount of points to select from the end of each spline");
- b.add_output<decl::Bool>("Selection")
+ .description(N_("The amount of points to select from the end of each spline"));
+ b.add_output<decl::Bool>(N_("Selection"))
.field_source()
- .description("The selection from the start and end of the splines based on the input sizes");
+ .description(
+ N_("The selection from the start and end of the splines based on the input sizes"));
}
static void select_by_spline(const int start, const int end, MutableSpan<bool> r_selection)
@@ -56,8 +57,11 @@ class EndpointFieldInput final : public fn::FieldInput {
public:
EndpointFieldInput(Field<int> start_size, Field<int> end_size)
- : FieldInput(CPPType::get<bool>(), "Selection"), start_size_(start_size), end_size_(end_size)
+ : FieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
+ start_size_(start_size),
+ end_size_(end_size)
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
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 953922531c1..219effadec4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -35,8 +35,8 @@ namespace blender::nodes {
static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
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 67ce5b00d6b..27d7d22b106 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -29,16 +29,16 @@ namespace blender::nodes {
static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Int>("Count").default_value(1).min(1).max(1000).supports_field();
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Count")).default_value(1).min(1).max(1000).supports_field();
+ b.add_input<decl::Float>(N_("Radius"))
.min(0.0f)
.max(FLT_MAX)
.subtype(PropertySubType::PROP_DISTANCE)
.default_value(0.25f)
.supports_field();
- b.add_input<decl::Bool>("Limit Radius");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Bool>(N_("Limit Radius"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
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 b565b1e4602..165f5da5f71 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
@@ -25,7 +25,7 @@ namespace blender::nodes {
static void geo_node_curve_handle_type_selection_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Selection").field_source();
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
}
static void geo_node_curve_handle_type_selection_layout(uiLayout *layout,
@@ -91,8 +91,9 @@ class HandleTypeFieldInput final : public fn::FieldInput {
public:
HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode)
- : FieldInput(CPPType::get<bool>(), "Selection"), type_(type), mode_(mode)
+ : FieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode)
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
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 ac7df35bb72..0d0dc0ec89c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -21,8 +21,8 @@ namespace blender::nodes {
static void geo_node_curve_length_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_output<decl::Float>("Length");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Float>(N_("Length"));
}
static void geo_node_curve_length_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
index 90853387ec7..4c89aba2e6d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Factor").field_source();
+ b.add_output<decl::Float>(N_("Factor")).field_source();
}
/**
@@ -150,8 +150,9 @@ static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve,
class CurveParameterFieldInput final : public fn::FieldInput {
public:
- CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter")
+ CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter node")
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
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 313473e3442..a755d47cc6a 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
@@ -25,14 +25,20 @@ namespace blender::nodes {
static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Start Handle")
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(1)
+ .max(256)
+ .subtype(PROP_UNSIGNED);
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Start Handle"))
.default_value({-0.5f, 0.5f, 0.0f})
.subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End Handle").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Vector>(N_("End Handle")).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout,
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 8d5f4855512..bf4f22d6578 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
@@ -25,15 +25,19 @@ namespace blender::nodes {
static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(32).min(3).max(512);
- b.add_input<decl::Vector>("Point 1")
+ b.add_input<decl::Int>(N_("Resolution")).default_value(32).min(3).max(512);
+ b.add_input<decl::Vector>(N_("Point 1"))
.default_value({-1.0f, 0.0f, 0.0f})
.subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
- b.add_output<decl::Vector>("Center");
+ b.add_input<decl::Vector>(N_("Point 2"))
+ .default_value({0.0f, 1.0f, 0.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Point 3"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Vector>(N_("Center"));
}
static void geo_node_curve_primitive_circle_layout(uiLayout *layout,
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 a3d2ada612f..5b215797052 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
@@ -25,11 +25,11 @@ namespace blender::nodes {
static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Start").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Direction").default_value({0.0f, 0.0f, 1.0f});
- b.add_input<decl::Float>("Length").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Vector>(N_("Start")).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("End")).default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Direction")).default_value({0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Length")).default_value(1.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_primitive_line_layout(uiLayout *layout,
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 a54fd971ac4..6041ddee02d 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
@@ -21,11 +21,19 @@ namespace blender::nodes {
static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(3)
+ .max(256)
+ .subtype(PROP_UNSIGNED);
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Middle"))
+ .default_value({0.0f, 2.0f, 0.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1,
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 07ddaa8f61e..7260da05a8d 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
@@ -23,18 +23,32 @@ namespace blender::nodes {
static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Offset").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Top Height").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Float>(N_("Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Height")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Bottom Width"))
+ .default_value(4.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Top Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Offset")).default_value(1.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Bottom Height"))
+ .default_value(3.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Top Height")).default_value(1.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Vector>(N_("Point 1"))
+ .default_value({-1.0f, -1.0f, 0.0f})
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Vector>(N_("Point 2"))
+ .default_value({1.0f, -1.0f, 0.0f})
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Vector>(N_("Point 3"))
+ .default_value({1.0f, 1.0f, 0.0f})
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Vector>(N_("Point 4"))
+ .default_value({-1.0f, 1.0f, 0.0f})
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout,
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 7292fafc8b0..1dc9cd7f107 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
@@ -22,13 +22,17 @@ namespace blender::nodes {
static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED);
- b.add_input<decl::Float>("Rotations").default_value(2.0f).min(0.0f);
- b.add_input<decl::Float>("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("End Radius").default_value(2.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Height").default_value(2.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Bool>("Reverse");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(32)
+ .min(1)
+ .max(1024)
+ .subtype(PROP_UNSIGNED);
+ b.add_input<decl::Float>(N_("Rotations")).default_value(2.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Start Radius")).default_value(1.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("End Radius")).default_value(2.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Height")).default_value(2.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Bool>(N_("Reverse"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
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 6261146562d..b5bafce17c6 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
@@ -22,11 +22,17 @@ namespace blender::nodes {
static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Float>("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Twist").subtype(PROP_ANGLE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Points")).default_value(8).min(3).max(256).subtype(PROP_UNSIGNED);
+ b.add_input<decl::Float>(N_("Inner Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Outer Radius"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Twist")).subtype(PROP_ANGLE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius,
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 2617b2f6646..945dac5650b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -34,11 +34,14 @@ namespace blender::nodes {
static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000).supports_field();
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).supports_field().subtype(
- PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(100000).supports_field();
+ b.add_input<decl::Float>(N_("Length"))
+ .default_value(0.1f)
+ .min(0.001f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -79,7 +82,7 @@ static SplinePtr resample_spline(const Spline &src, const int count)
Spline::copy_base_settings(src, *dst);
if (src.evaluated_edges_size() < 1 || count == 1) {
- dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first());
+ dst->add_point(src.positions().first(), src.radii().first(), src.tilts().first());
dst->attributes.reallocate(1);
src.attributes.foreach_attribute(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
@@ -234,7 +237,7 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set,
static void geo_node_resample_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
@@ -244,7 +247,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
Field<int> count = params.extract_input<Field<int>>("Count");
if (count < 1) {
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Curve", GeometrySet());
return;
}
mode_param.count.emplace(count);
@@ -257,7 +260,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); });
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index d4ccb768713..745012c1851 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -24,9 +24,9 @@ namespace blender::nodes {
static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
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 6d371c27d43..31b38c0dce7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -27,13 +27,15 @@ namespace blender::nodes {
static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
- b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field();
-
- b.add_output<decl::Vector>("Position").dependent_field();
- b.add_output<decl::Vector>("Tangent").dependent_field();
- b.add_output<decl::Vector>("Normal").dependent_field();
+ b.add_input<decl::Geometry>(N_("Curve"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Float>(N_("Factor")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
+ b.add_input<decl::Float>(N_("Length")).min(0.0f).subtype(PROP_DISTANCE).supports_field();
+
+ b.add_output<decl::Vector>(N_("Position")).dependent_field();
+ b.add_output<decl::Vector>(N_("Tangent")).dependent_field();
+ b.add_output<decl::Vector>(N_("Normal")).dependent_field();
}
static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -237,12 +239,6 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params)
params.set_output("Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
};
- if (geometry_set.has_instances()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("The node only supports realized curve data, instances are ignored"));
- }
-
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
if (component == nullptr) {
return return_default();
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 9e7ac60c29d..8b0a6ca840c 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
@@ -25,9 +25,9 @@ namespace blender::nodes {
static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_set_handles_layout(uiLayout *layout,
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 ec72154db13..ae4453929ac 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
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_spline_type_layout(uiLayout *layout,
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 5a79fcffd4d..b52de822c22 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -33,9 +33,9 @@ namespace blender::nodes {
static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Cuts").default_value(1).min(0).max(1000).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static Array<int> get_subdivided_offsets(const Spline &spline,
@@ -328,7 +328,7 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<int> cuts_field = params.extract_input<Field<int>>("Cuts");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
@@ -352,7 +352,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
geometry_set.replace_curve(output_curve.release());
});
- params.set_output("Geometry", geometry_set);
+ params.set_output("Curve", geometry_set);
}
} // namespace blender::nodes
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 00451946af9..1977b465de4 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,21 +27,29 @@ namespace blender::nodes {
static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Geometry>("Profile Curve");
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Geometry>(N_("Profile Curve"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Fill Caps"))
+ .description(
+ N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set)
+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();
if (profile_curve == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read());
+ Mesh *mesh = bke::curve_to_wire_mesh(*curve);
geometry_set.replace_mesh(mesh);
}
else {
- Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve);
+ Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
geometry_set.replace_mesh(mesh);
}
}
@@ -50,33 +58,17 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
-
- if (profile_set.has_instances()) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("Instances are not supported in the profile input"));
- params.set_output("Mesh", GeometrySet());
- return;
- }
-
- if (!profile_set.has_curve() && !profile_set.is_empty()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in the profile input"));
- }
+ const bool fill_caps = params.extract_input<bool>("Fill Caps");
bool has_curve = false;
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_curve()) {
has_curve = true;
- geometry_set_curve_to_mesh(geometry_set, profile_set);
+ geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
}
geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
});
- if (!has_curve && !curve_set.is_empty()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in curve input"));
- }
-
params.set_output("Mesh", std::move(curve_set));
}
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 4f4fde6c7df..38d7fb99e87 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
@@ -30,13 +30,13 @@ namespace blender::nodes {
static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Points");
- b.add_output<decl::Vector>("Tangent").field_source();
- b.add_output<decl::Vector>("Normal").field_source();
- b.add_output<decl::Vector>("Rotation").field_source();
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000);
+ b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Points"));
+ b.add_output<decl::Vector>(N_("Tangent")).field_source();
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
+ b.add_output<decl::Vector>(N_("Rotation")).field_source();
}
static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -357,17 +357,20 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
if (attribute_outputs.tangent_id) {
params.set_output(
"Tangent",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id),
+ params.attribute_producer_name()));
}
if (attribute_outputs.normal_id) {
params.set_output(
"Normal",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
+ params.attribute_producer_name()));
}
if (attribute_outputs.rotation_id) {
params.set_output(
"Rotation",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
+ params.attribute_producer_name()));
}
}
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 6b049b4d384..4e1a2910c7c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -28,21 +28,24 @@ namespace blender::nodes {
static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
- b.add_input<decl::Float>("End")
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Float>(N_("Start")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
+ b.add_input<decl::Float>(N_("End"))
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR)
.supports_field();
- b.add_input<decl::Float>("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE).supports_field();
- b.add_input<decl::Float>("End", "End_001")
+ b.add_input<decl::Float>(N_("Start"), "Start_001")
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_input<decl::Float>(N_("End"), "End_001")
.min(0.0f)
.default_value(1.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
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 e293cd9b8fe..e0a3faaefb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -50,13 +50,13 @@ namespace blender::nodes {
static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection"))
.default_value(true)
.hide_value()
.supports_field()
- .description("The parts of the geometry to be deleted");
- b.add_output<decl::Geometry>("Geometry");
+ .description(N_("The parts of the geometry to be deleted"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_delete_geometry_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
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 0d481011f00..fa439b04da0 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
@@ -42,22 +42,22 @@ namespace blender::nodes {
static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f);
- b.add_input<decl::Float>("Density").default_value(10.0f).supports_field();
- b.add_input<decl::Float>("Density Factor")
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Density")).default_value(10.0f).supports_field();
+ b.add_input<decl::Float>(N_("Density Factor"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR)
.supports_field();
- b.add_input<decl::Int>("Seed");
+ b.add_input<decl::Int>(N_("Seed"));
- b.add_output<decl::Geometry>("Points");
- b.add_output<decl::Vector>("Normal").field_source();
- b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
+ b.add_output<decl::Geometry>(N_("Points"));
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
+ b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_source();
}
static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
@@ -533,7 +533,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
const GeometryNodeDistributePointsOnFacesMode method =
static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1);
@@ -543,10 +543,10 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
AttributeOutputs attribute_outputs;
if (params.output_is_required("Normal")) {
- attribute_outputs.normal_id = StrongAnonymousAttributeID("normal");
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
}
if (params.output_is_required("Rotation")) {
- attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
@@ -562,12 +562,14 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
if (attribute_outputs.normal_id) {
params.set_output(
"Normal",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
+ params.attribute_producer_name()));
}
if (attribute_outputs.rotation_id) {
params.set_output(
"Rotation",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
+ params.attribute_producer_name()));
}
}
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 e97fc5c2c83..f562fb29e90 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -26,9 +26,9 @@ namespace blender::nodes {
static void geo_node_edge_split_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
new file mode 100644
index 00000000000..e1c72fbd438
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
@@ -0,0 +1,429 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BKE_image.h"
+
+#include "BLI_float4.hh"
+#include "BLI_threads.h"
+#include "BLI_timeit.hh"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+static void geo_node_image_texture_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Image>(N_("Image")).hide_label();
+ b.add_input<decl::Vector>(N_("Vector"))
+ .implicit_field()
+ .description(("Texture coordinates from 0 to 1"));
+ b.add_input<decl::Int>(N_("Frame")).min(0).max(MAXFRAMEF);
+ b.add_output<decl::Color>(N_("Color")).no_muted_links().dependent_field();
+ b.add_output<decl::Float>(N_("Alpha")).no_muted_links().dependent_field();
+}
+
+static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
+ uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
+}
+
+static void geo_node_image_texture_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN(
+ sizeof(NodeGeometryImageTexture), __func__);
+ node->storage = tex;
+}
+
+class ImageFieldsFunction : public fn::MultiFunction {
+ private:
+ const int interpolation_;
+ const int extension_;
+ Image &image_;
+ ImageUser image_user_;
+ void *image_lock_;
+ ImBuf *image_buffer_;
+
+ public:
+ ImageFieldsFunction(const int interpolation,
+ const int extension,
+ Image &image,
+ ImageUser image_user)
+ : interpolation_(interpolation),
+ extension_(extension),
+ image_(image),
+ image_user_(image_user)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+
+ image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_);
+ if (image_buffer_ == nullptr) {
+ throw std::runtime_error("cannot aquire image buffer");
+ }
+
+ if (image_buffer_->rect_float == nullptr) {
+ BLI_thread_lock(LOCK_IMAGE);
+ if (!image_buffer_->rect_float) {
+ IMB_float_from_rect(image_buffer_);
+ }
+ BLI_thread_unlock(LOCK_IMAGE);
+ }
+
+ if (image_buffer_->rect_float == nullptr) {
+ BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
+ throw std::runtime_error("cannot get float buffer");
+ }
+ }
+
+ ~ImageFieldsFunction() override
+ {
+ BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"ImageFunction"};
+ signature.single_input<float3>("Vector");
+ signature.single_output<ColorGeometry4f>("Color");
+ signature.single_output<float>("Alpha");
+ return signature.build();
+ }
+
+ static int wrap_periodic(int x, const int width)
+ {
+ x %= width;
+ if (x < 0) {
+ x += width;
+ }
+ return x;
+ }
+
+ static int wrap_clamp(const int x, const int width)
+ {
+ return std::clamp(x, 0, width - 1);
+ }
+
+ static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py)
+ {
+ if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x];
+ }
+
+ static float frac(const float x, int *ix)
+ {
+ const int i = (int)x - ((x < 0.0f) ? 1 : 0);
+ *ix = i;
+ return x - (float)i;
+ }
+
+ static float4 image_cubic_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int pix, piy, nix, niy;
+ const float tx = frac(px * (float)width - 0.5f, &pix);
+ const float ty = frac(py * (float)height - 0.5f, &piy);
+ int ppix, ppiy, nnix, nniy;
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_REPEAT: {
+ pix = wrap_periodic(pix, width);
+ piy = wrap_periodic(piy, height);
+ ppix = wrap_periodic(pix - 1, width);
+ ppiy = wrap_periodic(piy - 1, height);
+ nix = wrap_periodic(pix + 1, width);
+ niy = wrap_periodic(piy + 1, height);
+ nnix = wrap_periodic(pix + 2, width);
+ nniy = wrap_periodic(piy + 2, height);
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ ppix = pix - 1;
+ ppiy = piy - 1;
+ nix = pix + 1;
+ niy = piy + 1;
+ nnix = pix + 2;
+ nniy = piy + 2;
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ ppix = wrap_clamp(pix - 1, width);
+ ppiy = wrap_clamp(piy - 1, height);
+ nix = wrap_clamp(pix + 1, width);
+ niy = wrap_clamp(piy + 1, height);
+ nnix = wrap_clamp(pix + 2, width);
+ nniy = wrap_clamp(piy + 2, height);
+ pix = wrap_clamp(pix, width);
+ piy = wrap_clamp(piy, height);
+ break;
+ }
+ default:
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+
+ const int xc[4] = {ppix, pix, nix, nnix};
+ const int yc[4] = {ppiy, piy, niy, nniy};
+ float u[4], v[4];
+
+ u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f);
+ u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f);
+ u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f);
+ u[3] = (1.0f / 6.0f) * tx * tx * tx;
+
+ v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f);
+ v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f);
+ v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f);
+ v[3] = (1.0f / 6.0f) * ty * ty * ty;
+
+ return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) +
+ (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) +
+ (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) +
+ (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3]))));
+ }
+
+ static float4 image_linear_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int pix, piy, nix, niy;
+ const float nfx = frac(px * (float)width - 0.5f, &pix);
+ const float nfy = frac(py * (float)height - 0.5f, &piy);
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ nix = pix + 1;
+ niy = piy + 1;
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ nix = wrap_clamp(pix + 1, width);
+ niy = wrap_clamp(piy + 1, height);
+ pix = wrap_clamp(pix, width);
+ piy = wrap_clamp(piy, height);
+ break;
+ }
+ default:
+ case SHD_IMAGE_EXTENSION_REPEAT:
+ pix = wrap_periodic(pix, width);
+ piy = wrap_periodic(piy, height);
+ nix = wrap_periodic(pix + 1, width);
+ niy = wrap_periodic(piy + 1, height);
+ break;
+ }
+
+ const float ptx = 1.0f - nfx;
+ const float pty = 1.0f - nfy;
+
+ return image_pixel_lookup(ibuf, pix, piy) * ptx * pty +
+ image_pixel_lookup(ibuf, nix, piy) * nfx * pty +
+ image_pixel_lookup(ibuf, pix, niy) * ptx * nfy +
+ image_pixel_lookup(ibuf, nix, niy) * nfx * nfy;
+ }
+
+ static float4 image_closest_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int ix, iy;
+ const float tx = frac(px * (float)width - 0.5f, &ix);
+ const float ty = frac(py * (float)height - 0.5f, &iy);
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_REPEAT: {
+ ix = wrap_periodic(ix, width);
+ iy = wrap_periodic(iy, height);
+ return image_pixel_lookup(ibuf, ix, iy);
+ }
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ if (ix < 0 || iy < 0 || ix > width || iy > height) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ ATTR_FALLTHROUGH;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ ix = wrap_clamp(ix, width);
+ iy = wrap_clamp(iy, height);
+ return image_pixel_lookup(ibuf, ix, iy);
+ }
+ default:
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector");
+ MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>(
+ 1, "Color");
+ MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha");
+
+ MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()};
+
+ /* Sample image texture. */
+ switch (interpolation_) {
+ case SHD_INTERP_LINEAR:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ case SHD_INTERP_CLOSEST:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ case SHD_INTERP_CUBIC:
+ case SHD_INTERP_SMART:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ }
+
+ int alpha_mode = image_.alpha_mode;
+ if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) {
+ alpha_mode = IMA_ALPHA_CHANNEL_PACKED;
+ }
+
+ switch (alpha_mode) {
+ case IMA_ALPHA_STRAIGHT: {
+ /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */
+ for (int64_t i : mask) {
+ straight_to_premul_v4(color_data[i]);
+ }
+ break;
+ }
+ case IMA_ALPHA_PREMUL: {
+ /* Alpha is premultiplied already, nothing to do. */
+ break;
+ }
+ case IMA_ALPHA_CHANNEL_PACKED: {
+ /* Color and alpha channels shouldn't interact with each other, nothing to do. */
+ break;
+ }
+ case IMA_ALPHA_IGNORE: {
+ /* The image should be treated as being opaque. */
+ for (int64_t i : mask) {
+ color_data[i].w = 1.0f;
+ }
+ break;
+ }
+ }
+
+ if (!r_alpha.is_empty()) {
+ for (int64_t i : mask) {
+ r_alpha[i] = r_color[i].a;
+ }
+ }
+ }
+};
+
+static void geo_node_image_texture_exec(GeoNodeExecParams params)
+{
+ auto return_default = [&]() {
+ params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ params.set_output("Alpha", 1.0f);
+ };
+
+ Image *image = params.get_input<Image *>("Image");
+ if (image == nullptr) {
+ return return_default();
+ }
+
+ const bNode &node = params.node();
+ NodeGeometryImageTexture *data = (NodeGeometryImageTexture *)node.storage;
+
+ ImageUser image_user;
+ BKE_imageuser_default(&image_user);
+ image_user.cycl = false;
+ image_user.frames = INT_MAX;
+ image_user.sfra = 1;
+ image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0;
+
+ std::unique_ptr<ImageFieldsFunction> image_fn;
+ try {
+ image_fn = std::make_unique<ImageFieldsFunction>(
+ data->interpolation, data->extension, *image, image_user);
+ }
+ catch (const std::runtime_error &) {
+ return return_default();
+ }
+
+ Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
+
+ auto image_op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(image_fn), {std::move(vector_field)}));
+
+ params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
+ params.set_output("Alpha", Field<float>(image_op, 1));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_image_texture(void)
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE, 0);
+ ntype.declare = blender::nodes::geo_node_image_texture_declare;
+ ntype.draw_buttons = blender::nodes::geo_node_image_texture_layout;
+ node_type_init(&ntype, blender::nodes::geo_node_image_texture_init);
+ node_type_storage(
+ &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ ntype.geometry_node_execute = blender::nodes::geo_node_image_texture_exec;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
index 604b181918d..b8df545d073 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -20,8 +20,8 @@ namespace blender::nodes {
static void geo_node_input_curve_handles_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Left").field_source();
- b.add_output<decl::Vector>("Right").field_source();
+ b.add_output<decl::Vector>(N_("Left")).field_source();
+ b.add_output<decl::Vector>(N_("Right")).field_source();
}
static void geo_node_input_curve_handles_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc
index 5a24b7f3f07..f32db3842db 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_curve_tilt_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Tilt").field_source();
+ b.add_output<decl::Float>(N_("Tilt")).field_source();
}
static void geo_node_input_curve_tilt_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
new file mode 100644
index 00000000000..37d5bac0325
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_input_id_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("ID")).field_source();
+}
+
+static void geo_node_input_id_exec(GeoNodeExecParams params)
+{
+ Field<int> position_field{std::make_shared<bke::IDAttributeFieldInput>()};
+ params.set_output("ID", std::move(position_field));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_id()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT, 0);
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_id_exec;
+ ntype.declare = blender::nodes::geo_node_input_id_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
index 7fcbaf429dd..6200ac5e7a8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_index_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Index").field_source();
+ b.add_output<decl::Int>(N_("Index")).field_source();
}
static void geo_node_input_index_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
index 8e805bd1359..fc41188dee5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -23,7 +23,7 @@ namespace blender::nodes {
static void geo_node_input_material_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Material>("Material");
+ b.add_output<decl::Material>(N_("Material"));
}
static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc
index 702c83daea0..5d5d9e40032 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_material_index_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Material Index").field_source();
+ b.add_output<decl::Int>(N_("Material Index")).field_source();
}
static void geo_node_input_material_index_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
index 5a2495afb9e..92b89313d23 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -28,7 +28,7 @@ namespace blender::nodes {
static void geo_node_input_normal_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Normal").field_source();
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
}
static GVArrayPtr mesh_face_normals(const Mesh &mesh,
@@ -241,8 +241,9 @@ static const GVArray *construct_curve_normal_gvarray(const CurveComponent &compo
class NormalFieldInput final : public fn::FieldInput {
public:
- NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal")
+ NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal node")
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
index 44874259e20..a8477d4bc4f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_position_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Position").field_source();
+ b.add_output<decl::Vector>(N_("Position")).field_source();
}
static void geo_node_input_position_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc
index 586005511ad..6d2c4c38cbe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_radius_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Radius").default_value(1.0f).min(0.0f).field_source();
+ b.add_output<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).field_source();
}
static void geo_node_input_radius_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc
index de520787e78..dcd14b1c054 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_shade_smooth_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Smooth").field_source();
+ b.add_output<decl::Bool>(N_("Smooth")).field_source();
}
static void geo_node_input_shade_smooth_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc
index 44a1bb62de8..a8ee6dd8b12 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_spline_cyclic_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Cyclic").field_source();
+ b.add_output<decl::Bool>(N_("Cyclic")).field_source();
}
static void geo_node_input_spline_cyclic_exec(GeoNodeExecParams params)
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 b5f3e1b0c28..895efa6f0ed 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
@@ -22,7 +22,7 @@ namespace blender::nodes {
static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Length").field_source();
+ b.add_output<decl::Float>(N_("Length")).field_source();
}
static const GVArray *construct_spline_length_gvarray(const CurveComponent &component,
@@ -57,8 +57,9 @@ static const GVArray *construct_spline_length_gvarray(const CurveComponent &comp
class SplineLengthFieldInput final : public fn::FieldInput {
public:
- SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length")
+ SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length node")
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc
index eab95ebc46e..75fb8a13d38 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc
@@ -20,7 +20,7 @@ namespace blender::nodes {
static void geo_node_input_spline_resolution_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Resolution").field_source();
+ b.add_output<decl::Int>(N_("Resolution")).field_source();
}
static void geo_node_input_spline_resolution_exec(GeoNodeExecParams params)
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 d690642373a..6b1736fe2ac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Tangent").field_source();
+ b.add_output<decl::Vector>(N_("Tangent")).field_source();
}
static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents)
@@ -121,8 +121,9 @@ static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &comp
class TangentFieldInput final : public fn::FieldInput {
public:
- TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent")
+ TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent node")
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
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 78399fad2c0..aff29d973d4 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
@@ -28,28 +28,29 @@ namespace blender::nodes {
static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Points").description("Points to instance on");
- b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
- b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points");
- b.add_input<decl::Bool>("Pick Instance")
+ b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Geometry>(N_("Instance"))
+ .description(N_("Geometry that is instanced on the points"));
+ b.add_input<decl::Bool>(N_("Pick Instance"))
.supports_field()
.description("Place different instances on different points");
- b.add_input<decl::Int>("Instance Index")
+ b.add_input<decl::Int>(N_("Instance Index"))
.implicit_field()
- .description(
+ .description(N_(
"Index of the instance that used for each point. This is only used when Pick Instances "
- "is on. By default the point index is used");
- b.add_input<decl::Vector>("Rotation")
+ "is on. By default the point index is used"));
+ b.add_input<decl::Vector>(N_("Rotation"))
.subtype(PROP_EULER)
.supports_field()
- .description("Rotation of the instances");
- b.add_input<decl::Vector>("Scale")
+ .description(N_("Rotation of the instances"));
+ b.add_input<decl::Vector>(N_("Scale"))
.default_value({1.0f, 1.0f, 1.0f})
.subtype(PROP_XYZ)
.supports_field()
- .description("Scale of the instances");
+ .description(N_("Scale of the instances"));
- b.add_output<decl::Geometry>("Instances");
+ b.add_output<decl::Geometry>(N_("Instances"));
}
static void add_instances_from_component(InstancesComponent &dst_component,
@@ -77,7 +78,6 @@ static void add_instances_from_component(InstancesComponent &dst_component,
select_len);
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
select_len);
- MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, select_len);
FieldEvaluator field_evaluator{field_context, domain_size};
const VArray<bool> *pick_instance = nullptr;
@@ -86,7 +86,6 @@ static void add_instances_from_component(InstancesComponent &dst_component,
const VArray<float3> *scales = nullptr;
/* The evaluator could use the component's stable IDs as a destination directly, but only the
* selected indices should be copied. */
- GVArray_Typed<int> stable_ids = src_component.attribute_get_for_read("id", ATTR_DOMAIN_POINT, 0);
field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
@@ -119,7 +118,6 @@ static void add_instances_from_component(InstancesComponent &dst_component,
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
for (const int range_i : selection_range) {
const int64_t i = selection[range_i];
- dst_stable_ids[range_i] = (*stable_ids)[i];
/* Compute base transform for every instances. */
float4x4 &dst_transform = dst_transforms[range_i];
@@ -157,6 +155,17 @@ static void add_instances_from_component(InstancesComponent &dst_component,
}
});
+ GVArrayPtr id_attribute = src_component.attribute_try_get_for_read(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ if (id_attribute) {
+ GVArray_Typed<int> ids{*id_attribute};
+ VArray_Span<int> ids_span{ids};
+ MutableSpan<int> dst_ids = dst_component.instance_ids_ensure();
+ for (const int64_t i : selection.index_range()) {
+ dst_ids[i] = ids_span[selection[i]];
+ }
+ }
+
if (pick_instance->is_single()) {
if (pick_instance->get_internal_single()) {
if (instance.has_realized_data()) {
@@ -194,11 +203,15 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params)
instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params);
geometry_set.remove(GEO_COMPONENT_TYPE_CURVE);
}
- /* Unused references may have been added above. Remove those now so that other nodes don't
- * process them needlessly. */
- instances.remove_unused_references();
});
+ /* Unused references may have been added above. Remove those now so that other nodes don't
+ * process them needlessly.
+ * This should eventually be moved into the loop above, but currently this is quite tricky
+ * because it might remove references that the loop still wants to iterate over. */
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ instances.remove_unused_references();
+
params.set_output("Instances", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
index 63d1f88a442..c3955426e69 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -23,15 +23,15 @@ namespace blender::nodes {
static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Instances");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(0.05f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Geometry>(N_("Points"));
}
template<typename T>
@@ -77,13 +77,15 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const VArray<float> &radii = evaluator.get_evaluated<float>(1);
copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint});
- OutputAttribute_Typed<int> id_attribute = points.attribute_try_get_for_output<int>(
- "id", ATTR_DOMAIN_POINT, 0);
- MutableSpan<int> ids = id_attribute.as_span();
- for (const int i : selection.index_range()) {
- ids[i] = instances.instance_ids()[selection[i]];
+ if (!instances.instance_ids().is_empty()) {
+ OutputAttribute_Typed<int> id_attribute = points.attribute_try_get_for_output<int>(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ MutableSpan<int> ids = id_attribute.as_span();
+ for (const int i : selection.index_range()) {
+ ids[i] = instances.instance_ids()[selection[i]];
+ }
+ id_attribute.save();
}
- id_attribute.save();
}
static void geo_node_instances_to_points_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index f8a1c764f61..8e0e98f7bd5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -22,7 +22,7 @@ namespace blender::nodes {
static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Is Viewport");
+ b.add_output<decl::Bool>(N_("Is Viewport"));
}
static void geo_node_is_viewport_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index b628c5cbab8..cd385f364e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -33,8 +33,8 @@ namespace blender::nodes {
static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry").multi_input();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).multi_input();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components)
@@ -270,17 +270,16 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
}
Span<float4x4> src_transforms = src_component->instance_transforms();
- Span<int> src_ids = src_component->instance_ids();
Span<int> src_reference_handles = src_component->instance_reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = src_transforms[i];
- const int id = src_ids[i];
- dst_component.add_instance(dst_handle, transform, id);
+ dst_component.add_instance(dst_handle, transform);
}
}
+ join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
index f3562bed6e9..e4a62bd5267 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void geo_node_material_replace_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Old");
- b.add_input<decl::Material>("New");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Material>(N_("Old"));
+ b.add_input<decl::Material>(N_("New"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_material_replace_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index 9d4533b9bda..06c770820ee 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -30,8 +30,8 @@ namespace blender::nodes {
static void geo_node_material_selection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Material>("Material").hide_label(true);
- b.add_output<decl::Bool>("Selection").field_source();
+ b.add_input<decl::Material>(N_("Material")).hide_label(true);
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
}
static void select_mesh_by_material(const Mesh &mesh,
@@ -59,8 +59,9 @@ class MaterialSelectionFieldInput final : public fn::FieldInput {
public:
MaterialSelectionFieldInput(Material *material)
- : fn::FieldInput(CPPType::get<bool>(), "Material Selection"), material_(material)
+ : fn::FieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material)
{
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 9c477c639a2..685a8faff5c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -29,9 +29,9 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Vertices")).default_value(32).min(3);
+ b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_mesh_primitive_circle_layout(uiLayout *layout,
@@ -204,7 +204,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params)
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -212,7 +212,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params)
BLI_assert(BKE_mesh_is_valid(mesh));
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 93c35a1111b..206d48d40c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -29,13 +29,16 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(512);
- b.add_input<decl::Int>("Side Segments").default_value(1).min(1).max(512);
- b.add_input<decl::Int>("Fill Segments").default_value(1).min(1).max(512);
- b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Vertices")).default_value(32).min(3).max(512);
+ b.add_input<decl::Int>(N_("Side Segments")).default_value(1).min(1).max(512);
+ b.add_input<decl::Int>(N_("Fill Segments")).default_value(1).min(1).max(512);
+ b.add_input<decl::Float>(N_("Radius Top")).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Radius Bottom"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Depth")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -708,14 +711,14 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
const int circle_segments = params.extract_input<int>("Vertices");
if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
const int side_segments = params.extract_input<int>("Side Segments");
if (side_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -723,7 +726,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
if (fill_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -737,7 +740,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
/* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index 5a520d36296..3a211993bdc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -26,11 +26,14 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION);
- b.add_input<decl::Int>("Vertices X").default_value(2).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Y").default_value(2).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Z").default_value(2).min(2).max(1000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Vector>(N_("Size"))
+ .default_value(float3(1))
+ .min(0.0f)
+ .subtype(PROP_TRANSLATION);
+ b.add_input<decl::Int>(N_("Vertices X")).default_value(2).min(2).max(1000);
+ b.add_input<decl::Int>(N_("Vertices Y")).default_value(2).min(2).max(1000);
+ b.add_input<decl::Int>(N_("Vertices Z")).default_value(2).min(2).max(1000);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
struct CuboidConfig {
@@ -476,13 +479,13 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params)
const int verts_z = params.extract_input<int>("Vertices Z");
if (verts_x < 1 || verts_y < 1 || verts_z < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index 287ea896ade..3bcf42b40b1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -29,32 +29,32 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices")
+ b.add_input<decl::Int>(N_("Vertices"))
.default_value(32)
.min(3)
.max(512)
- .description("The number of vertices around the circumference");
- b.add_input<decl::Int>("Side Segments")
+ .description(N_("The number of vertices around the circumference"));
+ b.add_input<decl::Int>(N_("Side Segments"))
.default_value(1)
.min(1)
.max(512)
- .description("The number of segments along the side");
- b.add_input<decl::Int>("Fill Segments")
+ .description(N_("The number of segments along the side"));
+ b.add_input<decl::Int>(N_("Fill Segments"))
.default_value(1)
.min(1)
.max(512)
- .description("The number of concentric segments of the fill");
- b.add_input<decl::Float>("Radius")
+ .description(N_("The number of concentric segments of the fill"));
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE)
- .description("The radius of the cylinder");
- b.add_input<decl::Float>("Depth")
+ .description(N_("The radius of the cylinder"));
+ b.add_input<decl::Float>(N_("Depth"))
.default_value(2.0f)
.min(0.0f)
.subtype(PROP_DISTANCE)
- .description("The height of the cylinder on the Z axis");
- b.add_output<decl::Geometry>("Geometry");
+ .description(N_("The height of the cylinder on the Z axis"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout,
@@ -102,14 +102,14 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
const int circle_segments = params.extract_input<int>("Vertices");
if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
const int side_segments = params.extract_input<int>("Side Segments");
if (side_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -117,7 +117,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
if (fill_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -125,7 +125,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
Mesh *mesh = create_cylinder_or_cone_mesh(
radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 858ef8648f8..c4e476981c1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -29,11 +29,11 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Int>("Vertices X").default_value(3).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Y").default_value(3).min(2).max(1000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>(N_("Size X")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Size Y")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>(N_("Vertices X")).default_value(3).min(2).max(1000);
+ b.add_input<decl::Int>(N_("Vertices Y")).default_value(3).min(2).max(1000);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void calculate_uvs(
@@ -160,7 +160,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 1 || verts_y < 1) {
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
@@ -168,7 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
BLI_assert(BKE_mesh_is_valid(mesh));
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index 5ea7165ac31..da3dfef3aea 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -28,9 +28,9 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Int>("Subdivisions").default_value(1).min(1).max(7);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>(N_("Subdivisions")).default_value(1).min(1).max(7);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
@@ -66,7 +66,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index 031223b5ca6..6515afe5966 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -29,11 +29,13 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(10000);
- b.add_input<decl::Float>("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Start Location").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(10000);
+ b.add_input<decl::Float>(N_("Resolution")).default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Vector>(N_("Start Location")).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Offset"))
+ .default_value({0.0f, 0.0f, 1.0f})
+ .subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_mesh_primitive_line_layout(uiLayout *layout,
@@ -154,7 +156,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
mesh = create_line_mesh(start, delta, count);
}
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index 6fd6cdf5747..54a762fc15d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -29,10 +29,10 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Segments").default_value(32).min(3).max(1024);
- b.add_input<decl::Int>("Rings").default_value(16).min(2).max(1024);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Segments")).default_value(32).min(3).max(1024);
+ b.add_input<decl::Int>(N_("Rings")).default_value(16).min(2).max(1024);
+ b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static int sphere_vert_total(const int segments, const int rings)
@@ -291,14 +291,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
if (rings_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3"));
}
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Mesh", GeometrySet());
return;
}
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
index d1dd5b1bf8b..d99c0c851a8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
@@ -74,12 +74,12 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev
static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
return;
#endif
@@ -87,14 +87,14 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
if (subdiv_level == 0) {
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
return;
}
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes
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 7bca9ec141b..11865c635b8 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
@@ -22,9 +22,9 @@ namespace blender::nodes {
static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
index df0fdd8eccd..92911e89f59 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -30,15 +30,15 @@ namespace blender::nodes {
static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(0.05f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Geometry>(N_("Points"));
}
static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index e61709ed86a..3ba32c4b674 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -25,15 +25,15 @@ namespace blender::nodes {
static void geo_node_object_info_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Object>("Object").hide_label();
- b.add_input<decl::Bool>("As Instance")
+ b.add_input<decl::Object>(N_("Object")).hide_label();
+ b.add_input<decl::Bool>(N_("As Instance"))
.description(
- "Output the entire object as single instance. "
- "This allows instancing non-geometry object types");
- b.add_output<decl::Vector>("Location");
- b.add_output<decl::Vector>("Rotation");
- b.add_output<decl::Vector>("Scale");
- b.add_output<decl::Geometry>("Geometry");
+ N_("Output the entire object as single instance. "
+ "This allows instancing non-geometry object types"));
+ b.add_output<decl::Vector>(N_("Location"));
+ b.add_output<decl::Vector>(N_("Rotation"));
+ b.add_output<decl::Vector>(N_("Scale"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index afd0ced6360..3e0096824d3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -27,9 +27,9 @@ namespace blender::nodes {
static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Points");
- b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
template<typename T>
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 719523f64f0..312ea7df919 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
@@ -32,16 +32,16 @@ namespace blender::nodes {
static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
- b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f);
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Points"));
+ b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(0.5f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_output<decl::Geometry>(N_("Volume"));
}
static void geo_node_points_to_volume_layout(uiLayout *layout,
@@ -239,17 +239,17 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
#ifdef WITH_OPENVDB
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
initialize_volume_component_from_points(params, geometry_set);
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Volume", std::move(geometry_set));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_output("Geometry", GeometrySet());
+ params.set_output("Volume", GeometrySet());
#endif
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
index ec4d6ceb728..c05476b982b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
@@ -31,10 +31,12 @@ namespace blender::nodes {
static void geo_node_proximity_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Target");
- b.add_input<decl::Vector>("Source Position").implicit_field();
- b.add_output<decl::Vector>("Position").dependent_field();
- b.add_output<decl::Float>("Distance").dependent_field();
+ b.add_input<decl::Geometry>(N_("Target"))
+ .only_realized_data()
+ .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ b.add_input<decl::Vector>(N_("Source Position")).implicit_field();
+ b.add_output<decl::Vector>(N_("Position")).dependent_field();
+ b.add_output<decl::Float>(N_("Distance")).dependent_field();
}
static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -214,12 +216,6 @@ static void geo_node_proximity_exec(GeoNodeExecParams params)
params.set_output("Distance", fn::make_constant_field<float>(0.0f));
};
- if (geometry_set_target.has_instances()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("The node only supports realized mesh or point cloud data, instances are ignored"));
- }
-
if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
return return_default();
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index 1e687f163e6..34946b1115c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -31,32 +31,36 @@ namespace blender::nodes {
static void geo_node_raycast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Target Geometry");
-
- b.add_input<decl::Vector>("Attribute").hide_value().supports_field();
- b.add_input<decl::Float>("Attribute", "Attribute_001").hide_value().supports_field();
- b.add_input<decl::Color>("Attribute", "Attribute_002").hide_value().supports_field();
- b.add_input<decl::Bool>("Attribute", "Attribute_003").hide_value().supports_field();
- b.add_input<decl::Int>("Attribute", "Attribute_004").hide_value().supports_field();
-
- b.add_input<decl::Vector>("Source Position").implicit_field();
- b.add_input<decl::Vector>("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field();
- b.add_input<decl::Float>("Ray Length")
+ b.add_input<decl::Geometry>(N_("Target Geometry"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_MESH);
+
+ b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field();
+
+ b.add_input<decl::Vector>(N_("Source Position")).implicit_field();
+ b.add_input<decl::Vector>(N_("Ray Direction"))
+ .default_value({0.0f, 0.0f, -1.0f})
+ .supports_field();
+ b.add_input<decl::Float>(N_("Ray Length"))
.default_value(100.0f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_output<decl::Bool>("Is Hit").dependent_field();
- b.add_output<decl::Vector>("Hit Position").dependent_field();
- b.add_output<decl::Vector>("Hit Normal").dependent_field();
- b.add_output<decl::Float>("Hit Distance").dependent_field();
+ b.add_output<decl::Bool>(N_("Is Hit")).dependent_field();
+ b.add_output<decl::Vector>(N_("Hit Position")).dependent_field();
+ b.add_output<decl::Vector>(N_("Hit Normal")).dependent_field();
+ b.add_output<decl::Float>(N_("Hit Distance")).dependent_field();
- b.add_output<decl::Vector>("Attribute").dependent_field({1, 2, 3, 4, 5, 6});
- b.add_output<decl::Float>("Attribute", "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6});
- b.add_output<decl::Color>("Attribute", "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6});
- b.add_output<decl::Bool>("Attribute", "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6});
- b.add_output<decl::Int>("Attribute", "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Vector>(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6});
}
static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -389,26 +393,11 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
});
};
- if (target.has_instances()) {
- if (target.has_realized_data()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("The node only supports realized mesh data, instances are ignored"));
- }
- else {
- params.error_message_add(NodeWarningType::Error,
- TIP_("The target geometry must contain realized data"));
- return return_default();
- }
- }
-
if (target.is_empty()) {
return return_default();
}
if (!target.has_mesh()) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("The target geometry must contain a mesh"));
return return_default();
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
index 3be79d5ba3b..6c51c1f738f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
@@ -23,8 +23,8 @@ namespace blender::nodes {
static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void geo_node_realize_instances_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
index 09d92d7fa1e..abf44b1aaf8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -22,12 +22,12 @@ namespace blender::nodes {
static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).supports_field();
- b.add_input<decl::Vector>("Pivot Point").subtype(PROP_TRANSLATION).supports_field();
- b.add_input<decl::Bool>("Local Space").default_value(true).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).supports_field();
+ b.add_input<decl::Vector>(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
};
static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
@@ -55,34 +55,55 @@ static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &inst
for (const int i_selection : range) {
const int i = selection[i_selection];
const float3 pivot = pivots[i];
+ const float3 euler = rotations[i];
float4x4 &instance_transform = instance_transforms[i];
- const float4x4 rotation_matrix = float4x4::from_loc_eul_scale(
- {0, 0, 0}, rotations[i], {1, 1, 1});
+
+ float4x4 rotation_matrix;
+ float3 used_pivot;
if (local_spaces[i]) {
- instance_transform *= float4x4::from_location(pivot);
- instance_transform *= rotation_matrix;
- instance_transform *= float4x4::from_location(-pivot);
+ /* Find rotation axis from the matrix. This should work even if the instance is skewed. */
+ const float3 rotation_axis_x = instance_transform.values[0];
+ const float3 rotation_axis_y = instance_transform.values[1];
+ const float3 rotation_axis_z = instance_transform.values[2];
+
+ /* Create rotations around the individual axis. This could be optimized to skip some axis
+ * when the angle is zero. */
+ float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3];
+ axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x);
+ axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y);
+ axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z);
+
+ /* Combine the previously computed rotations into the final rotation matrix. */
+ float rotation[3][3];
+ mul_m3_series(rotation, rotation_z, rotation_y, rotation_x);
+ copy_m4_m3(rotation_matrix.values, rotation);
+
+ /* Transform the passed in pivot into the local space of the instance. */
+ used_pivot = instance_transform * pivot;
}
else {
- const float4x4 orgiginal_transform = instance_transform;
- instance_transform = float4x4::from_location(pivot);
- instance_transform *= rotation_matrix;
- instance_transform *= float4x4::from_location(-pivot);
- instance_transform *= orgiginal_transform;
+ used_pivot = pivot;
+ eul_to_mat4(rotation_matrix.values, euler);
}
+ /* Move the pivot to the origin so that we can rotate around it. */
+ sub_v3_v3(instance_transform.values[3], used_pivot);
+ /* Perform the actual rotation. */
+ mul_m4_m4_pre(instance_transform.values, rotation_matrix.values);
+ /* Undo the pivot shifting done before. */
+ add_v3_v3(instance_transform.values[3], used_pivot);
}
});
}
static void geo_node_rotate_instances_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
rotate_instances(params, instances);
}
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Instances", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
index 33897ef354d..ea2b458410e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -22,12 +22,15 @@ namespace blender::nodes {
static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field();
- b.add_input<decl::Vector>("Center").subtype(PROP_TRANSLATION).supports_field();
- b.add_input<decl::Bool>("Local Space").default_value(true).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Scale"))
+ .subtype(PROP_XYZ)
+ .default_value({1, 1, 1})
+ .supports_field();
+ b.add_input<decl::Vector>(N_("Center")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
};
static void scale_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
@@ -74,12 +77,12 @@ static void scale_instances(GeoNodeExecParams &params, InstancesComponent &insta
static void geo_node_scale_instances_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
scale_instances(params, instances);
}
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Instances", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
index dafd10cee2d..a16fb712b13 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
@@ -20,12 +20,12 @@ namespace blender::nodes {
static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Mesh");
- b.add_output<decl::Geometry>("Point Cloud");
- b.add_output<decl::Geometry>("Curve");
- b.add_output<decl::Geometry>("Volume");
- b.add_output<decl::Geometry>("Instances");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+ b.add_output<decl::Geometry>(N_("Point Cloud"));
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Geometry>(N_("Volume"));
+ b.add_output<decl::Geometry>(N_("Instances"));
}
static void geo_node_separate_components_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc
index 970d49e0626..28e214c0ccc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc
@@ -23,16 +23,16 @@ namespace blender::nodes {
static void geo_node_separate_geometry_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection"))
.default_value(true)
.hide_value()
.supports_field()
- .description("The parts of the geometry that go into the first output");
- b.add_output<decl::Geometry>("Selection")
- .description("The parts of the geometry in the selection");
- b.add_output<decl::Geometry>("Inverted")
- .description("The parts of the geometry not in the selection");
+ .description(N_("The parts of the geometry that go into the first output"));
+ b.add_output<decl::Geometry>(N_("Selection"))
+ .description(N_("The parts of the geometry in the selection"));
+ b.add_output<decl::Geometry>(N_("Inverted"))
+ .description(N_("The parts of the geometry not in the selection"));
}
static void geo_node_separate_geometry_layout(uiLayout *layout,
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 d7aaaffc7c6..b64aa266330 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
@@ -25,10 +25,10 @@ namespace blender::nodes {
static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void geo_node_set_curve_handles_layout(uiLayout *layout,
@@ -125,7 +125,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params)
(NodeGeometrySetCurveHandlePositions *)params.node().storage;
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
@@ -144,7 +144,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params)
params.error_message_add(NodeWarningType::Info,
TIP_("The input geometry does not contain a Bezier spline"));
}
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
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 8fa4ff1a808..e47ce7dea30 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
@@ -20,11 +20,14 @@ namespace blender::nodes {
static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Float>("Radius").min(0.0f).default_value(1.0f).supports_field().subtype(
- PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Radius"))
+ .min(0.0f)
+ .default_value(1.0f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void set_radius_in_component(GeometryComponent &component,
@@ -52,7 +55,7 @@ static void set_radius_in_component(GeometryComponent &component,
static void geo_node_set_curve_radius_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
@@ -63,7 +66,7 @@ static void geo_node_set_curve_radius_exec(GeoNodeExecParams params)
}
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
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 113149613ef..dde6d0bab92 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
@@ -20,10 +20,10 @@ namespace blender::nodes {
static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Float>("Tilt").subtype(PROP_ANGLE).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Tilt")).subtype(PROP_ANGLE).supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static void set_tilt_in_component(GeometryComponent &component,
@@ -51,7 +51,7 @@ static void set_tilt_in_component(GeometryComponent &component,
static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
@@ -62,7 +62,7 @@ static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params)
}
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
new file mode 100644
index 00000000000..77d8e786501
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -0,0 +1,94 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_set_id_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("ID")).implicit_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_id_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<int> &id_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ /* Since adding the ID attribute can change the result of the field evaluation (the random value
+ * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating
+ * the field. However, as an optimization, use a faster code path when it already exists. */
+ fn::FieldEvaluator id_evaluator{field_context, &selection};
+ if (component.attribute_exists("id")) {
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ id_evaluator.add_with_destination(id_field, id_attribute.varray());
+ id_evaluator.evaluate();
+ id_attribute.save();
+ }
+ else {
+ id_evaluator.add(id_field);
+ id_evaluator.evaluate();
+ const VArray<int> &result_ids = id_evaluator.get_evaluated<int>(0);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ result_ids.materialize(selection, id_attribute.as_span());
+ id_attribute.save();
+ }
+}
+
+static void geo_node_set_id_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<int> id_field = params.extract_input<Field<int>>("ID");
+
+ for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE}) {
+ if (geometry_set.has(type)) {
+ set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_set_id()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY, 0);
+ ntype.geometry_node_execute = blender::nodes::geo_node_set_id_exec;
+ ntype.declare = blender::nodes::geo_node_set_id_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
index 0d85af60944..040509ad6d1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void geo_node_set_material_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Material>("Material").hide_label();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Material>(N_("Material")).hide_label();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
index a25fe332916..a8bb1bd8644 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -20,10 +20,10 @@ namespace blender::nodes {
static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Int>("Material Index").supports_field().min(0);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Material Index")).supports_field().min(0);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void set_material_index_in_component(GeometryComponent &component,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
index b59c9a9e8f5..9ff299542b4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -20,11 +20,14 @@ namespace blender::nodes {
static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Float>("Radius").default_value(0.05f).min(0.0f).supports_field().subtype(
- PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(0.05f)
+ .min(0.0f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Points"));
}
static void set_radius_in_component(GeometryComponent &component,
@@ -52,7 +55,7 @@ static void set_radius_in_component(GeometryComponent &component,
static void geo_node_set_point_radius_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
@@ -64,7 +67,7 @@ static void geo_node_set_point_radius_exec(GeoNodeExecParams params)
}
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Points", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 15930508e78..4e564386a28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -22,17 +22,17 @@ namespace blender::nodes {
static void geo_node_set_position_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_input<decl::Bool>("Offset").default_value(false).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Vector>(N_("Offset")).supports_field().subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void set_position_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<float3> &position_field,
- const Field<bool> &offset_field)
+ const Field<float3> &offset_field)
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
@@ -45,10 +45,6 @@ static void set_position_in_component(GeometryComponent &component,
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- MutableSpan<float3> position_mutable = positions.as_span();
-
fn::FieldEvaluator position_evaluator{field_context, &selection};
position_evaluator.add(position_field);
position_evaluator.add(offset_field);
@@ -58,11 +54,14 @@ static void set_position_in_component(GeometryComponent &component,
* value or not */
const VArray<float3> &positions_input = position_evaluator.get_evaluated<float3>(0);
- const VArray<bool> &offsets_input = position_evaluator.get_evaluated<bool>(1);
+ const VArray<float3> &offsets_input = position_evaluator.get_evaluated<float3>(1);
+
+ OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ MutableSpan<float3> position_mutable = positions.as_span();
for (int i : selection) {
- position_mutable[i] = offsets_input[i] ? position_mutable[i] + positions_input[i] :
- positions_input[i];
+ position_mutable[i] = positions_input[i] + offsets_input[i];
}
positions.save();
}
@@ -71,7 +70,7 @@ static void geo_node_set_position_exec(GeoNodeExecParams params)
{
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- Field<bool> offset_field = params.extract_input<Field<bool>>("Offset");
+ Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
index ca77041ba7c..06e25c2ed55 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -20,10 +20,10 @@ namespace blender::nodes {
static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Bool>("Shade Smooth").supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Shade Smooth")).supports_field().default_value(true);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void set_smooth_in_component(GeometryComponent &component,
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 50e00ff3758..ec751ae1d2b 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
@@ -20,10 +20,10 @@ namespace blender::nodes {
static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Bool>("Cyclic").supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Cyclic")).supports_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void set_cyclic_in_component(GeometryComponent &component,
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 dccb0b1a969..ccf419975ca 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
@@ -22,10 +22,10 @@ namespace blender::nodes {
static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Int>("Resolution").default_value(12).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Resolution")).default_value(12).supports_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void set_resolution_in_component(GeometryComponent &component,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
index 515f072e976..98d0aca084a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
@@ -20,9 +20,9 @@ namespace blender::nodes {
static void geo_node_string_join_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("Delimiter");
- b.add_input<decl::String>("Strings").multi_input().hide_value();
- b.add_output<decl::String>("String");
+ b.add_input<decl::String>(N_("Delimiter"));
+ b.add_input<decl::String>(N_("Strings")).multi_input().hide_value();
+ b.add_output<decl::String>(N_("String"));
};
static void geo_node_string_join_exec(GeoNodeExecParams params)
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 1cb6d43f685..95e94a22d81 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
@@ -34,18 +34,30 @@ namespace blender::nodes {
static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("String");
- b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Character Spacing")
+ b.add_input<decl::String>(N_("String"));
+ b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Character Spacing"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curves");
- b.add_output<decl::String>("Remainder");
+ b.add_input<decl::Float>(N_("Word Spacing"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Line Spacing"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Text Box Width"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Text Box Height"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curves"));
+ b.add_output<decl::String>(N_("Remainder"));
}
static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
@@ -242,13 +254,11 @@ static void add_instances_from_handles(InstancesComponent &instances,
instances.resize(positions.size());
MutableSpan<int> handles = instances.instance_reference_handles();
MutableSpan<float4x4> transforms = instances.instance_transforms();
- MutableSpan<int> instance_ids = instances.instance_ids();
threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
for (const int i : range) {
handles[i] = char_handles.lookup(charcodes[i]);
transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
- instance_ids[i] = i;
}
});
}
@@ -271,8 +281,9 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
/* Convert UTF-8 encoded string to UTF-32. */
size_t len_bytes;
size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
- Array<char32_t> char_codes(len_chars + 1);
- BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1);
+ Array<char32_t> char_codes_with_null(len_chars + 1);
+ BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1);
+ const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1);
/* Create and add instances. */
GeometrySet geometry_set_out;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index 410c9a8bb35..2b3430a5ed0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -31,29 +31,23 @@ namespace blender::nodes {
static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
- b.add_input<decl::Float>("Crease")
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_input<decl::Float>(N_("Crease"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.supports_field()
.subtype(PROP_FACTOR);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_subdivision_surface_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
-#ifdef WITH_OPENSUBDIV
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE);
-#else
- UNUSED_VARS(layout, ptr);
-#endif
+ uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE);
}
static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -67,7 +61,7 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
@@ -82,7 +76,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
/* Only process subdivision if level is greater than 0. */
if (subdiv_level == 0) {
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
return;
}
@@ -148,7 +142,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
BKE_subdiv_free(subdiv);
});
#endif
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index c01fcf5bb5f..7e07a552650 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -30,48 +30,57 @@ namespace blender::nodes {
static void geo_node_switch_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Bool>("Switch").default_value(false).supports_field();
- b.add_input<decl::Bool>("Switch", "Switch_001").default_value(false);
-
- b.add_input<decl::Float>("False").supports_field();
- b.add_input<decl::Float>("True").supports_field();
- b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000).supports_field();
- b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000).supports_field();
- b.add_input<decl::Bool>("False", "False_002").default_value(false).hide_value().supports_field();
- b.add_input<decl::Bool>("True", "True_002").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("False", "False_003").supports_field();
- b.add_input<decl::Vector>("True", "True_003").supports_field();
- b.add_input<decl::Color>("False", "False_004")
+ b.add_input<decl::Bool>(N_("Switch")).default_value(false).supports_field();
+ b.add_input<decl::Bool>(N_("Switch"), "Switch_001").default_value(false);
+
+ b.add_input<decl::Float>(N_("False")).supports_field();
+ b.add_input<decl::Float>(N_("True")).supports_field();
+ b.add_input<decl::Int>(N_("False"), "False_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>(N_("True"), "True_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Bool>(N_("False"), "False_002")
+ .default_value(false)
+ .hide_value()
+ .supports_field();
+ b.add_input<decl::Bool>(N_("True"), "True_002")
+ .default_value(true)
+ .hide_value()
+ .supports_field();
+ b.add_input<decl::Vector>(N_("False"), "False_003").supports_field();
+ b.add_input<decl::Vector>(N_("True"), "True_003").supports_field();
+ b.add_input<decl::Color>(N_("False"), "False_004")
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
.supports_field();
- b.add_input<decl::Color>("True", "True_004")
+ b.add_input<decl::Color>(N_("True"), "True_004")
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
.supports_field();
- b.add_input<decl::String>("False", "False_005").supports_field();
- b.add_input<decl::String>("True", "True_005").supports_field();
-
- b.add_input<decl::Geometry>("False", "False_006");
- b.add_input<decl::Geometry>("True", "True_006");
- b.add_input<decl::Object>("False", "False_007");
- b.add_input<decl::Object>("True", "True_007");
- b.add_input<decl::Collection>("False", "False_008");
- b.add_input<decl::Collection>("True", "True_008");
- b.add_input<decl::Texture>("False", "False_009");
- b.add_input<decl::Texture>("True", "True_009");
- b.add_input<decl::Material>("False", "False_010");
- b.add_input<decl::Material>("True", "True_010");
-
- b.add_output<decl::Float>("Output").dependent_field();
- b.add_output<decl::Int>("Output", "Output_001").dependent_field();
- b.add_output<decl::Bool>("Output", "Output_002").dependent_field();
- b.add_output<decl::Vector>("Output", "Output_003").dependent_field();
- b.add_output<decl::Color>("Output", "Output_004").dependent_field();
- b.add_output<decl::String>("Output", "Output_005").dependent_field();
- b.add_output<decl::Geometry>("Output", "Output_006");
- b.add_output<decl::Object>("Output", "Output_007");
- b.add_output<decl::Collection>("Output", "Output_008");
- b.add_output<decl::Texture>("Output", "Output_009");
- b.add_output<decl::Material>("Output", "Output_010");
+ b.add_input<decl::String>(N_("False"), "False_005").supports_field();
+ b.add_input<decl::String>(N_("True"), "True_005").supports_field();
+
+ b.add_input<decl::Geometry>(N_("False"), "False_006");
+ b.add_input<decl::Geometry>(N_("True"), "True_006");
+ b.add_input<decl::Object>(N_("False"), "False_007");
+ b.add_input<decl::Object>(N_("True"), "True_007");
+ b.add_input<decl::Collection>(N_("False"), "False_008");
+ b.add_input<decl::Collection>(N_("True"), "True_008");
+ b.add_input<decl::Texture>(N_("False"), "False_009");
+ b.add_input<decl::Texture>(N_("True"), "True_009");
+ b.add_input<decl::Material>(N_("False"), "False_010");
+ b.add_input<decl::Material>(N_("True"), "True_010");
+ b.add_input<decl::Image>(N_("False"), "False_011");
+ b.add_input<decl::Image>(N_("True"), "True_011");
+
+ b.add_output<decl::Float>(N_("Output")).dependent_field();
+ b.add_output<decl::Int>(N_("Output"), "Output_001").dependent_field();
+ b.add_output<decl::Bool>(N_("Output"), "Output_002").dependent_field();
+ b.add_output<decl::Vector>(N_("Output"), "Output_003").dependent_field();
+ b.add_output<decl::Color>(N_("Output"), "Output_004").dependent_field();
+ b.add_output<decl::String>(N_("Output"), "Output_005").dependent_field();
+ b.add_output<decl::Geometry>(N_("Output"), "Output_006");
+ b.add_output<decl::Object>(N_("Output"), "Output_007");
+ b.add_output<decl::Collection>(N_("Output"), "Output_008");
+ b.add_output<decl::Texture>(N_("Output"), "Output_009");
+ b.add_output<decl::Material>(N_("Output"), "Output_010");
+ b.add_output<decl::Image>(N_("Output"), "Output_011");
}
static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -274,6 +283,10 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
switch_no_fields<Material *>(params, "_010");
break;
}
+ case SOCK_IMAGE: {
+ switch_no_fields<Image *>(params, "_011");
+ break;
+ }
default:
BLI_assert_unreachable();
break;
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 1c287a0f1bf..a889678537f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -40,22 +40,24 @@ namespace blender::nodes {
static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Target");
-
- b.add_input<decl::Vector>("Attribute").hide_value().supports_field();
- b.add_input<decl::Float>("Attribute", "Attribute_001").hide_value().supports_field();
- b.add_input<decl::Color>("Attribute", "Attribute_002").hide_value().supports_field();
- b.add_input<decl::Bool>("Attribute", "Attribute_003").hide_value().supports_field();
- b.add_input<decl::Int>("Attribute", "Attribute_004").hide_value().supports_field();
-
- b.add_input<decl::Vector>("Source Position").implicit_field();
- b.add_input<decl::Int>("Index").implicit_field();
-
- b.add_output<decl::Vector>("Attribute").dependent_field({6, 7});
- b.add_output<decl::Float>("Attribute", "Attribute_001").dependent_field({6, 7});
- b.add_output<decl::Color>("Attribute", "Attribute_002").dependent_field({6, 7});
- b.add_output<decl::Bool>("Attribute", "Attribute_003").dependent_field({6, 7});
- b.add_output<decl::Int>("Attribute", "Attribute_004").dependent_field({6, 7});
+ b.add_input<decl::Geometry>(N_("Target"))
+ .only_realized_data()
+ .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD});
+
+ b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field();
+
+ b.add_input<decl::Vector>(N_("Source Position")).implicit_field();
+ b.add_input<decl::Int>(N_("Index")).implicit_field();
+
+ b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7});
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7});
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7});
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7});
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7});
}
static void geo_node_transfer_attribute_layout(uiLayout *layout,
@@ -628,13 +630,14 @@ class IndexTransferFieldInput : public FieldInput {
GField src_field,
Field<int> index_field,
const AttributeDomain domain)
- : FieldInput(src_field.cpp_type(), "Attribute Transfer Index"),
+ : FieldInput(src_field.cpp_type(), "Attribute Transfer node"),
src_geometry_(std::move(geometry)),
src_field_(std::move(src_field)),
index_field_(std::move(index_field)),
domain_(domain)
{
src_geometry_.ensure_owns_direct_data();
+ category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const FieldContext &context,
@@ -746,21 +749,9 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params)
});
};
- if (geometry.has_instances()) {
- if (geometry.has_realized_data()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("Only realized geometry is supported, instances will not be used"));
- }
- else {
- params.error_message_add(NodeWarningType::Error,
- TIP_("Target geometry must contain realized data"));
- return return_default();
- }
- /* Since the instances are not used, there is no point in keeping
- * a reference to them while the field is passed around. */
- geometry.remove(GEO_COMPONENT_TYPE_INSTANCES);
- }
+ /* Since the instances are not used, there is no point in keeping
+ * a reference to them while the field is passed around. */
+ geometry.remove(GEO_COMPONENT_TYPE_INSTANCES);
GField output_field;
switch (mapping) {
@@ -790,8 +781,6 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params)
}
case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("Curve targets are not currently supported"));
return return_default();
}
auto fn = std::make_unique<NearestTransferFunction>(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 005714a9580..2c55a255b5d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -37,11 +37,11 @@ namespace blender::nodes {
static void geo_node_transform_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER);
- b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER);
+ b.add_input<decl::Vector>(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static bool use_translate(const float3 rotation, const float3 scale)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
index 8fc2843fd8a..fa05d858a07 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -22,11 +22,11 @@ namespace blender::nodes {
static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION).supports_field();
- b.add_input<decl::Bool>("Local Space").default_value(true).supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
};
static void translate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
@@ -62,12 +62,12 @@ static void translate_instances(GeoNodeExecParams &params, InstancesComponent &i
static void geo_node_translate_instances_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
translate_instances(params, instances);
}
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Instances", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 7ef0913622c..c869846e1f8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -31,9 +31,9 @@ namespace blender::nodes {
static void geo_node_triangulate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Minimum Vertices").default_value(4).min(4).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -50,7 +50,7 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
static void geo_node_triangulate_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
@@ -67,7 +67,7 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params)
}
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
index 3331962341f..194d1a751ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
@@ -14,13 +14,69 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_viewer_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Value")).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Value"), "Value_001").supports_field().hide_value();
+ b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field().hide_value();
+ b.add_input<decl::Int>(N_("Value"), "Value_003").supports_field().hide_value();
+ b.add_input<decl::Bool>(N_("Value"), "Value_004").supports_field().hide_value();
+}
+
+static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+
+ node->storage = data;
+}
+
+static void geo_node_viewer_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
}
+
+static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return SOCK_FLOAT;
+ case CD_PROP_INT32:
+ return SOCK_INT;
+ case CD_PROP_FLOAT3:
+ return SOCK_VECTOR;
+ case CD_PROP_BOOL:
+ return SOCK_BOOLEAN;
+ case CD_PROP_COLOR:
+ return SOCK_RGBA;
+ default:
+ BLI_assert_unreachable();
+ return SOCK_FLOAT;
+ }
+}
+
+static void geo_node_viewer_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryViewer &storage = *(const NodeGeometryViewer *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_GEOMETRY) {
+ continue;
+ }
+ nodeSetSocketAvailability(socket, socket->type == socket_type);
+ }
+}
+
} // namespace blender::nodes
void register_node_type_geo_viewer()
@@ -28,6 +84,11 @@ void register_node_type_geo_viewer()
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0);
+ node_type_storage(
+ &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage);
+ node_type_update(&ntype, blender::nodes::geo_node_viewer_update);
+ node_type_init(&ntype, blender::nodes::geo_node_viewer_init);
ntype.declare = blender::nodes::geo_node_viewer_declare;
+ ntype.draw_buttons_ex = blender::nodes::geo_node_viewer_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index 229a35e0007..416d502dc59 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -39,13 +39,12 @@ namespace blender::nodes {
static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Density");
- b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f);
- b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f);
- b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f);
+ b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -60,11 +59,6 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN(
sizeof(NodeGeometryVolumeToMesh), __func__);
data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
-
- bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
- bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
- STRNCPY(grid_socket_value->value, "density");
-
node->storage = data;
}
@@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node
#ifdef WITH_OPENVDB
-static void create_mesh_from_volume(GeometrySet &geometry_set_in,
- GeometrySet &geometry_set_out,
- GeoNodeExecParams &params)
+static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams &params)
{
- if (!geometry_set_in.has<VolumeComponent>()) {
- return;
- }
-
const NodeGeometryVolumeToMesh &storage =
*(const NodeGeometryVolumeToMesh *)params.node().storage;
bke::VolumeToMeshResolution resolution;
resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
- resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
- if (resolution.settings.voxel_amount <= 0.0f) {
- return;
- }
+ resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f);
}
else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
- resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
- if (resolution.settings.voxel_size <= 0.0f) {
- return;
- }
+ resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f);
+ }
+
+ return resolution;
+}
+
+static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids,
+ const float threshold,
+ const float adaptivity,
+ const bke::VolumeToMeshResolution &resolution)
+{
+ Array<bke::OpenVDBMeshData> mesh_data(grids.size());
+ for (const int i : grids.index_range()) {
+ mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity);
+ }
+
+ int vert_offset = 0;
+ int poly_offset = 0;
+ int loop_offset = 0;
+ Array<int> vert_offsets(mesh_data.size());
+ Array<int> poly_offsets(mesh_data.size());
+ Array<int> loop_offsets(mesh_data.size());
+ for (const int i : grids.index_range()) {
+ const bke::OpenVDBMeshData &data = mesh_data[i];
+ vert_offsets[i] = vert_offset;
+ poly_offsets[i] = poly_offset;
+ loop_offsets[i] = loop_offset;
+ vert_offset += data.verts.size();
+ poly_offset += (data.tris.size() + data.quads.size());
+ loop_offset += (3 * data.tris.size() + 4 * data.quads.size());
}
- const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
- const Volume *volume = component->get_for_read();
+ Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ for (const int i : grids.index_range()) {
+ const bke::OpenVDBMeshData &data = mesh_data[i];
+ bke::fill_mesh_from_openvdb_data(data.verts,
+ data.tris,
+ data.quads,
+ vert_offsets[i],
+ poly_offsets[i],
+ loop_offsets[i],
+ verts,
+ polys,
+ loops);
+ }
+
+ BKE_mesh_calc_edges(mesh, false, false);
+ BKE_mesh_normals_tag_dirty(mesh);
+
+ return mesh;
+}
+
+static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams &params)
+{
+ const Volume *volume = geometry_set.get_volume_for_read();
if (volume == nullptr) {
- return;
+ return nullptr;
}
+ const bke::VolumeToMeshResolution resolution = get_resolution_param(params);
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
- const std::string grid_name = params.get_input<std::string>("Density");
- const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
- if (volume_grid == nullptr) {
- return;
+ Vector<openvdb::GridBase::ConstPtr> grids;
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ grids.append(std::move(grid));
}
- float threshold = params.get_input<float>("Threshold");
- float adaptivity = params.get_input<float>("Adaptivity");
-
- const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
- Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity);
- if (mesh == nullptr) {
- return;
+ if (grids.is_empty()) {
+ return nullptr;
}
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
- dst_component.replace(mesh);
+
+ return create_mesh_from_volume_grids(grids,
+ params.get_input<float>("Threshold"),
+ params.get_input<float>("Adaptivity"),
+ resolution);
}
#endif /* WITH_OPENVDB */
static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
- GeometrySet geometry_set_out;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
#ifdef WITH_OPENVDB
- create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = create_mesh_from_volume(geometry_set, params);
+ geometry_set.replace_mesh(mesh);
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
- params.set_output("Geometry", geometry_set_out);
+ params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -163,7 +202,7 @@ void register_node_type_geo_volume_to_mesh()
ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare;
node_type_storage(
&ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
- node_type_size(&ntype, 200, 120, 700);
+ node_type_size(&ntype, 170, 120, 700);
node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init);
node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update);
ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec;
diff --git a/source/blender/nodes/intern/extern_implementations.cc b/source/blender/nodes/intern/extern_implementations.cc
index 42d4b2878bc..35de319f20b 100644
--- a/source/blender/nodes/intern/extern_implementations.cc
+++ b/source/blender/nodes/intern/extern_implementations.cc
@@ -15,6 +15,7 @@
*/
#include "NOD_socket_declarations.hh"
+#include "NOD_socket_declarations_geometry.hh"
namespace blender::nodes {
#define MAKE_EXTERN_SOCKET_IMPLEMENTATION(TYPE) \
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 852f52f38cd..ddd3c991518 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -21,9 +21,16 @@
#include "DNA_modifier_types.h"
#include "DNA_space_types.h"
+#include "FN_field_cpp_type.hh"
+
+#include "BLT_translation.h"
+
namespace blender::nodes::geometry_nodes_eval_log {
using fn::CPPType;
+using fn::FieldCPPType;
+using fn::FieldInput;
+using fn::GField;
ModifierLog::ModifierLog(GeoLogger &logger)
: input_geometry_log_(std::move(logger.input_geometry_log_)),
@@ -168,6 +175,34 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket
return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index);
}
+GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type())
+{
+ Set<std::reference_wrapper<const FieldInput>> field_inputs_set;
+ field.node().foreach_field_input(
+ [&](const FieldInput &field_input) { field_inputs_set.add(field_input); });
+
+ Vector<std::reference_wrapper<const FieldInput>> field_inputs;
+ field_inputs.extend(field_inputs_set.begin(), field_inputs_set.end());
+
+ std::sort(
+ field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) {
+ const int index_a = (int)a.category();
+ const int index_b = (int)b.category();
+ if (index_a == index_b) {
+ return a.socket_inspection_name().size() < b.socket_inspection_name().size();
+ }
+ return index_a < index_b;
+ });
+
+ for (const FieldInput &field_input : field_inputs) {
+ input_tooltips_.append(field_input.socket_inspection_name());
+ }
+
+ if (log_full_field) {
+ field_ = std::move(field);
+ }
+}
+
GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry)
{
static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
@@ -175,13 +210,19 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
GEO_COMPONENT_TYPE_MESH,
GEO_COMPONENT_TYPE_POINT_CLOUD,
GEO_COMPONENT_TYPE_VOLUME};
+
+ /* Keep track handled attribute names to make sure that we do not return the same name twice.
+ * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge
+ * attributes with the same name but different domains or data types on separate components. */
+ Set<StringRef> names;
+
geometry_set.attribute_foreach(
all_component_types,
true,
[&](const bke::AttributeIDRef &attribute_id,
const AttributeMetaData &meta_data,
const GeometryComponent &UNUSED(component)) {
- if (attribute_id.is_named()) {
+ if (attribute_id.is_named() && names.add(attribute_id.name())) {
this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
}
});
@@ -376,6 +417,26 @@ void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value
geometry_set, log_full_geometry);
values_.append({copied_sockets, std::move(value_log)});
}
+ else if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) {
+ GField field = field_type->get_gfield(value.get());
+ bool log_full_field = false;
+ if (!field.node().depends_on_input()) {
+ /* Always log constant fields so that their value can be shown in socket inspection.
+ * In the future we can also evaluate the field here and only store the value. */
+ log_full_field = true;
+ }
+ if (!log_full_field) {
+ for (const DSocket &socket : sockets) {
+ if (main_logger_->log_full_sockets_.contains(socket)) {
+ log_full_field = true;
+ break;
+ }
+ }
+ }
+ destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>(
+ std::move(field), log_full_field);
+ values_.append({copied_sockets, std::move(value_log)});
+ }
else {
void *buffer = allocator_->allocate(type.size(), type.alignment());
type.copy_construct(value.get(), buffer);
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a3bbca90731..c7a3e795c33 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -36,6 +36,72 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin
local_logger.log_node_warning(provider_->dnode, type, std::move(message));
}
+void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
+ const GeometrySet &geometry_set) const
+{
+ const int input_index = provider_->dnode->input_by_identifier(identifier).index();
+ const SocketDeclaration &decl = *provider_->dnode->declaration()->inputs()[input_index];
+ const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl);
+ if (geo_decl == nullptr) {
+ return;
+ }
+
+ const bool only_realized_data = geo_decl->only_realized_data();
+ const bool only_instances = geo_decl->only_instances();
+ const Span<GeometryComponentType> supported_types = geo_decl->supported_types();
+
+ if (only_realized_data) {
+ if (geometry_set.has_instances()) {
+ this->error_message_add(NodeWarningType::Info,
+ TIP_("Instances in input geometry are ignored"));
+ }
+ }
+ if (only_instances) {
+ if (geometry_set.has_realized_data()) {
+ this->error_message_add(NodeWarningType::Info,
+ TIP_("Realized data in input geometry is ignored"));
+ }
+ }
+ if (supported_types.is_empty()) {
+ /* Assume all types are supported. */
+ return;
+ }
+ const Vector<GeometryComponentType> types_in_geometry = geometry_set.gather_component_types(
+ true, true);
+ for (const GeometryComponentType type : types_in_geometry) {
+ if (type == GEO_COMPONENT_TYPE_INSTANCES) {
+ continue;
+ }
+ if (supported_types.contains(type)) {
+ continue;
+ }
+ std::string message = TIP_("Input geometry has unsupported type: ");
+ switch (type) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ message += TIP_("Mesh");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ message += TIP_("Point Cloud");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ BLI_assert_unreachable();
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ message += TIP_("Volume");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ message += TIP_("Curve");
+ break;
+ }
+ }
+ this->error_message_add(NodeWarningType::Info, std::move(message));
+ }
+}
+
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
for (const InputSocketRef *socket : provider_->dnode->inputs()) {
@@ -183,6 +249,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
+std::string GeoNodeExecParams::attribute_producer_name() const
+{
+ return provider_->dnode->label_or_name() + TIP_(" node");
+}
+
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{
diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc
index e823476f9e4..ed5691ebf7f 100644
--- a/source/blender/nodes/intern/node_socket_declarations.cc
+++ b/source/blender/nodes/intern/node_socket_declarations.cc
@@ -15,6 +15,7 @@
*/
#include "NOD_socket_declarations.hh"
+#include "NOD_socket_declarations_geometry.hh"
#include "BKE_node.h"
@@ -333,6 +334,46 @@ bool Geometry::matches(const bNodeSocket &socket) const
return true;
}
+Span<GeometryComponentType> Geometry::supported_types() const
+{
+ return supported_types_;
+}
+
+bool Geometry::only_realized_data() const
+{
+ return only_realized_data_;
+}
+
+bool Geometry::only_instances() const
+{
+ return only_instances_;
+}
+
+GeometryBuilder &GeometryBuilder::supported_type(GeometryComponentType supported_type)
+{
+ decl_->supported_types_ = {supported_type};
+ return *this;
+}
+
+GeometryBuilder &GeometryBuilder::supported_type(
+ blender::Vector<GeometryComponentType> supported_types)
+{
+ decl_->supported_types_ = std::move(supported_types);
+ return *this;
+}
+
+GeometryBuilder &GeometryBuilder::only_realized_data(bool value)
+{
+ decl_->only_realized_data_ = value;
+ return *this;
+}
+
+GeometryBuilder &GeometryBuilder::only_instances(bool value)
+{
+ decl_->only_instances_ = value;
+ return *this;
+}
+
/** \} */
} // namespace blender::nodes::decl
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 2ca797009da..5481465aef6 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -502,11 +502,15 @@ bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const
return this->any_output_is_directly_linked();
}
-/**
- * Sort nodes topologically from left to right or right to left.
- * In the future the result if this could be cached on #NodeTreeRef.
- */
-Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) const
+struct ToposortNodeState {
+ bool is_done = false;
+ bool is_in_stack = false;
+};
+
+static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction,
+ const NodeRef &start_node,
+ MutableSpan<ToposortNodeState> node_states,
+ NodeTreeRef::ToposortResult &result)
{
struct Item {
const NodeRef *node;
@@ -516,64 +520,97 @@ Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction)
int link_index = 0;
};
- Vector<const NodeRef *> toposort;
- toposort.reserve(nodes_by_id_.size());
- Array<bool> node_is_done_by_id(nodes_by_id_.size(), false);
- Stack<Item> nodes_to_check;
+ /* Do a depth-first search to sort nodes topologically. */
+ Stack<Item, 64> nodes_to_check;
+ nodes_to_check.push({&start_node});
+ while (!nodes_to_check.is_empty()) {
+ Item &item = nodes_to_check.peek();
+ const NodeRef &node = *item.node;
+ const Span<const SocketRef *> sockets = node.sockets(
+ direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
+
+ while (true) {
+ if (item.socket_index == sockets.size()) {
+ /* All sockets have already been visited. */
+ break;
+ }
+ const SocketRef &socket = *sockets[item.socket_index];
+ const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
+ if (item.link_index == linked_sockets.size()) {
+ /* All links connected to this socket have already been visited. */
+ item.socket_index++;
+ item.link_index = 0;
+ continue;
+ }
+ const SocketRef &linked_socket = *linked_sockets[item.link_index];
+ const NodeRef &linked_node = linked_socket.node();
+ ToposortNodeState &linked_node_state = node_states[linked_node.id()];
+ if (linked_node_state.is_done) {
+ /* The linked node has already been visited. */
+ item.link_index++;
+ continue;
+ }
+ if (linked_node_state.is_in_stack) {
+ result.has_cycle = true;
+ }
+ else {
+ nodes_to_check.push({&linked_node});
+ linked_node_state.is_in_stack = true;
+ }
+ break;
+ }
- for (const NodeRef *start_node : nodes_by_id_) {
- if (node_is_done_by_id[start_node->id()]) {
+ /* If no other element has been pushed, the current node can be pushed to the sorted list. */
+ if (&item == &nodes_to_check.peek()) {
+ ToposortNodeState &node_state = node_states[node.id()];
+ node_state.is_done = true;
+ node_state.is_in_stack = false;
+ result.sorted_nodes.append(&node);
+ nodes_to_check.pop();
+ }
+ }
+}
+
+/**
+ * Sort nodes topologically from left to right or right to left.
+ * In the future the result if this could be cached on #NodeTreeRef.
+ */
+NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const
+{
+ ToposortResult result;
+ result.sorted_nodes.reserve(nodes_by_id_.size());
+
+ Array<ToposortNodeState> node_states(nodes_by_id_.size());
+
+ for (const NodeRef *node : nodes_by_id_) {
+ if (node_states[node->id()].is_done) {
/* Ignore nodes that are done already. */
continue;
}
- if (start_node->any_socket_is_directly_linked(
+ if (node->any_socket_is_directly_linked(
direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
/* Ignore non-start nodes. */
continue;
}
- /* Do a depth-first search to sort nodes topologically. */
- nodes_to_check.push({start_node});
- while (!nodes_to_check.is_empty()) {
- Item &item = nodes_to_check.peek();
- const NodeRef &node = *item.node;
- const Span<const SocketRef *> sockets = node.sockets(
- direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
-
- while (true) {
- if (item.socket_index == sockets.size()) {
- /* All sockets have already been visited. */
- break;
- }
- const SocketRef &socket = *sockets[item.socket_index];
- const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
- if (item.link_index == linked_sockets.size()) {
- /* All links connected to this socket have already been visited. */
- item.socket_index++;
- item.link_index = 0;
- continue;
- }
- const SocketRef &linked_socket = *linked_sockets[item.link_index];
- const NodeRef &linked_node = linked_socket.node();
- if (node_is_done_by_id[linked_node.id()]) {
- /* The linked node has already been visited. */
- item.link_index++;
- continue;
- }
- nodes_to_check.push({&linked_node});
- break;
- }
+ toposort_from_start_node(direction, *node, node_states, result);
+ }
- /* If no other element has been pushed, the current node can be pushed to the sorted list. */
- if (&item == &nodes_to_check.peek()) {
- node_is_done_by_id[node.id()] = true;
- toposort.append(&node);
- nodes_to_check.pop();
+ /* Check if the loop above forgot some nodes because there is a cycle. */
+ if (result.sorted_nodes.size() < nodes_by_id_.size()) {
+ result.has_cycle = true;
+ for (const NodeRef *node : nodes_by_id_) {
+ if (node_states[node->id()].is_done) {
+ /* Ignore nodes that are done already. */
+ continue;
}
+ /* Start toposort at this node which is somewhere in the middle of a loop. */
+ toposort_from_start_node(direction, *node, node_states, result);
}
}
- return toposort;
+ BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size());
+ return result;
}
const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const
diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
index e8d4239937f..57a992a4275 100644
--- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void sh_node_clamp_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Value").min(0.0f).max(1.0f).default_value(1.0f);
- b.add_input<decl::Float>("Min").default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_output<decl::Float>("Result");
+ b.add_input<decl::Float>(N_("Value")).min(0.0f).max(1.0f).default_value(1.0f);
+ b.add_input<decl::Float>(N_("Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Float>(N_("Result"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index 875e6fa0c35..f8f0ee97eae 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -28,9 +28,9 @@ namespace blender::nodes {
static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Vector>("Vector").min(-1.0f).max(1.0f);
- b.add_output<decl::Vector>("Vector");
+ b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>(N_("Vector")).min(-1.0f).max(1.0f);
+ b.add_output<decl::Vector>(N_("Vector"));
};
} // namespace blender::nodes
@@ -175,9 +175,9 @@ namespace blender::nodes {
static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_output<decl::Color>("Color");
+ b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Color>(N_("Color"));
};
} // namespace blender::nodes
@@ -352,9 +352,13 @@ namespace blender::nodes {
static void sh_node_curve_float_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Value").default_value(1.0f);
- b.add_output<decl::Float>("Value");
+ b.add_input<decl::Float>(N_("Factor"))
+ .min(0.0f)
+ .max(1.0f)
+ .default_value(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Value")).default_value(1.0f);
+ b.add_output<decl::Float>(N_("Value"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 5ea194ddc83..c866a154e8c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -30,13 +30,13 @@ namespace blender::nodes {
static void sh_node_map_range_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f);
- b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
- b.add_input<decl::Float>("To Min").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f);
- b.add_input<decl::Float>("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f);
- b.add_output<decl::Float>("Result");
+ b.add_input<decl::Float>(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f);
+ b.add_input<decl::Float>(N_("From Min")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f);
+ b.add_input<decl::Float>(N_("To Min")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f);
+ b.add_input<decl::Float>(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f);
+ b.add_output<decl::Float>(N_("Result"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 96d1be49c04..284a5f1189f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -32,10 +32,16 @@ namespace blender::nodes {
static void sh_node_math_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f);
- b.add_output<decl::Float>("Value");
+ b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"), "Value_001")
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"), "Value_002")
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f);
+ b.add_output<decl::Float>(N_("Value"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index d4d02e80ada..06fafff578e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::Color>("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_output<decl::Color>("Color");
+ b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Color1")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::Color>(N_("Color2")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_output<decl::Color>(N_("Color"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 24c5dcf7ba3..08a9e01786e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void sh_node_seprgb_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Float>("R");
- b.add_output<decl::Float>("G");
- b.add_output<decl::Float>("B");
+ b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_output<decl::Float>(N_("R"));
+ b.add_output<decl::Float>(N_("G"));
+ b.add_output<decl::Float>(N_("B"));
};
} // namespace blender::nodes
@@ -121,10 +121,10 @@ namespace blender::nodes {
static void sh_node_combrgb_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("R").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("G").min(0.0f).max(1.0f);
- b.add_input<decl::Float>("B").min(0.0f).max(1.0f);
- b.add_output<decl::Color>("Image");
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
+ b.add_output<decl::Color>(N_("Image"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index 8ca8fc19521..1bbfa629462 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -28,10 +28,10 @@ namespace blender::nodes {
static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
- b.add_output<decl::Float>("X");
- b.add_output<decl::Float>("Y");
- b.add_output<decl::Float>("Z");
+ b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Float>(N_("X"));
+ b.add_output<decl::Float>(N_("Y"));
+ b.add_output<decl::Float>(N_("Z"));
};
} // namespace blender::nodes
@@ -105,10 +105,10 @@ namespace blender::nodes {
static void sh_node_combxyz_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f);
- b.add_output<decl::Vector>("Vector");
+ b.add_input<decl::Float>(N_("X")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Y")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Z")).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Vector>(N_("Vector"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
index e90dae60189..b840bd75e42 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
@@ -19,39 +19,42 @@
#include "../node_shader_util.h"
+#include "BLI_float2.hh"
+#include "BLI_float4.hh"
+
namespace blender::nodes {
static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field();
- b.add_input<decl::Color>("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f});
- b.add_input<decl::Color>("Mortar").default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links();
- b.add_input<decl::Float>("Scale")
+ b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field();
+ b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f});
+ b.add_input<decl::Color>(N_("Mortar")).default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links();
+ b.add_input<decl::Float>(N_("Scale"))
.min(-1000.0f)
.max(1000.0f)
.default_value(5.0f)
.no_muted_links();
- b.add_input<decl::Float>("Mortar Size")
+ b.add_input<decl::Float>(N_("Mortar Size"))
.min(0.0f)
.max(0.125f)
.default_value(0.02f)
.no_muted_links();
- b.add_input<decl::Float>("Mortar Smooth").min(0.0f).max(1.0f).no_muted_links();
- b.add_input<decl::Float>("Bias").min(-1.0f).max(1.0f).no_muted_links();
- b.add_input<decl::Float>("Brick Width")
+ b.add_input<decl::Float>(N_("Mortar Smooth")).min(0.0f).max(1.0f).no_muted_links();
+ b.add_input<decl::Float>(N_("Bias")).min(-1.0f).max(1.0f).no_muted_links();
+ b.add_input<decl::Float>(N_("Brick Width"))
.min(0.01f)
.max(100.0f)
.default_value(0.5f)
.no_muted_links();
- b.add_input<decl::Float>("Row Height")
+ b.add_input<decl::Float>(N_("Row Height"))
.min(0.01f)
.max(100.0f)
.default_value(0.25f)
.no_muted_links();
- b.add_output<decl::Color>("Color");
- b.add_output<decl::Float>("Fac");
+ b.add_output<decl::Color>(N_("Color"));
+ b.add_output<decl::Float>(N_("Fac"));
};
} // namespace blender::nodes
@@ -98,18 +101,185 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat,
GPU_constant(&squash_freq));
}
-/* node type definition */
+namespace blender::nodes {
+
+class BrickFunction : public fn::MultiFunction {
+ private:
+ const float offset_;
+ const int offset_freq_;
+ const float squash_;
+ const int squash_freq_;
+
+ public:
+ BrickFunction(const float offset,
+ const int offset_freq,
+ const float squash,
+ const int squash_freq)
+ : offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"BrickTexture"};
+ signature.single_input<float3>("Vector");
+ signature.single_input<ColorGeometry4f>("Color1");
+ signature.single_input<ColorGeometry4f>("Color2");
+ signature.single_input<ColorGeometry4f>("Mortar");
+ signature.single_input<float>("Scale");
+ signature.single_input<float>("Mortar Size");
+ signature.single_input<float>("Mortar Smooth");
+ signature.single_input<float>("Bias");
+ signature.single_input<float>("Brick Width");
+ signature.single_input<float>("Row Height");
+ signature.single_output<ColorGeometry4f>("Color");
+ signature.single_output<float>("Fac");
+ return signature.build();
+ }
+
+ /* Fast integer noise. */
+ static float brick_noise(uint n)
+ {
+ n = (n + 1013) & 0x7fffffff;
+ n = (n >> 13) ^ n;
+ const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ return 0.5f * ((float)nn / 1073741824.0f);
+ }
+
+ static float smoothstepf(const float f)
+ {
+ const float ff = f * f;
+ return (3.0f * ff - 2.0f * ff * f);
+ }
+
+ static float2 brick(float3 p,
+ float mortar_size,
+ float mortar_smooth,
+ float bias,
+ float brick_width,
+ float row_height,
+ float offset_amount,
+ int offset_frequency,
+ float squash_amount,
+ int squash_frequency)
+ {
+ float offset = 0.0f;
+
+ const int rownum = (int)floorf(p.y / row_height);
+
+ if (offset_frequency && squash_frequency) {
+ brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount;
+ offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount);
+ }
+
+ const int bricknum = (int)floorf((p.x + offset) / brick_width);
+
+ const float x = (p.x + offset) - brick_width * bricknum;
+ const float y = p.y - row_height * rownum;
+
+ const float tint = clamp_f(
+ brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f);
+ float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y));
+
+ float mortar;
+ if (min_dist >= mortar_size) {
+ mortar = 0.0f;
+ }
+ else if (mortar_smooth == 0.0f) {
+ mortar = 1.0f;
+ }
+ else {
+ min_dist = 1.0f - min_dist / mortar_size;
+ mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f;
+ }
+
+ return float2(tint, mortar);
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector");
+ const VArray<ColorGeometry4f> &color1_values = params.readonly_single_input<ColorGeometry4f>(
+ 1, "Color1");
+ const VArray<ColorGeometry4f> &color2_values = params.readonly_single_input<ColorGeometry4f>(
+ 2, "Color2");
+ const VArray<ColorGeometry4f> &mortar_values = params.readonly_single_input<ColorGeometry4f>(
+ 3, "Mortar");
+ const VArray<float> &scale = params.readonly_single_input<float>(4, "Scale");
+ const VArray<float> &mortar_size = params.readonly_single_input<float>(5, "Mortar Size");
+ const VArray<float> &mortar_smooth = params.readonly_single_input<float>(6, "Mortar Smooth");
+ const VArray<float> &bias = params.readonly_single_input<float>(7, "Bias");
+ const VArray<float> &brick_width = params.readonly_single_input<float>(8, "Brick Width");
+ const VArray<float> &row_height = params.readonly_single_input<float>(9, "Row Height");
+
+ MutableSpan<ColorGeometry4f> r_color =
+ params.uninitialized_single_output_if_required<ColorGeometry4f>(10, "Color");
+ MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(11, "Fac");
+
+ const bool store_fac = !r_fac.is_empty();
+ const bool store_color = !r_color.is_empty();
+
+ for (int64_t i : mask) {
+ const float2 f2 = brick(vector[i] * scale[i],
+ mortar_size[i],
+ mortar_smooth[i],
+ bias[i],
+ brick_width[i],
+ row_height[i],
+ offset_,
+ offset_freq_,
+ squash_,
+ squash_freq_);
+
+ float4 color_data, color1, color2, mortar;
+ copy_v4_v4(color_data, color1_values[i]);
+ copy_v4_v4(color1, color1_values[i]);
+ copy_v4_v4(color2, color2_values[i]);
+ copy_v4_v4(mortar, mortar_values[i]);
+ const float tint = f2.x;
+ const float f = f2.y;
+
+ if (f != 1.0f) {
+ const float facm = 1.0f - tint;
+ color_data = color1 * facm + color2 * tint;
+ }
+
+ if (store_color) {
+ color_data = color_data * (1.0f - f) + mortar * f;
+ copy_v4_v4(r_color[i], color_data);
+ }
+ if (store_fac) {
+ r_fac[i] = f;
+ }
+ }
+ }
+};
+
+static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
+{
+ bNode &node = builder.node();
+ NodeTexBrick *tex = (NodeTexBrick *)node.storage;
+
+ builder.construct_and_set_matching_fn<BrickFunction>(
+ tex->offset, tex->offset_freq, tex->squash, tex->squash_freq);
+}
+
+} // namespace blender::nodes
+
void register_node_type_sh_tex_brick(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0);
ntype.declare = blender::nodes::sh_node_tex_brick_declare;
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, node_shader_init_tex_brick);
node_type_storage(
&ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_brick);
+ ntype.build_multi_function = blender::nodes::sh_node_brick_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc
index 7ba468a93e0..7c1223a6a32 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc
@@ -24,16 +24,16 @@ namespace blender::nodes {
static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field();
- b.add_input<decl::Color>("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f});
- b.add_input<decl::Float>("Scale")
+ b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field();
+ b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f});
+ b.add_input<decl::Float>(N_("Scale"))
.min(-10000.0f)
.max(10000.0f)
.default_value(5.0f)
.no_muted_links();
- b.add_output<decl::Color>("Color");
- b.add_output<decl::Float>("Fac");
+ b.add_output<decl::Color>(N_("Color"));
+ b.add_output<decl::Float>(N_("Fac"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
index 4796af02361..33832c42b3c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
@@ -24,9 +24,9 @@ namespace blender::nodes {
static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").hide_value().implicit_field();
- b.add_output<decl::Color>("Color").no_muted_links();
- b.add_output<decl::Float>("Fac").no_muted_links();
+ b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field();
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
+ b.add_output<decl::Float>(N_("Fac")).no_muted_links();
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
index df1051c07b4..f20fc85cbe0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
@@ -24,9 +24,9 @@ namespace blender::nodes {
static void sh_node_tex_image_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").implicit_field();
- b.add_output<decl::Color>("Color").no_muted_links();
- b.add_output<decl::Float>("Alpha").no_muted_links();
+ b.add_input<decl::Vector>(N_("Vector")).implicit_field();
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
+ b.add_output<decl::Float>(N_("Alpha")).no_muted_links();
};
}; // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
index b6cdcf86528..62e68d53d03 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
@@ -24,11 +24,11 @@ namespace blender::nodes {
static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").implicit_field();
- b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
- b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(1.0f);
- b.add_output<decl::Color>("Color").no_muted_links();
- b.add_output<decl::Float>("Fac").no_muted_links();
+ b.add_input<decl::Vector>(N_("Vector")).implicit_field();
+ b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
+ b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(1.0f);
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
+ b.add_output<decl::Float>(N_("Fac")).no_muted_links();
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
index 61c26d07e2f..3bf4e24ed53 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
@@ -26,15 +26,15 @@ namespace blender::nodes {
static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").hide_value().implicit_field();
- b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
- b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
- b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f);
- b.add_input<decl::Float>("Dimension").min(0.0f).max(1000.0f).default_value(2.0f);
- b.add_input<decl::Float>("Lacunarity").min(0.0f).max(1000.0f).default_value(2.0f);
- b.add_input<decl::Float>("Offset").min(-1000.0f).max(1000.0f);
- b.add_input<decl::Float>("Gain").min(0.0f).max(1000.0f).default_value(1.0f);
- b.add_output<decl::Float>("Fac").no_muted_links();
+ b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field();
+ b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f);
+ b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
+ b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f);
+ b.add_input<decl::Float>(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f);
+ b.add_input<decl::Float>(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f);
+ b.add_input<decl::Float>(N_("Offset")).min(-1000.0f).max(1000.0f);
+ b.add_input<decl::Float>(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f);
+ b.add_output<decl::Float>(N_("Fac")).no_muted_links();
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
index 6ffc8979815..72892c32795 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
@@ -26,18 +26,18 @@ namespace blender::nodes {
static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").implicit_field();
- b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
- b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
- b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f);
- b.add_input<decl::Float>("Roughness")
+ b.add_input<decl::Vector>(N_("Vector")).implicit_field();
+ b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f);
+ b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
+ b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f);
+ b.add_input<decl::Float>(N_("Roughness"))
.min(0.0f)
.max(1.0f)
.default_value(0.5f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f);
- b.add_output<decl::Float>("Fac").no_muted_links();
- b.add_output<decl::Color>("Color").no_muted_links();
+ b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f);
+ b.add_output<decl::Float>(N_("Fac")).no_muted_links();
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
index 574260f3c36..422268b98c3 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc
@@ -26,25 +26,25 @@ namespace blender::nodes {
static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").hide_value().implicit_field();
- b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
- b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
- b.add_input<decl::Float>("Smoothness")
+ b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field();
+ b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f);
+ b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
+ b.add_input<decl::Float>(N_("Smoothness"))
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Exponent").min(0.0f).max(32.0f).default_value(0.5f);
- b.add_input<decl::Float>("Randomness")
+ b.add_input<decl::Float>(N_("Exponent")).min(0.0f).max(32.0f).default_value(0.5f);
+ b.add_input<decl::Float>(N_("Randomness"))
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR);
- b.add_output<decl::Float>("Distance").no_muted_links();
- b.add_output<decl::Color>("Color").no_muted_links();
- b.add_output<decl::Vector>("Position").no_muted_links();
- b.add_output<decl::Float>("W").no_muted_links();
- b.add_output<decl::Float>("Radius").no_muted_links();
+ b.add_output<decl::Float>(N_("Distance")).no_muted_links();
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
+ b.add_output<decl::Vector>(N_("Position")).no_muted_links();
+ b.add_output<decl::Float>(N_("W")).no_muted_links();
+ b.add_output<decl::Float>(N_("Radius")).no_muted_links();
};
} // namespace blender::nodes
@@ -239,16 +239,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
return params.readonly_single_input<float>(param_index, "Randomness");
};
auto get_r_distance = [&](int param_index) -> MutableSpan<float> {
- return params.uninitialized_single_output<float>(param_index, "Distance");
+ return params.uninitialized_single_output_if_required<float>(param_index, "Distance");
};
auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> {
- return params.uninitialized_single_output<ColorGeometry4f>(param_index, "Color");
+ return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color");
};
auto get_r_position = [&](int param_index) -> MutableSpan<float3> {
- return params.uninitialized_single_output<float3>(param_index, "Position");
+ return params.uninitialized_single_output_if_required<float3>(param_index, "Position");
};
auto get_r_w = [&](int param_index) -> MutableSpan<float> {
- return params.uninitialized_single_output<float>(param_index, "W");
+ return params.uninitialized_single_output_if_required<float>(param_index, "W");
};
int param = 0;
@@ -263,6 +263,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -271,12 +274,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -288,6 +295,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -296,12 +306,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -314,6 +328,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
@@ -324,12 +341,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -346,6 +367,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -353,11 +377,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
}
break;
}
@@ -369,6 +397,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -376,11 +407,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
}
break;
}
@@ -393,6 +428,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
@@ -402,11 +440,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
exponent[i],
rand,
SHD_VORONOI_MINKOWSKI,
- &r_distance[i],
- &col,
- &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
}
break;
}
@@ -425,16 +467,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_f1(p, exponent[i], rand, SHD_VORONOI_F1, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_f1(p,
+ exponent[i],
+ rand,
+ SHD_VORONOI_F1,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
@@ -448,17 +508,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_f2(
- p, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_f2(p,
+ exponent[i],
+ rand,
+ SHD_VORONOI_MINKOWSKI,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
@@ -473,18 +550,36 @@ class VoronoiMinowskiFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_smooth_f1(
- p, smth, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_smooth_f1(p,
+ smth,
+ exponent[i],
+ rand,
+ SHD_VORONOI_MINKOWSKI,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
@@ -572,16 +667,16 @@ class VoronoiMetricFunction : public fn::MultiFunction {
return params.readonly_single_input<float>(param_index, "Randomness");
};
auto get_r_distance = [&](int param_index) -> MutableSpan<float> {
- return params.uninitialized_single_output<float>(param_index, "Distance");
+ return params.uninitialized_single_output_if_required<float>(param_index, "Distance");
};
auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> {
- return params.uninitialized_single_output<ColorGeometry4f>(param_index, "Color");
+ return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color");
};
auto get_r_position = [&](int param_index) -> MutableSpan<float3> {
- return params.uninitialized_single_output<float3>(param_index, "Position");
+ return params.uninitialized_single_output_if_required<float3>(param_index, "Position");
};
auto get_r_w = [&](int param_index) -> MutableSpan<float> {
- return params.uninitialized_single_output<float>(param_index, "W");
+ return params.uninitialized_single_output_if_required<float>(param_index, "W");
};
int param = 0;
@@ -595,13 +690,24 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float p = w[i] * scale[i];
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
- noise::voronoi_f1(p, rand, &r_distance[i], &col, &r_w[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_w[i] = safe_divide(r_w[i], scale[i]);
+ noise::voronoi_f1(p,
+ rand,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_w ? &r_w[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_w) {
+ r_w[i] = safe_divide(r_w[i], scale[i]);
+ }
}
break;
}
@@ -612,13 +718,24 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float p = w[i] * scale[i];
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
- noise::voronoi_f2(p, rand, &r_distance[i], &col, &r_w[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_w[i] = safe_divide(r_w[i], scale[i]);
+ noise::voronoi_f2(p,
+ rand,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_w ? &r_w[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_w) {
+ r_w[i] = safe_divide(r_w[i], scale[i]);
+ }
}
break;
}
@@ -630,14 +747,26 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float p = w[i] * scale[i];
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
- noise::voronoi_smooth_f1(p, smth, rand, &r_distance[i], &col, &r_w[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_w[i] = safe_divide(r_w[i], scale[i]);
+ noise::voronoi_smooth_f1(p,
+ smth,
+ rand,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_w ? &r_w[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_w) {
+ r_w[i] = safe_divide(r_w[i], scale[i]);
+ }
}
break;
}
@@ -653,6 +782,9 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -661,12 +793,16 @@ class VoronoiMetricFunction : public fn::MultiFunction {
0.0f,
rand,
metric_,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -677,6 +813,9 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
@@ -685,12 +824,16 @@ class VoronoiMetricFunction : public fn::MultiFunction {
0.0f,
rand,
metric_,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -702,6 +845,9 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
@@ -712,12 +858,16 @@ class VoronoiMetricFunction : public fn::MultiFunction {
0.0f,
rand,
metric_,
- &r_distance[i],
- &col,
- &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float2::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, 0.0f);
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ pos = float2::safe_divide(pos, scale[i]);
+ r_position[i] = float3(pos.x, pos.y, 0.0f);
+ }
}
break;
}
@@ -733,13 +883,25 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
- noise::voronoi_f1(
- vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ noise::voronoi_f1(vector[i] * scale[i],
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
}
break;
}
@@ -750,13 +912,25 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
float3 col;
- noise::voronoi_f2(
- vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ noise::voronoi_f2(vector[i] * scale[i],
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
}
break;
}
@@ -768,21 +942,31 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<float> r_distance = get_r_distance(param++);
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
- for (int64_t i : mask) {
- const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
- const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
- float3 col;
- noise::voronoi_smooth_f1(vector[i] * scale[i],
- smth,
- 0.0f,
- rand,
- metric_,
- &r_distance[i],
- &col,
- &r_position[i]);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ {
+ for (int64_t i : mask) {
+ const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
+ const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
+ float3 col;
+ noise::voronoi_smooth_f1(vector[i] * scale[i],
+ smth,
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position ? &r_position[i] : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position) {
+ r_position[i] = float3::safe_divide(r_position[i], scale[i]);
+ }
+ }
}
+
break;
}
}
@@ -799,16 +983,34 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_f1(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_f1(p,
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
@@ -821,16 +1023,34 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_f2(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_f2(p,
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
@@ -844,17 +1064,36 @@ class VoronoiMetricFunction : public fn::MultiFunction {
MutableSpan<ColorGeometry4f> r_color = get_r_color(param++);
MutableSpan<float3> r_position = get_r_position(param++);
MutableSpan<float> r_w = get_r_w(param++);
+ const bool calc_distance = !r_distance.is_empty();
+ const bool calc_color = !r_color.is_empty();
+ const bool calc_position = !r_position.is_empty();
+ const bool calc_w = !r_w.is_empty();
for (int64_t i : mask) {
const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f);
const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f);
const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i];
float3 col;
float4 pos;
- noise::voronoi_smooth_f1(p, smth, 0.0f, rand, metric_, &r_distance[i], &col, &pos);
- r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
- pos = float4::safe_divide(pos, scale[i]);
- r_position[i] = float3(pos.x, pos.y, pos.z);
- r_w[i] = pos.w;
+ noise::voronoi_smooth_f1(p,
+ smth,
+ 0.0f,
+ rand,
+ metric_,
+ calc_distance ? &r_distance[i] : nullptr,
+ calc_color ? &col : nullptr,
+ calc_position || calc_w ? &pos : nullptr);
+ if (calc_color) {
+ r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f);
+ }
+ if (calc_position || calc_w) {
+ pos = float4::safe_divide(pos, scale[i]);
+ if (calc_position) {
+ r_position[i] = float3(pos.x, pos.y, pos.z);
+ }
+ if (calc_w) {
+ r_w[i] = pos.w;
+ }
+ }
}
break;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
index 25e65e3d3f0..144aa1885bd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
@@ -26,19 +26,19 @@ namespace blender::nodes {
static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").implicit_field();
- b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
- b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f);
- b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f);
- b.add_input<decl::Float>("Detail Scale").min(-1000.0f).max(1000.0f).default_value(1.0f);
- b.add_input<decl::Float>("Detail Roughness")
+ b.add_input<decl::Vector>(N_("Vector")).implicit_field();
+ b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
+ b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f);
+ b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f);
+ b.add_input<decl::Float>(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f);
+ b.add_input<decl::Float>(N_("Detail Roughness"))
.min(0.0f)
.max(1.0f)
.default_value(0.5f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Phase Offset").min(-1000.0f).max(1000.0f).default_value(0.0f);
- b.add_output<decl::Color>("Color").no_muted_links();
- b.add_output<decl::Float>("Fac").no_muted_links();
+ b.add_input<decl::Float>(N_("Phase Offset")).min(-1000.0f).max(1000.0f).default_value(0.0f);
+ b.add_output<decl::Color>(N_("Color")).no_muted_links();
+ b.add_output<decl::Float>(N_("Fac")).no_muted_links();
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
index 445b201e419..43ee9400551 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
@@ -26,10 +26,10 @@ namespace blender::nodes {
static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field();
- b.add_input<decl::Float>("W").min(-10000.0f).max(10000.0f);
- b.add_output<decl::Float>("Value");
- b.add_output<decl::Color>("Color");
+ b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field();
+ b.add_input<decl::Float>(N_("W")).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Float>(N_("Value"));
+ b.add_output<decl::Color>(N_("Color"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 2544ea1921c..e4f1b2c76f0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -34,9 +34,9 @@ namespace blender::nodes {
static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>("Color");
- b.add_output<decl::Float>("Alpha");
+ b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Color"));
+ b.add_output<decl::Float>(N_("Alpha"));
};
} // namespace blender::nodes
@@ -192,8 +192,8 @@ namespace blender::nodes {
static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>("Color").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_output<decl::Float>("Val");
+ b.add_input<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_output<decl::Float>(N_("Val"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 1344ce5c5d9..b0f152d8526 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -27,7 +27,7 @@ namespace blender::nodes {
static void sh_node_value_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Value");
+ b.add_output<decl::Float>(N_("Value"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index f49ff06cef1..ca5aeea9a7d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -30,12 +30,12 @@ namespace blender::nodes {
static void sh_node_vector_math_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Vector>("Vector", "Vector_001").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Vector>("Vector", "Vector_002").min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>("Scale").default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_output<decl::Vector>("Vector");
- b.add_output<decl::Float>("Value");
+ b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_002").min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_output<decl::Vector>(N_("Vector"));
+ b.add_output<decl::Float>(N_("Value"));
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index c9b26fa5199..1ab643bc3fa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -28,12 +28,12 @@ namespace blender::nodes {
static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value();
- b.add_input<decl::Vector>("Center");
- b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});
- b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER);
- b.add_output<decl::Vector>("Vector");
+ b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value();
+ b.add_input<decl::Vector>(N_("Center"));
+ b.add_input<decl::Vector>(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE);
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER);
+ b.add_output<decl::Vector>(N_("Vector"));
};
} // namespace blender::nodes
diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c
index 5e938db0c35..2211cc931da 100644
--- a/source/blender/python/generic/bl_math_py_api.c
+++ b/source/blender/python/generic/bl_math_py_api.c
@@ -99,21 +99,21 @@ static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args)
return PyFloat_FromDouble(a * (1.0 - x) + b * x);
}
-PyDoc_STRVAR(
- py_bl_math_smoothstep_doc,
- ".. function:: smoothstep(from_value, to_value, value)\n"
- "\n"
- " Performs smooth interpolation between 0 and 1 as value changes between from and to values.\n"
- " Outside the range the function returns the same value as the nearest edge.\n"
- "\n"
- " :arg from_value: The edge value where the result is 0.\n"
- " :type from_value: float\n"
- " :arg to_value: The edge value where the result is 1.\n"
- " :type to_value: float\n"
- " :arg factor: The interpolation value.\n"
- " :type factor: float\n"
- " :return: The interpolated value in [0.0, 1.0].\n"
- " :rtype: float\n");
+PyDoc_STRVAR(py_bl_math_smoothstep_doc,
+ ".. function:: smoothstep(from_value, to_value, value)\n"
+ "\n"
+ " Performs smooth interpolation between 0 and 1 as value changes between from and "
+ "to values.\n"
+ " Outside the range the function returns the same value as the nearest edge.\n"
+ "\n"
+ " :arg from_value: The edge value where the result is 0.\n"
+ " :type from_value: float\n"
+ " :arg to_value: The edge value where the result is 1.\n"
+ " :type to_value: float\n"
+ " :arg factor: The interpolation value.\n"
+ " :type factor: float\n"
+ " :return: The interpolated value in [0.0, 1.0].\n"
+ " :rtype: float\n");
static PyObject *py_bl_math_smoothstep(PyObject *UNUSED(self), PyObject *args)
{
double a, b, x;
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index d2a47a13db3..4de7c09640b 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -104,6 +104,11 @@ void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
SeqCollection *collection);
void SEQ_filter_selected_strips(SeqCollection *collection);
+/* Utilities to access these as tags. */
+int SEQ_query_rendered_strips_to_tag(ListBase *seqbase,
+ const int timeline_frame,
+ const int displayed_channel);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h
index c9024614dfd..df3c9a40409 100644
--- a/source/blender/sequencer/SEQ_time.h
+++ b/source/blender/sequencer/SEQ_time.h
@@ -32,6 +32,8 @@ struct Scene;
struct Sequence;
struct rctf;
+void SEQ_timeline_init_boundbox(const struct Scene *scene, struct rctf *rect);
+void SEQ_timeline_expand_boundbox(const struct ListBase *seqbase, struct rctf *rect);
void SEQ_timeline_boundbox(const struct Scene *scene,
const struct ListBase *seqbase,
struct rctf *rect);
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 427a8835879..b9ee23a9186 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -2451,7 +2451,6 @@ static void do_transform_effect(const SeqRenderData *context,
int total_lines,
ImBuf *out)
{
- Scene *scene = context->scene;
TransformVars *transform = (TransformVars *)seq->effectdata;
float scale_x, scale_y, translate_x, translate_y, rotate_radians;
@@ -2469,10 +2468,14 @@ static void do_transform_effect(const SeqRenderData *context,
/* Translate */
if (!transform->percent) {
- float rd_s = (scene->r.size / 100.0f);
+ /* Compensate text size for preview render size. */
+ double proxy_size_comp = context->scene->r.size / 100.0;
+ if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
+ proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size);
+ }
- translate_x = transform->xIni * rd_s + (x / 2.0f);
- translate_y = transform->yIni * rd_s + (y / 2.0f);
+ translate_x = transform->xIni * proxy_size_comp + (x / 2.0f);
+ translate_y = transform->yIni * proxy_size_comp + (y / 2.0f);
}
else {
translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f);
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index a12a5cbdc61..68f632ddb28 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -520,3 +520,29 @@ void SEQ_filter_selected_strips(SeqCollection *collection)
}
}
}
+
+static void seq_collection_to_tag(ListBase *seqbase, SeqCollection *collection)
+{
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ seq->tmp_tag = false;
+ }
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ seq->tmp_tag = true;
+ }
+}
+
+/* Utilities to access these as tags. */
+int SEQ_query_rendered_strips_to_tag(ListBase *seqbase,
+ const int timeline_frame,
+ const int displayed_channel)
+{
+ SeqCollection *collection = SEQ_query_rendered_strips(
+ seqbase, timeline_frame, displayed_channel);
+
+ seq_collection_to_tag(seqbase, collection);
+
+ const int len = SEQ_collection_len(collection);
+ SEQ_collection_free(collection);
+ return len;
+}
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 1c5f4c3ab76..92ac580f3b1 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -376,19 +376,27 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq)
}
/**
- * Define boundary rectangle of sequencer timeline and fill in rect data
+ * Initialize given rectangle with the Scene's timeline boundaries.
*
- * \param scene: Scene in which strips are located
- * \param seqbase: ListBase in which strips are located
- * \param rect: data structure describing rectangle, that will be filled in by this function
+ * \param scene: the Scene instance whose timeline boundaries are extracted from
+ * \param rect: output parameter to be filled with timeline boundaries
*/
-void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect)
+void SEQ_timeline_init_boundbox(const Scene *scene, rctf *rect)
{
rect->xmin = scene->r.sfra;
rect->xmax = scene->r.efra + 1;
rect->ymin = 0.0f;
rect->ymax = 8.0f;
+}
+/**
+ * Stretch the given rectangle to include the given strips boundaries
+ *
+ * \param seqbase: ListBase in which strips are located
+ * \param rect: output parameter to be filled with strips' boundaries
+ */
+void SEQ_timeline_expand_boundbox(const ListBase *seqbase, rctf *rect)
+{
if (seqbase == NULL) {
return;
}
@@ -406,6 +414,19 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re
}
}
+/**
+ * Define boundary rectangle of sequencer timeline and fill in rect data
+ *
+ * \param scene: Scene in which strips are located
+ * \param seqbase: ListBase in which strips are located
+ * \param rect: data structure describing rectangle, that will be filled in by this function
+ */
+void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect)
+{
+ SEQ_timeline_init_boundbox(scene, rect);
+ SEQ_timeline_expand_boundbox(seqbase, rect);
+}
+
static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame)
{
Sequence *seq;
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 4d65726fe2b..03b2fb49085 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -202,6 +202,7 @@ if(WITH_XR_OPENXR)
xr/intern/wm_xr_action.c
xr/intern/wm_xr_actionmap.c
xr/intern/wm_xr_draw.c
+ xr/intern/wm_xr_operators.c
xr/intern/wm_xr_session.c
xr/wm_xr.h
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index eaf32c06aba..112d76a3e65 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -276,10 +276,10 @@ typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm,
struct wmEventHandler_Keymap *handler,
struct wmEventHandler_KeymapResult *km_result);
-void WM_event_get_keymap_from_toolsystem_fallback(struct wmWindowManager *wm,
- struct wmWindow *win,
- struct wmEventHandler_Keymap *handler,
- wmEventHandler_KeymapResult *km_result);
+void WM_event_get_keymap_from_toolsystem_with_gizmos(struct wmWindowManager *wm,
+ struct wmWindow *win,
+ struct wmEventHandler_Keymap *handler,
+ wmEventHandler_KeymapResult *km_result);
void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm,
struct wmWindow *win,
struct wmEventHandler_Keymap *handler,
@@ -740,24 +740,38 @@ struct wmDropBox *WM_dropbox_add(
void (*copy)(struct wmDrag *, struct wmDropBox *),
void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *),
WMDropboxTooltipFunc tooltip);
+void WM_drag_draw_item_name_fn(struct bContext *C,
+ struct wmWindow *win,
+ struct wmDrag *drag,
+ const int xy[2]);
+void WM_drag_draw_default_fn(struct bContext *C,
+ struct wmWindow *win,
+ struct wmDrag *drag,
+ const int xy[2]);
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
/* ID drag and drop */
+ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra);
+bool WM_drag_asset_will_import_linked(const wmDrag *drag);
void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent);
struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode);
struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode);
bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode);
wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset,
+ struct AssetMetaData *metadata,
const char *path,
int import_type);
struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode);
+struct AssetMetaData *WM_drag_get_asset_meta_data(const struct wmDrag *drag, int idcode);
struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode);
void WM_drag_free_imported_drag_ID(struct Main *bmain,
struct wmDrag *drag,
struct wmDropBox *drop);
+struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag);
+
void WM_drag_add_asset_list_item(wmDrag *drag,
const struct bContext *C,
const struct AssetLibraryReference *asset_library_ref,
@@ -923,6 +937,7 @@ bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
bool WM_event_is_mouse_drag(const struct wmEvent *event);
bool WM_event_is_mouse_drag_or_press(const wmEvent *event);
+bool WM_cursor_test_motion_and_update(const int mval[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
int WM_event_drag_threshold(const struct wmEvent *event);
bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]);
@@ -1017,6 +1032,13 @@ bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr,
bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4]);
+bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]);
+void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]);
+bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]);
+void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]);
+bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale);
+void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale);
+void WM_xr_session_state_navigation_reset(struct wmXrSessionState *state);
struct ARegionType *WM_xr_surface_controller_region_type_get(void);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index f4595869baf..9f427a90353 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -119,6 +119,7 @@ struct wmWindowManager;
#include "BLI_compiler_attrs.h"
#include "DNA_listBase.h"
+#include "DNA_uuid_types.h"
#include "DNA_vec_types.h"
#include "DNA_xr_types.h"
#include "RNA_types.h"
@@ -967,6 +968,7 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata
#define WM_DRAG_VALUE 6
#define WM_DRAG_COLOR 7
#define WM_DRAG_DATASTACK 8
+#define WM_DRAG_ASSET_CATALOG 9
typedef enum wmDragFlags {
WM_DRAG_NOP = 0,
@@ -989,6 +991,7 @@ typedef struct wmDragAsset {
/* Always freed. */
const char *path;
int id_type;
+ struct AssetMetaData *metadata;
int import_type; /* eFileAssetImportType */
/* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the
@@ -999,6 +1002,10 @@ typedef struct wmDragAsset {
struct bContext *evil_C;
} wmDragAsset;
+typedef struct wmDragAssetCatalog {
+ bUUID drag_catalog_id;
+} wmDragAssetCatalog;
+
/**
* For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no
* proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really
@@ -1020,7 +1027,7 @@ typedef struct wmDragAssetListItem {
typedef char *(*WMDropboxTooltipFunc)(struct bContext *,
struct wmDrag *,
- const struct wmEvent *event,
+ const int xy[2],
struct wmDropBox *drop);
typedef struct wmDrag {
@@ -1038,8 +1045,16 @@ typedef struct wmDrag {
float scale;
int sx, sy;
- /** If filled, draws operator tooltip/operator name. */
- char tooltip[200];
+ /** Informs which dropbox is activated with the drag item.
+ * When this value changes, the #draw_activate and #draw_deactivate dropbox callbacks are
+ * triggered.
+ */
+ struct wmDropBox *active_dropbox;
+ /* Text to show when the operator poll fails. Typically the message the
+ * operator set with CTX_wm_operator_poll_msg_set(). */
+ const char *disabled_info;
+ bool free_disabled_info;
+
unsigned int flags;
/** List of wmDragIDs, all are guaranteed to have the same ID type. */
@@ -1067,6 +1082,18 @@ typedef struct wmDropBox {
*/
void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *);
+ /** Override the default drawing function. */
+ void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *);
+
+ /** Called when pool returns true the first time. */
+ void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag);
+
+ /** Called when pool returns false the first time or when the drag event ends. */
+ void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag);
+
+ /** Custom data for drawing. */
+ void *draw_data;
+
/** Custom tooltip shown during dragging. */
WMDropboxTooltipFunc tooltip;
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 0b7d5e5f1f4..47ee296823b 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -86,18 +86,23 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data)
wmWindowManager *wm = (wmWindowManager *)id;
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
- BKE_LIB_FOREACHID_PROCESS(data, win->scene, IDWALK_CB_USER_ONE);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->scene, IDWALK_CB_USER_ONE);
/* This pointer can be NULL during old files reading, better be safe than sorry. */
if (win->workspace_hook != NULL) {
ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook);
- BKE_LIB_FOREACHID_PROCESS_ID(data, workspace, IDWALK_CB_NOP);
+ BKE_lib_query_foreachid_process(data, &workspace, IDWALK_CB_USER);
/* Allow callback to set a different workspace. */
BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace);
+ if (BKE_lib_query_foreachid_iter_stop(data)) {
+ return;
+ }
}
+
if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) {
LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
- BKE_screen_foreach_id_screen_area(data, area);
+ BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
+ BKE_screen_foreach_id_screen_area(data, area));
}
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 9af90355a79..b9f0e09d106 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -40,9 +40,13 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_screen.h"
+
+#include "GHOST_C-api.h"
#include "BLO_readfile.h"
@@ -63,6 +67,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_event_system.h"
+#include "wm_window.h"
/* ****************************************************** */
@@ -176,6 +181,7 @@ wmDrag *WM_event_start_drag(
}
break;
case WM_DRAG_ASSET:
+ case WM_DRAG_ASSET_CATALOG:
/* Move ownership of poin to wmDrag. */
drag->poin = poin;
drag->flags |= WM_DRAG_FREE_DATA;
@@ -202,6 +208,31 @@ wmDrag *WM_event_start_drag(
return drag;
}
+/**
+ * Additional work to cleanly end dragging. Additional because this doesn't actually remove the
+ * drag items. Should be called whenever dragging is stopped (successful or not, also when
+ * canceled).
+ */
+void wm_drags_exit(wmWindowManager *wm, wmWindow *win)
+{
+ bool any_active = false;
+ LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) {
+ if (drag->active_dropbox) {
+ any_active = true;
+ break;
+ }
+ }
+
+ /* If there is no active drop-box #wm_drags_check_ops() set a stop-cursor, which needs to be
+ * restored. */
+ if (!any_active) {
+ WM_cursor_modal_restore(win);
+ /* Ensure the correct area cursor is restored. */
+ win->tag_cursor_refresh = true;
+ WM_event_add_mousemove(win);
+ }
+}
+
void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy)
{
drag->imb = imb;
@@ -229,9 +260,15 @@ void WM_drag_data_free(int dragtype, void *poin)
void WM_drag_free(wmDrag *drag)
{
+ if (drag->active_dropbox && drag->active_dropbox->draw_deactivate) {
+ drag->active_dropbox->draw_deactivate(drag->active_dropbox, drag);
+ }
if (drag->flags & WM_DRAG_FREE_DATA) {
WM_drag_data_free(drag->type, drag->poin);
}
+ if (drag->free_disabled_info) {
+ MEM_SAFE_FREE(drag->disabled_info);
+ }
BLI_freelistN(&drag->ids);
LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) {
if (asset_item->is_external) {
@@ -250,11 +287,11 @@ void WM_drag_free_list(struct ListBase *lb)
}
}
-static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop)
+static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop)
{
char *tooltip = NULL;
if (drop->tooltip) {
- tooltip = drop->tooltip(C, drag, event, drop);
+ tooltip = drop->tooltip(C, drag, xy, drop);
}
if (!tooltip) {
tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr));
@@ -269,15 +306,35 @@ static wmDropBox *dropbox_active(bContext *C,
wmDrag *drag,
const wmEvent *event)
{
+ if (drag->free_disabled_info) {
+ MEM_SAFE_FREE(drag->disabled_info);
+ }
+ drag->disabled_info = NULL;
+
LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
if (handler->dropboxes) {
LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
- if (drop->poll(C, drag, event) &&
- WM_operator_poll_context(C, drop->ot, drop->opcontext)) {
+ 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
+ * can't be executed. */
+ continue;
+ }
+
+ if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) {
return drop;
}
+
+ /* Attempt to set the disabled hint when the poll fails. Will always be the last hint set
+ * when there are multiple failing polls (could allow multiple disabled-hints too). */
+ bool free_disabled_info = false;
+ const char *disabled_hint = CTX_wm_operator_poll_msg_get(C, &free_disabled_info);
+ if (disabled_hint) {
+ drag->disabled_info = disabled_hint;
+ drag->free_disabled_info = free_disabled_info;
+ }
}
}
}
@@ -286,7 +343,7 @@ static wmDropBox *dropbox_active(bContext *C,
}
/* return active operator tooltip/name when mouse is in box */
-static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
+static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event);
@@ -298,13 +355,13 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
ARegion *region = CTX_wm_region(C);
drop = dropbox_active(C, &region->handlers, drag, event);
}
- if (drop) {
- return dropbox_tooltip(C, drag, event, drop);
- }
- return NULL;
+ return drop;
}
-static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event)
+/**
+ * Update dropping information for the current mouse position in \a event.
+ */
+static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
const int winsize_x = WM_window_pixels_x(win);
@@ -316,24 +373,30 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e
return;
}
- drag->tooltip[0] = 0;
-
- /* check buttons (XXX todo rna and value) */
- if (UI_but_active_drop_name(C)) {
- BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip));
+ wmDropBox *drop_prev = drag->active_dropbox;
+ wmDropBox *drop = wm_dropbox_active(C, drag, event);
+ if (drop != drop_prev) {
+ if (drop_prev && drop_prev->draw_deactivate) {
+ drop_prev->draw_deactivate(drop_prev, drag);
+ BLI_assert(drop_prev->draw_data == NULL);
+ }
+ if (drop && drop->draw_activate) {
+ drop->draw_activate(drop, drag);
+ }
+ drag->active_dropbox = drop;
}
- else {
- char *tooltip = wm_dropbox_active(C, drag, event);
+}
- if (tooltip) {
- BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip));
- MEM_freeN(tooltip);
- // WM_cursor_modal_set(win, WM_CURSOR_COPY);
- }
- // else
- // WM_cursor_modal_restore(win);
- /* unsure about cursor type, feels to be too much */
+void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop)
+{
+ /* Optionally copy drag information to operator properties. Don't call it if the
+ * operator fails anyway, it might do more than just set properties (e.g.
+ * typically import an asset). */
+ if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) {
+ drop->copy(drag, drop);
}
+
+ wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C));
}
/* called in inner handler loop, region context */
@@ -341,8 +404,19 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event)
{
wmWindowManager *wm = CTX_wm_manager(C);
+ bool any_active = false;
LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
- wm_drop_operator_options(C, drag, event);
+ wm_drop_update_active(C, drag, event);
+
+ if (drag->active_dropbox) {
+ any_active = true;
+ }
+ }
+
+ /* Change the cursor to display that dropping isn't possible here. But only if there is something
+ * being dragged actually. Cursor will be restored in #wm_drags_exit(). */
+ if (!BLI_listbase_is_empty(&wm->drags)) {
+ WM_cursor_modal_set(CTX_wm_window(C), any_active ? WM_CURSOR_DEFAULT : WM_CURSOR_STOP);
}
}
@@ -407,11 +481,15 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode)
/**
* \note: Does not store \a asset in any way, so it's fine to pass a temporary.
*/
-wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type)
+wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset,
+ AssetMetaData *metadata,
+ const char *path,
+ int import_type)
{
wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset");
BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name));
+ asset_drag->metadata = metadata;
asset_drag->path = path;
asset_drag->id_type = ED_asset_handle_get_id_type(asset);
asset_drag->import_type = import_type;
@@ -435,8 +513,30 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL;
}
-static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
+struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode)
+{
+ wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode);
+ if (drag_asset) {
+ return drag_asset->metadata;
+ }
+
+ ID *local_id = WM_drag_get_local_ID(drag, idcode);
+ if (local_id) {
+ return local_id->asset_data;
+ }
+
+ return NULL;
+}
+
+/**
+ * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag).
+ */
+ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra)
{
+ /* Only support passing in limited flags. */
+ BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT));
+ eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION;
+
const char *name = asset_drag->name;
ID_Type idtype = asset_drag->id_type;
@@ -450,14 +550,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
switch ((eFileAssetImportType)asset_drag->import_type) {
case FILE_ASSET_IMPORT_LINK:
- return WM_file_link_datablock(bmain,
- scene,
- view_layer,
- view3d,
- asset_drag->path,
- idtype,
- name,
- FILE_ACTIVE_COLLECTION);
+ return WM_file_link_datablock(
+ bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag);
case FILE_ASSET_IMPORT_APPEND:
return WM_file_append_datablock(bmain,
scene,
@@ -466,7 +560,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
asset_drag->path,
idtype,
name,
- BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION |
+ flag | BLO_LIBLINK_APPEND_RECURSIVE |
BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR);
case FILE_ASSET_IMPORT_APPEND_REUSE:
return WM_file_append_datablock(G_MAIN,
@@ -476,7 +570,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
asset_drag->path,
idtype,
name,
- BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION |
+ flag | BLO_LIBLINK_APPEND_RECURSIVE |
BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR |
BLO_LIBLINK_APPEND_LOCAL_ID_REUSE);
}
@@ -485,12 +579,24 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
return NULL;
}
+bool WM_drag_asset_will_import_linked(const wmDrag *drag)
+{
+ if (drag->type != WM_DRAG_ASSET) {
+ return false;
+ }
+
+ const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0);
+ return asset_drag->import_type == FILE_ASSET_IMPORT_LINK;
+}
+
/**
* When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append
* that depending on what was chosen by the drag-box (currently append only in fact).
*
* Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset
* import is rolled back if the drop operator fails.
+ *
+ * \param flag: #eFileSel_Params_Flag passed to linking code.
*/
ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode)
{
@@ -508,7 +614,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode)
}
/* Link/append the asset. */
- return wm_drag_asset_id_import(asset_drag);
+ return WM_drag_asset_id_import(asset_drag, 0);
}
/**
@@ -544,6 +650,15 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *
}
}
+wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag)
+{
+ if (drag->type != WM_DRAG_ASSET_CATALOG) {
+ return NULL;
+ }
+
+ return drag->poin;
+}
+
/**
* \note: Does not store \a asset in any way, so it's fine to pass a temporary.
*/
@@ -568,11 +683,12 @@ void WM_drag_add_asset_list_item(
drag_asset->asset_data.local_id = local_id;
}
else {
+ AssetMetaData *metadata = ED_asset_handle_get_metadata(asset);
char asset_blend_path[FILE_MAX_LIBEXTRA];
ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path);
drag_asset->is_external = true;
drag_asset->asset_data.external_info = WM_drag_create_asset_data(
- asset, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND);
+ asset, metadata, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND);
}
BLI_addtail(&drag->asset_items, drag_asset);
}
@@ -603,6 +719,17 @@ static void wm_drop_operator_draw(const char *name, int x, int y)
UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg);
}
+static void wm_drop_redalert_draw(const char *redalert_str, int x, int y)
+{
+ const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
+ const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f};
+ float col_fg[4];
+
+ UI_GetThemeColor4fv(TH_REDALERT, col_fg);
+
+ UI_fontstyle_draw_simple_backdrop(fstyle, x, y, redalert_str, col_fg, col_bg);
+}
+
const char *WM_drag_get_item_name(wmDrag *drag)
{
switch (drag->type) {
@@ -629,132 +756,172 @@ const char *WM_drag_get_item_name(wmDrag *drag)
return "";
}
-static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2)
+static void wm_drag_draw_icon(bContext *UNUSED(C),
+ wmWindow *UNUSED(win),
+ wmDrag *drag,
+ const int xy[2])
{
- if (rect->xmin > x1) {
- rect->xmin = x1;
- }
- if (rect->xmax < x2) {
- rect->xmax = x2;
+ int x, y;
+ if (drag->imb) {
+ x = xy[0] - drag->sx / 2;
+ y = xy[1] - drag->sy / 2;
+
+ float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ immDrawPixelsTexScaled(&state,
+ x,
+ y,
+ drag->imb->x,
+ drag->imb->y,
+ GPU_RGBA8,
+ false,
+ drag->imb->rect,
+ drag->scale,
+ drag->scale,
+ 1.0f,
+ 1.0f,
+ col);
}
- if (rect->ymin > y1) {
- rect->ymin = y1;
- }
- if (rect->ymax < y2) {
- rect->ymax = y2;
+ else {
+ int padding = 4 * UI_DPI_FAC;
+ x = xy[0] - 2 * padding;
+ y = xy[1] - 2 * UI_DPI_FAC;
+
+ const uchar text_col[] = {255, 255, 255, 255};
+ UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false);
}
}
-/* called in wm_draw.c */
-/* if rect set, do not draw */
-void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
+static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- wmWindowManager *wm = CTX_wm_manager(C);
- const int winsize_y = WM_window_pixels_y(win);
+ const uchar text_col[] = {255, 255, 255, 255};
+ UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col);
+}
- int cursorx = win->eventstate->xy[0];
- int cursory = win->eventstate->xy[1];
- if (rect) {
- rect->xmin = rect->xmax = cursorx;
- rect->ymin = rect->ymax = cursory;
- }
+void WM_drag_draw_item_name_fn(bContext *UNUSED(C),
+ wmWindow *UNUSED(win),
+ wmDrag *drag,
+ const int xy[2])
+{
+ int x = xy[0] + 10 * UI_DPI_FAC;
+ int y = xy[1] + 1 * UI_DPI_FAC;
- /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */
- GPU_blend(GPU_BLEND_ALPHA);
- LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
- const uchar text_col[] = {255, 255, 255, 255};
- int iconsize = UI_DPI_ICON_SIZE;
- int padding = 4 * UI_DPI_FAC;
+ wm_drag_draw_item_name(drag, x, y);
+}
- /* image or icon */
- int x, y;
- if (drag->imb) {
- x = cursorx - drag->sx / 2;
- y = cursory - drag->sy / 2;
+static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
+{
+ if (!CTX_wm_region(C)) {
+ /* Some callbacks require the region. */
+ return;
+ }
+ int iconsize = UI_DPI_ICON_SIZE;
+ int padding = 4 * UI_DPI_FAC;
- if (rect) {
- drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
- }
- else {
- float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
- immDrawPixelsTexScaled(&state,
- x,
- y,
- drag->imb->x,
- drag->imb->y,
- GPU_RGBA8,
- false,
- drag->imb->rect,
- drag->scale,
- drag->scale,
- 1.0f,
- 1.0f,
- col);
- }
- }
- else {
- x = cursorx - 2 * padding;
- y = cursory - 2 * UI_DPI_FAC;
+ char *tooltip = NULL;
+ if (drag->active_dropbox) {
+ tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox);
+ }
- if (rect) {
- drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize);
- }
- else {
- UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false);
- }
- }
+ if (!tooltip && !drag->disabled_info) {
+ return;
+ }
+
+ const int winsize_y = WM_window_pixels_y(win);
+ int x, y;
+ if (drag->imb) {
+ x = xy[0] - drag->sx / 2;
- /* item name */
- if (drag->imb) {
- x = cursorx - drag->sx / 2;
- y = cursory - drag->sy / 2 - iconsize;
+ if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) {
+ y = xy[1] + drag->sy / 2 + padding;
}
else {
- x = cursorx + 10 * UI_DPI_FAC;
- y = cursory + 1 * UI_DPI_FAC;
+ y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize;
}
+ }
+ else {
+ x = xy[0] - 2 * padding;
- if (rect) {
- int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag));
- drag_rect_minmax(rect, x, y, x + w, y + iconsize);
+ if (xy[1] + iconsize + iconsize < winsize_y) {
+ y = (xy[1] + iconsize) + padding;
}
else {
- UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col);
+ y = (xy[1] - iconsize) - padding;
}
+ }
- /* operator name with roundbox */
- if (drag->tooltip[0]) {
- if (drag->imb) {
- x = cursorx - drag->sx / 2;
+ if (tooltip) {
+ wm_drop_operator_draw(tooltip, x, y);
+ MEM_freeN(tooltip);
+ }
+ else if (drag->disabled_info) {
+ wm_drop_redalert_draw(drag->disabled_info, x, y);
+ }
+}
- if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) {
- y = cursory + drag->sy / 2 + padding;
- }
- else {
- y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize;
- }
- }
- else {
- x = cursorx - 2 * padding;
+static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
+{
+ int xy_tmp[2] = {UNPACK2(xy)};
- if (cursory + iconsize + iconsize < winsize_y) {
- y = (cursory + iconsize) + padding;
- }
- else {
- y = (cursory - iconsize) - padding;
- }
- }
+ /* Image or icon. */
+ wm_drag_draw_icon(C, win, drag, xy_tmp);
- if (rect) {
- int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag));
- drag_rect_minmax(rect, x, y, x + w, y + iconsize);
- }
- else {
- wm_drop_operator_draw(drag->tooltip, x, y);
- }
+ /* Item name. */
+ if (drag->imb) {
+ int iconsize = UI_DPI_ICON_SIZE;
+ xy_tmp[0] = xy[0] - (drag->sx / 2);
+ xy_tmp[1] = xy[1] - (drag->sy / 2) - iconsize;
+ }
+ else {
+ xy_tmp[0] = xy[0] + 10 * UI_DPI_FAC;
+ xy_tmp[1] = xy[1] + 1 * UI_DPI_FAC;
+ }
+ wm_drag_draw_item_name(drag, UNPACK2(xy_tmp));
+
+ /* Operator name with roundbox. */
+ wm_drag_draw_tooltip(C, win, drag, xy);
+}
+
+void WM_drag_draw_default_fn(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
+{
+ wm_drag_draw_default(C, win, drag, xy);
+}
+
+/* Called in #wm_draw_window_onscreen. */
+void wm_drags_draw(bContext *C, wmWindow *win)
+{
+ int xy[2];
+ if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
+ wm_cursor_position_get(win, &xy[0], &xy[1]);
+ }
+ else {
+ xy[0] = win->eventstate->xy[0];
+ xy[1] = win->eventstate->xy[1];
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy));
+ ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy));
+ if (region) {
+ BLI_assert(!CTX_wm_area(C) && !CTX_wm_region(C));
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+ }
+
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */
+ GPU_blend(GPU_BLEND_ALPHA);
+ LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
+ if (drag->active_dropbox && drag->active_dropbox->draw) {
+ drag->active_dropbox->draw(C, win, drag, xy);
+ continue;
}
+
+ wm_drag_draw_default(C, win, drag, xy);
}
GPU_blend(GPU_BLEND_NONE);
+ CTX_wm_area_set(C, NULL);
+ CTX_wm_region_set(C, NULL);
}
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index b5e81528e2b..8acce240046 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -856,9 +856,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
wm_gesture_draw(win);
}
- /* needs pixel coords in screen */
+ /* Needs pixel coords in screen. */
if (wm->drags.first) {
- wm_drags_draw(C, win, NULL);
+ wm_drags_draw(C, win);
}
GPU_debug_group_end();
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 9ee114674ed..ef733837bf7 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -276,6 +276,28 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
(ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS));
}
+/**
+ * Detect motion between selection (callers should only use this for selection picking),
+ * typically mouse press/click events.
+ *
+ * \param mval: Region relative coordinates, call with (-1, -1) resets the last cursor location.
+ * \returns True when there was motion since last called.
+ *
+ * NOTE(@campbellbarton): The logic used here isn't foolproof.
+ * It's possible that users move the cursor past #WM_EVENT_CURSOR_MOTION_THRESHOLD then back to
+ * a position within the threshold (between mouse clicks).
+ * In practice users never reported this since the threshold is very small (a few pixels).
+ * To prevent the unlikely case of values matching from another region,
+ * changing regions resets this value to (-1, -1).
+ */
+bool WM_cursor_test_motion_and_update(const int mval[2])
+{
+ static int mval_prev[2] = {-1, -1};
+ bool use_cycle = (len_manhattan_v2v2_int(mval, mval_prev) <= WM_EVENT_CURSOR_MOTION_THRESHOLD);
+ copy_v2_v2_int(mval_prev, mval);
+ return !use_cycle;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index df976d9a4cd..798f60fba3d 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3055,12 +3055,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
ListBase *lb = (ListBase *)event->customdata;
LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) {
if (drop->poll(C, drag, event)) {
- /* Optionally copy drag information to operator properties. Don't call it if the
- * operator fails anyway, it might do more than just set properties (e.g.
- * typically import an asset). */
- if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) {
- drop->copy(drag, drop);
- }
+ wm_drop_prepare(C, drag, drop);
/* Pass single matched wmDrag onto the operator. */
BLI_remlink(lb, drag);
@@ -3094,6 +3089,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
break;
}
}
+ /* Always exit all drags on a drop event, even if poll didn't succeed. */
+ wm_drags_exit(wm, win);
}
}
}
@@ -3390,6 +3387,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
screen->do_draw_drag = true;
}
else if (event->type == EVT_ESCKEY) {
+ wm_drags_exit(wm, win);
WM_drag_free_list(&wm->drags);
screen->do_draw_drag = true;
@@ -3630,7 +3628,8 @@ void wm_event_do_handlers(bContext *C)
/* Clear tool-tip on mouse move. */
if (screen->tool_tip && screen->tool_tip->exit_on_event) {
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
- if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > U.move_threshold) {
+ if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) >
+ WM_EVENT_CURSOR_MOTION_THRESHOLD) {
WM_tooltip_clear(C, win);
}
}
@@ -4010,10 +4009,12 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap
*
* Follow #wmEventHandler_KeymapDynamicFn signature.
*/
-void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
- wmWindow *win,
- wmEventHandler_Keymap *handler,
- wmEventHandler_KeymapResult *km_result)
+static void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm,
+ wmWindow *win,
+ wmEventHandler_Keymap *handler,
+ wmEventHandler_KeymapResult *km_result,
+ /* Extra arguments. */
+ const bool with_gizmos)
{
memset(km_result, 0x0, sizeof(*km_result));
@@ -4046,7 +4047,8 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) {
add_keymap = true;
}
- if (tref_rt->gizmo_group[0] != '\0') {
+
+ if (with_gizmos && (tref_rt->gizmo_group[0] != '\0')) {
wmGizmoMap *gzmap = NULL;
wmGizmoGroup *gzgroup = NULL;
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
@@ -4070,6 +4072,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
}
}
}
+
if (add_keymap) {
keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback;
}
@@ -4097,32 +4100,20 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
}
}
+void WM_event_get_keymap_from_toolsystem_with_gizmos(wmWindowManager *wm,
+ wmWindow *win,
+ wmEventHandler_Keymap *handler,
+ wmEventHandler_KeymapResult *km_result)
+{
+ wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, true);
+}
+
void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm,
- wmWindow *UNUSED(win),
+ wmWindow *win,
wmEventHandler_Keymap *handler,
wmEventHandler_KeymapResult *km_result)
{
- memset(km_result, 0x0, sizeof(*km_result));
-
- ScrArea *area = handler->dynamic.user_data;
- handler->keymap_tool = NULL;
- bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL;
- if (tref_rt && tref_rt->keymap[0]) {
- const char *keymap_id = tref_rt->keymap;
- {
- wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty(
- &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW);
- /* We shouldn't use keymaps from unrelated spaces. */
- if (km != NULL) {
- handler->keymap_tool = area->runtime.tool;
- km_result->keymaps[km_result->keymaps_len++] = km;
- }
- else {
- printf(
- "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname);
- }
- }
- }
+ wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, false);
}
struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic(
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index e203281297b..67222cc07f9 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -1644,6 +1644,7 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C,
area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
if (area) {
v3d = area->spacedata.first;
+ region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
}
}
@@ -1787,6 +1788,7 @@ static bool wm_file_write(bContext *C,
/* Call pre-save callbacks before writing preview,
* that way you can generate custom file thumbnail. */
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE);
+ ED_assets_pre_save(bmain);
/* Enforce full override check/generation on file save. */
BKE_lib_override_library_main_operations_create(bmain, true);
@@ -2104,6 +2106,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
}
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE);
+ ED_assets_pre_save(bmain);
/* check current window and close it if temp */
if (win && WM_window_is_temp_screen(win)) {
@@ -2139,7 +2142,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
}
printf("ok\n");
-
+ BKE_report(op->reports, RPT_INFO, "Startup file saved");
G.save_over = 0;
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST);
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index c88e577df6a..7d74ac9605b 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -583,8 +583,13 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data)
/* While we do not want to add non-linkable ID (shape keys...) to the list of linked items,
* unfortunately they can use fully linkable valid IDs too, like actions. Those need to be
* processed, so we need to recursively deal with them here. */
- BKE_library_foreach_ID_link(
- cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP);
+ /* 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
+ * shapekey referencing the shapekey itself). */
+ if (id != cb_data->id_self) {
+ BKE_library_foreach_ID_link(
+ cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP);
+ }
return IDWALK_RET_NOP;
}
@@ -807,7 +812,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
BLI_assert(!ID_IS_LINKED(id));
- BKE_libblock_relink_to_newid_new(bmain, id);
+ BKE_libblock_relink_to_newid(bmain, id);
}
/* Remove linked IDs when a local existing data has been reused instead. */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index d0693d37ef4..c382af03c4a 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -548,7 +548,6 @@ void WM_exit_ex(bContext *C, const bool do_python)
ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */
ED_assetlist_storage_exit();
- ED_view3d_cursor_snap_exit();
if (wm) {
/* Before BKE_blender_free! - since the ListBases get freed there. */
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 89f0206d72e..1130ad9a558 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -3759,87 +3759,6 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
/** \} */
-#ifdef WITH_XR_OPENXR
-
-static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data)
-{
- const bool session_exists = WM_xr_session_exists(xr_data);
-
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) {
- if (slink->spacetype == SPACE_VIEW3D) {
- View3D *v3d = (View3D *)slink;
-
- if (v3d->flag & V3D_XR_SESSION_MIRROR) {
- ED_view3d_xr_mirror_update(area, v3d, session_exists);
- }
-
- if (session_exists) {
- wmWindowManager *wm = bmain->wm.first;
- const Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
-
- ED_view3d_xr_shading_update(wm, v3d, scene);
- }
- /* Ensure no 3D View is tagged as session root. */
- else {
- v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT;
- }
- }
- }
- }
- }
-
- WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL);
-}
-
-static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data)
-{
- /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */
- wm_xr_session_update_screen(G_MAIN, xr_data);
-}
-
-static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win = CTX_wm_window(C);
- View3D *v3d = CTX_wm_view3d(C);
-
- /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */
- if (wm_xr_init(wm) == false) {
- return OPERATOR_CANCELLED;
- }
-
- v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT;
- wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb);
- wm_xr_session_update_screen(bmain, &wm->xr);
-
- WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_xr_session_toggle(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Toggle VR Session";
- ot->idname = "WM_OT_xr_session_toggle";
- ot->description =
- "Open a view for use with virtual reality headsets, or close it if already "
- "opened";
-
- /* callbacks */
- ot->exec = wm_xr_session_toggle_exec;
- ot->poll = ED_operator_view3d_active;
-
- /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
- * UI instead. Not meant as a permanent solution. */
- ot->flag = OPTYPE_INTERNAL;
-}
-
-#endif /* WITH_XR_OPENXR */
-
/* -------------------------------------------------------------------- */
/** \name Operator Registration & Keymaps
* \{ */
@@ -3881,9 +3800,6 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_call_panel);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_stereo3d_set);
-#ifdef WITH_XR_OPENXR
- WM_operatortype_append(WM_OT_xr_session_toggle);
-#endif
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif
@@ -3891,6 +3807,10 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_previews_clear);
WM_operatortype_append(WM_OT_doc_view_manual_ui_context);
+#ifdef WITH_XR_OPENXR
+ wm_xr_operatortypes_register();
+#endif
+
/* gizmos */
WM_operatortype_append(GIZMOGROUP_OT_gizmo_select);
WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak);
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index d1eb10787e2..40e4d905fcd 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -172,8 +172,10 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab
/* wm_dropbox.c */
void wm_dropbox_free(void);
+void wm_drags_exit(wmWindowManager *wm, wmWindow *win);
+void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop);
void wm_drags_check_ops(bContext *C, const wmEvent *event);
-void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect);
+void wm_drags_draw(bContext *C, wmWindow *win);
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index 628d50f05bd..72d88bb3043 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -49,6 +49,16 @@ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
copy_v3_v3(r_mat[3], pose->position);
}
+void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
+{
+ wm_xr_pose_to_mat(pose, r_mat);
+
+ BLI_assert(scale > 0.0f);
+ mul_v3_fl(r_mat[0], scale);
+ mul_v3_fl(r_mat[1], scale);
+ mul_v3_fl(r_mat[2], scale);
+}
+
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
{
float iquat[4];
@@ -57,15 +67,32 @@ void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
+{
+ float iquat[4];
+ invert_qt_qt_normalized(iquat, pose->orientation_quat);
+ quat_to_mat4(r_imat, iquat);
+
+ BLI_assert(scale > 0.0f);
+ scale = 1.0f / scale;
+ mul_v3_fl(r_imat[0], scale);
+ mul_v3_fl(r_imat[1], scale);
+ mul_v3_fl(r_imat[2], scale);
+
+ translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
- float r_view_mat[4][4],
- float r_proj_mat[4][4])
+ const wmXrSessionState *session_state,
+ float r_viewmat[4][4],
+ float r_projmat[4][4])
{
GHOST_XrPose eye_pose;
- float eye_inv[4][4], base_inv[4][4];
+ float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4];
+ /* Calculate inverse eye matrix. */
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
@@ -76,12 +103,14 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
}
wm_xr_pose_to_imat(&eye_pose, eye_inv);
- /* Calculate the base pose matrix (in world space!). */
- wm_xr_pose_to_imat(&draw_data->base_pose, base_inv);
- mul_m4_m4m4(r_view_mat, eye_inv, base_inv);
+ /* Apply base pose and navigation. */
+ wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv);
+ wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv);
+ mul_m4_m4m4(m, eye_inv, base_inv);
+ mul_m4_m4m4(r_viewmat, m, nav_inv);
- perspective_m4_fov(r_proj_mat,
+ perspective_m4_fov(r_projmat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
draw_view->fov.angle_up,
@@ -131,7 +160,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
BLI_assert(WM_xr_session_is_ready(xr_data));
wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data);
- wm_xr_draw_matrices_create(draw_data, draw_view, settings, viewmat, winmat);
+ wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat);
wm_xr_session_state_update(settings, draw_data, draw_view, session_state);
if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) {
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 2cd0ba5c056..7de1f254224 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -33,6 +33,8 @@ typedef struct wmXrSessionState {
GHOST_XrPose viewer_pose;
/** The last known view matrix, calculated from above's viewer pose. */
float viewer_viewmat[4][4];
+ /** The last known viewer matrix, without navigation applied. */
+ float viewer_mat_base[4][4];
float focal_len;
/** Copy of XrSessionSettings.base_pose_ data to detect changes that need
@@ -43,6 +45,8 @@ typedef struct wmXrSessionState {
int prev_settings_flag;
/** Copy of wmXrDrawData.base_pose. */
GHOST_XrPose prev_base_pose;
+ /** Copy of wmXrDrawData.base_scale. */
+ float prev_base_scale;
/** Copy of GHOST_XrDrawViewInfo.local_pose. */
GHOST_XrPose prev_local_pose;
/** Copy of wmXrDrawData.eye_position_ofs. */
@@ -51,6 +55,15 @@ typedef struct wmXrSessionState {
bool force_reset_to_base_pose;
bool is_view_data_set;
+ /** Current navigation transforms. */
+ GHOST_XrPose nav_pose;
+ float nav_scale;
+ /** Navigation transforms from the last actions sync, used to calculate the viewer/controller
+ * poses. */
+ GHOST_XrPose nav_pose_prev;
+ float nav_scale_prev;
+ bool is_navigation_dirty;
+
/** Last known controller data. */
ListBase controllers; /* #wmXrController */
@@ -106,6 +119,8 @@ typedef struct wmXrDrawData {
* space). With positional tracking enabled, it should be the same as the base pose, when
* disabled it also contains a location delta from the moment the option was toggled. */
GHOST_XrPose base_pose;
+ /** Base scale (uniform, world space). */
+ float base_scale;
/** Offset to _substract_ from the OpenXR eye and viewer pose to get the wanted effective pose
* (e.g. a pose exactly at the landmark position). */
float eye_position_ofs[3]; /* Local/view space. */
@@ -123,9 +138,11 @@ typedef struct wmXrController {
/** Pose (in world space) that represents the user's hand when holding the controller. */
GHOST_XrPose grip_pose;
float grip_mat[4][4];
+ float grip_mat_base[4][4];
/** Pose (in world space) that represents the controller's aiming source. */
GHOST_XrPose aim_pose;
float aim_mat[4][4];
+ float aim_mat_base[4][4];
/** Controller model. */
struct GPUBatch *model;
@@ -192,7 +209,7 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
void wm_xr_session_data_free(wmXrSessionState *state);
wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm,
const wmXrRuntimeData *runtime_data);
-void wm_xr_session_draw_data_update(const wmXrSessionState *state,
+void wm_xr_session_draw_data_update(wmXrSessionState *state,
const XrSessionSettings *settings,
const GHOST_XrDrawViewInfo *draw_view,
wmXrDrawData *draw_data);
@@ -214,6 +231,8 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state);
/* wm_xr_draw.c */
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
+void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]);
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]);
+void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c
new file mode 100644
index 00000000000..36af0147cb8
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c
@@ -0,0 +1,1537 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ *
+ * \name Window-Manager XR Operators
+ *
+ * Collection of XR-related operators.
+ */
+
+#include "BLI_kdopbvh.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_transform_snap_object_context.h"
+#include "ED_view3d.h"
+
+#include "GHOST_Types.h"
+
+#include "GPU_immediate.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "PIL_time.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Conditions
+ * \{ */
+
+/* op->poll */
+static bool wm_xr_operator_sessionactive(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return WM_xr_session_is_ready(&wm->xr);
+}
+
+static bool wm_xr_operator_test_event(const wmOperator *op, const wmEvent *event)
+{
+ if (event->type != EVT_XR_ACTION) {
+ return false;
+ }
+
+ BLI_assert(event->custom == EVT_DATA_XR);
+ BLI_assert(event->customdata);
+
+ wmXrActionData *actiondata = event->customdata;
+ return (actiondata->ot == op->type &&
+ IDP_EqualsProperties(actiondata->op_properties, op->properties));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Session Toggle
+ *
+ * Toggles an XR session, creating an XR context if necessary.
+ * \{ */
+
+static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data)
+{
+ const bool session_exists = WM_xr_session_exists(xr_data);
+
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) {
+ if (slink->spacetype == SPACE_VIEW3D) {
+ View3D *v3d = (View3D *)slink;
+
+ if (v3d->flag & V3D_XR_SESSION_MIRROR) {
+ ED_view3d_xr_mirror_update(area, v3d, session_exists);
+ }
+
+ if (session_exists) {
+ wmWindowManager *wm = bmain->wm.first;
+ const Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
+
+ ED_view3d_xr_shading_update(wm, v3d, scene);
+ }
+ /* Ensure no 3D View is tagged as session root. */
+ else {
+ v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT;
+ }
+ }
+ }
+ }
+ }
+
+ WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL);
+}
+
+static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data)
+{
+ /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */
+ wm_xr_session_update_screen(G_MAIN, xr_data);
+}
+
+static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ /* Lazily-create XR context - tries to dynamic-link to the runtime,
+ * reading `active_runtime.json`. */
+ if (wm_xr_init(wm) == false) {
+ return OPERATOR_CANCELLED;
+ }
+
+ v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT;
+ wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb);
+ wm_xr_session_update_screen(bmain, &wm->xr);
+
+ WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void WM_OT_xr_session_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Toggle VR Session";
+ ot->idname = "WM_OT_xr_session_toggle";
+ ot->description =
+ "Open a view for use with virtual reality headsets, or close it if already "
+ "opened";
+
+ /* callbacks */
+ ot->exec = wm_xr_session_toggle_exec;
+ ot->poll = ED_operator_view3d_active;
+
+ /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
+ * UI instead. Not meant as a permanent solution. */
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Grab Utilities
+ * \{ */
+
+typedef struct XrGrabData {
+ float mat_prev[4][4];
+ float mat_other_prev[4][4];
+ bool bimanual_prev;
+ bool loc_lock, locz_lock, rot_lock, rotz_lock, scale_lock;
+} XrGrabData;
+
+static void wm_xr_grab_init(wmOperator *op)
+{
+ BLI_assert(op->customdata == NULL);
+
+ op->customdata = MEM_callocN(sizeof(XrGrabData), __func__);
+}
+
+static void wm_xr_grab_uninit(wmOperator *op)
+{
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata)
+{
+ XrGrabData *data = op->customdata;
+
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+
+ if (actiondata->bimanual) {
+ quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other);
+ copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other);
+ data->bimanual_prev = true;
+ }
+ else {
+ data->bimanual_prev = false;
+ }
+}
+
+static void orient_mat_z_normalized(float R[4][4], const float z_axis[3])
+{
+ const float scale = len_v3(R[0]);
+ float x_axis[3], y_axis[3];
+
+ cross_v3_v3v3(y_axis, z_axis, R[0]);
+ normalize_v3(y_axis);
+ mul_v3_v3fl(R[1], y_axis, scale);
+
+ cross_v3_v3v3(x_axis, R[1], z_axis);
+ normalize_v3(x_axis);
+ mul_v3_v3fl(R[0], x_axis, scale);
+
+ mul_v3_v3fl(R[2], z_axis, scale);
+}
+
+static void wm_xr_navlocks_apply(const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool loc_lock,
+ bool locz_lock,
+ bool rotz_lock,
+ float r_prev[4][4],
+ float r_curr[4][4])
+{
+ /* Locked in base pose coordinates. */
+ float prev_base[4][4], curr_base[4][4];
+
+ mul_m4_m4m4(prev_base, nav_inv, r_prev);
+ mul_m4_m4m4(curr_base, nav_inv, r_curr);
+
+ if (rotz_lock) {
+ const float z_axis[3] = {0.0f, 0.0f, 1.0f};
+ orient_mat_z_normalized(prev_base, z_axis);
+ orient_mat_z_normalized(curr_base, z_axis);
+ }
+
+ if (loc_lock) {
+ copy_v3_v3(curr_base[3], prev_base[3]);
+ }
+ else if (locz_lock) {
+ curr_base[3][2] = prev_base[3][2];
+ }
+
+ mul_m4_m4m4(r_prev, nav_mat, prev_base);
+ mul_m4_m4m4(r_curr, nav_mat, curr_base);
+}
+
+/**
+ * Compute transformation delta for a one-handed grab interaction.
+ *
+ * \param actiondata: Contains current controller pose in world space.
+ * \param data: Contains previous controller pose in world space.
+ *
+ * The delta is computed as the difference between the current and previous
+ * controller poses i.e. delta = curr * prev^-1.
+ */
+static void wm_xr_grab_compute(const wmXrActionData *actiondata,
+ const XrGrabData *data,
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool reverse,
+ float r_delta[4][4])
+{
+ const bool nav_lock = (nav_mat && nav_inv);
+ float prev[4][4], curr[4][4];
+
+ if (!data->rot_lock) {
+ copy_m4_m4(prev, data->mat_prev);
+ zero_v3(prev[3]);
+ quat_to_mat4(curr, actiondata->controller_rot);
+ }
+ else {
+ unit_m4(prev);
+ unit_m4(curr);
+ }
+
+ if (!data->loc_lock || nav_lock) {
+ copy_v3_v3(prev[3], data->mat_prev[3]);
+ copy_v3_v3(curr[3], actiondata->controller_loc);
+ }
+
+ if (nav_lock) {
+ wm_xr_navlocks_apply(
+ nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr);
+ }
+
+ if (reverse) {
+ invert_m4(curr);
+ mul_m4_m4m4(r_delta, prev, curr);
+ }
+ else {
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+ }
+}
+
+/**
+ * Compute transformation delta for a two-handed (bimanual) grab interaction.
+ *
+ * \param actiondata: Contains current controller poses in world space.
+ * \param data: Contains previous controller poses in world space.
+ *
+ * The delta is computed as the difference (delta = curr * prev^-1) between the current
+ * and previous transformations, where the transformations themselves are determined as follows:
+ * - Translation: Averaged controller positions.
+ * - Rotation: Rotation of axis line between controllers.
+ * - Scale: Distance between controllers.
+ */
+static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata,
+ const XrGrabData *data,
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ bool reverse,
+ float r_delta[4][4])
+{
+ const bool nav_lock = (nav_mat && nav_inv);
+ float prev[4][4], curr[4][4];
+ unit_m4(prev);
+ unit_m4(curr);
+
+ if (!data->rot_lock) {
+ /* Rotation. */
+ float x_axis_prev[3], x_axis_curr[3], y_axis_prev[3], y_axis_curr[3], z_axis_prev[3],
+ z_axis_curr[3];
+ float m0[3][3], m1[3][3];
+ quat_to_mat3(m0, actiondata->controller_rot);
+ quat_to_mat3(m1, actiondata->controller_rot_other);
+
+ /* x-axis is the base line between the two controllers. */
+ sub_v3_v3v3(x_axis_prev, data->mat_prev[3], data->mat_other_prev[3]);
+ sub_v3_v3v3(x_axis_curr, actiondata->controller_loc, actiondata->controller_loc_other);
+ /* y-axis is the average of the controllers' y-axes. */
+ add_v3_v3v3(y_axis_prev, data->mat_prev[1], data->mat_other_prev[1]);
+ mul_v3_fl(y_axis_prev, 0.5f);
+ add_v3_v3v3(y_axis_curr, m0[1], m1[1]);
+ mul_v3_fl(y_axis_curr, 0.5f);
+ /* z-axis is the cross product of the two. */
+ cross_v3_v3v3(z_axis_prev, x_axis_prev, y_axis_prev);
+ cross_v3_v3v3(z_axis_curr, x_axis_curr, y_axis_curr);
+ /* Fix the y-axis to be orthogonal. */
+ cross_v3_v3v3(y_axis_prev, z_axis_prev, x_axis_prev);
+ cross_v3_v3v3(y_axis_curr, z_axis_curr, x_axis_curr);
+ /* Normalize. */
+ normalize_v3_v3(prev[0], x_axis_prev);
+ normalize_v3_v3(prev[1], y_axis_prev);
+ normalize_v3_v3(prev[2], z_axis_prev);
+ normalize_v3_v3(curr[0], x_axis_curr);
+ normalize_v3_v3(curr[1], y_axis_curr);
+ normalize_v3_v3(curr[2], z_axis_curr);
+ }
+
+ if (!data->loc_lock || nav_lock) {
+ /* Translation: translation of the averaged controller locations. */
+ add_v3_v3v3(prev[3], data->mat_prev[3], data->mat_other_prev[3]);
+ mul_v3_fl(prev[3], 0.5f);
+ add_v3_v3v3(curr[3], actiondata->controller_loc, actiondata->controller_loc_other);
+ mul_v3_fl(curr[3], 0.5f);
+ }
+
+ if (!data->scale_lock) {
+ /* Scaling: distance between controllers. */
+ float scale, v[3];
+
+ sub_v3_v3v3(v, data->mat_prev[3], data->mat_other_prev[3]);
+ scale = len_v3(v);
+ mul_v3_fl(prev[0], scale);
+ mul_v3_fl(prev[1], scale);
+ mul_v3_fl(prev[2], scale);
+
+ sub_v3_v3v3(v, actiondata->controller_loc, actiondata->controller_loc_other);
+ scale = len_v3(v);
+ mul_v3_fl(curr[0], scale);
+ mul_v3_fl(curr[1], scale);
+ mul_v3_fl(curr[2], scale);
+ }
+
+ if (nav_lock) {
+ wm_xr_navlocks_apply(
+ nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr);
+ }
+
+ if (reverse) {
+ invert_m4(curr);
+ mul_m4_m4m4(r_delta, prev, curr);
+ }
+ else {
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Grab
+ *
+ * Navigates the scene by grabbing with XR controllers.
+ * \{ */
+
+static int wm_xr_navigation_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+
+ wm_xr_grab_init(op);
+ wm_xr_grab_update(op, actiondata);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_xr_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static bool wm_xr_navigation_grab_can_do_bimanual(const wmXrActionData *actiondata,
+ const XrGrabData *data)
+{
+ /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both
+ controllers are pressed) and 2) bimanual interaction occurred on the last update. This second
+ part is needed to avoid "jumpy" navigation changes when transitioning from one-handed to
+ two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how navigation deltas
+ are calculated). */
+ return (actiondata->bimanual && data->bimanual_prev);
+}
+
+static bool wm_xr_navigation_grab_is_bimanual_ending(const wmXrActionData *actiondata,
+ const XrGrabData *data)
+{
+ return (!actiondata->bimanual && data->bimanual_prev);
+}
+
+static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool bimanual)
+{
+ if (bimanual) {
+ return data->loc_lock && data->rot_lock && data->scale_lock;
+ }
+ /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */
+ return data->loc_lock && data->rot_lock;
+}
+
+static void wm_xr_navigation_grab_apply(wmXrData *xr,
+ const wmXrActionData *actiondata,
+ const XrGrabData *data,
+ bool bimanual)
+{
+ GHOST_XrPose nav_pose;
+ float nav_scale;
+ float nav_mat[4][4], nav_inv[4][4], delta[4][4], out[4][4];
+
+ const bool need_navinv = (data->loc_lock || data->locz_lock || data->rotz_lock);
+
+ WM_xr_session_state_nav_location_get(xr, nav_pose.position);
+ WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ wm_xr_pose_scale_to_mat(&nav_pose, nav_scale, nav_mat);
+ if (need_navinv) {
+ wm_xr_pose_scale_to_imat(&nav_pose, nav_scale, nav_inv);
+ }
+
+ if (bimanual) {
+ wm_xr_grab_compute_bimanual(
+ actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta);
+ }
+ else {
+ wm_xr_grab_compute(
+ actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta);
+ }
+
+ mul_m4_m4m4(out, delta, nav_mat);
+
+ /* Limit scale to reasonable values. */
+ nav_scale = len_v3(out[0]);
+
+ if (!(nav_scale < xr->session_settings.clip_start ||
+ nav_scale > xr->session_settings.clip_end)) {
+ WM_xr_session_state_nav_location_set(xr, out[3]);
+ if (!data->rot_lock) {
+ mat4_to_quat(nav_pose.orientation_quat, out);
+ normalize_qt(nav_pose.orientation_quat);
+ WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat);
+ }
+ if (!data->scale_lock && bimanual) {
+ WM_xr_session_state_nav_scale_set(xr, nav_scale);
+ }
+ }
+}
+
+static void wm_xr_navigation_grab_bimanual_state_update(const wmXrActionData *actiondata,
+ XrGrabData *data)
+{
+ if (actiondata->bimanual) {
+ if (!data->bimanual_prev) {
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+ quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other);
+ copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other);
+ }
+ data->bimanual_prev = true;
+ }
+ else {
+ if (data->bimanual_prev) {
+ quat_to_mat4(data->mat_prev, actiondata->controller_rot);
+ copy_v3_v3(data->mat_prev[3], actiondata->controller_loc);
+ }
+ data->bimanual_prev = false;
+ }
+}
+
+static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+ XrGrabData *data = op->customdata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+
+ const bool do_bimanual = wm_xr_navigation_grab_can_do_bimanual(actiondata, data);
+
+ data->loc_lock = RNA_boolean_get(op->ptr, "lock_location");
+ data->locz_lock = RNA_boolean_get(op->ptr, "lock_location_z");
+ data->rot_lock = RNA_boolean_get(op->ptr, "lock_rotation");
+ data->rotz_lock = RNA_boolean_get(op->ptr, "lock_rotation_z");
+ data->scale_lock = RNA_boolean_get(op->ptr, "lock_scale");
+
+ /* Check if navigation is locked. */
+ if (!wm_xr_navigation_grab_is_locked(data, do_bimanual)) {
+ /* Prevent unwanted snapping (i.e. "jumpy" navigation changes when transitioning from
+ two-handed to one-handed interaction) at the end of a bimanual interaction. */
+ if (!wm_xr_navigation_grab_is_bimanual_ending(actiondata, data)) {
+ wm_xr_navigation_grab_apply(xr, actiondata, data, do_bimanual);
+ }
+ }
+
+ wm_xr_navigation_grab_bimanual_state_update(actiondata, data);
+
+ /* Note: KM_PRESS and KM_RELEASE are the only two values supported by XR events during event
+ dispatching (see #wm_xr_session_action_states_interpret()). For modal XR operators, modal
+ handling starts when an input is "pressed" (action state exceeds the action threshold) and
+ ends when the input is "released" (state falls below the threshold). */
+ switch (event->val) {
+ case KM_PRESS:
+ return OPERATOR_RUNNING_MODAL;
+ case KM_RELEASE:
+ wm_xr_grab_uninit(op);
+ return OPERATOR_FINISHED;
+ default:
+ BLI_assert_unreachable();
+ wm_xr_grab_uninit(op);
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static void WM_OT_xr_navigation_grab(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Grab";
+ ot->idname = "WM_OT_xr_navigation_grab";
+ ot->description = "Navigate the VR scene by grabbing with controllers";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_grab_invoke;
+ ot->exec = wm_xr_navigation_grab_exec;
+ ot->modal = wm_xr_navigation_grab_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ RNA_def_boolean(
+ ot->srna, "lock_location", false, "Lock Location", "Prevent changes to viewer location");
+ RNA_def_boolean(
+ ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation");
+ RNA_def_boolean(
+ ot->srna, "lock_rotation", false, "Lock Rotation", "Prevent changes to viewer rotation");
+ RNA_def_boolean(ot->srna,
+ "lock_rotation_z",
+ false,
+ "Lock Up Orientation",
+ "Prevent changes to viewer up orientation");
+ RNA_def_boolean(ot->srna, "lock_scale", false, "Lock Scale", "Prevent changes to viewer scale");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Raycast Utilities
+ * \{ */
+
+static const float g_xr_default_raycast_axis[3] = {0.0f, 0.0f, -1.0f};
+static const float g_xr_default_raycast_color[4] = {0.35f, 0.35f, 1.0f, 1.0f};
+
+typedef struct XrRaycastData {
+ bool from_viewer;
+ float origin[3];
+ float direction[3];
+ float end[3];
+ float color[4];
+ void *draw_handle;
+} XrRaycastData;
+
+static void wm_xr_raycast_draw(const bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *customdata)
+{
+ const XrRaycastData *data = customdata;
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ if (data->from_viewer) {
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformColor4fv(data->color);
+
+ GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_point_size(7.0f);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, data->end);
+ immEnd();
+ }
+ else {
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ immUniform1f("lineWidth", 3.0f * U.pixelsize);
+
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immAttrSkip(col);
+ immVertex3fv(pos, data->origin);
+ immAttr4fv(col, data->color);
+ immVertex3fv(pos, data->end);
+ immEnd();
+ }
+
+ immUnbindProgram();
+}
+
+static void wm_xr_raycast_init(wmOperator *op)
+{
+ BLI_assert(op->customdata == NULL);
+
+ op->customdata = MEM_callocN(sizeof(XrRaycastData), __func__);
+
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (!st) {
+ return;
+ }
+
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (!art) {
+ return;
+ }
+
+ XrRaycastData *data = op->customdata;
+ data->draw_handle = ED_region_draw_cb_activate(
+ art, wm_xr_raycast_draw, op->customdata, REGION_DRAW_POST_VIEW);
+}
+
+static void wm_xr_raycast_uninit(wmOperator *op)
+{
+ if (!op->customdata) {
+ return;
+ }
+
+ SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
+ if (st) {
+ ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
+ if (art) {
+ XrRaycastData *data = op->customdata;
+ ED_region_draw_cb_exit(art, data->draw_handle);
+ }
+ }
+
+ MEM_freeN(op->customdata);
+}
+
+static void wm_xr_raycast_update(wmOperator *op,
+ const wmXrData *xr,
+ const wmXrActionData *actiondata)
+{
+ XrRaycastData *data = op->customdata;
+ float ray_length, axis[3];
+
+ data->from_viewer = RNA_boolean_get(op->ptr, "from_viewer");
+ RNA_float_get_array(op->ptr, "axis", axis);
+ RNA_float_get_array(op->ptr, "color", data->color);
+
+ if (data->from_viewer) {
+ float viewer_rot[4];
+ WM_xr_session_state_viewer_pose_location_get(xr, data->origin);
+ WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_rot);
+ mul_qt_v3(viewer_rot, axis);
+ ray_length = (xr->session_settings.clip_start + xr->session_settings.clip_end) / 2.0f;
+ }
+ else {
+ copy_v3_v3(data->origin, actiondata->controller_loc);
+ mul_qt_v3(actiondata->controller_rot, axis);
+ ray_length = xr->session_settings.clip_end;
+ }
+
+ copy_v3_v3(data->direction, axis);
+ madd_v3_v3v3fl(data->end, data->origin, data->direction, ray_length);
+}
+
+static void wm_xr_raycast(Scene *scene,
+ Depsgraph *depsgraph,
+ const float origin[3],
+ const float direction[3],
+ float *ray_dist,
+ bool selectable_only,
+ float r_location[3],
+ float r_normal[3],
+ int *r_index,
+ Object **r_ob,
+ float r_obmat[4][4])
+{
+ /* Uses same raycast method as Scene.ray_cast(). */
+ SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, 0);
+
+ ED_transform_snap_object_project_ray_ex(
+ sctx,
+ depsgraph,
+ NULL,
+ &(const struct SnapObjectParams){
+ .snap_select = (selectable_only ? SNAP_SELECTABLE : SNAP_ALL)},
+ origin,
+ direction,
+ ray_dist,
+ r_location,
+ r_normal,
+ r_index,
+ r_ob,
+ r_obmat);
+
+ ED_transform_snap_object_context_destroy(sctx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Fly
+ *
+ * Navigates the scene by moving/turning relative to navigation space or the XR viewer or
+ * controller.
+ * \{ */
+
+#define XR_DEFAULT_FLY_SPEED_MOVE 0.054f
+#define XR_DEFAULT_FLY_SPEED_TURN 0.03f
+
+typedef enum eXrFlyMode {
+ XR_FLY_FORWARD = 0,
+ XR_FLY_BACK = 1,
+ XR_FLY_LEFT = 2,
+ XR_FLY_RIGHT = 3,
+ XR_FLY_UP = 4,
+ XR_FLY_DOWN = 5,
+ XR_FLY_TURNLEFT = 6,
+ XR_FLY_TURNRIGHT = 7,
+ XR_FLY_VIEWER_FORWARD = 8,
+ XR_FLY_VIEWER_BACK = 9,
+ XR_FLY_VIEWER_LEFT = 10,
+ XR_FLY_VIEWER_RIGHT = 11,
+ XR_FLY_CONTROLLER_FORWARD = 12,
+} eXrFlyMode;
+
+typedef struct XrFlyData {
+ float viewer_rot[4];
+ double time_prev;
+} XrFlyData;
+
+static void wm_xr_fly_init(wmOperator *op, const wmXrData *xr)
+{
+ BLI_assert(op->customdata == NULL);
+
+ XrFlyData *data = op->customdata = MEM_callocN(sizeof(XrFlyData), __func__);
+
+ WM_xr_session_state_viewer_pose_rotation_get(xr, data->viewer_rot);
+ data->time_prev = PIL_check_seconds_timer();
+}
+
+static void wm_xr_fly_uninit(wmOperator *op)
+{
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void wm_xr_fly_compute_move(eXrFlyMode mode,
+ float speed,
+ const float ref_quat[4],
+ const float nav_mat[4][4],
+ bool locz_lock,
+ float r_delta[4][4])
+{
+ float ref_axes[3][3];
+ quat_to_mat3(ref_axes, ref_quat);
+
+ unit_m4(r_delta);
+
+ switch (mode) {
+ /* Navigation space reference. */
+ case XR_FLY_FORWARD:
+ madd_v3_v3fl(r_delta[3], ref_axes[1], speed);
+ return;
+ case XR_FLY_BACK:
+ madd_v3_v3fl(r_delta[3], ref_axes[1], -speed);
+ return;
+ case XR_FLY_LEFT:
+ madd_v3_v3fl(r_delta[3], ref_axes[0], -speed);
+ return;
+ case XR_FLY_RIGHT:
+ madd_v3_v3fl(r_delta[3], ref_axes[0], speed);
+ return;
+ case XR_FLY_UP:
+ case XR_FLY_DOWN:
+ if (!locz_lock) {
+ madd_v3_v3fl(r_delta[3], ref_axes[2], (mode == XR_FLY_UP) ? speed : -speed);
+ }
+ return;
+ /* Viewer/controller space reference. */
+ case XR_FLY_VIEWER_FORWARD:
+ case XR_FLY_CONTROLLER_FORWARD:
+ negate_v3_v3(r_delta[3], ref_axes[2]);
+ break;
+ case XR_FLY_VIEWER_BACK:
+ copy_v3_v3(r_delta[3], ref_axes[2]);
+ break;
+ case XR_FLY_VIEWER_LEFT:
+ negate_v3_v3(r_delta[3], ref_axes[0]);
+ break;
+ case XR_FLY_VIEWER_RIGHT:
+ copy_v3_v3(r_delta[3], ref_axes[0]);
+ break;
+ /* Unused. */
+ case XR_FLY_TURNLEFT:
+ case XR_FLY_TURNRIGHT:
+ BLI_assert_unreachable();
+ return;
+ }
+
+ if (locz_lock) {
+ /* Lock elevation in navigation space. */
+ float z_axis[3], projected[3];
+
+ normalize_v3_v3(z_axis, nav_mat[2]);
+ project_v3_v3v3_normalized(projected, r_delta[3], z_axis);
+ sub_v3_v3(r_delta[3], projected);
+
+ normalize_v3(r_delta[3]);
+ }
+
+ mul_v3_fl(r_delta[3], speed);
+}
+
+static void wm_xr_fly_compute_turn(eXrFlyMode mode,
+ float speed,
+ const float viewer_mat[4][4],
+ const float nav_mat[4][4],
+ const float nav_inv[4][4],
+ float r_delta[4][4])
+{
+ BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT);
+
+ float z_axis[3], m[3][3], prev[4][4], curr[4][4];
+
+ /* Turn around Z-axis in navigation space. */
+ normalize_v3_v3(z_axis, nav_mat[2]);
+ axis_angle_normalized_to_mat3(m, z_axis, (mode == XR_FLY_TURNLEFT) ? speed : -speed);
+ copy_m4_m3(r_delta, m);
+
+ copy_m4_m4(prev, viewer_mat);
+ mul_m4_m4m4(curr, r_delta, viewer_mat);
+
+ /* Lock location in base pose space. */
+ wm_xr_navlocks_apply(nav_mat, nav_inv, true, false, false, prev, curr);
+
+ invert_m4(prev);
+ mul_m4_m4m4(r_delta, curr, prev);
+}
+
+static void wm_xr_basenav_rotation_calc(const wmXrData *xr,
+ const float nav_rotation[4],
+ float r_rotation[4])
+{
+ /* Apply nav rotation to base pose Z-rotation. */
+ float base_eul[3], base_quatz[4];
+ quat_to_eul(base_eul, xr->runtime->session_state.prev_base_pose.orientation_quat);
+ axis_angle_to_quat_single(base_quatz, 'Z', base_eul[2]);
+ mul_qt_qtqt(r_rotation, nav_rotation, base_quatz);
+}
+
+static int wm_xr_navigation_fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ wm_xr_fly_init(op, &wm->xr);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_xr_navigation_fly_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static int wm_xr_navigation_fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ if (event->val == KM_RELEASE) {
+ wm_xr_fly_uninit(op);
+ return OPERATOR_FINISHED;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+ XrFlyData *data = op->customdata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ eXrFlyMode mode;
+ bool turn, locz_lock, dir_lock, speed_frame_based;
+ bool speed_interp_cubic = false;
+ float speed, speed_max, speed_p0[2], speed_p1[2];
+ GHOST_XrPose nav_pose;
+ float nav_mat[4][4], delta[4][4], out[4][4];
+
+ const double time_now = PIL_check_seconds_timer();
+
+ mode = (eXrFlyMode)RNA_enum_get(op->ptr, "mode");
+ turn = (mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT);
+
+ locz_lock = RNA_boolean_get(op->ptr, "lock_location_z");
+ dir_lock = RNA_boolean_get(op->ptr, "lock_direction");
+ speed_frame_based = RNA_boolean_get(op->ptr, "speed_frame_based");
+ speed = RNA_float_get(op->ptr, "speed_min");
+ speed_max = RNA_float_get(op->ptr, "speed_max");
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "speed_interpolation0");
+ if (prop && RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_float_get_array(op->ptr, prop, speed_p0);
+ speed_interp_cubic = true;
+ }
+ else {
+ speed_p0[0] = speed_p0[1] = 0.0f;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "speed_interpolation1");
+ if (prop && RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_float_get_array(op->ptr, prop, speed_p1);
+ speed_interp_cubic = true;
+ }
+ else {
+ speed_p1[0] = speed_p1[1] = 1.0f;
+ }
+
+ /* Ensure valid interpolation. */
+ if (speed_max < speed) {
+ speed_max = speed;
+ }
+
+ /* Interpolate between min/max speeds based on button state. */
+ switch (actiondata->type) {
+ case XR_BOOLEAN_INPUT:
+ speed = speed_max;
+ break;
+ case XR_FLOAT_INPUT:
+ case XR_VECTOR2F_INPUT: {
+ float state = (actiondata->type == XR_FLOAT_INPUT) ? fabsf(actiondata->state[0]) :
+ len_v2(actiondata->state);
+ float speed_t = (actiondata->float_threshold < 1.0f) ?
+ (state - actiondata->float_threshold) /
+ (1.0f - actiondata->float_threshold) :
+ 1.0f;
+ if (speed_interp_cubic) {
+ float start[2], end[2], p[2];
+
+ start[0] = 0.0f;
+ start[1] = speed;
+ speed_p0[1] = speed + speed_p0[1] * (speed_max - speed);
+ speed_p1[1] = speed + speed_p1[1] * (speed_max - speed);
+ end[0] = 1.0f;
+ end[1] = speed_max;
+
+ interp_v2_v2v2v2v2_cubic(p, start, speed_p0, speed_p1, end, speed_t);
+ speed = p[1];
+ }
+ else {
+ speed += speed_t * (speed_max - speed);
+ }
+ break;
+ }
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ if (!speed_frame_based) {
+ /* Adjust speed based on last update time. */
+ speed *= time_now - data->time_prev;
+ }
+ data->time_prev = time_now;
+
+ WM_xr_session_state_nav_location_get(xr, nav_pose.position);
+ WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat);
+ wm_xr_pose_to_mat(&nav_pose, nav_mat);
+
+ if (turn) {
+ if (dir_lock) {
+ unit_m4(delta);
+ }
+ else {
+ GHOST_XrPose viewer_pose;
+ float viewer_mat[4][4], nav_inv[4][4];
+
+ WM_xr_session_state_viewer_pose_location_get(xr, viewer_pose.position);
+ WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_pose.orientation_quat);
+ wm_xr_pose_to_mat(&viewer_pose, viewer_mat);
+ wm_xr_pose_to_imat(&nav_pose, nav_inv);
+
+ wm_xr_fly_compute_turn(mode, speed, viewer_mat, nav_mat, nav_inv, delta);
+ }
+ }
+ else {
+ float nav_scale, ref_quat[4];
+
+ /* Adjust speed for base and navigation scale. */
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+ speed *= xr->session_settings.base_scale * nav_scale;
+
+ switch (mode) {
+ /* Move relative to navigation space. */
+ case XR_FLY_FORWARD:
+ case XR_FLY_BACK:
+ case XR_FLY_LEFT:
+ case XR_FLY_RIGHT:
+ case XR_FLY_UP:
+ case XR_FLY_DOWN:
+ wm_xr_basenav_rotation_calc(xr, nav_pose.orientation_quat, ref_quat);
+ break;
+ /* Move relative to viewer. */
+ case XR_FLY_VIEWER_FORWARD:
+ case XR_FLY_VIEWER_BACK:
+ case XR_FLY_VIEWER_LEFT:
+ case XR_FLY_VIEWER_RIGHT:
+ if (dir_lock) {
+ copy_qt_qt(ref_quat, data->viewer_rot);
+ }
+ else {
+ WM_xr_session_state_viewer_pose_rotation_get(xr, ref_quat);
+ }
+ break;
+ /* Move relative to controller. */
+ case XR_FLY_CONTROLLER_FORWARD:
+ copy_qt_qt(ref_quat, actiondata->controller_rot);
+ break;
+ /* Unused. */
+ case XR_FLY_TURNLEFT:
+ case XR_FLY_TURNRIGHT:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ wm_xr_fly_compute_move(mode, speed, ref_quat, nav_mat, locz_lock, delta);
+ }
+
+ mul_m4_m4m4(out, delta, nav_mat);
+
+ WM_xr_session_state_nav_location_set(xr, out[3]);
+ if (turn) {
+ mat4_to_quat(nav_pose.orientation_quat, out);
+ WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat);
+ }
+
+ if (event->val == KM_PRESS) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* XR events currently only support press and release. */
+ BLI_assert_unreachable();
+ wm_xr_fly_uninit(op);
+ return OPERATOR_CANCELLED;
+}
+
+static void WM_OT_xr_navigation_fly(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Fly";
+ ot->idname = "WM_OT_xr_navigation_fly";
+ ot->description = "Move/turn relative to the VR viewer or controller";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_fly_invoke;
+ ot->exec = wm_xr_navigation_fly_exec;
+ ot->modal = wm_xr_navigation_fly_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ static const EnumPropertyItem fly_modes[] = {
+ {XR_FLY_FORWARD, "FORWARD", 0, "Forward", "Move along navigation forward axis"},
+ {XR_FLY_BACK, "BACK", 0, "Back", "Move along navigation back axis"},
+ {XR_FLY_LEFT, "LEFT", 0, "Left", "Move along navigation left axis"},
+ {XR_FLY_RIGHT, "RIGHT", 0, "Right", "Move along navigation right axis"},
+ {XR_FLY_UP, "UP", 0, "Up", "Move along navigation up axis"},
+ {XR_FLY_DOWN, "DOWN", 0, "Down", "Move along navigation down axis"},
+ {XR_FLY_TURNLEFT,
+ "TURNLEFT",
+ 0,
+ "Turn Left",
+ "Turn counter-clockwise around navigation up axis"},
+ {XR_FLY_TURNRIGHT, "TURNRIGHT", 0, "Turn Right", "Turn clockwise around navigation up axis"},
+ {XR_FLY_VIEWER_FORWARD,
+ "VIEWER_FORWARD",
+ 0,
+ "Viewer Forward",
+ "Move along viewer's forward axis"},
+ {XR_FLY_VIEWER_BACK, "VIEWER_BACK", 0, "Viewer Back", "Move along viewer's back axis"},
+ {XR_FLY_VIEWER_LEFT, "VIEWER_LEFT", 0, "Viewer Left", "Move along viewer's left axis"},
+ {XR_FLY_VIEWER_RIGHT, "VIEWER_RIGHT", 0, "Viewer Right", "Move along viewer's right axis"},
+ {XR_FLY_CONTROLLER_FORWARD,
+ "CONTROLLER_FORWARD",
+ 0,
+ "Controller Forward",
+ "Move along controller's forward axis"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const float default_speed_p0[2] = {0.0f, 0.0f};
+ static const float default_speed_p1[2] = {1.0f, 1.0f};
+
+ RNA_def_enum(ot->srna, "mode", fly_modes, XR_FLY_VIEWER_FORWARD, "Mode", "Fly mode");
+ RNA_def_boolean(
+ ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation");
+ RNA_def_boolean(ot->srna,
+ "lock_direction",
+ false,
+ "Lock Direction",
+ "Limit movement to viewer's intial direction");
+ RNA_def_boolean(ot->srna,
+ "speed_frame_based",
+ true,
+ "Frame Based Speed",
+ "Apply fixed movement deltas every update");
+ RNA_def_float(ot->srna,
+ "speed_min",
+ XR_DEFAULT_FLY_SPEED_MOVE / 3.0f,
+ 0.0f,
+ 1000.0f,
+ "Minimum Speed",
+ "Minimum move (turn) speed in meters (radians) per second or frame",
+ 0.0f,
+ 1000.0f);
+ RNA_def_float(ot->srna,
+ "speed_max",
+ XR_DEFAULT_FLY_SPEED_MOVE,
+ 0.0f,
+ 1000.0f,
+ "Maximum Speed",
+ "Maximum move (turn) speed in meters (radians) per second or frame",
+ 0.0f,
+ 1000.0f);
+ RNA_def_float_vector(ot->srna,
+ "speed_interpolation0",
+ 2,
+ default_speed_p0,
+ 0.0f,
+ 1.0f,
+ "Speed Interpolation 0",
+ "First cubic spline control point between min/max speeds",
+ 0.0f,
+ 1.0f);
+ RNA_def_float_vector(ot->srna,
+ "speed_interpolation1",
+ 2,
+ default_speed_p1,
+ 0.0f,
+ 1.0f,
+ "Speed Interpolation 1",
+ "Second cubic spline control point between min/max speeds",
+ 0.0f,
+ 1.0f);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Teleport
+ *
+ * Casts a ray from an XR controller's pose and teleports to any hit geometry.
+ * \{ */
+
+static void wm_xr_navigation_teleport(bContext *C,
+ wmXrData *xr,
+ const float origin[3],
+ const float direction[3],
+ float *ray_dist,
+ bool selectable_only,
+ const bool teleport_axes[3],
+ float teleport_t,
+ float teleport_ofs)
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ float location[3];
+ float normal[3];
+ int index;
+ Object *ob = NULL;
+ float obmat[4][4];
+
+ wm_xr_raycast(scene,
+ depsgraph,
+ origin,
+ direction,
+ ray_dist,
+ selectable_only,
+ location,
+ normal,
+ &index,
+ &ob,
+ obmat);
+
+ /* Teleport. */
+ if (ob) {
+ float nav_location[3], nav_rotation[4], viewer_location[3];
+ float nav_axes[3][3], projected[3], v0[3], v1[3];
+ float out[3] = {0.0f, 0.0f, 0.0f};
+
+ WM_xr_session_state_nav_location_get(xr, nav_location);
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_viewer_pose_location_get(xr, viewer_location);
+
+ wm_xr_basenav_rotation_calc(xr, nav_rotation, nav_rotation);
+ quat_to_mat3(nav_axes, nav_rotation);
+
+ /* Project locations onto navigation axes. */
+ for (int a = 0; a < 3; ++a) {
+ project_v3_v3v3_normalized(projected, nav_location, nav_axes[a]);
+ if (teleport_axes[a]) {
+ /* Interpolate between projected locations. */
+ project_v3_v3v3_normalized(v0, location, nav_axes[a]);
+ project_v3_v3v3_normalized(v1, viewer_location, nav_axes[a]);
+ sub_v3_v3(v0, v1);
+ madd_v3_v3fl(projected, v0, teleport_t);
+ /* Subtract offset. */
+ project_v3_v3v3_normalized(v0, normal, nav_axes[a]);
+ madd_v3_v3fl(projected, v0, teleport_ofs);
+ }
+ /* Add to final location. */
+ add_v3_v3(out, projected);
+ }
+
+ WM_xr_session_state_nav_location_set(xr, out);
+ }
+}
+
+static int wm_xr_navigation_teleport_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ wm_xr_raycast_init(op);
+
+ int retval = op->type->modal(C, op, event);
+
+ if ((retval & OPERATOR_RUNNING_MODAL) != 0) {
+ WM_event_add_modal_handler(C, op);
+ }
+
+ return retval;
+}
+
+static int wm_xr_navigation_teleport_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return OPERATOR_CANCELLED;
+}
+
+static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!wm_xr_operator_test_event(op, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ const wmXrActionData *actiondata = event->customdata;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+
+ wm_xr_raycast_update(op, xr, actiondata);
+
+ switch (event->val) {
+ case KM_PRESS:
+ return OPERATOR_RUNNING_MODAL;
+ case KM_RELEASE: {
+ XrRaycastData *data = op->customdata;
+ bool selectable_only, teleport_axes[3];
+ float teleport_t, teleport_ofs, ray_dist;
+
+ RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes);
+ teleport_t = RNA_float_get(op->ptr, "interpolation");
+ teleport_ofs = RNA_float_get(op->ptr, "offset");
+ selectable_only = RNA_boolean_get(op->ptr, "selectable_only");
+ ray_dist = RNA_float_get(op->ptr, "distance");
+
+ wm_xr_navigation_teleport(C,
+ xr,
+ data->origin,
+ data->direction,
+ &ray_dist,
+ selectable_only,
+ teleport_axes,
+ teleport_t,
+ teleport_ofs);
+
+ wm_xr_raycast_uninit(op);
+
+ return OPERATOR_FINISHED;
+ }
+ default:
+ /* XR events currently only support press and release. */
+ BLI_assert_unreachable();
+ wm_xr_raycast_uninit(op);
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static void WM_OT_xr_navigation_teleport(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Teleport";
+ ot->idname = "WM_OT_xr_navigation_teleport";
+ ot->description = "Set VR viewer location to controller raycast hit location";
+
+ /* callbacks */
+ ot->invoke = wm_xr_navigation_teleport_invoke;
+ ot->exec = wm_xr_navigation_teleport_exec;
+ ot->modal = wm_xr_navigation_teleport_modal;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ static bool default_teleport_axes[3] = {true, true, true};
+
+ RNA_def_boolean_vector(ot->srna,
+ "teleport_axes",
+ 3,
+ default_teleport_axes,
+ "Teleport Axes",
+ "Enabled teleport axes in navigation space");
+ RNA_def_float(ot->srna,
+ "interpolation",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Interpolation",
+ "Interpolation factor between viewer and hit locations",
+ 0.0f,
+ 1.0f);
+ RNA_def_float(ot->srna,
+ "offset",
+ 0.0f,
+ 0.0f,
+ FLT_MAX,
+ "Offset",
+ "Offset along hit normal to subtract from final location",
+ 0.0f,
+ FLT_MAX);
+ RNA_def_boolean(ot->srna,
+ "selectable_only",
+ true,
+ "Selectable Only",
+ "Only allow selectable objects to influence raycast result");
+ RNA_def_float(ot->srna,
+ "distance",
+ BVH_RAYCAST_DIST_MAX,
+ 0.0,
+ BVH_RAYCAST_DIST_MAX,
+ "",
+ "Maximum raycast distance",
+ 0.0,
+ BVH_RAYCAST_DIST_MAX);
+ RNA_def_boolean(
+ ot->srna, "from_viewer", false, "From Viewer", "Use viewer pose as raycast origin");
+ RNA_def_float_vector(ot->srna,
+ "axis",
+ 3,
+ g_xr_default_raycast_axis,
+ -1.0f,
+ 1.0f,
+ "Axis",
+ "Raycast axis in controller/viewer space",
+ -1.0f,
+ 1.0f);
+ RNA_def_float_color(ot->srna,
+ "color",
+ 4,
+ g_xr_default_raycast_color,
+ 0.0f,
+ 1.0f,
+ "Color",
+ "Raycast color",
+ 0.0f,
+ 1.0f);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Navigation Reset
+ *
+ * Resets XR navigation deltas relative to session base pose.
+ * \{ */
+
+static int wm_xr_navigation_reset_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrData *xr = &wm->xr;
+ bool reset_loc, reset_rot, reset_scale;
+
+ reset_loc = RNA_boolean_get(op->ptr, "location");
+ reset_rot = RNA_boolean_get(op->ptr, "rotation");
+ reset_scale = RNA_boolean_get(op->ptr, "scale");
+
+ if (reset_loc) {
+ float loc[3];
+ if (!reset_scale) {
+ float nav_rotation[4], nav_scale;
+
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ /* Adjust location based on scale. */
+ mul_v3_v3fl(loc, xr->runtime->session_state.prev_base_pose.position, nav_scale);
+ sub_v3_v3(loc, xr->runtime->session_state.prev_base_pose.position);
+ mul_qt_v3(nav_rotation, loc);
+ negate_v3(loc);
+ }
+ else {
+ zero_v3(loc);
+ }
+ WM_xr_session_state_nav_location_set(xr, loc);
+ }
+
+ if (reset_rot) {
+ float rot[4];
+ unit_qt(rot);
+ WM_xr_session_state_nav_rotation_set(xr, rot);
+ }
+
+ if (reset_scale) {
+ if (!reset_loc) {
+ float nav_location[3], nav_rotation[4], nav_scale;
+ float nav_axes[3][3], v[3];
+
+ WM_xr_session_state_nav_location_get(xr, nav_location);
+ WM_xr_session_state_nav_rotation_get(xr, nav_rotation);
+ WM_xr_session_state_nav_scale_get(xr, &nav_scale);
+
+ /* Offset any location changes when changing scale. */
+ mul_v3_v3fl(v, xr->runtime->session_state.prev_base_pose.position, nav_scale);
+ sub_v3_v3(v, xr->runtime->session_state.prev_base_pose.position);
+ mul_qt_v3(nav_rotation, v);
+ add_v3_v3(nav_location, v);
+
+ /* Reset elevation to base pose value. */
+ quat_to_mat3(nav_axes, nav_rotation);
+ project_v3_v3v3_normalized(v, nav_location, nav_axes[2]);
+ sub_v3_v3(nav_location, v);
+
+ WM_xr_session_state_nav_location_set(xr, nav_location);
+ }
+ WM_xr_session_state_nav_scale_set(xr, 1.0f);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void WM_OT_xr_navigation_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "XR Navigation Reset";
+ ot->idname = "WM_OT_xr_navigation_reset";
+ ot->description = "Reset VR navigation deltas relative to session base pose";
+
+ /* callbacks */
+ ot->exec = wm_xr_navigation_reset_exec;
+ ot->poll = wm_xr_operator_sessionactive;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "location", true, "Location", "Reset location deltas");
+ RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "Reset rotation deltas");
+ RNA_def_boolean(ot->srna, "scale", true, "Scale", "Reset scale deltas");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Registration
+ * \{ */
+
+void wm_xr_operatortypes_register(void)
+{
+ WM_operatortype_append(WM_OT_xr_session_toggle);
+ WM_operatortype_append(WM_OT_xr_navigation_grab);
+ WM_operatortype_append(WM_OT_xr_navigation_fly);
+ WM_operatortype_append(WM_OT_xr_navigation_teleport);
+ WM_operatortype_append(WM_OT_xr_navigation_reset);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 9f53db1347c..3224869b04a 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -66,11 +66,20 @@ static void wm_xr_session_create_cb(void)
Main *bmain = G_MAIN;
wmWindowManager *wm = bmain->wm.first;
wmXrData *xr_data = &wm->xr;
+ wmXrSessionState *state = &xr_data->runtime->session_state;
+ XrSessionSettings *settings = &xr_data->session_settings;
/* Get action set data from Python. */
BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
wm_xr_session_actions_init(xr_data);
+
+ /* Initialize navigation. */
+ WM_xr_session_state_navigation_reset(state);
+ if (settings->base_scale < FLT_EPSILON) {
+ settings->base_scale = 1.0f;
+ }
+ state->prev_base_scale = settings->base_scale;
}
static void wm_xr_session_controller_data_free(wmXrSessionState *state)
@@ -128,8 +137,10 @@ void wm_xr_session_toggle(wmWindowManager *wm,
wmXrData *xr_data = &wm->xr;
if (WM_xr_session_exists(xr_data)) {
- GHOST_XrSessionEnd(xr_data->runtime->context);
+ /* Must set first, since #GHOST_XrSessionEnd() may immediately free the runtime. */
xr_data->runtime->session_state.is_started = false;
+
+ GHOST_XrSessionEnd(xr_data->runtime->context);
}
else {
GHOST_XrSessionBeginInfo begin_info;
@@ -167,7 +178,8 @@ bool WM_xr_session_is_ready(const wmXrData *xr)
static void wm_xr_session_base_pose_calc(const Scene *scene,
const XrSessionSettings *settings,
- GHOST_XrPose *r_base_pose)
+ GHOST_XrPose *r_base_pose,
+ float *r_base_scale)
{
const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) &&
settings->base_pose_object) ?
@@ -198,6 +210,8 @@ static void wm_xr_session_base_pose_calc(const Scene *scene,
copy_v3_fl(r_base_pose->position, 0.0f);
axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2);
}
+
+ *r_base_scale = settings->base_scale;
}
static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
@@ -213,7 +227,8 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
r_draw_data->xr_data = xr_data;
r_draw_data->surface_data = g_xr_surface->customdata;
- wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose);
+ wm_xr_session_base_pose_calc(
+ r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale);
}
wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm,
@@ -291,7 +306,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState
return SESSION_STATE_EVENT_NONE;
}
-void wm_xr_session_draw_data_update(const wmXrSessionState *state,
+void wm_xr_session_draw_data_update(wmXrSessionState *state,
const XrSessionSettings *settings,
const GHOST_XrDrawViewInfo *draw_view,
wmXrDrawData *draw_data)
@@ -319,6 +334,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
else {
copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
}
+ /* Reset navigation. */
+ WM_xr_session_state_navigation_reset(state);
break;
case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE:
if (use_position_tracking) {
@@ -348,29 +365,32 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
wmXrSessionState *state)
{
GHOST_XrPose viewer_pose;
- const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING;
- const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING;
-
- mul_qt_qtqt(viewer_pose.orientation_quat,
- draw_data->base_pose.orientation_quat,
- draw_view->local_pose.orientation_quat);
- copy_v3_v3(viewer_pose.position, draw_data->base_pose.position);
- /* The local pose and the eye pose (which is copied from an earlier local pose) both are view
- * space, so Y-up. In this case we need them in regular Z-up. */
- if (use_position_tracking) {
- viewer_pose.position[0] += draw_view->local_pose.position[0];
- viewer_pose.position[1] -= draw_view->local_pose.position[2];
- viewer_pose.position[2] += draw_view->local_pose.position[1];
- }
- if (!use_absolute_tracking) {
- viewer_pose.position[0] -= draw_data->eye_position_ofs[0];
- viewer_pose.position[1] += draw_data->eye_position_ofs[2];
- viewer_pose.position[2] -= draw_data->eye_position_ofs[1];
- }
-
- copy_v3_v3(state->viewer_pose.position, viewer_pose.position);
- copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat);
- wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat);
+ float viewer_mat[4][4], base_mat[4][4], nav_mat[4][4];
+
+ /* Calculate viewer matrix. */
+ copy_qt_qt(viewer_pose.orientation_quat, draw_view->local_pose.orientation_quat);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ zero_v3(viewer_pose.position);
+ }
+ else {
+ copy_v3_v3(viewer_pose.position, draw_view->local_pose.position);
+ }
+ if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
+ sub_v3_v3(viewer_pose.position, draw_data->eye_position_ofs);
+ }
+ wm_xr_pose_to_mat(&viewer_pose, viewer_mat);
+
+ /* Apply base pose and navigation. */
+ wm_xr_pose_scale_to_mat(&draw_data->base_pose, draw_data->base_scale, base_mat);
+ wm_xr_pose_scale_to_mat(&state->nav_pose_prev, state->nav_scale_prev, nav_mat);
+ mul_m4_m4m4(state->viewer_mat_base, base_mat, viewer_mat);
+ mul_m4_m4m4(viewer_mat, nav_mat, state->viewer_mat_base);
+
+ /* Save final viewer pose and viewmat. */
+ mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat);
+ wm_xr_pose_scale_to_imat(
+ &state->viewer_pose, draw_data->base_scale * state->nav_scale_prev, state->viewer_viewmat);
+
/* No idea why, but multiplying by two seems to make it match the VR view more. */
state->focal_len = 2.0f *
fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left,
@@ -378,7 +398,10 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ state->prev_base_scale = draw_data->base_scale;
memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
+ copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -503,6 +526,74 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
return true;
}
+bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.nav_pose.position);
+ return true;
+}
+
+void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3])
+{
+ if (WM_xr_session_exists(xr)) {
+ copy_v3_v3(xr->runtime->session_state.nav_pose.position, location);
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ copy_qt_qt(r_rotation, xr->runtime->session_state.nav_pose.orientation_quat);
+ return true;
+}
+
+void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4])
+{
+ if (WM_xr_session_exists(xr)) {
+ BLI_ASSERT_UNIT_QUAT(rotation);
+ copy_qt_qt(xr->runtime->session_state.nav_pose.orientation_quat, rotation);
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale)
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) {
+ *r_scale = 1.0f;
+ return false;
+ }
+
+ *r_scale = xr->runtime->session_state.nav_scale;
+ return true;
+}
+
+void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale)
+{
+ if (WM_xr_session_exists(xr)) {
+ /* Clamp to reasonable values. */
+ CLAMP(scale, xr->session_settings.clip_start, xr->session_settings.clip_end);
+ xr->runtime->session_state.nav_scale = scale;
+ xr->runtime->session_state.is_navigation_dirty = true;
+ }
+}
+
+void WM_xr_session_state_navigation_reset(wmXrSessionState *state)
+{
+ zero_v3(state->nav_pose.position);
+ unit_qt(state->nav_pose.orientation_quat);
+ state->nav_scale = 1.0f;
+ state->is_navigation_dirty = true;
+}
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Actions
*
@@ -522,16 +613,21 @@ void wm_xr_session_actions_init(wmXrData *xr)
static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose,
const float view_ofs[3],
const float base_mat[4][4],
+ const float nav_mat[4][4],
GHOST_XrPose *r_pose,
- float r_mat[4][4])
+ float r_mat[4][4],
+ float r_mat_base[4][4])
{
float m[4][4];
/* Calculate controller matrix in world space. */
wm_xr_pose_to_mat(raw_pose, m);
- /* Apply eye position and base pose offsets. */
+ /* Apply eye position offset. */
sub_v3_v3(m[3], view_ofs);
- mul_m4_m4m4(r_mat, base_mat, m);
+
+ /* Apply base pose and navigation. */
+ mul_m4_m4m4(r_mat_base, base_mat, m);
+ mul_m4_m4m4(r_mat, nav_mat, r_mat_base);
/* Save final pose. */
mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat);
@@ -547,7 +643,7 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin
BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers));
unsigned int subaction_idx = 0;
- float view_ofs[3], base_mat[4][4];
+ float view_ofs[3], base_mat[4][4], nav_mat[4][4];
if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
copy_v3_v3(view_ofs, state->prev_local_pose.position);
@@ -559,19 +655,24 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin
add_v3_v3(view_ofs, state->prev_eye_position_ofs);
}
- wm_xr_pose_to_mat(&state->prev_base_pose, base_mat);
+ wm_xr_pose_scale_to_mat(&state->prev_base_pose, state->prev_base_scale, base_mat);
+ wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, nav_mat);
LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) {
wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx],
view_ofs,
base_mat,
+ nav_mat,
&controller->grip_pose,
- controller->grip_mat);
+ controller->grip_mat,
+ controller->grip_mat_base);
wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx],
view_ofs,
base_mat,
+ nav_mat,
&controller->aim_pose,
- controller->aim_mat);
+ controller->aim_mat,
+ controller->aim_mat_base);
if (!controller->model) {
/* Notify GHOST to load/continue loading the controller model data. This can be called more
@@ -1094,10 +1195,26 @@ void wm_xr_session_actions_update(wmWindowManager *wm)
return;
}
+ XrSessionSettings *settings = &xr->session_settings;
GHOST_XrContextHandle xr_context = xr->runtime->context;
wmXrSessionState *state = &xr->runtime->session_state;
wmXrActionSet *active_action_set = state->active_action_set;
+ if (state->is_navigation_dirty) {
+ memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev));
+ state->nav_scale_prev = state->nav_scale;
+ state->is_navigation_dirty = false;
+
+ /* Update viewer pose with any navigation changes since the last actions sync so that data
+ * is correct for queries. */
+ float m[4][4], viewer_mat[4][4];
+ wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, m);
+ mul_m4_m4m4(viewer_mat, m, state->viewer_mat_base);
+ mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat);
+ wm_xr_pose_scale_to_imat(
+ &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) {
return;
@@ -1108,7 +1225,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm)
wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime);
if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) {
- wm_xr_session_controller_data_update(&xr->session_settings,
+ wm_xr_session_controller_data_update(settings,
active_action_set->controller_grip_action,
active_action_set->controller_aim_action,
xr_context,
diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h
index 0f0fbe8bc00..caba6038c56 100644
--- a/source/blender/windowmanager/xr/wm_xr.h
+++ b/source/blender/windowmanager/xr/wm_xr.h
@@ -30,3 +30,6 @@ bool wm_xr_init(wmWindowManager *wm);
void wm_xr_exit(wmWindowManager *wm);
void wm_xr_session_toggle(wmWindowManager *wm, wmWindow *win, wmXrSessionExitFn session_exit_fn);
bool wm_xr_events_handle(wmWindowManager *wm);
+
+/* wm_xr_operators.c */
+void wm_xr_operatortypes_register(void);