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/editors')
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c10
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c74
-rw-r--r--source/blender/editors/animation/anim_markers.c2
-rw-r--r--source/blender/editors/animation/anim_ops.c5
-rw-r--r--source/blender/editors/animation/drivers.c2
-rw-r--r--source/blender/editors/animation/keyframes_draw.c38
-rw-r--r--source/blender/editors/animation/keyframes_keylist.cc4
-rw-r--r--source/blender/editors/animation/keyframing.c36
-rw-r--r--source/blender/editors/animation/keyingsets.c66
-rw-r--r--source/blender/editors/armature/armature_add.c11
-rw-r--r--source/blender/editors/armature/armature_edit.c7
-rw-r--r--source/blender/editors/armature/armature_naming.c9
-rw-r--r--source/blender/editors/armature/armature_relations.c27
-rw-r--r--source/blender/editors/armature/armature_skinning.c1
-rw-r--r--source/blender/editors/armature/pose_lib.c2
-rw-r--r--source/blender/editors/armature/pose_select.c9
-rw-r--r--source/blender/editors/curve/editcurve.c42
-rw-r--r--source/blender/editors/curve/editcurve_paint.c8
-rw-r--r--source/blender/editors/curve/editcurve_pen.c10
-rw-r--r--source/blender/editors/curves/CMakeLists.txt5
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc2
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc513
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt19
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc131
-rw-r--r--source/blender/editors/geometry/geometry_intern.hh1
-rw-r--r--source/blender/editors/geometry/geometry_ops.cc1
-rw-r--r--source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c62
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c30
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c112
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c4
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c4
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.cc1
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c62
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h10
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c17
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c2
-rw-r--r--source/blender/editors/include/BIF_glutil.h20
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_clip.h42
-rw-r--r--source/blender/editors/include/ED_curve.h2
-rw-r--r--source/blender/editors/include/ED_datafiles.h38
-rw-r--r--source/blender/editors/include/ED_fileselect.h8
-rw-r--r--source/blender/editors/include/ED_geometry.h12
-rw-r--r--source/blender/editors/include/ED_gizmo_library.h22
-rw-r--r--source/blender/editors/include/ED_gpencil.h6
-rw-r--r--source/blender/editors/include/ED_image.h43
-rw-r--r--source/blender/editors/include/ED_keyframing.h22
-rw-r--r--source/blender/editors/include/ED_mask.h29
-rw-r--r--source/blender/editors/include/ED_mesh.h16
-rw-r--r--source/blender/editors/include/ED_object.h14
-rw-r--r--source/blender/editors/include/ED_scene.h5
-rw-r--r--source/blender/editors/include/ED_screen.h5
-rw-r--r--source/blender/editors/include/ED_sculpt.h2
-rw-r--r--source/blender/editors/include/ED_select_utils.h20
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h66
-rw-r--r--source/blender/editors/include/ED_uvedit.h4
-rw-r--r--source/blender/editors/include/ED_view3d.h30
-rw-r--r--source/blender/editors/include/UI_grid_view.hh264
-rw-r--r--source/blender/editors/include/UI_icons.h17
-rw-r--r--source/blender/editors/include/UI_interface.h155
-rw-r--r--source/blender/editors/include/UI_interface.hh5
-rw-r--r--source/blender/editors/include/UI_interface_icons.h2
-rw-r--r--source/blender/editors/include/UI_tree_view.hh6
-rw-r--r--source/blender/editors/interface/CMakeLists.txt2
-rw-r--r--source/blender/editors/interface/grid_view.cc525
-rw-r--r--source/blender/editors/interface/interface.cc264
-rw-r--r--source/blender/editors/interface/interface_anim.c2
-rw-r--r--source/blender/editors/interface/interface_drag.cc144
-rw-r--r--source/blender/editors/interface/interface_draw.c4
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c8
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c2
-rw-r--r--source/blender/editors/interface/interface_eyedropper_depth.c2
-rw-r--r--source/blender/editors/interface/interface_handlers.c435
-rw-r--r--source/blender/editors/interface/interface_icons.c17
-rw-r--r--source/blender/editors/interface/interface_icons_event.c6
-rw-r--r--source/blender/editors/interface/interface_intern.h50
-rw-r--r--source/blender/editors/interface/interface_layout.c55
-rw-r--r--source/blender/editors/interface/interface_ops.c46
-rw-r--r--source/blender/editors/interface/interface_query.cc47
-rw-r--r--source/blender/editors/interface/interface_region_color_picker.cc10
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.cc6
-rw-r--r--source/blender/editors/interface/interface_region_search.cc44
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c2
-rw-r--r--source/blender/editors/interface/interface_style.cc20
-rw-r--r--source/blender/editors/interface/interface_template_attribute_search.cc11
-rw-r--r--source/blender/editors/interface/interface_template_list.cc9
-rw-r--r--source/blender/editors/interface/interface_templates.c29
-rw-r--r--source/blender/editors/interface/interface_utils.cc166
-rw-r--r--source/blender/editors/interface/interface_view.cc89
-rw-r--r--source/blender/editors/interface/interface_widgets.c485
-rw-r--r--source/blender/editors/interface/tree_view.cc15
-rw-r--r--source/blender/editors/interface/view2d.cc21
-rw-r--r--source/blender/editors/io/CMakeLists.txt28
-rw-r--r--source/blender/editors/io/io_alembic.c2
-rw-r--r--source/blender/editors/io/io_cache.c18
-rw-r--r--source/blender/editors/io/io_collada.c4
-rw-r--r--source/blender/editors/io/io_gpencil_export.c54
-rw-r--r--source/blender/editors/io/io_gpencil_import.c40
-rw-r--r--source/blender/editors/io/io_gpencil_utils.c14
-rw-r--r--source/blender/editors/io/io_obj.c136
-rw-r--r--source/blender/editors/io/io_ops.c17
-rw-r--r--source/blender/editors/io/io_stl_ops.c133
-rw-r--r--source/blender/editors/io/io_stl_ops.h12
-rw-r--r--source/blender/editors/io/io_usd.c63
-rw-r--r--source/blender/editors/mask/mask_add.c60
-rw-r--r--source/blender/editors/mask/mask_draw.c25
-rw-r--r--source/blender/editors/mask/mask_edit.c32
-rw-r--r--source/blender/editors/mask/mask_intern.h3
-rw-r--r--source/blender/editors/mask/mask_ops.c26
-rw-r--r--source/blender/editors/mask/mask_query.c6
-rw-r--r--source/blender/editors/mask/mask_relationships.c3
-rw-r--r--source/blender/editors/mask/mask_select.c29
-rw-r--r--source/blender/editors/mask/mask_shapekey.c8
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt6
-rw-r--r--source/blender/editors/mesh/editface.cc (renamed from source/blender/editors/mesh/editface.c)172
-rw-r--r--source/blender/editors/mesh/editmesh_add.c18
-rw-r--r--source/blender/editors/mesh/editmesh_add_gizmo.c2
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c2
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c641
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c2
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c8
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c16
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c33
-rw-r--r--source/blender/editors/mesh/mesh_data.cc (renamed from source/blender/editors/mesh/mesh_data.c)148
-rw-r--r--source/blender/editors/mesh/mesh_intern.h13
-rw-r--r--source/blender/editors/mesh/meshtools.cc (renamed from source/blender/editors/mesh/meshtools.c)236
-rw-r--r--source/blender/editors/object/object_add.cc215
-rw-r--r--source/blender/editors/object/object_bake.c20
-rw-r--r--source/blender/editors/object/object_bake_api.c119
-rw-r--r--source/blender/editors/object/object_collection.c18
-rw-r--r--source/blender/editors/object/object_constraint.c26
-rw-r--r--source/blender/editors/object/object_data_transfer.c15
-rw-r--r--source/blender/editors/object/object_edit.c35
-rw-r--r--source/blender/editors/object/object_modes.c10
-rw-r--r--source/blender/editors/object/object_modifier.cc56
-rw-r--r--source/blender/editors/object/object_ops.c12
-rw-r--r--source/blender/editors/object/object_relations.c267
-rw-r--r--source/blender/editors/object/object_remesh.cc13
-rw-r--r--source/blender/editors/object/object_transform.cc2
-rw-r--r--source/blender/editors/object/object_vgroup.c26
-rw-r--r--source/blender/editors/render/render_preview.cc24
-rw-r--r--source/blender/editors/scene/scene_edit.c12
-rw-r--r--source/blender/editors/screen/area.c10
-rw-r--r--source/blender/editors/screen/screen_edit.c6
-rw-r--r--source/blender/editors/screen/screen_ops.c35
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt7
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_add.cc674
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_brush.cc183
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_comb.cc157
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_delete.cc181
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc206
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh99
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc54
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection.cc105
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc394
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc167
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.cc20
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c55
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_ops_paint.cc6
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c330
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h1
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c8
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c148
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c27
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.cc83
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc (renamed from source/blender/editors/sculpt_paint/paint_vertex_color_ops.c)58
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c171
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.cc (renamed from source/blender/editors/sculpt_paint/sculpt_automasking.c)59
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c49
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c30
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c45
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c18
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h27
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c14
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c39
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c23
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_image.cc86
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c180
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c25
-rw-r--r--source/blender/editors/sound/sound_ops.c3
-rw-r--r--source/blender/editors/space_action/action_select.c1
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c3
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c6
-rw-r--r--source/blender/editors/space_clip/clip_draw.c62
-rw-r--r--source/blender/editors/space_clip/clip_editor.c22
-rw-r--r--source/blender/editors/space_clip/clip_intern.h6
-rw-r--r--source/blender/editors/space_clip/clip_ops.c6
-rw-r--r--source/blender/editors/space_clip/space_clip.c168
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c367
-rw-r--r--source/blender/editors/space_clip/tracking_select.c86
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc11
-rw-r--r--source/blender/editors/space_file/file_intern.h1
-rw-r--r--source/blender/editors/space_file/file_ops.c46
-rw-r--r--source/blender/editors/space_file/filelist.c25
-rw-r--r--source/blender/editors/space_file/filesel.c21
-rw-r--r--source/blender/editors/space_file/fsmenu.c10
-rw-r--r--source/blender/editors/space_file/fsmenu.h4
-rw-r--r--source/blender/editors/space_file/space_file.c1
-rw-r--r--source/blender/editors/space_graph/graph_select.c1
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_image/image_buttons.c5
-rw-r--r--source/blender/editors/space_image/image_edit.c20
-rw-r--r--source/blender/editors/space_image/image_intern.h1
-rw-r--r--source/blender/editors/space_image/image_ops.c530
-rw-r--r--source/blender/editors/space_image/image_sequence.c4
-rw-r--r--source/blender/editors/space_image/image_undo.c4
-rw-r--r--source/blender/editors/space_image/space_image.c7
-rw-r--r--source/blender/editors/space_info/info_stats.cc18
-rw-r--r--source/blender/editors/space_nla/nla_channels.c116
-rw-r--r--source/blender/editors/space_nla/nla_draw.c129
-rw-r--r--source/blender/editors/space_nla/nla_edit.c6
-rw-r--r--source/blender/editors/space_node/drawnode.cc5
-rw-r--r--source/blender/editors/space_node/node_add.cc130
-rw-r--r--source/blender/editors/space_node/node_context_path.cc4
-rw-r--r--source/blender/editors/space_node/node_draw.cc90
-rw-r--r--source/blender/editors/space_node/node_edit.cc205
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc12
-rw-r--r--source/blender/editors/space_node/node_intern.hh23
-rw-r--r--source/blender/editors/space_node/node_relationships.cc6
-rw-r--r--source/blender/editors/space_node/node_select.cc180
-rw-r--r--source/blender/editors/space_node/node_view.cc6
-rw-r--r--source/blender/editors/space_node/space_node.cc92
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_outliner/outliner_context.cc12
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.cc13
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc575
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc308
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh18
-rw-r--r--source/blender/editors/space_outliner/outliner_query.cc48
-rw-r--r--source/blender/editors/space_outliner/outliner_select.cc50
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc517
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.cc14
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.cc3
-rw-r--r--source/blender/editors/space_outliner/space_outliner.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.cc4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh16
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_libraries.cc8
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc14
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_scenes.cc5
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_view_layer.cc11
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.cc40
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.hh25
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.cc10
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id_library.cc19
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id_library.hh2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc70
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.hh9
-rw-r--r--source/blender/editors/space_outliner/tree/tree_iterator.cc59
-rw-r--r--source/blender/editors/space_outliner/tree/tree_iterator.hh35
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c155
-rw-r--r--source/blender/editors/space_sequencer/sequencer_channels_draw.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_drag_drop.c5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c140
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c355
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c70
-rw-r--r--source/blender/editors/space_sequencer/sequencer_thumbnails.c26
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c6
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc3
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc3
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc31
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc10
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_ops.cc10
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc79
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc5
-rw-r--r--source/blender/editors/space_text/text_draw.c2
-rw-r--r--source/blender/editors/space_view3d/drawobject.c4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c17
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c32
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c31
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_roll.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_rotate.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c21
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c57
-rw-r--r--source/blender/editors/transform/transform.c41
-rw-r--r--source/blender/editors/transform/transform.h33
-rw-r--r--source/blender/editors/transform/transform_convert.c90
-rw-r--r--source/blender/editors/transform/transform_convert.h2
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c2
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c5
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c10
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c5
-rw-r--r--source/blender/editors/transform/transform_convert_node.c4
-rw-r--r--source/blender/editors/transform/transform_convert_object.c2
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c379
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c81
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c21
-rw-r--r--source/blender/editors/transform/transform_generics.c4
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c448
-rw-r--r--source/blender/editors/transform/transform_mode.c2
-rw-r--r--source/blender/editors/transform/transform_mode.h2
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c20
-rw-r--r--source/blender/editors/transform/transform_mode_trackball.c61
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c12
-rw-r--r--source/blender/editors/transform/transform_ops.c8
-rw-r--r--source/blender/editors/transform/transform_snap.c182
-rw-r--r--source/blender/editors/transform/transform_snap.h13
-rw-r--r--source/blender/editors/transform/transform_snap_object.cc401
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c28
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_util.c22
-rw-r--r--source/blender/editors/util/ed_util_ops.cc10
-rw-r--r--source/blender/editors/util/select_utils.c76
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h1
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c1
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c602
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c303
328 files changed, 12385 insertions, 7404 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index c2d517588b2..9eeabf2d05e 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4351,15 +4351,15 @@ void ANIM_channel_setting_set(bAnimContext *ac,
/* --------------------------- */
-/* size of icons */
+/** Size of icons. */
#define ICON_WIDTH (0.85f * U.widget_unit)
-/* width of sliders */
+/** Width of sliders. */
#define SLIDER_WIDTH (4 * U.widget_unit)
-/* min-width of rename textboxes */
+/** Min-width of rename text-boxes. */
#define RENAME_TEXT_MIN_WIDTH (U.widget_unit)
-/* width of graph editor color bands */
+/** Width of graph editor color bands. */
#define GRAPH_COLOR_BAND_WIDTH (0.3f * U.widget_unit)
-/* extra offset for the visibility icons in the graph editor */
+/** Extra offset for the visibility icons in the graph editor. */
#define GRAPH_ICON_VISIBILITY_OFFSET (GRAPH_COLOR_BAND_WIDTH * 1.5f)
/* Helper - Check if a channel needs renaming */
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index f148bb5b77d..b223a1493fd 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -711,14 +711,14 @@ static bool animedit_poll_channels_active(bContext *C)
/* channels region test */
/* TODO: could enhance with actually testing if channels region? */
if (ELEM(NULL, area, CTX_wm_region(C))) {
- return 0;
+ return false;
}
/* animation editor test */
if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
/* Poll callback for Animation Editor channels list region + not in NLA-tweak-mode for NLA. */
@@ -730,21 +730,21 @@ static bool animedit_poll_channels_nla_tweakmode_off(bContext *C)
/* channels region test */
/* TODO: could enhance with actually testing if channels region? */
if (ELEM(NULL, area, CTX_wm_region(C))) {
- return 0;
+ return false;
}
/* animation editor test */
if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
- return 0;
+ return false;
}
/* NLA tweak-mode test. */
if (area->spacetype == SPACE_NLA) {
if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON)) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
/* ****************** Rearrange Channels Operator ******************* */
@@ -791,7 +791,7 @@ static bool rearrange_island_ok(tReorderChannelIsland *island)
{
/* island must not be untouchable */
if (island->flag & REORDER_ISLAND_UNTOUCHABLE) {
- return 0;
+ return false;
}
/* island should be selected to be moved */
@@ -809,10 +809,10 @@ static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
/* make it first element */
BLI_insertlinkbefore(list, list->first, island);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
@@ -833,11 +833,11 @@ static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
/* push it up */
BLI_insertlinkbefore(list, prev, island);
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
@@ -1083,7 +1083,7 @@ static bool rearrange_animchannel_islands(ListBase *list,
/* don't waste effort on an empty list */
if (BLI_listbase_is_empty(list)) {
- return 0;
+ return false;
}
/* group channels into islands */
@@ -1589,7 +1589,7 @@ static bool animchannels_grouping_poll(bContext *C)
/* channels region test */
/* TODO: could enhance with actually testing if channels region? */
if (ELEM(NULL, area, CTX_wm_region(C))) {
- return 0;
+ return false;
}
/* animation editor test - must be suitable modes only */
@@ -1602,7 +1602,7 @@ static bool animchannels_grouping_poll(bContext *C)
/* dopesheet and action only - all others are for other datatypes or have no groups */
if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0) {
- return 0;
+ return false;
}
break;
@@ -1612,17 +1612,17 @@ static bool animchannels_grouping_poll(bContext *C)
/* drivers can't have groups... */
if (sipo->mode != SIPO_MODE_ANIMATION) {
- return 0;
+ return false;
}
break;
}
/* unsupported... */
default:
- return 0;
+ return false;
}
- return 1;
+ return true;
}
/* ----------------------------------------------------------- */
@@ -2428,15 +2428,15 @@ static bool animchannels_enable_poll(bContext *C)
/* channels region test */
/* TODO: could enhance with actually testing if channels region? */
if (ELEM(NULL, area, CTX_wm_region(C))) {
- return 0;
+ return false;
}
/* animation editor test - Action/Dopesheet/etc. and Graph only */
if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH) == 0) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
@@ -2504,7 +2504,7 @@ static bool animchannels_select_filter_poll(bContext *C)
ScrArea *area = CTX_wm_area(C);
if (area == NULL) {
- return 0;
+ return false;
}
/* animation editor with dopesheet */
@@ -2791,13 +2791,35 @@ static bool rename_anim_channels(bAnimContext *ac, int channel_index)
return false;
}
- /* don't allow renaming linked channels */
- if ((ale->fcurve_owner_id != NULL &&
- (ID_IS_LINKED(ale->fcurve_owner_id) || ID_IS_OVERRIDE_LIBRARY(ale->fcurve_owner_id))) ||
- (ale->id != NULL && (ID_IS_LINKED(ale->id) || ID_IS_OVERRIDE_LIBRARY(ale->id)))) {
+ /* Don't allow renaming linked/liboverride channels. */
+ if (ale->fcurve_owner_id != NULL &&
+ (ID_IS_LINKED(ale->fcurve_owner_id) || ID_IS_OVERRIDE_LIBRARY(ale->fcurve_owner_id))) {
ANIM_animdata_freelist(&anim_data);
return false;
}
+ if (ale->id != NULL) {
+ if (ID_IS_LINKED(ale->id)) {
+ ANIM_animdata_freelist(&anim_data);
+ return false;
+ }
+ /* There is one exception to not allowing renaming on liboverride channels: locally-inserted
+ * NLA tracks. */
+ if (ID_IS_OVERRIDE_LIBRARY(ale->id)) {
+ switch (ale->type) {
+ case ANIMTYPE_NLATRACK: {
+ NlaTrack *nlt = (NlaTrack *)ale->data;
+ if ((nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
+ ANIM_animdata_freelist(&anim_data);
+ return false;
+ }
+ break;
+ }
+ default:
+ ANIM_animdata_freelist(&anim_data);
+ return false;
+ }
+ }
+ }
/* check that channel can be renamed */
acf = ANIM_channel_get_typeinfo(ale);
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index 1a3ab100768..ad06b185132 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -543,6 +543,8 @@ void ED_markers_draw(const bContext *C, int flag)
View2D *v2d = UI_view2d_fromcontext(C);
int cfra = CTX_data_scene(C)->r.cfra;
+ GPU_line_width(1.0f);
+
rctf markers_region_rect;
get_marker_region_rect(v2d, &markers_region_rect);
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 5b0c5eac11b..84f99ec0ac0 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -38,7 +38,6 @@
#include "SEQ_iterator.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
-#include "SEQ_transform.h"
#include "anim_intern.h"
@@ -112,9 +111,9 @@ static int seq_frame_apply_snap(bContext *C, Scene *scene, const int timeline_fr
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
seq_frame_snap_update_best(
- SEQ_transform_get_left_handle_frame(seq), timeline_frame, &best_frame, &best_distance);
+ SEQ_time_left_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance);
seq_frame_snap_update_best(
- SEQ_transform_get_right_handle_frame(seq), timeline_frame, &best_frame, &best_distance);
+ SEQ_time_right_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance);
}
SEQ_collection_free(strips);
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index 1b759f5dfa2..c6f68228807 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -180,7 +180,7 @@ static int add_driver_with_target(ReportList *UNUSED(reports),
}
else if ((RNA_property_unit(src_prop) == PROP_UNIT_ROTATION) &&
(RNA_property_unit(dst_prop) != PROP_UNIT_ROTATION)) {
- /* Rotation Source: radians -> normal, so convert src to degrees
+ /* Rotation Source: radians -> normal, so convert src to degrees
* (However, if both input and output is a rotation, don't apply such corrections)
*/
BLI_strncpy(driver->expression, "degrees(var)", sizeof(driver->expression));
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 58d093c678d..786204a52ed 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -168,11 +168,11 @@ void draw_keyframe_shape(float x,
/* Common attributes shared between the draw calls. */
typedef struct DrawKeylistUIData {
float alpha;
- float icon_sz;
- float half_icon_sz;
- float smaller_sz;
- float ipo_sz;
- float gpencil_sz;
+ float icon_size;
+ float half_icon_size;
+ float smaller_size;
+ float ipo_size;
+ float gpencil_size;
float screenspace_margin;
float sel_color[4];
float unsel_color[4];
@@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx,
/* TODO: allow this opacity factor to be themed? */
ctx->alpha = channel_locked ? 0.25f : 1.0f;
- ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac;
- ctx->half_icon_sz = 0.5f * ctx->icon_sz;
- ctx->smaller_sz = 0.35f * ctx->icon_sz;
- ctx->ipo_sz = 0.1f * ctx->icon_sz;
- ctx->gpencil_sz = ctx->smaller_sz * 0.8f;
+ ctx->icon_size = U.widget_unit * 0.5f * yscale_fac;
+ ctx->half_icon_size = 0.5f * ctx->icon_size;
+ ctx->smaller_size = 0.35f * ctx->icon_size;
+ ctx->ipo_size = 0.1f * ctx->icon_size;
+ ctx->gpencil_size = ctx->smaller_size * 0.8f;
ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d);
ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0;
@@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra),
- .ymin = ypos - ctx->gpencil_sz,
- .ymax = ypos + ctx->gpencil_sz,
+ .ymin = ypos - ctx->gpencil_size,
+ .ymax = ypos + ctx->gpencil_size,
},
true,
0.25f * (float)UI_UNIT_X,
@@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->smaller_sz,
- .ymax = ypos + ctx->smaller_sz,
+ .ymin = ypos - ctx->smaller_size,
+ .ymax = ypos + ctx->smaller_size,
},
true,
3.0f,
@@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->half_icon_sz,
- .ymax = ypos + ctx->half_icon_sz,
+ .ymin = ypos - ctx->half_icon_size,
+ .ymax = ypos + ctx->half_icon_size,
},
true,
3.0f,
@@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->ipo_sz,
- .ymax = ypos + ctx->ipo_sz,
+ .ymin = ypos - ctx->ipo_size,
+ .ymax = ypos + ctx->ipo_size,
},
true,
3.0f,
@@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx,
draw_keyframe_shape(ak->cfra,
ypos,
- ctx->icon_sz,
+ ctx->icon_size,
(ak->sel & SELECT),
ak->key_type,
KEYFRAME_SHAPE_BOTH,
diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc
index 3356ef4d47d..8dc598e6e2d 100644
--- a/source/blender/editors/animation/keyframes_keylist.cc
+++ b/source/blender/editors/animation/keyframes_keylist.cc
@@ -132,7 +132,7 @@ static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist)
return;
}
- keylist->runtime.list_wrapper.first = &keylist->runtime.key_columns[0];
+ keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data();
keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1];
}
@@ -309,7 +309,7 @@ static void keylist_first_last(const struct AnimKeylist *keylist,
const struct ActKeyColumn **last_column)
{
if (keylist->is_runtime_initialized) {
- *first_column = &keylist->runtime.key_columns[0];
+ *first_column = keylist->runtime.key_columns.data();
*last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
}
else {
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 14a3b958ea6..aa99a4e50c3 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -2051,6 +2051,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
/* keyingset to use (idname) */
prop = RNA_def_string(
ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
+ RNA_def_property_string_search_func_runtime(
+ prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION);
RNA_def_property_flag(prop, PROP_HIDDEN);
ot->prop = prop;
}
@@ -2246,6 +2248,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot)
/* keyingset to use (idname) */
prop = RNA_def_string(
ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
+ RNA_def_property_string_search_func_runtime(
+ prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION);
RNA_def_property_flag(prop, PROP_HIDDEN);
ot->prop = prop;
}
@@ -3090,8 +3094,13 @@ bool ED_autokeyframe_pchan(
return false;
}
-bool ED_autokeyframe_property(
- bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra)
+bool ED_autokeyframe_property(bContext *C,
+ Scene *scene,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ int rnaindex,
+ float cfra,
+ const bool only_if_property_keyed)
{
Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
@@ -3110,7 +3119,9 @@ bool ED_autokeyframe_property(
fcu = BKE_fcurve_find_by_rna_context_ui(
C, ptr, prop, rnaindex_check, NULL, &action, &driven, &special);
- if (fcu == NULL) {
+ /* Only early out when we actually want an existing F-curve already
+ * (e.g. auto-keyframing from buttons). */
+ if (fcu == NULL && (driven || special || only_if_property_keyed)) {
return changed;
}
@@ -3146,23 +3157,28 @@ bool ED_autokeyframe_property(
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
const eInsertKeyFlags flag = ANIM_get_keyframing_flags(scene, true);
+ char *path = RNA_path_from_ID_to_property(ptr, prop);
- /* NOTE: We use rnaindex instead of fcu->array_index,
- * because a button may control all items of an array at once.
- * E.g., color wheels (see T42567). */
- BLI_assert((fcu->array_index == rnaindex) || (rnaindex == -1));
+ if (only_if_property_keyed) {
+ /* NOTE: We use rnaindex instead of fcu->array_index,
+ * because a button may control all items of an array at once.
+ * E.g., color wheels (see T42567). */
+ BLI_assert((fcu->array_index == rnaindex) || (rnaindex == -1));
+ }
changed = insert_keyframe(bmain,
reports,
id,
action,
- ((fcu->grp) ? (fcu->grp->name) : (NULL)),
- fcu->rna_path,
+ (fcu && fcu->grp) ? fcu->grp->name : NULL,
+ fcu ? fcu->rna_path : path,
rnaindex,
&anim_eval_context,
ts->keyframe_type,
NULL,
flag) != 0;
-
+ if (path) {
+ MEM_freeN(path);
+ }
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
}
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index 6fcdd21bad8..97b81277008 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -708,6 +708,72 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra
return ANIM_builtin_keyingset_get_named(NULL, transformKSName);
}
+static void anim_keyingset_visit_for_search_impl(const bContext *C,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data,
+ const bool use_poll)
+{
+ /* Poll requires context. */
+ if (use_poll && (C == NULL)) {
+ return;
+ }
+
+ Scene *scene = C ? CTX_data_scene(C) : NULL;
+ KeyingSet *ks;
+
+ /* Active Keying Set. */
+ if (!use_poll || (scene && scene->active_keyingset)) {
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = "__ACTIVE__";
+ visit_params.info = "Active Keying Set";
+ visit_fn(visit_user_data, &visit_params);
+ }
+
+ /* User-defined Keying Sets. */
+ if (scene && scene->keyingsets.first) {
+ for (ks = scene->keyingsets.first; ks; ks = ks->next) {
+ if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) {
+ continue;
+ }
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = ks->idname;
+ visit_params.info = ks->name;
+ visit_fn(visit_user_data, &visit_params);
+ }
+ }
+
+ /* Builtin Keying Sets. */
+ for (ks = builtin_keyingsets.first; ks; ks = ks->next) {
+ if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) {
+ continue;
+ }
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = ks->idname;
+ visit_params.info = ks->name;
+ visit_fn(visit_user_data, &visit_params);
+ }
+}
+
+void ANIM_keyingset_visit_for_search(const bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, false);
+}
+
+void ANIM_keyingset_visit_for_search_no_poll(const bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, true);
+}
+
/* Menu of All Keying Sets ----------------------------- */
const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C,
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index f9b973733af..dd43e3e6613 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -375,13 +375,10 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
/* does this constraint have a subtarget in
* this armature?
*/
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(curcon, &targets);
-
+ if (BKE_constraint_targets_get(curcon, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if ((ct->tar == ob) && (ct->subtarget[0])) {
oldtarget = get_named_editbone(editbones, ct->subtarget);
@@ -409,9 +406,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(curcon, &targets, 0);
- }
+ BKE_constraint_targets_flush(curcon, &targets, 0);
}
}
}
@@ -1152,7 +1147,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
}
if (axis_delta == 0.0f) {
- /* both mirrored bones exist and point to eachother and overlap exactly.
+ /* Both mirrored bones exist and point to each other and overlap exactly.
*
* in this case there's no well defined solution, so de-select both and skip.
*/
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index 963d7ea1149..3c445f46902 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -229,7 +229,7 @@ typedef enum eCalcRollTypes {
} eCalcRollTypes;
static const EnumPropertyItem prop_calc_roll_types[] = {
- {0, "", 0, N_("Positive"), ""},
+ RNA_ENUM_ITEM_HEADING(N_("Positive"), NULL),
{CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""},
{CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""},
@@ -237,8 +237,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = {
{CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""},
{CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""},
- {0, "", 0, N_("Negative"), ""},
-
+ RNA_ENUM_ITEM_HEADING(N_("Negative"), NULL),
{CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""},
{CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""},
@@ -246,7 +245,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = {
{CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""},
{CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""},
- {0, "", 0, N_("Other"), ""},
+ RNA_ENUM_ITEM_HEADING(N_("Other"), NULL),
{CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""},
{CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""},
{CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""},
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index e1f2605481f..1f02e24666d 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -109,13 +109,10 @@ static void constraint_bone_name_fix(Object *ob,
bConstraintTarget *ct;
for (curcon = conlist->first; curcon; curcon = curcon->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
/* constraint targets */
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(curcon, &targets);
-
+ if (BKE_constraint_targets_get(curcon, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == ob) {
if (STREQ(ct->subtarget, oldname)) {
@@ -124,9 +121,7 @@ static void constraint_bone_name_fix(Object *ob,
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(curcon, &targets, 0);
- }
+ BKE_constraint_targets_flush(curcon, &targets, 0);
}
/* action constraints */
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index be4829c02be..950178e865d 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -68,14 +68,11 @@ static void joined_armature_fix_links_constraints(Main *bmain,
bool changed = false;
for (con = lb->first; con; con = con->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
+ if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == srcArm) {
if (ct->subtarget[0] == '\0') {
@@ -90,9 +87,7 @@ static void joined_armature_fix_links_constraints(Main *bmain,
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
- }
+ BKE_constraint_targets_flush(con, &targets, 0);
}
/* action constraint? (pose constraints only) */
@@ -459,14 +454,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
if (ob->type == OB_ARMATURE) {
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
for (con = pchan->constraints.first; con; con = con->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
+ if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
/* Any targets which point to original armature
* are redirected to the new one only if:
@@ -487,9 +479,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
- }
+ BKE_constraint_targets_flush(con, &targets, 0);
}
}
}
@@ -498,14 +488,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
/* fix object-level constraints */
if (ob != origArm) {
for (con = ob->constraints.first; con; con = con->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
+ if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
/* any targets which point to original armature are redirected to the new one only if:
* - the target isn't origArm/newArm itself
@@ -525,9 +512,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
- }
+ BKE_constraint_targets_flush(con, &targets, 0);
}
}
}
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index 64035732a39..3a1e7419d3c 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -9,6 +9,7 @@
#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index e0e5693c79b..ea54c3014ca 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -1431,7 +1431,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con
ret = OPERATOR_PASS_THROUGH;
break;
- /* quicky compare to original */
+ /* Quickly compare to original. */
case EVT_TABKEY:
pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
pld->redraw = PL_PREVIEW_REDRAWALL;
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 8790a10f3e5..b6b5d3ee495 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -680,13 +680,10 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
if (pchan->bone->flag & BONE_SELECTED) {
for (con = pchan->constraints.first; con; con = con->next) {
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
+ if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
Object *ob = ct->tar;
@@ -702,9 +699,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 1);
- }
+ BKE_constraint_targets_flush(con, &targets, 1);
}
}
}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 9da9845116d..4c0df48f65b 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -4729,6 +4729,7 @@ void CURVE_OT_make_segment(wmOperatorType *ot)
bool ED_curve_editnurb_select_pick(bContext *C,
const int mval[2],
const int dist_px,
+ const bool vert_without_handles,
const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -4744,6 +4745,9 @@ bool ED_curve_editnurb_select_pick(bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_v2_v2_int(vc.mval, mval);
+ const bool use_handle_select = vert_without_handles &&
+ (vc.v3d->overlay.handle_display != CURVE_HANDLE_NONE);
+
bool found = ED_curve_pick_vert_ex(&vc, 1, dist_px, &nu, &bezt, &bp, &hand, &basact);
if (params->sel_op == SEL_OP_SET) {
@@ -4779,7 +4783,12 @@ bool ED_curve_editnurb_select_pick(bContext *C,
case SEL_OP_ADD: {
if (bezt) {
if (hand == 1) {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ if (use_handle_select) {
+ bezt->f2 |= SELECT;
+ }
+ else {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ }
}
else {
if (hand == 0) {
@@ -4800,7 +4809,12 @@ bool ED_curve_editnurb_select_pick(bContext *C,
case SEL_OP_SUB: {
if (bezt) {
if (hand == 1) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ if (use_handle_select) {
+ bezt->f2 &= ~SELECT;
+ }
+ else {
+ select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ }
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
}
@@ -4824,13 +4838,23 @@ bool ED_curve_editnurb_select_pick(bContext *C,
if (bezt) {
if (hand == 1) {
if (bezt->f2 & SELECT) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ if (use_handle_select) {
+ bezt->f2 &= ~SELECT;
+ }
+ else {
+ select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ }
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
}
}
else {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ if (use_handle_select) {
+ bezt->f2 |= SELECT;
+ }
+ else {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ }
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
}
@@ -4861,7 +4885,12 @@ bool ED_curve_editnurb_select_pick(bContext *C,
if (bezt) {
if (hand == 1) {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ if (use_handle_select) {
+ bezt->f2 |= SELECT;
+ }
+ else {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ }
}
else {
if (hand == 0) {
@@ -5568,7 +5597,8 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
vc.v3d,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
- .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
+ .snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE :
+ SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
},
mval,
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 598779c6ace..6946c09e6f1 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -105,7 +105,7 @@ struct CurveDrawData {
} radius;
struct {
- float mouse[2];
+ float mval[2];
/* Used in case we can't calculate the depth. */
float location_world[3];
@@ -463,8 +463,8 @@ static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
}
copy_v3_v3(cdd->prev.location_world, selem->location_world);
- float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval);
- copy_v2_v2(cdd->prev.mouse, selem->mval);
+ float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval);
+ copy_v2_v2(cdd->prev.mval, selem->mval);
if (cdd->sample.use_substeps && cdd->prev.selem) {
const struct StrokeElem selem_target = *selem;
@@ -1165,7 +1165,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (cdd->state == CURVE_DRAW_PAINTING) {
const float mval_fl[2] = {UNPACK2(event->mval)};
- if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {
+ if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {
curve_draw_event_add(op, event);
}
}
diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c
index fca850076ae..729ad46877a 100644
--- a/source/blender/editors/curve/editcurve_pen.c
+++ b/source/blender/editors/curve/editcurve_pen.c
@@ -843,7 +843,7 @@ static bool insert_point_to_segment(const ViewContext *vc, const wmEvent *event)
{
Curve *cu = vc->obedit->data;
CutData cd = init_cut_data(event);
- float mval[2] = {UNPACK2(event->mval)};
+ const float mval[2] = {UNPACK2(event->mval)};
const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
const bool near_spline = update_cut_data_for_all_nurbs(
vc, BKE_curve_editNurbs_get(cu), mval, threshold_dist_px, &cd);
@@ -985,7 +985,7 @@ static void extrude_vertices_from_selected_endpoints(EditNurb *editnurb,
else {
BPoint *last_bp = nu1->bp + nu1->pntsu - 1;
const bool first_sel = nu1->bp->f1 & SELECT;
- const bool last_sel = last_bp->f1 & SELECT;
+ const bool last_sel = last_bp->f1 & SELECT && nu1->pntsu > 1;
if (first_sel) {
if (last_sel) {
BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BPoint), __func__);
@@ -1134,7 +1134,7 @@ static bool is_spline_nearby(ViewContext *vc,
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
CutData cd = init_cut_data(event);
- float mval[2] = {UNPACK2(event->mval)};
+ const float mval[2] = {UNPACK2(event->mval)};
const bool nearby = update_cut_data_for_all_nurbs(vc, nurbs, mval, sel_dist, &cd);
if (nearby) {
@@ -1655,7 +1655,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
else if (ELEM(event->type, LEFTMOUSE)) {
if (ELEM(event->val, KM_RELEASE, KM_DBL_CLICK)) {
if (delete_point && !cpd->new_point && !cpd->dragging) {
- if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, &params)) {
+ if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, &params)) {
cpd->acted = delete_point_under_mouse(&vc, event);
}
}
@@ -1714,7 +1714,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
else if (select_point) {
- ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, &params);
+ ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, &params);
}
}
diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt
index 1731d224b3e..3c31e8014ff 100644
--- a/source/blender/editors/curves/CMakeLists.txt
+++ b/source/blender/editors/curves/CMakeLists.txt
@@ -7,10 +7,14 @@ set(INC
../../blentranslation
../../depsgraph
../../functions
+ ../../geometry
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
+
+ # RNA_prototypes.h
+ ${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(SRC
@@ -24,3 +28,4 @@ set(LIB
)
blender_add_lib(bf_editor_curves "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+add_dependencies(bf_editor_curves bf_rna)
diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc
index 7d9e663c444..552ef1d96c8 100644
--- a/source/blender/editors/curves/intern/curves_add.cc
+++ b/source/blender/editors/curves/intern/curves_add.cc
@@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
MutableSpan<float3> positions = curves.positions_for_write();
float *radius_data = (float *)CustomData_add_layer_named(
- &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius");
+ &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius");
MutableSpan<float> radii{radius_data, curves.points_num()};
for (const int i : offsets.index_range()) {
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index 7d07c211542..25bcba6cfb3 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -6,20 +6,27 @@
#include <atomic>
+#include "BLI_devirtualize_parameters.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector_set.hh"
#include "ED_curves.h"
#include "ED_object.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "WM_api.h"
+#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_report.h"
@@ -32,9 +39,14 @@
#include "DNA_scene_types.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
+#include "RNA_prototypes.h"
+
+#include "GEO_reverse_uv_sampler.hh"
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
@@ -46,6 +58,41 @@
namespace blender::ed::curves {
+static bool object_has_editable_curves(const Main &bmain, const Object &object)
+{
+ if (object.type != OB_CURVES) {
+ return false;
+ }
+ if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
+ return false;
+ }
+ if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
+ return false;
+ }
+ return true;
+}
+
+static VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
+{
+ VectorSet<Curves *> unique_curves;
+
+ const Main &bmain = *CTX_data_main(&C);
+
+ Object *object = CTX_data_active_object(&C);
+ if (object && object_has_editable_curves(bmain, *object)) {
+ unique_curves.add_new(static_cast<Curves *>(object->data));
+ }
+
+ CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
+ if (object_has_editable_curves(bmain, *object)) {
+ unique_curves.add(static_cast<Curves *>(object->data));
+ }
+ }
+ CTX_DATA_END;
+
+ return unique_curves;
+}
+
using bke::CurvesGeometry;
namespace convert_to_particle_system {
@@ -140,25 +187,20 @@ static void try_convert_single_object(Object &curves_ob,
}
Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
+ BVHTreeFromMesh surface_bvh;
+ BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+
const Span<float3> positions_cu = curves.positions();
- const VArray<int> looptri_indices = curves.surface_triangle_indices();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
BKE_mesh_runtime_looptri_len(&surface_me)};
- /* Find indices of curves that can be transferred to the old hair system. */
- Vector<int> curves_indices_to_transfer;
- for (const int curve_i : curves.curves_range()) {
- const int looptri_i = looptri_indices[curve_i];
- if (looptri_i >= 0 && looptri_i < looptris.size()) {
- curves_indices_to_transfer.append(curve_i);
- }
- else {
- *r_could_not_convert_some_curves = true;
- }
+ if (looptris.is_empty()) {
+ *r_could_not_convert_some_curves = true;
}
- const int hairs_num = curves_indices_to_transfer.size();
- if (hairs_num == 0) {
+ const int hair_num = curves.curves_num();
+ if (hair_num == 0) {
return;
}
@@ -184,15 +226,15 @@ static void try_convert_single_object(Object &curves_ob,
psys_changed_type(&surface_ob, particle_system);
MutableSpan<ParticleData> particles{
- static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
- hairs_num};
+ static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)),
+ hair_num};
/* The old hair system still uses #MFace, so make sure those are available on the mesh. */
BKE_mesh_tessface_calc(&surface_me);
- /* Prepare utility data structure to map hair roots to mfaces. */
+ /* Prepare utility data structure to map hair roots to #MFace's. */
const Span<int> mface_to_poly_map{
- static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
+ static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
surface_me.totface};
Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
for (const int mface_i : mface_to_poly_map.index_range()) {
@@ -206,17 +248,23 @@ static void try_convert_single_object(Object &curves_ob,
const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
- for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
- const int curve_i = curves_indices_to_transfer[new_hair_i];
+ for (const int new_hair_i : IndexRange(hair_num)) {
+ const int curve_i = new_hair_i;
const IndexRange points = curves.points_for_curve(curve_i);
- const int looptri_i = looptri_indices[curve_i];
- const MLoopTri &looptri = looptris[looptri_i];
- const int poly_i = looptri.poly;
-
const float3 &root_pos_cu = positions_cu[points.first()];
const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(
+ surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
+ BLI_assert(nearest.index >= 0);
+
+ const int looptri_i = nearest.index;
+ const MLoopTri &looptri = looptris[looptri_i];
+ const int poly_i = looptri.poly;
+
const int mface_i = find_mface_for_root_position(
surface_me, poly_to_mface_map[poly_i], root_pos_su);
const MFace &mface = surface_me.mface[mface_i];
@@ -315,6 +363,140 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
}
+namespace convert_from_particle_system {
+
+static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys)
+{
+ ParticleSettings &settings = *psys.part;
+ if (psys.part->type != PART_HAIR) {
+ return {};
+ }
+
+ const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0;
+
+ const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached};
+ const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache};
+
+ int points_num = 0;
+ Vector<int> curve_offsets;
+ Vector<int> parents_to_transfer;
+ Vector<int> children_to_transfer;
+ if (transfer_parents) {
+ for (const int parent_i : parents_cache.index_range()) {
+ const int segments = parents_cache[parent_i]->segments;
+ if (segments <= 0) {
+ continue;
+ }
+ parents_to_transfer.append(parent_i);
+ curve_offsets.append(points_num);
+ points_num += segments + 1;
+ }
+ }
+ for (const int child_i : children_cache.index_range()) {
+ const int segments = children_cache[child_i]->segments;
+ if (segments <= 0) {
+ continue;
+ }
+ children_to_transfer.append(child_i);
+ curve_offsets.append(points_num);
+ points_num += segments + 1;
+ }
+ const int curves_num = parents_to_transfer.size() + children_to_transfer.size();
+ curve_offsets.append(points_num);
+ BLI_assert(curve_offsets.size() == curves_num + 1);
+ bke::CurvesGeometry curves(points_num, curves_num);
+ curves.offsets_for_write().copy_from(curve_offsets);
+
+ const float4x4 object_to_world_mat = object.obmat;
+ const float4x4 world_to_object_mat = object_to_world_mat.inverted();
+
+ MutableSpan<float3> positions = curves.positions_for_write();
+
+ const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
+ const Span<int> indices_to_transfer,
+ const int curve_index_offset) {
+ threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int hair_i = indices_to_transfer[i];
+ const int curve_i = i + curve_index_offset;
+ const IndexRange points = curves.points_for_curve(curve_i);
+ const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
+ for (const int key_i : keys.index_range()) {
+ const float3 key_pos_wo = keys[key_i].co;
+ positions[points[key_i]] = world_to_object_mat * key_pos_wo;
+ }
+ }
+ });
+ };
+
+ if (transfer_parents) {
+ copy_hair_to_curves(parents_cache, parents_to_transfer, 0);
+ }
+ copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size());
+
+ curves.update_curve_types();
+ curves.tag_topology_changed();
+ return curves;
+}
+
+static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main &bmain = *CTX_data_main(C);
+ ViewLayer &view_layer = *CTX_data_view_layer(C);
+ Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
+ Object *ob_from_orig = ED_object_active_context(C);
+ ParticleSystem *psys_orig = static_cast<ParticleSystem *>(
+ CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
+ if (psys_orig == nullptr) {
+ psys_orig = psys_get_current(ob_from_orig);
+ }
+ if (psys_orig == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+ Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig);
+ ParticleSystem *psys_eval = nullptr;
+ LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) {
+ if (md->type != eModifierType_ParticleSystem) {
+ continue;
+ }
+ ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md);
+ if (!STREQ(psmd->psys->name, psys_orig->name)) {
+ continue;
+ }
+ psys_eval = psmd->psys;
+ }
+
+ Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name);
+ ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */
+ Curves *curves_id = static_cast<Curves *>(ob_new->data);
+ BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false);
+ bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval);
+
+ DEG_relations_tag_update(&bmain);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool curves_convert_from_particle_system_poll(bContext *C)
+{
+ return ED_object_active_context(C) != nullptr;
+}
+
+} // namespace convert_from_particle_system
+
+static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot)
+{
+ ot->name = "Convert Particle System to Curves";
+ ot->idname = "CURVES_OT_convert_from_particle_system";
+ ot->description = "Add a new curves object based on the current state of the particle system";
+
+ ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll;
+ ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec;
+
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+}
+
namespace snap_curves_to_surface {
enum class AttachMode {
@@ -342,7 +524,7 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
{
const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
- std::atomic<bool> found_invalid_looptri_index = false;
+ std::atomic<bool> found_invalid_uv = false;
CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
if (curves_ob->type != OB_CURVES) {
@@ -359,9 +541,19 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
}
Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
+ MeshComponent surface_mesh_component;
+ surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly);
+
+ VArray_Span<float2> surface_uv_map;
+ if (curves_id.surface_uv_map != nullptr) {
+ surface_uv_map = surface_mesh_component
+ .attribute_try_get_for_read(
+ curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
+ .typed<float2>();
+ }
+
MutableSpan<float3> positions_cu = curves.positions_for_write();
- MutableSpan<int> surface_triangle_indices = curves.surface_triangle_indices_for_write();
- MutableSpan<float2> surface_triangle_coords = curves.surface_triangle_coords_for_write();
+ MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
BKE_mesh_runtime_looptri_len(&surface_mesh)};
@@ -407,36 +599,51 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
pos_cu += pos_diff_cu;
}
- surface_triangle_indices[curve_i] = looptri_index;
-
- const MLoopTri &looptri = surface_looptris[looptri_index];
- const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
- const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
- float3 bary_coords;
- interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
- surface_triangle_coords[curve_i] = bke::curves::encode_surface_bary_coord(bary_coords);
+ if (!surface_uv_map.is_empty()) {
+ const MLoopTri &looptri = surface_looptris[looptri_index];
+ const int corner0 = looptri.tri[0];
+ const int corner1 = looptri.tri[1];
+ const int corner2 = looptri.tri[2];
+ const float2 &uv0 = surface_uv_map[corner0];
+ const float2 &uv1 = surface_uv_map[corner1];
+ const float2 &uv2 = surface_uv_map[corner2];
+ const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
+ const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
+ const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
+ float3 bary_coords;
+ interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
+ const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
+ surface_uv_coords[curve_i] = uv;
+ }
}
});
break;
}
case AttachMode::Deform: {
+ if (surface_uv_map.is_empty()) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Curves do not have attachment information that can be used for deformation");
+ break;
+ }
+ using geometry::ReverseUVSampler;
+ ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
+
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
- const int looptri_index = surface_triangle_indices[curve_i];
- if (!surface_looptris.index_range().contains(looptri_index)) {
- found_invalid_looptri_index = true;
+ const float2 uv = surface_uv_coords[curve_i];
+ ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
+ if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
+ found_invalid_uv = true;
continue;
}
- const MLoopTri &looptri = surface_looptris[looptri_index];
-
- const float3 bary_coords = bke::curves::decode_surface_bary_coord(
- surface_triangle_coords[curve_i]);
+ const MLoopTri &looptri = *lookup_result.looptri;
+ const float3 &bary_coords = lookup_result.bary_weights;
const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
@@ -460,7 +667,7 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- if (found_invalid_looptri_index) {
+ if (found_invalid_uv) {
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
}
@@ -508,11 +715,231 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
"How to find the point on the surface to attach to");
}
+static bool selection_poll(bContext *C)
+{
+ const Object *object = CTX_data_active_object(C);
+ if (object == nullptr) {
+ return false;
+ }
+ if (object->type != OB_CURVES) {
+ return false;
+ }
+ if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) {
+ return false;
+ }
+ return true;
+}
+
+namespace set_selection_domain {
+
+static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
+{
+ const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain"));
+
+ for (Curves *curves_id : get_unique_editable_curves(*C)) {
+ if (curves_id->selection_domain == domain && (curves_id->flag & CV_SCULPT_SELECTION_ENABLED)) {
+ continue;
+ }
+
+ const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain);
+ curves_id->selection_domain = domain;
+ curves_id->flag |= CV_SCULPT_SELECTION_ENABLED;
+
+ CurveComponent component;
+ component.replace(curves_id, GeometryOwnershipType::Editable);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+
+ if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
+ VArray<float> curve_selection = curves.adapt_domain(
+ curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+ curve_selection.materialize(curves.selection_curve_float_for_write());
+ component.attribute_try_delete(".selection_point_float");
+ }
+ else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
+ VArray<float> point_selection = curves.adapt_domain(
+ curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ point_selection.materialize(curves.selection_point_float_for_write());
+ component.attribute_try_delete(".selection_curve_float");
+ }
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace set_selection_domain
+
+static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Set Select Mode";
+ ot->idname = __func__;
+ ot->description = "Change the mode used for selection masking in curves sculpt mode";
+
+ ot->exec = set_selection_domain::curves_set_selection_domain_exec;
+ ot->poll = selection_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = prop = RNA_def_enum(
+ ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+}
+
+namespace disable_selection {
+
+static int curves_disable_selection_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ for (Curves *curves_id : get_unique_editable_curves(*C)) {
+ curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace disable_selection
+
+static void CURVES_OT_disable_selection(wmOperatorType *ot)
+{
+ ot->name = "Disable Selection";
+ ot->idname = __func__;
+ ot->description = "Disable the drawing of influence of selection in sculpt mode";
+
+ ot->exec = disable_selection::curves_disable_selection_exec;
+ ot->poll = selection_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+namespace select_all {
+
+static bool varray_contains_nonzero(const VArray<float> &data)
+{
+ bool contains_nonzero = false;
+ devirtualize_varray(data, [&](const auto array) {
+ for (const int i : data.index_range()) {
+ if (array[i] != 0.0f) {
+ contains_nonzero = true;
+ break;
+ }
+ }
+ });
+ return contains_nonzero;
+}
+
+static bool any_point_selected(const CurvesGeometry &curves)
+{
+ return varray_contains_nonzero(curves.selection_point_float());
+}
+
+static bool any_point_selected(const Span<Curves *> curves_ids)
+{
+ for (const Curves *curves_id : curves_ids) {
+ if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void invert_selection(MutableSpan<float> selection)
+{
+ threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ selection[i] = 1.0f - selection[i];
+ }
+ });
+}
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+ int action = RNA_enum_get(op->ptr, "action");
+
+ VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
+
+ if (action == SEL_TOGGLE) {
+ action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
+ }
+
+ for (Curves *curves_id : unique_curves) {
+ if (action == SEL_SELECT) {
+ /* The optimization to avoid storing the selection when everything is selected causes too
+ * many problems at the moment, since there is no proper visualization yet. Keep the code but
+ * disable it for now. */
+#if 0
+ CurveComponent component;
+ component.replace(curves_id, GeometryOwnershipType::Editable);
+ component.attribute_try_delete(".selection_point_float");
+ component.attribute_try_delete(".selection_curve_float");
+#else
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
+ curves.selection_point_float_for_write() :
+ curves.selection_curve_float_for_write();
+ selection.fill(1.0f);
+#endif
+ }
+ else {
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
+ curves.selection_point_float_for_write() :
+ curves.selection_curve_float_for_write();
+ if (action == SEL_DESELECT) {
+ selection.fill(0.0f);
+ }
+ else if (action == SEL_INVERT) {
+ invert_selection(selection);
+ }
+ }
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace select_all
+
+static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot)
+{
+ ot->name = "(De)select All";
+ ot->idname = __func__;
+ ot->description = "(De)select all control points";
+
+ ot->exec = select_all::select_all_exec;
+ ot->poll = selection_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
} // namespace blender::ed::curves
void ED_operatortypes_curves()
{
using namespace blender::ed::curves;
WM_operatortype_append(CURVES_OT_convert_to_particle_system);
+ WM_operatortype_append(CURVES_OT_convert_from_particle_system);
WM_operatortype_append(CURVES_OT_snap_curves_to_surface);
+ WM_operatortype_append(CURVES_OT_set_selection_domain);
+ WM_operatortype_append(SCULPT_CURVES_OT_select_all);
+ WM_operatortype_append(CURVES_OT_disable_selection);
}
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index d8ecb2c5ae0..9da2c4819a3 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -776,14 +776,19 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.curves.sculpt_comb
ops.curves.sculpt_cut
ops.curves.sculpt_delete
+ ops.curves.sculpt_density
ops.curves.sculpt_grow_shrink
+ ops.curves.sculpt_pinch
ops.curves.sculpt_puff
+ ops.curves.sculpt_slide
+ ops.curves.sculpt_smooth
ops.curves.sculpt_snake_hook
ops.generic.cursor
ops.generic.select
ops.generic.select_box
ops.generic.select_circle
ops.generic.select_lasso
+ ops.generic.select_paint
ops.gpencil.draw
ops.gpencil.draw.eraser
ops.gpencil.draw.line
@@ -928,6 +933,7 @@ if(WITH_BLENDER)
data_to_c_simple(../../../../release/datafiles/brushicons/mask.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/mix.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/nudge.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/paint_select.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/pinch.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/scrape.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/smear.png SRC)
@@ -965,6 +971,19 @@ if(WITH_BLENDER)
data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_hard.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_stroke.png SRC)
+ # curve sculpt
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_add.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_comb.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_cut.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_delete.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_density.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_grow_shrink.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_pinch.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_puff.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_slide.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_smooth.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_snake_hook.png SRC)
+
endif()
data_to_c_simple(../../../../release/datafiles/startup.blend SRC)
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index cfc158b117f..c7e782b7b89 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -33,6 +33,7 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "ED_geometry.h"
#include "ED_object.h"
#include "geometry_intern.hh"
@@ -43,9 +44,9 @@ namespace blender::ed::geometry {
static bool geometry_attributes_poll(bContext *C)
{
- Object *ob = ED_object_context(C);
- Main *bmain = CTX_data_main(C);
- ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr;
+ const Object *ob = ED_object_context(C);
+ const Main *bmain = CTX_data_main(C);
+ const ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr;
return (ob && BKE_id_is_editable(bmain, &ob->id) && data && BKE_id_is_editable(bmain, data)) &&
BKE_id_attributes_supported(data);
}
@@ -89,8 +90,8 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
char name[MAX_NAME];
RNA_string_get(op->ptr, "name", name);
- CustomDataType type = (CustomDataType)RNA_enum_get(op->ptr, "data_type");
- AttributeDomain domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain");
+ eCustomDataType type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type");
+ eAttrDomain domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain");
CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports);
if (layer == nullptr) {
@@ -105,7 +106,7 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static void next_color_attribute(struct ID *id, CustomDataLayer *layer, bool is_render)
+static void next_color_attribute(ID *id, CustomDataLayer *layer, bool is_render)
{
int index = BKE_id_attribute_to_index(id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
@@ -128,7 +129,7 @@ static void next_color_attribute(struct ID *id, CustomDataLayer *layer, bool is_
}
}
-static void next_color_attributes(struct ID *id, CustomDataLayer *layer)
+static void next_color_attributes(ID *id, CustomDataLayer *layer)
{
next_color_attribute(id, layer, false); /* active */
next_color_attribute(id, layer, true); /* render */
@@ -181,7 +182,7 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op)
next_color_attributes(id, layer);
- if (!BKE_id_attribute_remove(id, layer, op->reports)) {
+ if (!BKE_id_attribute_remove(id, layer->name, op->reports)) {
return OPERATOR_CANCELLED;
}
@@ -218,8 +219,8 @@ static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op)
char name[MAX_NAME];
RNA_string_get(op->ptr, "name", name);
- CustomDataType type = (CustomDataType)RNA_enum_get(op->ptr, "data_type");
- AttributeDomain domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain");
+ eCustomDataType type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type");
+ eAttrDomain domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain");
CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports);
float color[4];
@@ -260,8 +261,11 @@ static bool geometry_attribute_convert_poll(bContext *C)
if (GS(data->name) != ID_ME) {
return false;
}
- CustomDataLayer *layer = BKE_id_attributes_active_get(data);
- if (layer == nullptr) {
+ if (CTX_data_edit_object(C) != nullptr) {
+ CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode");
+ return false;
+ }
+ if (BKE_id_attributes_active_get(data) == nullptr) {
return false;
}
return true;
@@ -288,9 +292,8 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
* 4. Create a new attribute based on the previously copied data. */
switch (mode) {
case ConvertAttributeMode::Generic: {
- const AttributeDomain dst_domain = static_cast<AttributeDomain>(
- RNA_enum_get(op->ptr, "domain"));
- const CustomDataType dst_type = static_cast<CustomDataType>(
+ const eAttrDomain dst_domain = static_cast<eAttrDomain>(RNA_enum_get(op->ptr, "domain"));
+ const eCustomDataType dst_type = static_cast<eCustomDataType>(
RNA_enum_get(op->ptr, "data_type"));
if (ELEM(dst_type, CD_PROP_STRING)) {
@@ -402,7 +405,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot)
static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
prop = RNA_def_float_color(
- ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
+ ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
RNA_def_property_float_array_default(prop, default_color);
}
@@ -465,7 +468,7 @@ static int geometry_color_attribute_remove_exec(bContext *C, wmOperator *op)
next_color_attributes(id, layer);
- if (!BKE_id_attribute_remove(id, layer, op->reports)) {
+ if (!BKE_id_attribute_remove(id, layer->name, op->reports)) {
return OPERATOR_CANCELLED;
}
@@ -495,6 +498,7 @@ static bool geometry_color_attributes_remove_poll(bContext *C)
return false;
}
+
void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot)
{
/* identifiers */
@@ -510,6 +514,64 @@ void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static int geometry_color_attribute_duplicate_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ ID *id = static_cast<ID *>(ob->data);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(id);
+
+ if (layer == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ CustomDataLayer *new_layer = BKE_id_attribute_duplicate(id, layer->name, op->reports);
+ if (new_layer == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_id_attributes_active_color_set(id, new_layer);
+
+ DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, id);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool geometry_color_attributes_duplicate_poll(bContext *C)
+{
+ if (!geometry_attributes_poll(C)) {
+ return false;
+ }
+ if (CTX_data_edit_object(C) != nullptr) {
+ CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode");
+ return false;
+ }
+
+ Object *ob = ED_object_context(C);
+ ID *data = ob ? static_cast<ID *>(ob->data) : nullptr;
+
+ if (BKE_id_attributes_active_color_get(data) != nullptr) {
+ return true;
+ }
+
+ return false;
+}
+
+void GEOMETRY_OT_color_attribute_duplicate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Duplicate Color Attribute";
+ ot->description = "Duplicate color attribute";
+ ot->idname = "GEOMETRY_OT_color_attribute_duplicate";
+
+ /* api callbacks */
+ ot->exec = geometry_color_attribute_duplicate_exec;
+ ot->poll = geometry_color_attributes_duplicate_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
@@ -572,3 +634,38 @@ void GEOMETRY_OT_attribute_convert(wmOperatorType *ot)
}
} // namespace blender::ed::geometry
+
+using blender::CPPType;
+using blender::GVArray;
+
+bool ED_geometry_attribute_convert(Mesh *mesh,
+ const char *layer_name,
+ eCustomDataType old_type,
+ eAttrDomain old_domain,
+ eCustomDataType new_type,
+ eAttrDomain new_domain)
+{
+ CustomDataLayer *layer = BKE_id_attribute_find(&mesh->id, layer_name, old_type, old_domain);
+ const std::string name = layer->name;
+
+ if (!layer) {
+ return false;
+ }
+
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ GVArray src_varray = mesh_component.attribute_get_for_read(name, new_domain, new_type);
+
+ const CPPType &cpp_type = src_varray.type();
+ void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
+ src_varray.materialize_to_uninitialized(new_data);
+ mesh_component.attribute_try_delete(name);
+ mesh_component.attribute_try_create(name, new_domain, new_type, AttributeInitMove(new_data));
+
+ int *active_index = BKE_id_attributes_active_index_p(&mesh->id);
+ if (*active_index > 0) {
+ *active_index -= 1;
+ }
+
+ return true;
+}
diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh
index bbcb682d6bf..a1000a5d01f 100644
--- a/source/blender/editors/geometry/geometry_intern.hh
+++ b/source/blender/editors/geometry/geometry_intern.hh
@@ -17,6 +17,7 @@ void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_remove(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_render_set(struct wmOperatorType *ot);
+void GEOMETRY_OT_color_attribute_duplicate(struct wmOperatorType *ot);
void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot);
} // namespace blender::ed::geometry
diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc
index 23f6e6f29f4..acac757ecf1 100644
--- a/source/blender/editors/geometry/geometry_ops.cc
+++ b/source/blender/editors/geometry/geometry_ops.cc
@@ -22,5 +22,6 @@ void ED_operatortypes_geometry(void)
WM_operatortype_append(GEOMETRY_OT_color_attribute_add);
WM_operatortype_append(GEOMETRY_OT_color_attribute_remove);
WM_operatortype_append(GEOMETRY_OT_color_attribute_render_set);
+ WM_operatortype_append(GEOMETRY_OT_color_attribute_duplicate);
WM_operatortype_append(GEOMETRY_OT_attribute_convert);
}
diff --git a/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c b/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c
index 5b514e02099..429bf39234f 100644
--- a/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c
+++ b/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c
@@ -7,6 +7,8 @@
#include "../gizmo_geometry.h"
+/* The numerical values in the `verts` array are used in arrow3d_gizmo.c
+ * If you change this mesh geometry, update the selection code also. */
static float verts[][3] = {
{-0.000000, 0.012320, 0.000000}, {-0.000000, 0.012320, 0.974306},
{0.008711, 0.008711, 0.000000}, {0.008711, 0.008711, 0.974306},
diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
index c6303c197e7..178a2b52b62 100644
--- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
@@ -95,7 +95,7 @@ void wm_gizmo_vec_draw(
immEnd();
}
else if (primitive_type == GPU_PRIM_TRI_FAN) {
- /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small
+ /* NOTE(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small
* primitive counts. */
int tri_count = vert_count - 2;
immBegin(GPU_PRIM_TRIS, tri_count * 3);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index a1fb0e205b1..cde702294d0 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -50,16 +50,19 @@
/* to use custom arrows exported to geom_arrow_gizmo.c */
//#define USE_GIZMO_CUSTOM_ARROWS
-/** Margins to add when selecting the arrow stem. */
-#define ARROW_SELECT_THRESHOLD_PX_STEM (5 * UI_DPI_FAC)
-/** Margins to add when selecting the arrow head. */
-#define ARROW_SELECT_THRESHOLD_PX_HEAD (12 * UI_DPI_FAC)
+/* Margin to add when selecting the arrow. */
+#define ARROW_SELECT_THRESHOLD_PX (5)
typedef struct ArrowGizmo3D {
wmGizmo gizmo;
GizmoCommonData data;
} ArrowGizmo3D;
+typedef struct ArrowGizmoInteraction {
+ GizmoInteraction inter;
+ float init_arrow_length;
+} ArrowGizmoInteraction;
+
/* -------------------------------------------------------------------- */
static void gizmo_arrow_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4])
@@ -70,7 +73,10 @@ static void gizmo_arrow_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4]
madd_v3_v3fl(r_matrix[3], arrow->gizmo.matrix_basis[2], arrow->data.offset);
}
-static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const float color[4])
+static void arrow_draw_geom(const ArrowGizmo3D *arrow,
+ const bool select,
+ const float color[4],
+ const float arrow_length)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
bool unbind_shader = true;
@@ -113,16 +119,14 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const
#ifdef USE_GIZMO_CUSTOM_ARROWS
wm_gizmo_geometryinfo_draw(&wm_gizmo_geom_data_arrow, select, color);
#else
- const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length");
-
const float vec[2][3] = {
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, arrow_length},
};
if (draw_options & ED_GIZMO_ARROW_DRAW_FLAG_STEM) {
- const float stem_width = (arrow->gizmo.line_width * U.pixelsize) +
- (select ? ARROW_SELECT_THRESHOLD_PX_STEM : 0);
+ const float stem_width = arrow->gizmo.line_width * U.pixelsize +
+ (select ? ARROW_SELECT_THRESHOLD_PX * U.dpi_fac : 0);
immUniform1f("lineWidth", stem_width);
wm_gizmo_vec_draw(color, vec, ARRAY_SIZE(vec), pos, GPU_PRIM_LINE_STRIP);
}
@@ -134,7 +138,7 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const
GPU_matrix_push();
- /* NOTE: ideally #ARROW_SELECT_THRESHOLD_PX_HEAD would be added here, however adding a
+ /* NOTE: ideally #ARROW_SELECT_THRESHOLD_PX would be added here, however adding a
* margin in pixel space isn't so simple, nor is it as important as for the arrow stem. */
if (draw_style == ED_GIZMO_ARROW_STYLE_BOX) {
const float size = 0.05f;
@@ -178,6 +182,7 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const
static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool highlight)
{
wmGizmo *gz = &arrow->gizmo;
+ const float arrow_length = RNA_float_get(gz->ptr, "length");
float color[4];
float matrix_final[4][4];
@@ -188,19 +193,20 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool
GPU_matrix_push();
GPU_matrix_mul(matrix_final);
GPU_blend(GPU_BLEND_ALPHA);
- arrow_draw_geom(arrow, select, color);
+ arrow_draw_geom(arrow, select, color, arrow_length);
GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
if (gz->interaction_data) {
- GizmoInteraction *inter = gz->interaction_data;
+ ArrowGizmoInteraction *arrow_inter = gz->interaction_data;
GPU_matrix_push();
- GPU_matrix_mul(inter->init_matrix_final);
+ GPU_matrix_mul(arrow_inter->inter.init_matrix_final);
GPU_blend(GPU_BLEND_ALPHA);
- arrow_draw_geom(arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f});
+ arrow_draw_geom(
+ arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}, arrow_inter->init_arrow_length);
GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
@@ -223,9 +229,15 @@ static void gizmo_arrow_draw(const bContext *UNUSED(C), wmGizmo *gz)
*/
static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2])
{
+ /* This following values are based on manual inspection of `verts[]` defined in
+ * geom_arrow_gizmo.c */
+ const float head_center_z = (0.974306f + 1.268098f) / 2;
+ const float head_geo_x = 0.051304f;
+ const float stem_geo_x = 0.012320f;
+
/* Project into 2D space since it simplifies pixel threshold tests. */
ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
- const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length");
+ const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length") * head_center_z;
float matrix_final[4][4];
WM_gizmo_calc_matrix_final(gz, matrix_final);
@@ -239,12 +251,15 @@ static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int m
copy_v2_v2(arrow_end, co);
}
+ const float scale_final = mat4_to_scale(matrix_final);
+ const float head_width = ARROW_SELECT_THRESHOLD_PX * scale_final * head_geo_x;
+ const float stem_width = ARROW_SELECT_THRESHOLD_PX * scale_final * stem_geo_x;
+ float select_threshold_base = gz->line_width * U.pixelsize;
+
const float mval_fl[2] = {UNPACK2(mval)};
- const float arrow_stem_threshold_px = ARROW_SELECT_THRESHOLD_PX_STEM;
- const float arrow_head_threshold_px = ARROW_SELECT_THRESHOLD_PX_HEAD;
/* Distance to arrow head. */
- if (len_squared_v2v2(mval_fl, arrow_end) < square_f(arrow_head_threshold_px)) {
+ if (len_squared_v2v2(mval_fl, arrow_end) < square_f(select_threshold_base + head_width)) {
return 0;
}
@@ -253,8 +268,8 @@ static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int m
const float lambda = closest_to_line_v2(co_isect, mval_fl, arrow_start, arrow_end);
/* Clamp inside the line, to avoid overlapping with other gizmos,
* especially around the start of the arrow. */
- if (lambda >= 0.0 && lambda <= 1.0) {
- if (len_squared_v2v2(mval_fl, co_isect) < square_f(arrow_stem_threshold_px)) {
+ if (lambda >= 0.0f && lambda <= 1.0f) {
+ if (len_squared_v2v2(mval_fl, co_isect) < square_f(select_threshold_base + stem_width)) {
return 0;
}
}
@@ -373,7 +388,7 @@ static void gizmo_arrow_setup(wmGizmo *gz)
static int gizmo_arrow_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event)
{
ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
- GizmoInteraction *inter = MEM_callocN(sizeof(GizmoInteraction), __func__);
+ GizmoInteraction *inter = MEM_callocN(sizeof(ArrowGizmoInteraction), __func__);
wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
/* Some gizmos don't use properties. */
@@ -389,6 +404,8 @@ static int gizmo_arrow_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *e
gizmo_arrow_matrix_basis_get(gz, inter->init_matrix_basis);
WM_gizmo_calc_matrix_final(gz, inter->init_matrix_final);
+ ((ArrowGizmoInteraction *)inter)->init_arrow_length = RNA_float_get(gz->ptr, "length");
+
gz->interaction_data = inter;
return OPERATOR_RUNNING_MODAL;
@@ -513,7 +530,8 @@ static void GIZMO_GT_arrow_3d(wmGizmoType *gzt)
"");
RNA_def_enum_flag(gzt->srna, "transform", rna_enum_transform_items, 0, "Transform", "");
- RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX);
+ RNA_def_float(
+ gzt->srna, "length", 1.0f, -FLT_MAX, FLT_MAX, "Arrow Line Length", "", -FLT_MAX, FLT_MAX);
RNA_def_float_vector(
gzt->srna, "aspect", 2, NULL, 0, FLT_MAX, "Aspect", "Cone/box style only", 0.0f, FLT_MAX);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
index b326d6d1859..11309402220 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -57,8 +57,6 @@ typedef struct ButtonGizmo2D {
GPUBatch *shape_batch[2];
} ButtonGizmo2D;
-#define CIRCLE_RESOLUTION 32
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -68,27 +66,33 @@ typedef struct ButtonGizmo2D {
static void button2d_geom_draw_backdrop(const wmGizmo *gz,
const float color[4],
const float fill_alpha,
- const bool select)
+ const bool select,
+ const float screen_scale)
{
float viewport[4];
GPU_viewport_size_get_f(viewport);
+ const float max_pixel_error = 0.25f;
+ int nsegments = (int)(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale)));
+ nsegments = max_ff(nsegments, 8);
+ nsegments = min_ff(nsegments, 1000);
+
GPUVertFormat *format = immVertexFormat();
- /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
+ /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
/* TODO: other draw styles. */
if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
- imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
immUnbindProgram();
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
immUnbindProgram();
}
else {
@@ -97,7 +101,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(fill_color);
- imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
immUnbindProgram();
}
@@ -107,7 +111,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
immUnbindProgram();
}
}
@@ -184,9 +188,11 @@ static void button2d_draw_intern(const bContext *C,
GPU_matrix_mul(matrix_align);
}
+ const float screen_scale = mat4_to_scale(matrix_final);
+
if (select) {
BLI_assert(is_3d);
- button2d_geom_draw_backdrop(gz, color, 1.0, select);
+ button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale);
}
else {
@@ -194,7 +200,7 @@ static void button2d_draw_intern(const bContext *C,
if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha");
- button2d_geom_draw_backdrop(gz, color, fill_alpha, select);
+ button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale);
}
if (button->shape_batch[0] != NULL) {
@@ -315,10 +321,11 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz)
return WM_CURSOR_DEFAULT;
}
+#define CIRCLE_RESOLUTION_3D 32
static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
{
ScrArea *area = CTX_wm_area(C);
- float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f;
+ float rad = CIRCLE_RESOLUTION_3D * U.dpi_fac / 2.0f;
const float *co = NULL;
float matrix_final[4][4];
float co_proj[3];
@@ -342,6 +349,7 @@ static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box
}
}
else {
+ rad = mat4_to_scale(matrix_final);
co = matrix_final[3];
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
index a76242404ba..9b7b157dc7e 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
@@ -77,6 +77,21 @@ typedef struct DialInteraction {
#define DIAL_CLIP_BIAS 0.02
/* -------------------------------------------------------------------- */
+struct Dial3dParams {
+ int draw_options;
+ float angle_ofs;
+ float angle_delta;
+ float angle_increment;
+ float arc_partial_angle;
+ float arc_inner_factor;
+ float *clip_plane;
+};
+static void dial_3d_draw_util(const float matrix_basis[4][4],
+ const float matrix_final[4][4],
+ const float line_width,
+ const float color[4],
+ const bool select,
+ struct Dial3dParams *params);
static void dial_geom_draw(const float color[4],
const float line_width,
@@ -96,7 +111,7 @@ static void dial_geom_draw(const float color[4],
ED_GIZMO_DIAL_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
if (clip_plane) {
@@ -411,23 +426,26 @@ static void dial_draw_intern(
if (WM_gizmo_target_property_is_valid(gz_prop)) {
angle_delta = WM_gizmo_target_property_float_get(gz, gz_prop);
}
+ if (gz->state & WM_GIZMO_STATE_MODAL) {
+ angle_increment = RNA_float_get(gz->ptr, "incremental_angle");
+ }
}
}
- ED_gizmotypes_dial_3d_draw_util(gz->matrix_basis,
- matrix_final,
- gz->line_width,
- color,
- select,
- &(struct Dial3dParams){
- .draw_options = draw_options,
- .angle_ofs = angle_ofs,
- .angle_delta = angle_delta,
- .angle_increment = angle_increment,
- .arc_partial_angle = arc_partial_angle,
- .arc_inner_factor = arc_inner_factor,
- .clip_plane = clip_plane,
- });
+ dial_3d_draw_util(gz->matrix_basis,
+ matrix_final,
+ gz->line_width,
+ color,
+ select,
+ &(struct Dial3dParams){
+ .draw_options = draw_options,
+ .angle_ofs = angle_ofs,
+ .angle_delta = angle_delta,
+ .angle_increment = angle_increment,
+ .arc_partial_angle = arc_partial_angle,
+ .arc_inner_factor = arc_inner_factor,
+ .clip_plane = clip_plane,
+ });
}
static void gizmo_dial_draw_select(const bContext *C, wmGizmo *gz, int select_id)
@@ -479,6 +497,10 @@ static int gizmo_dial_modal(bContext *C,
eWM_GizmoFlagTweak tweak_flag)
{
DialInteraction *inter = gz->interaction_data;
+ if (!inter) {
+ return OPERATOR_CANCELLED;
+ }
+
if ((event->type != MOUSEMOVE) && (inter->prev.tweak_flag == tweak_flag)) {
return OPERATOR_RUNNING_MODAL;
}
@@ -519,30 +541,33 @@ static int gizmo_dial_modal(bContext *C,
static void gizmo_dial_exit(bContext *C, wmGizmo *gz, const bool cancel)
{
DialInteraction *inter = gz->interaction_data;
- bool use_reset_value = false;
- float reset_value = 0.0f;
- if (cancel) {
- /* Set the property for the operator and call its modal function. */
- wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
- if (WM_gizmo_target_property_is_valid(gz_prop)) {
- use_reset_value = true;
- reset_value = inter->init.prop_angle;
- }
- }
- else {
- if (inter->has_drag == false) {
- PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "click_value");
- if (RNA_property_is_set(gz->ptr, prop)) {
+ if (inter) {
+ bool use_reset_value = false;
+ float reset_value = 0.0f;
+
+ if (cancel) {
+ /* Set the property for the operator and call its modal function. */
+ wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
+ if (WM_gizmo_target_property_is_valid(gz_prop)) {
use_reset_value = true;
- reset_value = RNA_property_float_get(gz->ptr, prop);
+ reset_value = inter->init.prop_angle;
+ }
+ }
+ else {
+ if (inter->has_drag == false) {
+ PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "click_value");
+ if (RNA_property_is_set(gz->ptr, prop)) {
+ use_reset_value = true;
+ reset_value = RNA_property_float_get(gz->ptr, prop);
+ }
}
}
- }
- if (use_reset_value) {
- wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
- if (WM_gizmo_target_property_is_valid(gz_prop)) {
- WM_gizmo_target_property_float_set(C, gz, gz_prop, reset_value);
+ if (use_reset_value) {
+ wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
+ if (WM_gizmo_target_property_is_valid(gz_prop)) {
+ WM_gizmo_target_property_float_set(C, gz, gz_prop, reset_value);
+ }
}
}
@@ -564,6 +589,11 @@ static void gizmo_dial_setup(wmGizmo *gz)
static int gizmo_dial_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event)
{
+ if (gz->custom_modal) {
+ /* #DialInteraction is only used for the inner modal. */
+ return OPERATOR_RUNNING_MODAL;
+ }
+
DialInteraction *inter = MEM_callocN(sizeof(DialInteraction), __func__);
inter->init.mval[0] = event->mval[0];
@@ -583,12 +613,12 @@ static int gizmo_dial_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *ev
/** \name Dial Gizmo API
* \{ */
-void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4],
- const float matrix_final[4][4],
- const float line_width,
- const float color[4],
- const bool select,
- struct Dial3dParams *params)
+static void dial_3d_draw_util(const float matrix_basis[4][4],
+ const float matrix_final[4][4],
+ const float line_width,
+ const float color[4],
+ const bool select,
+ struct Dial3dParams *params)
{
GPU_matrix_push();
GPU_matrix_mul(matrix_final);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 5fb1173521a..1ce67185c1e 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -98,7 +98,7 @@ static void move_geom_draw(const wmGizmo *gz,
ED_GIZMO_MOVE_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR :
@@ -280,7 +280,7 @@ static int gizmo_move_modal(bContext *C,
CTX_wm_view3d(C),
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
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 1659afb1254..d468906f127 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -96,7 +96,7 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C,
float r_loc[3],
float r_nor[3],
int r_elem_index[3],
- int *r_snap_elem)
+ eSnapMode *r_snap_elem)
{
if (C) {
/* Snap values are updated too late at the cursor. Be sure to update ahead of time. */
@@ -283,7 +283,7 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2])
ED_view3d_cursor_snap_data_update(snap_gizmo->snap_state, C, x, y);
V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get();
- if (snap_data->snap_elem) {
+ if (snap_data->snap_elem != SCE_SNAP_MODE_NONE) {
return 0;
}
return -1;
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 011a5a66695..24de50db397 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -80,9 +80,9 @@ typedef enum eDrawStrokeFlags {
GP_DRAWDATA_ONLYI2D = (1 << 3),
/** special hack for drawing strokes in Image Editor (weird coordinates) */
GP_DRAWDATA_IEDITHACK = (1 << 4),
- /** don't draw xray in 3D view (which is default) */
+ /** Don't draw XRAY in 3D view (which is default). */
GP_DRAWDATA_NO_XRAY = (1 << 5),
- /** no onionskins should be drawn (for animation playback) */
+ /** No onion-skins should be drawn (for animation playback). */
GP_DRAWDATA_NO_ONIONS = (1 << 6),
/** draw strokes as "volumetric" circular billboards */
GP_DRAWDATA_VOLUMETRIC = (1 << 7),
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc
index dfc74f6d225..66f53bea326 100644
--- a/source/blender/editors/gpencil/gpencil_bake_animation.cc
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc
@@ -320,6 +320,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
/* Update point location to new object space. */
for (int j = 0; j < gps->totpoints; j++) {
bGPDspoint *pt = &gps->points[j];
+ mul_m4_v3(ob_eval->obmat, &pt->x);
mul_m4_v3(invmat, &pt->x);
}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 4f9468cc9c4..0601d009bf7 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1104,7 +1104,7 @@ static void gpencil_stroke_to_bezier(bContext *C,
rad_fac,
minmax_weights);
- /* shift coord vects */
+ /* Shift coord vectors. */
copy_v3_v3(p3d_prev, p3d_cur);
copy_v3_v3(p3d_cur, p3d_next);
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 3c8e6d6e5f5..5028baf1589 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1811,7 +1811,16 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Create a new layer. */
- target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
+ PropertyRNA *prop;
+ char name[128];
+ prop = RNA_struct_find_property(op->ptr, "new_layer_name");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_string_get(op->ptr, prop, name);
+ }
+ else {
+ strcpy(name, "GP_Layer");
+ }
+ target_layer = BKE_gpencil_layer_addnew(gpd, name, true, false);
}
if (target_layer == NULL) {
@@ -1888,8 +1897,46 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void layer_new_name_get(bGPdata *gpd, char *rname)
+{
+ int index = 0;
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (strstr(gpl->info, "GP_Layer")) {
+ index++;
+ }
+ }
+
+ if (index == 0) {
+ BLI_strncpy(rname, "GP_Layer", 128);
+ return;
+ }
+ char *name = BLI_sprintfN("%.*s.%03d", 128, "GP_Layer", index);
+ BLI_strncpy(rname, name, 128);
+ MEM_freeN(name);
+}
+
+static int gpencil_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ PropertyRNA *prop;
+ if (RNA_int_get(op->ptr, "layer") == -1) {
+ prop = RNA_struct_find_property(op->ptr, "new_layer_name");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ char name[MAX_NAME];
+ bGPdata *gpd = ob->data;
+ layer_new_name_get(gpd, name);
+ RNA_property_string_set(op->ptr, prop, name);
+ return WM_operator_props_dialog_popup(C, op, 200);
+ }
+ }
+
+ return gpencil_move_to_layer_exec(C, op);
+}
+
void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Move Strokes to Layer";
ot->idname = "GPENCIL_OT_move_to_layer";
@@ -1898,15 +1945,20 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
/* callbacks */
ot->exec = gpencil_move_to_layer_exec;
- ot->poll = gpencil_stroke_edit_poll; /* XXX? */
+ ot->invoke = gpencil_move_to_layer_invoke;
+ ot->poll = gpencil_stroke_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* GPencil layer to use. */
- ot->prop = RNA_def_int(
- ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX);
- RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_int(ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_string(
+ ot->srna, "new_layer_name", NULL, MAX_NAME, "Name", "Name of the newly added layer");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ ot->prop = prop;
}
/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 56c94e4494d..d656241c463 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -726,16 +726,16 @@ struct GP_EditableStrokes_Iter {
(void)0
/**
- * Iterate over all editable editcurves in the current context,
- * stopping on each usable layer + stroke + curve pair (i.e. gpl, gps and gpc)
+ * Iterate over all editable edit-curves in the current context,
+ * stopping on each usable layer + stroke + curve pair (i.e. `gpl`, `gps` and `gpc`)
* to perform some operations on the curve.
*
* \param gpl: The identifier to use for the layer of the stroke being processed.
- * Choose a suitable value to avoid name clashes.
+ * Choose a suitable value to avoid name clashes.
* \param gps: The identifier to use for current stroke being processed.
- * Choose a suitable value to avoid name clashes.
+ * Choose a suitable value to avoid name clashes.
* \param gpc: The identifier to use for current editcurve being processed.
- * Choose a suitable value to avoid name clashes.
+ * Choose a suitable value to avoid name clashes.
*/
#define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc) \
{ \
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 8630b7f23d4..0039dbae674 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -1482,8 +1482,8 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
* Changes here will likely apply there too.
*/
static const EnumPropertyItem gpencil_interpolation_type_items[] = {
- /* interpolation */
- {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"},
+ /* Interpolation. */
+ RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"),
{GP_IPO_LINEAR,
"LINEAR",
ICON_IPO_LINEAR,
@@ -1495,13 +1495,10 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
"Custom",
"Custom interpolation defined using a curve map"},
- /* easing */
- {0,
- "",
- 0,
- N_("Easing (by strength)"),
- "Predefined inertial transitions, useful for motion graphics (from least to most "
- "''dramatic'')"},
+ /* Easing. */
+ RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"),
+ "Predefined inertial transitions, useful for motion graphics "
+ "(from least to most \"dramatic\")"),
{GP_IPO_SINE,
"SINE",
ICON_IPO_SINE,
@@ -1518,7 +1515,7 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
"Circular",
"Circular easing (strongest and most dynamic)"},
- {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"},
+ RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"),
{GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"},
{GP_IPO_BOUNCE,
"BOUNCE",
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 01ac02a9a1d..6d0848de36d 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -829,7 +829,7 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso,
mul_v2_fl(svec, fac);
}
- /* convert to dataspace */
+ /* Convert to data-space. */
if (gps->flag & GP_STROKE_3DSPACE) {
/* 3D: Project to 3D space */
bool flip;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index c4fd34212c3..e903d11a1e0 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -1701,7 +1701,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
const bool select = (sel_op != SEL_OP_SUB);
bool changed = false;
- /* for bounding rect around circle (for quicky intersection testing) */
+ /* For bounding `rect` around circle (for quickly intersection testing). */
rcti rect = {0};
rect.xmin = mx - radius;
rect.ymin = my - radius;
@@ -2681,6 +2681,7 @@ void GPENCIL_OT_select(wmOperatorType *ot)
ot->invoke = gpencil_select_invoke;
ot->exec = gpencil_select_exec;
ot->poll = gpencil_select_poll;
+ ot->get_name = ED_select_pick_get_name;
/* flag */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 2dc12125f40..7b659511aaa 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -388,6 +388,17 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C,
/* Create new layer */
/* TODO: have some way of specifying that we don't want this? */
+ {
+ /* "New Layer" entry */
+ item_tmp.identifier = "__CREATE__";
+ item_tmp.name = "New Layer";
+ item_tmp.value = -1;
+ item_tmp.icon = ICON_ADD;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+
+ /* separator */
+ RNA_enum_item_add_separator(&item, &totitem);
+ }
const int tot = BLI_listbase_count(&gpd->layers);
/* Existing layers */
@@ -405,17 +416,6 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C,
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
- {
- /* separator */
- RNA_enum_item_add_separator(&item, &totitem);
-
- /* "New Layer" entry */
- item_tmp.identifier = "__CREATE__";
- item_tmp.name = "New Layer";
- item_tmp.value = -1;
- item_tmp.icon = ICON_ADD;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
RNA_enum_item_end(&item, &totitem);
*r_free = true;
@@ -525,7 +525,7 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps
return (area->spacetype == SPACE_IMAGE);
}
if (gps->flag & GP_STROKE_2DSPACE) {
- /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */
+ /* 2D strokes (data-space) - for any 2D view (i.e. everything other than 3D view). */
return (area->spacetype != SPACE_VIEW3D);
}
/* view aligned - anything goes */
@@ -1149,7 +1149,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
depsgraph,
v3d,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
},
&ray_start[0],
&ray_normal[0],
@@ -1747,7 +1747,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat
float darkcolor[3];
float radius = 3.0f;
- int mval_i[2] = {x, y};
+ const int mval_i[2] = {x, y};
/* Check if cursor is in drawing region and has valid data-block. */
if ((!gpencil_check_cursor_region(C, mval_i)) || (gpd == NULL)) {
return;
@@ -2962,7 +2962,7 @@ void ED_gpencil_projected_2d_bound_box(const GP_SpaceConversion *gsc,
bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc,
bGPDstroke *gps,
- const float mouse[2],
+ const float mval[2],
const int radius,
const float diff_mat[4][4])
{
@@ -2980,7 +2980,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc,
rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]};
/* For mouse, add a small offset to avoid false negative in corners. */
- rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset};
+ rcti rect_mouse = {mval[0] - offset, mval[0] + offset, mval[1] - offset, mval[1] + offset};
/* Check collision between both rectangles. */
return BLI_rcti_isect(&rect_stroke, &rect_mouse, NULL);
@@ -2988,7 +2988,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc,
bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps,
const GP_SpaceConversion *gsc,
- const int mouse[2],
+ const int mval[2],
const float diff_mat[4][4])
{
bool hit = false;
@@ -3014,9 +3014,8 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps,
BLI_lasso_boundbox(&rect, mcoords, len);
/* Test if point inside stroke. */
- hit = ((!ELEM(V2D_IS_CLIPPED, mouse[0], mouse[1])) &&
- BLI_rcti_isect_pt(&rect, mouse[0], mouse[1]) &&
- BLI_lasso_is_point_inside(mcoords, len, mouse[0], mouse[1], INT_MAX));
+ hit = ((!ELEM(V2D_IS_CLIPPED, mval[0], mval[1])) && BLI_rcti_isect_pt(&rect, mval[0], mval[1]) &&
+ BLI_lasso_is_point_inside(mcoords, len, mval[0], mval[1], INT_MAX));
/* Free memory. */
MEM_SAFE_FREE(mcoords);
@@ -3077,6 +3076,11 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C,
continue;
}
+ /* Check that stroke is not closed. Closed strokes must not be included in the merge. */
+ if (gps_target->flag & GP_STROKE_CYCLIC) {
+ continue;
+ }
+
/* Check if one of the ends is inside target stroke bounding box. */
if ((!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat)) &&
(!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat))) {
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index c0888968a2d..865c4e360b5 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -587,7 +587,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op)
srgb_to_linearrgb_v4(color, color);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) {
- copy_v3_v3(pt->vert_color, color);
+ copy_v4_v4(pt->vert_color, color);
}
}
}
diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h
index 3f1b8d6ecda..84512653a24 100644
--- a/source/blender/editors/include/BIF_glutil.h
+++ b/source/blender/editors/include/BIF_glutil.h
@@ -55,17 +55,17 @@ IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin);
* finished.
*/
void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state,
- const float x,
- const float y,
- const int img_w,
- const int img_h,
- const eGPUTextureFormat gpu_format,
- const bool use_filter,
+ float x,
+ float y,
+ int img_w,
+ int img_h,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
const void *rect,
- const float scaleX,
- const float scaleY,
- const float xzoom,
- const float yzoom,
+ float scaleX,
+ float scaleY,
+ float xzoom,
+ float yzoom,
const float color[4]);
/**
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 60af05baed7..da40eef87fd 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -549,7 +549,7 @@ typedef enum eAnimChannels_SetFlag {
/* types of settings for AnimChannels */
typedef enum eAnimChannel_Settings {
ACHANNEL_SETTING_SELECT = 0,
- /** warning: for drawing UI's, need to check if this is off (maybe inverse this later) */
+ /** WARNING: for drawing UI's, need to check if this is off (maybe inverse this later). */
ACHANNEL_SETTING_PROTECT = 1,
ACHANNEL_SETTING_MUTE = 2,
ACHANNEL_SETTING_EXPAND = 3,
diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h
index 2c67b02611b..0f981e270a0 100644
--- a/source/blender/editors/include/ED_clip.h
+++ b/source/blender/editors/include/ED_clip.h
@@ -22,15 +22,53 @@ struct bScreen;
/* ** clip_editor.c ** */
-/* common poll functions */
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - There is a movie clip opened in it. */
bool ED_space_clip_poll(struct bContext *C);
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Clip view.
+ *
+ * It is not required to have movie clip opened for editing. */
bool ED_space_clip_view_clip_poll(struct bContext *C);
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Tracking mode.
+ *
+ * It is not required to have movie clip opened for editing. */
bool ED_space_clip_tracking_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Mask mode.
+ *
+ * It is not required to have mask opened for editing. */
bool ED_space_clip_maskedit_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Mask mode.
+ * - Mask has visible and editable splines.
+ *
+ * It is not required to have mask opened for editing. */
+bool ED_space_clip_maskedit_visible_splines_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Mask mode.
+ * - The space has mask opened. */
bool ED_space_clip_maskedit_mask_poll(struct bContext *C);
+/* Returns true when the following conditions are met:
+ * - Current space is Space Clip.
+ * - It is set to Mask mode.
+ * - The space has mask opened.
+ * - Mask has visible and editable splines. */
+bool ED_space_clip_maskedit_mask_visible_splines_poll(struct bContext *C);
+
void ED_space_clip_get_size(struct SpaceClip *sc, int *width, int *height);
void ED_space_clip_get_size_fl(struct SpaceClip *sc, float size[2]);
void ED_space_clip_get_zoom(struct SpaceClip *sc,
@@ -60,7 +98,7 @@ bool ED_space_clip_get_position(struct SpaceClip *sc,
*/
bool ED_space_clip_color_sample(struct SpaceClip *sc,
struct ARegion *region,
- int mval[2],
+ const int mval[2],
float r_col[3]);
void ED_clip_update_frame(const struct Main *mainp, int cfra);
diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h
index 9f4833bf1d7..061b783797d 100644
--- a/source/blender/editors/include/ED_curve.h
+++ b/source/blender/editors/include/ED_curve.h
@@ -49,10 +49,12 @@ void ED_curve_editnurb_free(struct Object *obedit);
/**
* \param dist_px: Maximum distance to pick (in pixels).
+ * \param vert_without_handles: When true, selecting the knot doesn't select the handles.
*/
bool ED_curve_editnurb_select_pick(struct bContext *C,
const int mval[2],
int dist_px,
+ bool vert_without_handles,
const struct SelectPick_Params *params);
struct Nurb *ED_curve_add_nurbs_primitive(
diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h
index c3a66e4a7dd..e3b091430e8 100644
--- a/source/blender/editors/include/ED_datafiles.h
+++ b/source/blender/editors/include/ED_datafiles.h
@@ -103,6 +103,9 @@ extern const char datatoc_multiply_png[];
extern int datatoc_nudge_png_size;
extern const char datatoc_nudge_png[];
+extern int datatoc_paint_select_png_size;
+extern const char datatoc_paint_select_png[];
+
extern int datatoc_pinch_png_size;
extern const char datatoc_pinch_png[];
@@ -284,6 +287,41 @@ extern const char datatoc_gp_brush_erase_hard_png[];
extern int datatoc_gp_brush_erase_stroke_png_size;
extern const char datatoc_gp_brush_erase_stroke_png[];
+/* curves sculpt brushes files */
+
+extern int datatoc_curves_sculpt_add_png_size;
+extern const char datatoc_curves_sculpt_add_png[];
+
+extern int datatoc_curves_sculpt_comb_png_size;
+extern const char datatoc_curves_sculpt_comb_png[];
+
+extern int datatoc_curves_sculpt_cut_png_size;
+extern const char datatoc_curves_sculpt_cut_png[];
+
+extern int datatoc_curves_sculpt_delete_png_size;
+extern const char datatoc_curves_sculpt_delete_png[];
+
+extern int datatoc_curves_sculpt_density_png_size;
+extern const char datatoc_curves_sculpt_density_png[];
+
+extern int datatoc_curves_sculpt_grow_shrink_png_size;
+extern const char datatoc_curves_sculpt_grow_shrink_png[];
+
+extern int datatoc_curves_sculpt_pinch_png_size;
+extern const char datatoc_curves_sculpt_pinch_png[];
+
+extern int datatoc_curves_sculpt_puff_png_size;
+extern const char datatoc_curves_sculpt_puff_png[];
+
+extern int datatoc_curves_sculpt_slide_png_size;
+extern const char datatoc_curves_sculpt_slide_png[];
+
+extern int datatoc_curves_sculpt_smooth_png_size;
+extern const char datatoc_curves_sculpt_smooth_png[];
+
+extern int datatoc_curves_sculpt_snake_hook_png_size;
+extern const char datatoc_curves_sculpt_snake_hook_png[];
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index c367072e6e7..e9fcd2bd5fe 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win,
int win_size[2],
bool *is_maximized);
+/**
+ * Return the File Browser area in which \a file_operator is active.
+ */
struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win,
const struct wmOperator *file_operator);
+/**
+ * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set)
+ * and return it.
+ */
+struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win);
/* TODO: Maybe we should move this to BLI?
* On the other hand, it's using defines from space-file area, so not sure... */
diff --git a/source/blender/editors/include/ED_geometry.h b/source/blender/editors/include/ED_geometry.h
index 74ff968828c..4620181894a 100644
--- a/source/blender/editors/include/ED_geometry.h
+++ b/source/blender/editors/include/ED_geometry.h
@@ -7,12 +7,22 @@
#pragma once
+#include "BKE_attribute.h"
+#include "DNA_customdata_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-void ED_operatortypes_geometry(void);
+struct Mesh;
+void ED_operatortypes_geometry(void);
+bool ED_geometry_attribute_convert(struct Mesh *mesh,
+ const char *layer_name,
+ eCustomDataType old_type,
+ eAttrDomain old_domain,
+ eCustomDataType new_type,
+ eAttrDomain new_domain);
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h
index 067e5ba4d7f..31c1f93f5bc 100644
--- a/source/blender/editors/include/ED_gizmo_library.h
+++ b/source/blender/editors/include/ED_gizmo_library.h
@@ -14,6 +14,8 @@
extern "C" {
#endif
+#include "DNA_scene_types.h"
+
/* initialize gizmos */
void ED_gizmotypes_arrow_3d(void);
void ED_gizmotypes_button_2d(void);
@@ -223,24 +225,6 @@ enum {
/* -------------------------------------------------------------------- */
/* Specific gizmos utils */
-/* dial3d_gizmo.c */
-
-struct Dial3dParams {
- int draw_options;
- float angle_ofs;
- float angle_delta;
- float angle_increment;
- float arc_partial_angle;
- float arc_inner_factor;
- float *clip_plane;
-};
-void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4],
- const float matrix_final[4][4],
- float line_width,
- const float color[4],
- bool select,
- struct Dial3dParams *params);
-
/* snap3d_gizmo.c */
struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *scene,
@@ -258,7 +242,7 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C,
float r_loc[3],
float r_nor[3],
int r_elem_index[3],
- int *r_snap_elem);
+ eSnapMode *r_snap_elem);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 01f60a81288..0943636a452 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -579,7 +579,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush,
*/
bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc,
struct bGPDstroke *gps,
- const float mouse[2],
+ const float mval[2],
int radius,
const float diff_mat[4][4]);
/**
@@ -587,13 +587,13 @@ bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc,
*
* \param gps: Stroke to check.
* \param gsc: Space conversion data.
- * \param mouse: Mouse position.
+ * \param mval: Region relative cursor position.
* \param diff_mat: View matrix.
* \return True if the point is inside.
*/
bool ED_gpencil_stroke_point_is_inside(const struct bGPDstroke *gps,
const struct GP_SpaceConversion *gsc,
- const int mouse[2],
+ const int mval[2],
const float diff_mat[4][4]);
/**
* Get the bigger 2D bound box points.
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index f79d3ce205d..91ae8286531 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -24,6 +24,7 @@ struct Scene;
struct SpaceImage;
struct View2D;
struct bContext;
+struct Paint;
struct wmOperator;
struct wmWindowManager;
@@ -63,8 +64,11 @@ bool ED_space_image_get_position(struct SpaceImage *sima,
/**
* Returns color in linear space, matching #ED_space_node_color_sample().
*/
-bool ED_space_image_color_sample(
- struct SpaceImage *sima, struct ARegion *region, int mval[2], float r_col[3], bool *r_is_data);
+bool ED_space_image_color_sample(struct SpaceImage *sima,
+ struct ARegion *region,
+ const int mval[2],
+ float r_col[3],
+ bool *r_is_data);
struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock, int tile);
/**
* Get the #SpaceImage flag that is valid for the given ibuf.
@@ -133,8 +137,39 @@ bool ED_space_image_paint_curve(const struct bContext *C);
* Matches clip function.
*/
bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, struct Object *obedit);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Image Editor.
+ * - The image editor is not a UV Editor.
+ * - It is set to Mask mode.
+ *
+ * It is not required to have mask opened for editing. */
bool ED_space_image_maskedit_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Image Editor.
+ * - The image editor is not a UV Editor.
+ * - It is set to Mask mode.
+ * - Mask has visible and editable splines.
+ *
+ * It is not required to have mask opened for editing. */
+bool ED_space_image_maskedit_visible_splines_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Image Editor.
+ * - The image editor is not an UV Editor.
+ * - It is set to Mask mode.
+ * - The space has mask opened. */
bool ED_space_image_maskedit_mask_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space is Image Editor.
+ * - The image editor is not an UV Editor.
+ * - It is set to Mask mode.
+ * - The space has mask opened.
+ * - Mask has visible and editable splines. */
+bool ED_space_image_maskedit_mask_visible_splines_poll(struct bContext *C);
+
bool ED_space_image_cursor_poll(struct bContext *C);
/**
@@ -173,6 +208,7 @@ typedef struct ImageFrameRange {
int length;
int offset;
/* UDIM tiles. */
+ bool udims_detected;
ListBase udim_tiles;
/* Temporary data. */
@@ -186,6 +222,9 @@ ListBase ED_image_filesel_detect_sequences(struct Main *bmain,
struct wmOperator *op,
bool detect_udim);
+bool ED_image_tools_paint_poll(struct bContext *C);
+void ED_paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 6a730225da9..a53042b70d6 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -351,6 +351,19 @@ int ANIM_scene_get_keyingset_index(struct Scene *scene, struct KeyingSet *ks);
struct KeyingSet *ANIM_get_keyingset_for_autokeying(const struct Scene *scene,
const char *transformKSName);
+void ANIM_keyingset_visit_for_search(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
+void ANIM_keyingset_visit_for_search_no_poll(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
/**
* Dynamically populate an enum of Keying Sets.
*/
@@ -645,15 +658,20 @@ bool ED_autokeyframe_pchan(struct bContext *C,
struct Object *ob,
struct bPoseChannel *pchan,
struct KeyingSet *ks);
+
/**
- * Use for auto-key-framing from the UI.
+ * Use for auto-key-framing
+ * \param only_if_property_keyed: if true, auto-key-framing only creates keyframes on already keyed
+ * properties. This is by design when using buttons. For other callers such as gizmos or VSE
+ * preview transform, creating new animation/keyframes also on non-keyed properties is desired.
*/
bool ED_autokeyframe_property(struct bContext *C,
struct Scene *scene,
PointerRNA *ptr,
PropertyRNA *prop,
int rnaindex,
- float cfra);
+ float cfra,
+ bool only_if_property_keyed);
/* Names for builtin keying sets so we don't confuse these with labels/text,
* defined in python script: `keyingsets_builtins.py`. */
diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h
index 7039d6d9fc7..8cadbfde185 100644
--- a/source/blender/editors/include/ED_mask.h
+++ b/source/blender/editors/include/ED_mask.h
@@ -22,6 +22,34 @@ struct wmKeyConfig;
/* mask_edit.c */
+/* Returns true when the following conditions are met:
+ * - Current space supports mask editing.
+ * - The space is configured to interact with mask.
+ *
+ * It is not required to have mask opened for editing. */
+bool ED_maskedit_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space supports mask editing.
+ * - The space is configured to interact with mask.
+ * - Mask has visible and editable splines.
+ *
+ * It is not required to have mask opened for editing. */
+bool ED_maskedit_visible_splines_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space supports mask editing.
+ * - The space is configured to interact with mask.
+ * - The space has mask open for editing. */
+bool ED_maskedit_mask_poll(struct bContext *C);
+
+/* Returns true when the following conditions are met:
+ * - Current space supports mask editing.
+ * - The space is configured to interact with mask.
+ * - The space has mask opened.
+ * - Mask has visible and editable splines. */
+bool ED_maskedit_mask_visible_splines_poll(struct bContext *C);
+
void ED_mask_deselect_all(const struct bContext *C);
void ED_operatortypes_mask(void);
@@ -73,6 +101,7 @@ void ED_mask_draw_region(struct Depsgraph *depsgraph,
char draw_flag,
char draw_type,
eMaskOverlayMode overlay_mode,
+ float blend_factor,
int width_i,
int height_i,
float aspx,
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 59a24ed22b6..30a98129ee6 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -384,7 +384,7 @@ void ED_operatormacros_mesh(void);
*/
void ED_keymap_mesh(struct wmKeyConfig *keyconf);
-/* editface.c */
+/* editface.cc */
/**
* Copy the face flags, most importantly selection from the mesh to the final derived mesh,
@@ -520,7 +520,7 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vert
*/
void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr);
-/* mesh_data.c */
+/* mesh_data.cc */
void ED_mesh_verts_add(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count);
@@ -536,12 +536,12 @@ void ED_mesh_geometry_clear(struct Mesh *mesh);
void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose);
-void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name);
-int ED_mesh_uv_texture_add(
+void ED_mesh_uv_ensure(struct Mesh *me, const char *name);
+int ED_mesh_uv_add(
struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports);
-bool ED_mesh_uv_texture_remove_index(struct Mesh *me, int n);
-bool ED_mesh_uv_texture_remove_active(struct Mesh *me);
-bool ED_mesh_uv_texture_remove_named(struct Mesh *me, const char *name);
+bool ED_mesh_uv_remove_index(struct Mesh *me, int n);
+bool ED_mesh_uv_remove_active(struct Mesh *me);
+bool ED_mesh_uv_remove_named(struct Mesh *me, const char *name);
void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me);
/**
* Without a #bContext, called when UV-editing.
@@ -591,7 +591,7 @@ void EDBM_redo_state_restore_and_free(struct BMBackup *backup,
bool recalc_looptri) ATTR_NONNULL(1, 2);
void EDBM_redo_state_free(struct BMBackup *backup) ATTR_NONNULL(1);
-/* *** meshtools.c *** */
+/* *** meshtools.cc *** */
int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op);
int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op);
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 28452ba8db9..39c7ad3556c 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -234,7 +234,7 @@ struct Base *ED_object_add_duplicate(struct Main *bmain,
void ED_object_parent(struct Object *ob, struct Object *parent, int type, const char *substr);
char *ED_object_ot_drop_named_material_tooltip(struct bContext *C,
- struct PointerRNA *properties,
+ const char *name,
const int mval[2]);
/* bitflags for enter/exit editmode */
@@ -536,12 +536,12 @@ bool ED_object_modifier_move_to_index(struct ReportList *reports,
struct ModifierData *md,
int index);
-bool ED_object_modifier_convert(struct ReportList *reports,
- struct Main *bmain,
- struct Depsgraph *depsgraph,
- struct ViewLayer *view_layer,
- struct Object *ob,
- struct ModifierData *md);
+bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports,
+ struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ struct ViewLayer *view_layer,
+ struct Object *ob,
+ struct ModifierData *md);
bool ED_object_modifier_apply(struct Main *bmain,
struct ReportList *reports,
struct Depsgraph *depsgraph,
diff --git a/source/blender/editors/include/ED_scene.h b/source/blender/editors/include/ED_scene.h
index f1a2e5795ee..f67bdb9c1d7 100644
--- a/source/blender/editors/include/ED_scene.h
+++ b/source/blender/editors/include/ED_scene.h
@@ -18,6 +18,11 @@ struct Scene *ED_scene_add(struct Main *bmain,
struct bContext *C,
struct wmWindow *win,
enum eSceneCopyMethod method) ATTR_NONNULL();
+/** Special mode for adding a scene assigned to sequencer strip. */
+struct Scene *ED_scene_sequencer_add(struct Main *bmain,
+ struct bContext *C,
+ enum eSceneCopyMethod method,
+ const bool assign_strip);
/**
* \note Only call outside of area/region loops.
* \return true if successful.
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 41123aff79f..a24c8625a63 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -595,8 +595,7 @@ bool ED_operator_object_active_local_editable_posemode_exclusive(struct bContext
bool ED_operator_posemode_context(struct bContext *C);
bool ED_operator_posemode(struct bContext *C);
bool ED_operator_posemode_local(struct bContext *C);
-bool ED_operator_mask(struct bContext *C);
-bool ED_operator_camera(struct bContext *C);
+bool ED_operator_camera_poll(struct bContext *C);
/* screen_user_menu.c */
@@ -666,7 +665,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2]);
/**
- * \note: This may return true for multiple overlapping regions.
+ * \note This may return true for multiple overlapping regions.
* If it matters, check overlapped regions first (#ARegion.overlap).
*/
bool ED_region_contains_xy(const struct ARegion *region, const int event_xy[2]);
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 54d67c50d5c..550040d2bc6 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -51,7 +51,7 @@ void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, int new_id);
int ED_sculpt_face_sets_active_update_and_get(struct bContext *C,
struct Object *ob,
- const float mval[2]);
+ const float mval_fl[2]);
/* Undo for changes happening on a base mesh for multires sculpting.
* if there is no multi-res sculpt active regular undo is used. */
diff --git a/source/blender/editors/include/ED_select_utils.h b/source/blender/editors/include/ED_select_utils.h
index dbe1b582132..8c856794ec8 100644
--- a/source/blender/editors/include/ED_select_utils.h
+++ b/source/blender/editors/include/ED_select_utils.h
@@ -14,6 +14,7 @@ extern "C" {
struct KDTree_1d;
struct wmOperator;
+struct wmOperatorType;
enum {
SEL_TOGGLE = 0,
@@ -39,11 +40,11 @@ typedef enum {
} eSelectOp;
/* Select Similar */
-enum {
+typedef enum {
SIM_CMP_EQ = 0,
SIM_CMP_GT,
SIM_CMP_LT,
-};
+} eSimilarCmp;
#define SEL_OP_USE_OUTSIDE(sel_op) (ELEM(sel_op, SEL_OP_AND))
#define SEL_OP_USE_PRE_DESELECT(sel_op) (ELEM(sel_op, SEL_OP_SET))
@@ -62,11 +63,11 @@ int ED_select_op_action(eSelectOp sel_op, bool is_select, bool is_inside);
*/
int ED_select_op_action_deselected(eSelectOp sel_op, bool is_select, bool is_inside);
-int ED_select_similar_compare_float(float delta, float thresh, int compare);
+bool ED_select_similar_compare_float(float delta, float thresh, eSimilarCmp compare);
bool ED_select_similar_compare_float_tree(const struct KDTree_1d *tree,
float length,
float thresh,
- int compare);
+ eSimilarCmp compare);
/**
* Utility to use for selection operations that run multiple times (circle select).
@@ -96,16 +97,23 @@ struct SelectPick_Params {
/**
* Utility to get #eSelectPickMode from booleans for convenience.
*/
-eSelectOp ED_select_op_from_operator(struct wmOperator *op)
+eSelectOp ED_select_op_from_operator(struct PointerRNA *ptr)
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Initialize `params` from `op`,
* these properties are defined by #WM_operator_properties_mouse_select.
*/
-void ED_select_pick_params_from_operator(struct wmOperator *op, struct SelectPick_Params *params)
+void ED_select_pick_params_from_operator(struct PointerRNA *ptr, struct SelectPick_Params *params)
ATTR_NONNULL(1, 2);
+/**
+ * Get-name callback for #wmOperatorType.get_name, this is mainly useful so the selection
+ * action is shown in the status-bar.
+ */
+const char *ED_select_pick_get_name(struct wmOperatorType *ot, PointerRNA *ptr);
+const char *ED_select_circle_get_name(struct wmOperatorType *ot, PointerRNA *ptr);
+
#ifdef __cplusplus
}
#endif
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 2919001c5ce..20353c21f93 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -6,6 +6,8 @@
#pragma once
+#include "DNA_scene_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -25,14 +27,6 @@ struct View3D;
/* ED_transform_snap_object_*** API */
-typedef enum eSnapSelect {
- SNAP_ALL = 0,
- SNAP_NOT_SELECTED = 1,
- SNAP_NOT_ACTIVE = 2,
- SNAP_NOT_EDITED = 3,
- SNAP_SELECTABLE = 4,
-} eSnapSelect;
-
typedef enum eSnapEditType {
SNAP_GEOM_FINAL = 0,
SNAP_GEOM_CAGE = 1,
@@ -59,7 +53,7 @@ struct SnapObjectHitDepth {
/** parameters that define which objects will be used to snap. */
struct SnapObjectParams {
/* Special context sensitive handling for the active or selected object. */
- eSnapSelect snap_select;
+ eSnapTargetSelect snap_target_select;
/* Geometry for snapping in edit mode. */
eSnapEditType edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
@@ -120,21 +114,21 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx,
bool sort,
struct ListBase *r_hit_list);
-short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
- struct Depsgraph *depsgraph,
- const ARegion *region,
- const View3D *v3d,
- unsigned short snap_to,
- const struct SnapObjectParams *params,
- const float mval[2],
- const float prev_co[3],
- float *dist_px,
- float r_loc[3],
- float r_no[3],
- int *r_index,
- struct Object **r_ob,
- float r_obmat[4][4],
- float r_face_nor[3]);
+eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
+ struct Depsgraph *depsgraph,
+ const ARegion *region,
+ const View3D *v3d,
+ eSnapMode snap_to,
+ const struct SnapObjectParams *params,
+ const float mval[2],
+ const float prev_co[3],
+ float *dist_px,
+ float r_loc[3],
+ float r_no[3],
+ int *r_index,
+ struct Object **r_ob,
+ float r_obmat[4][4],
+ float r_face_nor[3]);
/**
* Convenience function for performing snapping.
*
@@ -148,18 +142,18 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
* \param r_no: hit normal (optional).
* \return Snap success.
*/
-short ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx,
- struct Depsgraph *depsgraph,
- const ARegion *region,
- const View3D *v3d,
- unsigned short snap_to,
- const struct SnapObjectParams *params,
- const float mval[2],
- const float prev_co[3],
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3]);
+eSnapMode ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx,
+ struct Depsgraph *depsgraph,
+ const ARegion *region,
+ const View3D *v3d,
+ eSnapMode snap_to,
+ const struct SnapObjectParams *params,
+ const float mval[2],
+ const float prev_co[3],
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3]);
/**
* see: #ED_transform_snap_object_project_ray_all
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 54d36f44f82..80a75da27f8 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -266,6 +266,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene,
int *r_verts_len);
void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
+void ED_uvedit_get_aspect_from_material(Object *ob,
+ const int material_index,
+ float *r_aspx,
+ float *r_aspy);
void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l);
struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 414643dd0d6..0298983ed26 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -7,6 +7,9 @@
#pragma once
+#include "BLI_utildefines.h"
+#include "DNA_scene_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -256,6 +259,7 @@ typedef enum {
*/
V3D_PROJ_TEST_CLIP_CONTENT = (1 << 5),
} eV3DProjTest;
+ENUM_OPERATORS(eV3DProjTest, V3D_PROJ_TEST_CLIP_CONTENT);
#define V3D_PROJ_TEST_CLIP_DEFAULT \
(V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR)
@@ -296,7 +300,7 @@ typedef enum {
} eV3DPlaceOrient;
typedef struct V3DSnapCursorData {
- short snap_elem;
+ eSnapMode snap_elem;
float loc[3];
float nor[3];
float obmat[4][4];
@@ -319,7 +323,7 @@ typedef struct V3DSnapCursorState {
struct wmGizmoGroupType *gzgrp_type; /* Force cursor to be drawn only when gizmo is available. */
float *prevpoint;
float box_dimensions[3];
- short snap_elem_force; /* If zero, use scene settings. */
+ eSnapMode snap_elem_force; /* If SCE_SNAP_MODE_NONE, use scene settings. */
short plane_axis;
bool use_plane_axis_auto;
bool draw_point;
@@ -344,7 +348,7 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d,
const float normal[3],
const uchar color_line[4],
const uchar color_point[4],
- short snap_elem_type);
+ eSnapMode snap_elem_type);
/* view3d_iterators.c */
@@ -443,14 +447,14 @@ void pose_foreachScreenBone(struct ViewContext *vc,
void ED_view3d_project_float_v2_m4(const struct ARegion *region,
const float co[3],
float r_co[2],
- float mat[4][4]);
+ const float mat[4][4]);
/**
* \note use #ED_view3d_ob_project_mat_get to get projecting mat
*/
void ED_view3d_project_float_v3_m4(const struct ARegion *region,
const float co[3],
float r_co[3],
- float mat[4][4]);
+ const float mat[4][4]);
eV3DProjStatus ED_view3d_project_base(const struct ARegion *region, struct Base *base);
@@ -700,9 +704,9 @@ void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2],
* \param do_clip_planes: Optionally clip the ray by the view clipping planes.
* \return success, false if the segment is totally clipped.
*/
-bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph,
+bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph,
const struct ARegion *region,
- struct View3D *v3d,
+ const struct View3D *v3d,
const float mval[2],
float r_ray_start[3],
float r_ray_end[3],
@@ -733,7 +737,7 @@ void ED_view3d_dist_range_get(const struct View3D *v3d, float r_dist_range[2]);
/**
* \note copies logic of #ED_view3d_viewplane_get(), keep in sync.
*/
-bool ED_view3d_clip_range_get(struct Depsgraph *depsgraph,
+bool ED_view3d_clip_range_get(const struct Depsgraph *depsgraph,
const struct View3D *v3d,
const struct RegionView3D *rv3d,
float *r_clipsta,
@@ -1080,6 +1084,16 @@ bool ED_view3d_quat_to_axis_view(const float viewquat[4],
float epsilon,
char *r_view,
char *r_view_axis_rotation);
+/**
+ * A version of #ED_view3d_quat_to_axis_view that updates `viewquat`
+ * if it's within `epsilon` to an axis-view.
+ *
+ * \note Include the special case function since most callers need to perform these operations.
+ */
+bool ED_view3d_quat_to_axis_view_and_reset_quat(float viewquat[4],
+ float epsilon,
+ char *r_view,
+ char *r_view_axis_rotation);
char ED_view3d_lock_view_from_index(int index);
char ED_view3d_axis_view_opposite(char view);
diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh
new file mode 100644
index 00000000000..6f553f4fad1
--- /dev/null
+++ b/source/blender/editors/include/UI_grid_view.hh
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup editorui
+ *
+ * API for simple creation of grid UIs, supporting typically needed features.
+ * https://wiki.blender.org/wiki/Source/Interface/Views/Grid_Views
+ */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+#include "BLI_map.hh"
+#include "BLI_vector.hh"
+
+#include "UI_resources.h"
+
+struct bContext;
+struct PreviewImage;
+struct uiBlock;
+struct uiButGridTile;
+struct uiLayout;
+struct View2D;
+struct wmNotifier;
+
+namespace blender::ui {
+
+class AbstractGridView;
+
+/* ---------------------------------------------------------------------- */
+/** \name Grid-View Item Type
+ * \{ */
+
+class AbstractGridViewItem {
+ friend class AbstractGridView;
+ friend class GridViewLayoutBuilder;
+
+ const AbstractGridView *view_;
+
+ bool is_active_ = false;
+
+ protected:
+ /** Reference to a string that uniquely identifies this item in the view. */
+ StringRef identifier_{};
+ /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */
+ uiButGridTile *grid_tile_but_ = nullptr;
+
+ public:
+ virtual ~AbstractGridViewItem() = default;
+
+ virtual void build_grid_tile(uiLayout &layout) const = 0;
+
+ /**
+ * Compare this item's identifier to \a other to check if they represent the same data.
+ * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active,
+ * renaming, etc.).
+ */
+ bool matches(const AbstractGridViewItem &other) const;
+
+ const AbstractGridView &get_view() const;
+
+ /**
+ * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we
+ * can't be sure about the item state.
+ */
+ bool is_active() const;
+
+ protected:
+ AbstractGridViewItem(StringRef identifier);
+
+ /** Called when the item's state changes from inactive to active. */
+ virtual void on_activate();
+ /**
+ * If the result is not empty, it controls whether the item should be active or not,
+ * usually depending on the data that the view represents.
+ */
+ virtual std::optional<bool> should_be_active() const;
+
+ /**
+ * Copy persistent state (e.g. active, selection, etc.) from a matching item of
+ * the last redraw to this item. If sub-classes introduce more advanced state they should
+ * override this and make it update their state accordingly.
+ */
+ virtual void update_from_old(const AbstractGridViewItem &old);
+
+ /**
+ * Activates this item, deactivates other items, and calls the
+ * #AbstractGridViewItem::on_activate() function.
+ * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the
+ * actual item state is unknown, possibly calling state-change update functions incorrectly.
+ */
+ void activate();
+ void deactivate();
+
+ private:
+ /** See #AbstractTreeView::change_state_delayed() */
+ void change_state_delayed();
+ static void grid_tile_click_fn(bContext *, void *but_arg1, void *);
+ void add_grid_tile_button(uiBlock &block);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Grid-View Base Class
+ * \{ */
+
+struct GridViewStyle {
+ GridViewStyle(int width, int height);
+ int tile_width = 0;
+ int tile_height = 0;
+};
+
+class AbstractGridView {
+ friend class AbstractGridViewItem;
+ friend class GridViewBuilder;
+ friend class GridViewLayoutBuilder;
+
+ protected:
+ Vector<std::unique_ptr<AbstractGridViewItem>> items_;
+ /** <identifier, item> map to lookup items by identifier, used for efficient lookups in
+ * #update_from_old(). */
+ Map<StringRef, AbstractGridViewItem *> item_map_;
+ GridViewStyle style_;
+ bool is_reconstructed_ = false;
+
+ public:
+ AbstractGridView();
+ virtual ~AbstractGridView() = default;
+
+ using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>;
+ void foreach_item(ItemIterFn iter_fn) const;
+
+ /** Listen to a notifier, returning true if a redraw is needed. */
+ virtual bool listen(const wmNotifier &) const;
+
+ /**
+ * Convenience wrapper constructing the item by forwarding given arguments to the constructor of
+ * the type (\a ItemT).
+ *
+ * E.g. if your grid-item type has the following constructor:
+ * \code{.cpp}
+ * MyGridItem(std::string str, int i);
+ * \endcode
+ * You can add an item like this:
+ * \code
+ * add_item<MyGridItem>("blabla", 42);
+ * \endcode
+ */
+ template<class ItemT, typename... Args> inline ItemT &add_item(Args &&...args);
+ const GridViewStyle &get_style() const;
+ int get_item_count() const;
+
+ protected:
+ virtual void build_items() = 0;
+
+ /**
+ * Check if the view is fully (re-)constructed. That means, both #build_items() and
+ * #update_from_old() have finished.
+ */
+ bool is_reconstructed() const;
+
+ private:
+ /**
+ * Match the grid-view against an earlier version of itself (if any) and copy the old UI state
+ * (e.g. active, selected, renaming, etc.) to the new one. See
+ * #AbstractGridViewItem.update_from_old().
+ */
+ void update_from_old(uiBlock &new_block);
+ AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &item_to_match,
+ const AbstractGridView &view_to_search_in) const;
+ /**
+ * Items may want to do additional work when state changes. But these state changes can only be
+ * reliably detected after the view has completed reconstruction (see #is_reconstructed()). So
+ * the actual state changes are done in a delayed manner through this function.
+ */
+ void change_state_delayed();
+
+ /**
+ * Add an already constructed item, moving ownership to the grid-view.
+ * All items must be added through this, it handles important invariants!
+ */
+ AbstractGridViewItem &add_item(std::unique_ptr<AbstractGridViewItem> item);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Grid-View Builder
+ *
+ * TODO unify this with `TreeViewBuilder` and call view-specific functions via type erased view?
+ * \{ */
+
+class GridViewBuilder {
+ uiBlock &block_;
+
+ public:
+ GridViewBuilder(uiBlock &block);
+
+ /** Build \a grid_view into the previously provided block, clipped by \a view_bounds (view space,
+ * typically `View2D.cur`). */
+ void build_grid_view(AbstractGridView &grid_view, const View2D &v2d);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Predefined Grid-View Item Types
+ *
+ * Common, Basic Grid-View Item Types.
+ * \{ */
+
+/**
+ * A grid item that shows preview image icons at a nicely readable size (multiple of the normal UI
+ * unit size).
+ */
+class PreviewGridItem : public AbstractGridViewItem {
+ public:
+ using IsActiveFn = std::function<bool()>;
+ using ActivateFn = std::function<void(PreviewGridItem &new_active)>;
+
+ protected:
+ /** See #set_on_activate_fn() */
+ ActivateFn activate_fn_;
+ /** See #set_is_active_fn() */
+ IsActiveFn is_active_fn_;
+
+ public:
+ std::string label{};
+ int preview_icon_id = ICON_NONE;
+
+ PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id);
+
+ void build_grid_tile(uiLayout &layout) const override;
+
+ /**
+ * Set a custom callback to execute when activating this view item. This way users don't have to
+ * sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to
+ * do).
+ */
+ void set_on_activate_fn(ActivateFn fn);
+ /**
+ * Set a custom callback to check if this item should be active.
+ */
+ void set_is_active_fn(IsActiveFn fn);
+
+ private:
+ std::optional<bool> should_be_active() const override;
+ void on_activate() override;
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+
+template<class ItemT, typename... Args> inline ItemT &AbstractGridView::add_item(Args &&...args)
+{
+ static_assert(std::is_base_of<AbstractGridViewItem, ItemT>::value,
+ "Type must derive from and implement the AbstractGridViewItem interface");
+
+ return dynamic_cast<ItemT &>(add_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
+}
+
+} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index d1a6501408c..f1c0acf43f7 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -164,7 +164,7 @@ DEF_ICON(NLA)
DEF_ICON(PREFERENCES)
DEF_ICON(TIME)
DEF_ICON(NODETREE)
-DEF_ICON_BLANK(181)
+DEF_ICON(GEOMETRY_NODES)
DEF_ICON(CONSOLE)
DEF_ICON_BLANK(183)
DEF_ICON(TRACKER)
@@ -891,6 +891,7 @@ DEF_ICON_COLOR(BRUSH_LAYER)
DEF_ICON_COLOR(BRUSH_MASK)
DEF_ICON_COLOR(BRUSH_MIX)
DEF_ICON_COLOR(BRUSH_NUDGE)
+DEF_ICON_COLOR(BRUSH_PAINT_SELECT)
DEF_ICON_COLOR(BRUSH_PINCH)
DEF_ICON_COLOR(BRUSH_SCRAPE)
DEF_ICON_COLOR(BRUSH_SCULPT_DRAW)
@@ -903,7 +904,6 @@ DEF_ICON_COLOR(BRUSH_TEXFILL)
DEF_ICON_COLOR(BRUSH_TEXMASK)
DEF_ICON_COLOR(BRUSH_THUMB)
DEF_ICON_COLOR(BRUSH_ROTATE)
-DEF_ICON_COLOR(BRUSH_PAINT)
/* grease pencil sculpt */
DEF_ICON_COLOR(GPBRUSH_SMOOTH)
@@ -929,6 +929,19 @@ DEF_ICON_COLOR(GPBRUSH_ERASE_SOFT)
DEF_ICON_COLOR(GPBRUSH_ERASE_HARD)
DEF_ICON_COLOR(GPBRUSH_ERASE_STROKE)
+/* Curves sculpt. */
+DEF_ICON_COLOR(BRUSH_CURVES_ADD)
+DEF_ICON_COLOR(BRUSH_CURVES_COMB)
+DEF_ICON_COLOR(BRUSH_CURVES_CUT)
+DEF_ICON_COLOR(BRUSH_CURVES_DELETE)
+DEF_ICON_COLOR(BRUSH_CURVES_DENSITY)
+DEF_ICON_COLOR(BRUSH_CURVES_GROW_SHRINK)
+DEF_ICON_COLOR(BRUSH_CURVES_PINCH)
+DEF_ICON_COLOR(BRUSH_CURVES_PUFF)
+DEF_ICON_COLOR(BRUSH_CURVES_SLIDE)
+DEF_ICON_COLOR(BRUSH_CURVES_SMOOTH)
+DEF_ICON_COLOR(BRUSH_CURVES_SNAKE_HOOK)
+
/* Vector icons. */
DEF_ICON_VECTOR(KEYTYPE_KEYFRAME_VEC)
DEF_ICON_VECTOR(KEYTYPE_BREAKDOWN_VEC)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 301171a284d..b2ec2102ddd 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -64,6 +64,7 @@ struct wmKeyMapItem;
struct wmMsgBus;
struct wmOperator;
struct wmOperatorType;
+struct wmRegionListenerParams;
struct wmWindow;
typedef struct uiBlock uiBlock;
@@ -75,6 +76,10 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle;
typedef struct uiTreeViewHandle uiTreeViewHandle;
/* C handle for C++ #ui::AbstractTreeViewItem type. */
typedef struct uiTreeViewItemHandle uiTreeViewItemHandle;
+/* C handle for C++ #ui::AbstractGridView type. */
+typedef struct uiGridViewHandle uiGridViewHandle;
+/* C handle for C++ #ui::AbstractGridViewItem type. */
+typedef struct uiGridViewItemHandle uiGridViewItemHandle;
/* Defines */
@@ -184,26 +189,26 @@ enum {
/** #uiBut.flag general state flags. */
enum {
- /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */
- UI_BUT_ICON_SUBMENU = 1 << 7,
- UI_BUT_ICON_PREVIEW = 1 << 8,
+ /* WARNING: the first 8 flags are internal (see #UI_SELECT definition). */
+ UI_BUT_ICON_SUBMENU = 1 << 8,
+ UI_BUT_ICON_PREVIEW = 1 << 9,
- UI_BUT_NODE_LINK = 1 << 9,
- UI_BUT_NODE_ACTIVE = 1 << 10,
- UI_BUT_DRAG_LOCK = 1 << 11,
+ UI_BUT_NODE_LINK = 1 << 10,
+ UI_BUT_NODE_ACTIVE = 1 << 11,
+ UI_BUT_DRAG_LOCK = 1 << 12,
/** Grayed out and un-editable. */
- UI_BUT_DISABLED = 1 << 12,
+ UI_BUT_DISABLED = 1 << 13,
- UI_BUT_ANIMATED = 1 << 13,
- UI_BUT_ANIMATED_KEY = 1 << 14,
- UI_BUT_DRIVEN = 1 << 15,
- UI_BUT_REDALERT = 1 << 16,
+ UI_BUT_ANIMATED = 1 << 14,
+ UI_BUT_ANIMATED_KEY = 1 << 15,
+ UI_BUT_DRIVEN = 1 << 16,
+ UI_BUT_REDALERT = 1 << 17,
/** Grayed out but still editable. */
- UI_BUT_INACTIVE = 1 << 17,
- UI_BUT_LAST_ACTIVE = 1 << 18,
- UI_BUT_UNDO = 1 << 19,
- UI_BUT_IMMEDIATE = 1 << 20,
- UI_BUT_NO_UTF8 = 1 << 21,
+ UI_BUT_INACTIVE = 1 << 18,
+ UI_BUT_LAST_ACTIVE = 1 << 19,
+ UI_BUT_UNDO = 1 << 20,
+ /* UNUSED = 1 << 21, */
+ UI_BUT_NO_UTF8 = 1 << 22,
/** For popups, pressing return activates this button, overriding the highlighted button.
* For non-popups this is just used as a display hint for the user to let them
@@ -390,6 +395,8 @@ typedef enum {
UI_BTYPE_DECORATOR = 58 << 9,
/* An item in a tree view. Parent items may be collapsible. */
UI_BTYPE_TREEROW = 59 << 9,
+ /* An item in a grid view. */
+ UI_BTYPE_GRID_TILE = 60 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@@ -454,7 +461,6 @@ void UI_draw_safe_areas(uint pos,
enum {
UI_SCROLL_PRESSED = 1 << 0,
UI_SCROLL_ARROWS = 1 << 1,
- UI_SCROLL_NO_OUTLINE = 1 << 2,
};
/**
* Function in use for buttons and for view2d sliders.
@@ -852,33 +858,6 @@ void UI_block_translate(uiBlock *block, int x, int y);
int UI_but_return_value_get(uiBut *but);
-void UI_but_drag_set_id(uiBut *but, struct ID *id);
-/**
- * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`).
- * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image.
- */
-void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale);
-/**
- * \param asset: May be passed from a temporary variable, drag data only stores a copy of this.
- */
-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,
- float scale);
-void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr);
-void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free);
-void UI_but_drag_set_name(uiBut *but, const char *name);
-/**
- * Value from button itself.
- */
-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, bool use_free);
-
uiBut *UI_but_active_drop_name_button(const struct bContext *C);
/**
* Returns true if highlighted button allows drop of names.
@@ -1539,31 +1518,6 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block,
short height,
const char *tip);
-uiBut *uiDefKeyevtButS(uiBlock *block,
- int retval,
- const char *str,
- int x,
- int y,
- short width,
- short height,
- short *spoin,
- const char *tip);
-
-/**
- * Short pointers hard-coded.
- * \param modkeypoin: will be set to #KM_SHIFT, #KM_ALT, #KM_CTRL, #KM_OSKEY bits.
- */
-uiBut *uiDefHotKeyevtButS(uiBlock *block,
- int retval,
- const char *str,
- int x,
- int y,
- short width,
- short height,
- short *keypoin,
- const short *modkeypoin,
- const char *tip);
-
/**
* \param arg: A pointer to string/name, use #UI_but_func_search_set() below to make this work.
* here `a1` and `a2`, if set, control thumbnail preview rows/cols.
@@ -1674,15 +1628,16 @@ void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_f
* \param name: Text to display for the item.
* \param poin: Opaque pointer (for use by the caller).
* \param iconid: The icon, #ICON_NONE for no icon.
- * \param state: The buttons state flag, compatible with #uiBut.flag,
- * typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE.
+ * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically
+ * #UI_BUT_DISABLED, #UI_BUT_INACTIVE or #UI_BUT_HAS_SEP_CHAR.
+ *
* \return false if there is nothing to add.
*/
bool UI_search_item_add(uiSearchItems *items,
const char *name,
void *poin,
int iconid,
- int state,
+ int but_flag,
uint8_t name_prefix_offset);
/**
@@ -1792,6 +1747,14 @@ struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but,
struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon);
struct PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon);
+/**
+ * A decent size for a button (typically #UI_BTYPE_PREVIEW_TILE) to display a nicely readable
+ * preview with label in.
+ */
+int UI_preview_tile_size_x(void);
+int UI_preview_tile_size_y(void);
+int UI_preview_tile_size_y_no_label(void);
+
/* Autocomplete
*
* Tab complete helper functions, for use in uiButCompleteFunc callbacks.
@@ -1808,6 +1771,38 @@ AutoComplete *UI_autocomplete_begin(const char *startname, size_t maxlen);
void UI_autocomplete_update_name(AutoComplete *autocpl, const char *name);
int UI_autocomplete_end(AutoComplete *autocpl, char *autoname);
+/* Button drag-data (interface_drag.cc).
+ *
+ * Functions to set drag data for buttons. This enables dragging support, whereby the drag data is
+ * "dragged", not the button itself. */
+
+void UI_but_drag_set_id(uiBut *but, struct ID *id);
+/**
+ * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`).
+ * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image.
+ */
+void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale);
+/**
+ * \param asset: May be passed from a temporary variable, drag data only stores a copy of this.
+ */
+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,
+ float scale);
+void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr);
+void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free);
+void UI_but_drag_set_name(uiBut *but, const char *name);
+/**
+ * Value from button itself.
+ */
+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, bool use_free);
+
/* Panels
*
* Functions for creating, freeing and drawing panels. The API here
@@ -1891,7 +1886,7 @@ struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContex
const struct wmEvent *event);
void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data);
-/* Polyinstantiated panels for representing a list of data. */
+/* Poly-instantiated panels for representing a list of data. */
/**
* Called in situations where panels need to be added dynamically rather than
* having only one panel corresponding to each #PanelType.
@@ -3205,7 +3200,12 @@ void UI_interface_tag_script_reload(void);
/* Support click-drag motion which presses the button and closes a popover (like a menu). */
#define USE_UI_POPOVER_ONCE
+void UI_block_views_listen(const uiBlock *block,
+ const struct wmRegionListenerParams *listener_params);
+
+bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle);
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
+bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b);
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
/**
* Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't
@@ -3243,6 +3243,15 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *regi
const int xy[2]) ATTR_NONNULL(1, 2);
uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region);
+/**
+ * Listen to \a notifier, returning true if the region should redraw.
+ */
+bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier);
+/**
+ * Listen to \a notifier, returning true if the region should redraw.
+ */
+bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view, const wmNotifier *notifier);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
index db43ec54431..3dc56b01993 100644
--- a/source/blender/editors/include/UI_interface.hh
+++ b/source/blender/editors/include/UI_interface.hh
@@ -23,6 +23,7 @@ struct uiSearchItems;
namespace blender::ui {
+class AbstractGridView;
class AbstractTreeView;
/**
@@ -55,6 +56,10 @@ void attribute_search_add_items(
/**
* Override this for all available tree types.
*/
+blender::ui::AbstractGridView *UI_block_add_view(
+ uiBlock &block,
+ blender::StringRef idname,
+ std::unique_ptr<blender::ui::AbstractGridView> tree_view);
blender::ui::AbstractTreeView *UI_block_add_view(
uiBlock &block,
blender::StringRef idname,
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index 6416421f4f5..a1a98a4b08c 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -92,7 +92,7 @@ void UI_icon_render_id_ex(const struct bContext *C,
int UI_icon_preview_to_render_size(enum eIconSizes size);
/**
- * Draws icon with dpi scale factor.
+ * Draws icon with DPI scale factor.
*/
void UI_icon_draw(float x, float y, int icon_id);
void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha);
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index 5504e426f34..1aeb13ca5cc 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -28,6 +28,7 @@ struct uiButTreeRow;
struct uiLayout;
struct wmDrag;
struct wmEvent;
+struct wmNotifier;
namespace blender::ui {
@@ -128,6 +129,9 @@ class AbstractTreeView : public TreeViewItemContainer {
void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+ /** Listen to a notifier, returning true if a redraw is needed. */
+ virtual bool listen(const wmNotifier &) const;
+
/** Only one item can be renamed at a time. */
bool is_renaming() const;
@@ -185,7 +189,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
bool is_renaming_ = false;
protected:
- /** This label is used for identifying an item within its parent. */
+ /** This label is used as the default way to identifying an item within its parent. */
std::string label_{};
/** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */
uiButTreeRow *tree_row_but_ = nullptr;
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 444bb08f3fb..2a1852bd6e7 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -25,12 +25,14 @@ set(INC
)
set(SRC
+ grid_view.cc
interface.cc
interface_align.c
interface_anim.c
interface_button_group.c
interface_context_menu.c
interface_context_path.cc
+ interface_drag.cc
interface_draw.c
interface_dropboxes.cc
interface_eyedropper.c
diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc
new file mode 100644
index 00000000000..a82cb7798fe
--- /dev/null
+++ b/source/blender/editors/interface/grid_view.cc
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include <limits>
+#include <stdexcept>
+
+#include "BLI_index_range.hh"
+
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "interface_intern.h"
+
+#include "UI_grid_view.hh"
+
+namespace blender::ui {
+
+/* ---------------------------------------------------------------------- */
+
+AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y())
+{
+}
+
+AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridViewItem> item)
+{
+ items_.append(std::move(item));
+
+ AbstractGridViewItem &added_item = *items_.last();
+ added_item.view_ = this;
+
+ item_map_.add(added_item.identifier_, &added_item);
+
+ return added_item;
+}
+
+void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
+{
+ for (auto &item_ptr : items_) {
+ iter_fn(*item_ptr);
+ }
+}
+
+bool AbstractGridView::listen(const wmNotifier &) const
+{
+ /* Nothing by default. */
+ return false;
+}
+
+AbstractGridViewItem *AbstractGridView::find_matching_item(
+ const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const
+{
+ AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr(
+ item_to_match.identifier_);
+ BLI_assert(!match || item_to_match.matches(**match));
+
+ return match ? *match : nullptr;
+}
+
+void AbstractGridView::change_state_delayed()
+{
+ BLI_assert_msg(
+ is_reconstructed(),
+ "These state changes are supposed to be delayed until reconstruction is completed");
+ foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); });
+}
+
+void AbstractGridView::update_from_old(uiBlock &new_block)
+{
+ uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block(
+ &new_block, reinterpret_cast<uiGridViewHandle *>(this));
+ if (!old_view_handle) {
+ /* Initial construction, nothing to update. */
+ is_reconstructed_ = true;
+ return;
+ }
+
+ AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle);
+
+ foreach_item([this, &old_view](AbstractGridViewItem &new_item) {
+ const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view);
+ if (!matching_old_item) {
+ return;
+ }
+
+ new_item.update_from_old(*matching_old_item);
+ });
+
+ /* Finished (re-)constructing the tree. */
+ is_reconstructed_ = true;
+}
+
+bool AbstractGridView::is_reconstructed() const
+{
+ return is_reconstructed_;
+}
+
+const GridViewStyle &AbstractGridView::get_style() const
+{
+ return style_;
+}
+
+int AbstractGridView::get_item_count() const
+{
+ return items_.size();
+}
+
+GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height)
+{
+}
+
+/* ---------------------------------------------------------------------- */
+
+AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier)
+{
+}
+
+bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const
+{
+ return identifier_ == other.identifier_;
+}
+
+void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
+ void *but_arg1,
+ void * /*arg2*/)
+{
+ uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1;
+ AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
+ *grid_tile_but->view_item);
+
+ grid_item.activate();
+}
+
+void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
+{
+ const GridViewStyle &style = get_view().get_style();
+ grid_tile_but_ = (uiButGridTile *)uiDefBut(&block,
+ UI_BTYPE_GRID_TILE,
+ 0,
+ "",
+ 0,
+ 0,
+ style.tile_width,
+ style.tile_height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+
+ grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this);
+ UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr);
+}
+
+bool AbstractGridViewItem::is_active() const
+{
+ BLI_assert_msg(get_view().is_reconstructed(),
+ "State can't be queried until reconstruction is completed");
+ return is_active_;
+}
+
+void AbstractGridViewItem::on_activate()
+{
+ /* Do nothing by default. */
+}
+
+std::optional<bool> AbstractGridViewItem::should_be_active() const
+{
+ return std::nullopt;
+}
+
+void AbstractGridViewItem::change_state_delayed()
+{
+ const std::optional<bool> should_be_active = this->should_be_active();
+ if (should_be_active.has_value() && *should_be_active) {
+ activate();
+ }
+}
+
+void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old)
+{
+ is_active_ = old.is_active_;
+}
+
+void AbstractGridViewItem::activate()
+{
+ BLI_assert_msg(get_view().is_reconstructed(),
+ "Item activation can't be done until reconstruction is completed");
+
+ if (is_active()) {
+ return;
+ }
+
+ /* Deactivate other items in the tree. */
+ get_view().foreach_item([](auto &item) { item.deactivate(); });
+
+ on_activate();
+
+ is_active_ = true;
+}
+
+void AbstractGridViewItem::deactivate()
+{
+ is_active_ = false;
+}
+
+const AbstractGridView &AbstractGridViewItem::get_view() const
+{
+ if (UNLIKELY(!view_)) {
+ throw std::runtime_error(
+ "Invalid state, item must be added through AbstractGridView::add_item()");
+ }
+ return *view_;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/**
+ * Helper for only adding layout items for grid items that are actually in view. 3 main functions:
+ * - #is_item_visible(): Query if an item of a given index is visible in the view (others should be
+ * skipped when building the layout).
+ * - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so
+ * the layout height is the same as if all items were added (important to get the correct scroll
+ * height).
+ * - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible
+ * row.
+ *
+ * Does two assumptions:
+ * - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should
+ * probably provide queries for the scroll offset.
+ * - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the
+ * side(s) as well.
+ */
+class BuildOnlyVisibleButtonsHelper {
+ const View2D &v2d_;
+ const AbstractGridView &grid_view_;
+ const GridViewStyle &style_;
+ const int cols_per_row_ = 0;
+ /* Indices of items within the view. Calculated by constructor */
+ IndexRange visible_items_range_{};
+
+ public:
+ BuildOnlyVisibleButtonsHelper(const View2D &,
+ const AbstractGridView &grid_view,
+ int cols_per_row);
+
+ bool is_item_visible(int item_idx) const;
+ void fill_layout_before_visible(uiBlock &) const;
+ void fill_layout_after_visible(uiBlock &) const;
+
+ private:
+ IndexRange get_visible_range() const;
+ void add_spacer_button(uiBlock &, int row_count) const;
+};
+
+BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d,
+ const AbstractGridView &grid_view,
+ const int cols_per_row)
+ : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
+{
+ visible_items_range_ = get_visible_range();
+}
+
+IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
+{
+ int first_idx_in_view = 0;
+ int max_items_in_view = 0;
+
+ const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax);
+ if (!IS_EQF(scroll_ofs_y, 0)) {
+ const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height;
+
+ first_idx_in_view = scrolled_away_rows * cols_per_row_;
+ }
+
+ const float view_height = BLI_rctf_size_y(&v2d_.cur);
+ const int count_rows_in_view = std::max(round_fl_to_int(view_height / style_.tile_height), 1);
+ max_items_in_view = (count_rows_in_view + 1) * cols_per_row_;
+
+ BLI_assert(max_items_in_view > 0);
+ return IndexRange(first_idx_in_view, max_items_in_view);
+}
+
+bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const
+{
+ return visible_items_range_.contains(item_idx);
+}
+
+void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const
+{
+ const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax);
+
+ if (IS_EQF(scroll_ofs_y, 0)) {
+ return;
+ }
+
+ const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height;
+ add_spacer_button(block, scrolled_away_rows);
+}
+
+void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const
+{
+ const int last_item_idx = grid_view_.get_item_count() - 1;
+ const int last_visible_idx = visible_items_range_.last();
+
+ if (last_item_idx > last_visible_idx) {
+ const int remaining_rows = (cols_per_row_ > 0) ?
+ (last_item_idx - last_visible_idx) / cols_per_row_ :
+ 0;
+ BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows);
+ }
+}
+
+void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const
+{
+ /* UI code only supports button dimensions of `signed short` size, the layout height we want to
+ * fill may be bigger than that. So add multiple labels of the maximum size if necessary. */
+ for (int remaining_rows = row_count; remaining_rows > 0;) {
+ const short row_count_this_iter = std::min(
+ std::numeric_limits<short>::max() / style_.tile_height, remaining_rows);
+
+ uiDefBut(&block,
+ UI_BTYPE_LABEL,
+ 0,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X,
+ row_count_this_iter * style_.tile_height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ remaining_rows -= row_count_this_iter;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+class GridViewLayoutBuilder {
+ uiBlock &block_;
+
+ friend class GridViewBuilder;
+
+ public:
+ GridViewLayoutBuilder(uiBlock &block);
+
+ void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const;
+
+ private:
+ void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const;
+
+ uiLayout *current_layout() const;
+};
+
+GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block)
+{
+}
+
+void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout,
+ AbstractGridViewItem &item) const
+{
+ uiLayout *overlap = uiLayoutOverlap(&grid_layout);
+
+ item.add_grid_tile_button(block_);
+ item.build_grid_tile(*uiLayoutRow(overlap, false));
+}
+
+void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
+ const View2D &v2d) const
+{
+ uiLayout *prev_layout = current_layout();
+
+ uiLayout &layout = *uiLayoutColumn(current_layout(), false);
+ const GridViewStyle &style = grid_view.get_style();
+
+ const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1);
+
+ BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row);
+
+ build_visible_helper.fill_layout_before_visible(block_);
+
+ /* Use `-cols_per_row` because the grid layout uses a multiple of the passed absolute value for
+ * the number of columns then, rather than distributing the number of items evenly over rows and
+ * stretching the items to fit (see #uiLayoutItemGridFlow.columns_len). */
+ uiLayout *grid_layout = uiLayoutGridFlow(&layout, true, -cols_per_row, true, true, true);
+
+ int item_idx = 0;
+ grid_view.foreach_item([&](AbstractGridViewItem &item) {
+ /* Skip if item isn't visible. */
+ if (!build_visible_helper.is_item_visible(item_idx)) {
+ item_idx++;
+ return;
+ }
+
+ build_grid_tile(*grid_layout, item);
+ item_idx++;
+ });
+
+ /* If there are not enough items to fill the layout, add padding items so the layout doesn't
+ * stretch over the entire width. */
+ if (grid_view.get_item_count() < cols_per_row) {
+ for (int padding_item_idx = 0; padding_item_idx < (cols_per_row - grid_view.get_item_count());
+ padding_item_idx++) {
+ uiItemS(grid_layout);
+ }
+ }
+
+ UI_block_layout_set_current(&block_, prev_layout);
+
+ build_visible_helper.fill_layout_after_visible(block_);
+}
+
+uiLayout *GridViewLayoutBuilder::current_layout() const
+{
+ return block_.curlayout;
+}
+
+/* ---------------------------------------------------------------------- */
+
+GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block)
+{
+}
+
+void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d)
+{
+ grid_view.build_items();
+ grid_view.update_from_old(block_);
+ grid_view.change_state_delayed();
+
+ GridViewLayoutBuilder builder(block_);
+ builder.build_from_view(grid_view, v2d);
+}
+
+/* ---------------------------------------------------------------------- */
+
+PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id)
+ : AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id)
+{
+}
+
+void PreviewGridItem::build_grid_tile(uiLayout &layout) const
+{
+ const GridViewStyle &style = get_view().get_style();
+ uiBlock *block = uiLayoutGetBlock(&layout);
+
+ uiBut *but = uiDefBut(block,
+ UI_BTYPE_PREVIEW_TILE,
+ 0,
+ label.c_str(),
+ 0,
+ 0,
+ style.tile_width,
+ style.tile_height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ ui_def_but_icon(but,
+ preview_icon_id,
+ /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
+ UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+}
+
+void PreviewGridItem::set_on_activate_fn(ActivateFn fn)
+{
+ activate_fn_ = fn;
+}
+
+void PreviewGridItem::set_is_active_fn(IsActiveFn fn)
+{
+ is_active_fn_ = fn;
+}
+
+void PreviewGridItem::on_activate()
+{
+ if (activate_fn_) {
+ activate_fn_(*this);
+ }
+}
+
+std::optional<bool> PreviewGridItem::should_be_active() const
+{
+ if (is_active_fn_) {
+ return is_active_fn_();
+ }
+ return std::nullopt;
+}
+
+} // namespace blender::ui
+
+using namespace blender::ui;
+
+/* ---------------------------------------------------------------------- */
+/* C-API */
+
+using namespace blender::ui;
+
+bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle)
+{
+ const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle);
+ return item.is_active();
+}
+
+bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle,
+ const wmNotifier *notifier)
+{
+ const AbstractGridView &view = *reinterpret_cast<const AbstractGridView *>(view_handle);
+ return view.listen(*notifier);
+}
+
+bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle,
+ const uiGridViewItemHandle *b_handle)
+{
+ const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle);
+ const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle);
+ return a.matches(b);
+}
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index b7098c26bcd..3f623566807 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -778,6 +778,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
}
}
+ if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) {
+ uiButGridTile *but_gridtile = (uiButGridTile *)but;
+ uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut;
+ if (!but_gridtile->view_item || !oldbut_gridtile->view_item ||
+ !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) {
+ return false;
+ }
+ }
+
return true;
}
@@ -904,6 +913,12 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item);
break;
}
+ case UI_BTYPE_GRID_TILE: {
+ uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut;
+ uiButGridTile *gridtile_newbut = (uiButGridTile *)but;
+ SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item);
+ break;
+ }
default:
break;
}
@@ -927,9 +942,12 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
BLI_strncpy(oldbut->strdata, but->strdata, sizeof(oldbut->strdata));
}
- if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragpoin) {
SWAP(void *, but->dragpoin, oldbut->dragpoin);
}
+ if (but->imb) {
+ SWAP(ImBuf *, but->imb, oldbut->imb);
+ }
/* NOTE: if layout hasn't been applied yet, it uses old button pointers... */
}
@@ -993,9 +1011,9 @@ static bool ui_but_update_from_old_block(const bContext *C,
else {
int flag_copy = UI_BUT_DRAG_MULTI;
- /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row
+ /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item
* button which we also want to keep highlighted then. */
- if (but->type == UI_BTYPE_TREEROW) {
+ if (ui_but_is_view_item(but)) {
flag_copy |= UI_ACTIVE;
}
@@ -1861,15 +1879,32 @@ bool ui_but_context_poll_operator_ex(bContext *C,
const wmOperatorCallParams *optype_params)
{
bool result;
+ int old_but_flag = 0;
- if (but && but->context) {
- CTX_store_set(C, but->context);
+ if (but) {
+ old_but_flag = but->flag;
+
+ /* Temporarily make this button override the active one, in case the poll acts on the active
+ * button. */
+ const_cast<uiBut *>(but)->flag |= UI_BUT_ACTIVE_OVERRIDE;
+
+ if (but->context) {
+ CTX_store_set(C, but->context);
+ }
}
result = WM_operator_poll_context(C, optype_params->optype, optype_params->opcontext);
- if (but && but->context) {
- CTX_store_set(C, nullptr);
+ if (but) {
+ BLI_assert_msg((but->flag & ~UI_BUT_ACTIVE_OVERRIDE) ==
+ (old_but_flag & ~UI_BUT_ACTIVE_OVERRIDE),
+ "Operator polls shouldn't change button flags");
+
+ const_cast<uiBut *>(but)->flag = old_but_flag;
+
+ if (but->context) {
+ CTX_store_set(C, nullptr);
+ }
}
return result;
@@ -2219,6 +2254,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
}
break;
}
+ case UI_BTYPE_GRID_TILE: {
+ uiButGridTile *grid_tile_but = (uiButGridTile *)but;
+
+ is_push = -1;
+ if (grid_tile_but->view_item) {
+ is_push = UI_grid_view_item_is_active(grid_tile_but->view_item);
+ }
+ break;
+ }
default:
is_push = -1;
break;
@@ -3425,9 +3469,7 @@ static void ui_but_free(const bContext *C, uiBut *but)
IMB_freeImBuf((struct ImBuf *)but->poin);
}
- if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- }
+ ui_but_drag_free(but);
ui_but_extra_operator_icons_free(but);
BLI_assert(UI_butstore_is_registered(but->block, but) == false);
@@ -3832,21 +3874,22 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
}
case UI_BTYPE_HOTKEY_EVENT:
if (but->flag & UI_SELECT) {
+ const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
- if (but->modifier_key) {
+ if (hotkey_but->modifier_key) {
char *str = but->drawstr;
but->drawstr[0] = '\0';
- if (but->modifier_key & KM_SHIFT) {
+ if (hotkey_but->modifier_key & KM_SHIFT) {
str += BLI_strcpy_rlen(str, "Shift ");
}
- if (but->modifier_key & KM_CTRL) {
+ if (hotkey_but->modifier_key & KM_CTRL) {
str += BLI_strcpy_rlen(str, "Ctrl ");
}
- if (but->modifier_key & KM_ALT) {
+ if (hotkey_but->modifier_key & KM_ALT) {
str += BLI_strcpy_rlen(str, "Alt ");
}
- if (but->modifier_key & KM_OSKEY) {
+ if (hotkey_but->modifier_key & KM_OSKEY) {
str += BLI_strcpy_rlen(str, "Cmd ");
}
@@ -3972,6 +4015,14 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButTreeRow);
alloc_str = "uiButTreeRow";
break;
+ case UI_BTYPE_HOTKEY_EVENT:
+ alloc_size = sizeof(uiButHotkeyEvent);
+ alloc_str = "uiButHotkeyEvent";
+ break;
+ case UI_BTYPE_GRID_TILE:
+ alloc_size = sizeof(uiButGridTile);
+ alloc_str = "uiButGridTile";
+ break;
default:
alloc_size = sizeof(uiBut);
alloc_str = "uiBut";
@@ -4946,6 +4997,33 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname)
return match;
}
+#define PREVIEW_TILE_PAD (0.15f * UI_UNIT_X)
+
+int UI_preview_tile_size_x(void)
+{
+ const float pad = PREVIEW_TILE_PAD;
+ return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_X + 2.0f * pad);
+}
+
+int UI_preview_tile_size_y(void)
+{
+ const uiStyle *style = UI_style_get();
+ const float font_height = style->widget.points * UI_DPI_FAC;
+ const float pad = PREVIEW_TILE_PAD;
+
+ return round_fl_to_int(UI_preview_tile_size_y_no_label() + font_height +
+ /* Add some extra padding to make things less tight vertically. */
+ pad);
+}
+
+int UI_preview_tile_size_y_no_label(void)
+{
+ const float pad = PREVIEW_TILE_PAD;
+ return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_Y + 2.0f * pad);
+}
+
+#undef PREVIEW_TILE_PAD
+
static void ui_but_update_and_icon_set(uiBut *but, int icon)
{
if (icon) {
@@ -5856,104 +5934,6 @@ int UI_but_return_value_get(uiBut *but)
return but->retval;
}
-void UI_but_drag_set_id(uiBut *but, ID *id)
-{
- but->dragtype = WM_DRAG_ID;
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
- }
- but->dragpoin = (void *)id;
-}
-
-void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale)
-{
- but->imb = imb;
- but->imb_scale = scale;
-}
-
-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, metadata, path, import_type);
-
- /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the
- * #wmDropBox.
- * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its
- * copy callback.
- * */
- asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C);
-
- but->dragtype = WM_DRAG_ASSET;
- ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- }
- but->dragpoin = asset_drag;
- but->dragflag |= UI_BUT_DRAGPOIN_FREE;
- UI_but_drag_attach_image(but, imb, scale);
-}
-
-void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
-{
- but->dragtype = WM_DRAG_RNA;
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
- }
- but->dragpoin = (void *)ptr;
-}
-
-void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
-{
- but->dragtype = WM_DRAG_PATH;
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
- }
- but->dragpoin = (void *)path;
- if (use_free) {
- but->dragflag |= UI_BUT_DRAGPOIN_FREE;
- }
-}
-
-void UI_but_drag_set_name(uiBut *but, const char *name)
-{
- but->dragtype = WM_DRAG_NAME;
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
- }
- but->dragpoin = (void *)name;
-}
-
-void UI_but_drag_set_value(uiBut *but)
-{
- but->dragtype = WM_DRAG_VALUE;
-}
-
-void UI_but_drag_set_image(
- uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free)
-{
- but->dragtype = WM_DRAG_PATH;
- ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
- if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
- WM_drag_data_free(but->dragtype, but->dragpoin);
- but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
- }
- but->dragpoin = (void *)path;
- if (use_free) {
- but->dragflag |= UI_BUT_DRAGPOIN_FREE;
- }
- UI_but_drag_attach_image(but, imb, scale);
-}
-
PointerRNA *UI_but_operator_ptr_get(uiBut *but)
{
if (but->optype && !but->opptr) {
@@ -6263,64 +6243,6 @@ uiBut *uiDefIconBlockBut(uiBlock *block,
return but;
}
-uiBut *uiDefKeyevtButS(uiBlock *block,
- int retval,
- const char *str,
- int x,
- int y,
- short width,
- short height,
- short *spoin,
- const char *tip)
-{
- uiBut *but = ui_def_but(block,
- UI_BTYPE_KEY_EVENT | UI_BUT_POIN_SHORT,
- retval,
- str,
- x,
- y,
- width,
- height,
- spoin,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- tip);
- ui_but_update(but);
- return but;
-}
-
-uiBut *uiDefHotKeyevtButS(uiBlock *block,
- int retval,
- const char *str,
- int x,
- int y,
- short width,
- short height,
- short *keypoin,
- const short *modkeypoin,
- const char *tip)
-{
- uiBut *but = ui_def_but(block,
- UI_BTYPE_HOTKEY_EVENT | UI_BUT_POIN_SHORT,
- retval,
- str,
- x,
- y,
- width,
- height,
- keypoin,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- tip);
- but->modifier_key = *modkeypoin;
- ui_but_update(but);
- return but;
-}
-
uiBut *uiDefSearchBut(uiBlock *block,
void *arg,
int retval,
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c
index e838ce37d8e..0e69b4bb358 100644
--- a/source/blender/editors/interface/interface_anim.c
+++ b/source/blender/editors/interface/interface_anim.c
@@ -293,7 +293,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str)
void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
{
- ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra);
+ ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra, true);
}
void ui_but_anim_copy_driver(bContext *C)
diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc
new file mode 100644
index 00000000000..4c68870b2c7
--- /dev/null
+++ b/source/blender/editors/interface/interface_drag.cc
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+
+#include "interface_intern.h"
+
+void UI_but_drag_set_id(uiBut *but, ID *id)
+{
+ but->dragtype = WM_DRAG_ID;
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
+ }
+ but->dragpoin = (void *)id;
+}
+
+void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale)
+{
+ but->imb = imb;
+ but->imb_scale = scale;
+}
+
+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, metadata, path, import_type);
+
+ /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the
+ * #wmDropBox.
+ * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its
+ * copy callback.
+ * */
+ asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C);
+
+ but->dragtype = WM_DRAG_ASSET;
+ ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ }
+ but->dragpoin = asset_drag;
+ but->dragflag |= UI_BUT_DRAGPOIN_FREE;
+ UI_but_drag_attach_image(but, imb, scale);
+}
+
+void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
+{
+ but->dragtype = WM_DRAG_RNA;
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
+ }
+ but->dragpoin = (void *)ptr;
+}
+
+void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
+{
+ but->dragtype = WM_DRAG_PATH;
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
+ }
+ but->dragpoin = (void *)path;
+ if (use_free) {
+ but->dragflag |= UI_BUT_DRAGPOIN_FREE;
+ }
+}
+
+void UI_but_drag_set_name(uiBut *but, const char *name)
+{
+ but->dragtype = WM_DRAG_NAME;
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
+ }
+ but->dragpoin = (void *)name;
+}
+
+void UI_but_drag_set_value(uiBut *but)
+{
+ but->dragtype = WM_DRAG_VALUE;
+}
+
+void UI_but_drag_set_image(
+ uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free)
+{
+ but->dragtype = WM_DRAG_PATH;
+ ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
+ }
+ but->dragpoin = (void *)path;
+ if (use_free) {
+ but->dragflag |= UI_BUT_DRAGPOIN_FREE;
+ }
+ UI_but_drag_attach_image(but, imb, scale);
+}
+
+void ui_but_drag_free(uiBut *but)
+{
+ if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ }
+}
+
+bool ui_but_drag_is_draggable(const uiBut *but)
+{
+ return but->dragpoin != nullptr;
+}
+
+void ui_but_drag_start(bContext *C, uiBut *but)
+{
+ wmDrag *drag = WM_event_start_drag(C,
+ but->icon,
+ but->dragtype,
+ but->dragpoin,
+ ui_but_value_get(but),
+ (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA :
+ WM_DRAG_NOP);
+ /* wmDrag has ownership over dragpoin now, stop messing with it. */
+ but->dragpoin = NULL;
+
+ if (but->imb) {
+ WM_event_drag_image(drag, but->imb, but->imb_scale);
+ }
+
+ /* Special feature for assets: We add another drag item that supports multiple assets. It
+ * gets the assets from context. */
+ if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) {
+ WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP);
+ }
+}
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 18bad7949ee..d201820fbb6 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -130,7 +130,7 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect,
void UI_draw_roundbox_3ub_alpha(
const rctf *rect, bool filled, float rad, const uchar col[3], uchar alpha)
{
- float colv[4] = {
+ const float colv[4] = {
((float)col[0]) / 255,
((float)col[1]) / 255,
((float)col[2]) / 255,
@@ -142,7 +142,7 @@ void UI_draw_roundbox_3ub_alpha(
void UI_draw_roundbox_3fv_alpha(
const rctf *rect, bool filled, float rad, const float col[3], float alpha)
{
- float colv[4] = {col[0], col[1], col[2], alpha};
+ const float colv[4] = {col[0], col[1], col[2], alpha};
UI_draw_roundbox_4fv_ex(rect, (filled) ? colv : NULL, NULL, 1.0f, colv, U.pixelsize, rad);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c
index 895f8d0d840..c015a60de89 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -50,8 +50,6 @@
#include "RE_pipeline.h"
-#include "RE_pipeline.h"
-
#include "interface_eyedropper_intern.h"
typedef struct Eyedropper {
@@ -329,7 +327,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
SpaceImage *sima = area->spacedata.first;
- int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (ED_space_image_color_sample(sima, region, region_mval, r_col, NULL)) {
return;
@@ -340,7 +338,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
SpaceNode *snode = area->spacedata.first;
- int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) {
return;
@@ -351,7 +349,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
SpaceClip *sc = area->spacedata.first;
- int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
+ const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) {
return;
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index aec8f56678a..01b958576b6 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -156,7 +156,7 @@ static void datadropper_id_sample_pt(
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
- /* grr, always draw else we leave stale text */
+ /* Unfortunately it's necessary to always draw else we leave stale text. */
ED_region_tag_redraw(region);
if (area->spacetype == SPACE_VIEW3D) {
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c
index 56bc1a6d956..3c6f127582a 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/interface_eyedropper_depth.c
@@ -171,7 +171,7 @@ static void depthdropper_depth_sample_pt(bContext *C,
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
- /* grr, always draw else we leave stale text */
+ /* Unfortunately it's necessary to always draw otherwise we leave stale text. */
ED_region_tag_redraw(region);
view3d_operator_needs_opengl(C);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 2c408619fe7..7c00c4f1875 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -384,6 +384,12 @@ typedef struct uiHandleButtonData {
/* True when alt is held and the preference for displaying tooltips should be ignored. */
bool tooltip_force;
+ /**
+ * Behave as if #UI_BUT_DISABLED is set (without drawing grayed out).
+ * Needed so non-interactive labels can be activated for the purpose of showing tool-tips,
+ * without them blocking interaction with nodes, see: T97386.
+ */
+ bool disable_force;
/* auto open */
bool used_mouse;
@@ -536,7 +542,7 @@ static bool but_copypaste_profile_alive = false;
bool ui_but_is_editing(const uiBut *but)
{
- uiHandleButtonData *data = but->active;
+ const uiHandleButtonData *data = but->active;
return (data && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING));
}
@@ -654,21 +660,23 @@ static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx)
static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop)
{
/* Not very elegant, but ensures preference changes force re-save. */
- bool tag = false;
- if (prop && !(RNA_property_flag(prop) & PROP_NO_DEG_UPDATE)) {
- StructRNA *base = RNA_struct_base(ptr->type);
- if (base == NULL) {
- base = ptr->type;
- }
- if (ELEM(base,
- &RNA_AddonPreferences,
- &RNA_KeyConfigPreferences,
- &RNA_KeyMapItem,
- &RNA_UserAssetLibrary)) {
- tag = true;
- }
+
+ if (!prop) {
+ return false;
}
- return tag;
+ if (RNA_property_flag(prop) & PROP_NO_DEG_UPDATE) {
+ return false;
+ }
+
+ StructRNA *base = RNA_struct_base(ptr->type);
+ if (base == NULL) {
+ base = ptr->type;
+ }
+ return ELEM(base,
+ &RNA_AddonPreferences,
+ &RNA_KeyConfigPreferences,
+ &RNA_KeyMapItem,
+ &RNA_UserAssetLibrary);
}
bool UI_but_is_userdef(const uiBut *but)
@@ -894,64 +902,66 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
/* typically call ui_apply_but_undo(), ui_apply_but_autokey() */
static void ui_apply_but_undo(uiBut *but)
{
- if (but->flag & UI_BUT_UNDO) {
- const char *str = NULL;
- size_t str_len_clip = SIZE_MAX - 1;
- bool skip_undo = false;
+ if (!(but->flag & UI_BUT_UNDO)) {
+ return;
+ }
- /* define which string to use for undo */
- if (but->type == UI_BTYPE_MENU) {
- str = but->drawstr;
- str_len_clip = ui_but_drawstr_len_without_sep_char(but);
- }
- else if (but->drawstr[0]) {
- str = but->drawstr;
- str_len_clip = ui_but_drawstr_len_without_sep_char(but);
- }
- else {
- str = but->tip;
- str_len_clip = ui_but_tip_len_only_first_line(but);
- }
+ const char *str = NULL;
+ size_t str_len_clip = SIZE_MAX - 1;
+ bool skip_undo = false;
- /* fallback, else we don't get an undo! */
- if (str == NULL || str[0] == '\0' || str_len_clip == 0) {
- str = "Unknown Action";
- str_len_clip = strlen(str);
- }
+ /* define which string to use for undo */
+ if (but->type == UI_BTYPE_MENU) {
+ str = but->drawstr;
+ str_len_clip = ui_but_drawstr_len_without_sep_char(but);
+ }
+ else if (but->drawstr[0]) {
+ str = but->drawstr;
+ str_len_clip = ui_but_drawstr_len_without_sep_char(but);
+ }
+ else {
+ str = but->tip;
+ str_len_clip = ui_but_tip_len_only_first_line(but);
+ }
- /* Optionally override undo when undo system doesn't support storing properties. */
- if (but->rnapoin.owner_id) {
- /* Exception for renaming ID data, we always need undo pushes in this case,
- * because undo systems track data by their ID, see: T67002. */
- /* Exception for active shape-key, since changing this in edit-mode updates
- * the shape key from object mode data. */
- if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) {
- /* pass */
- }
- else {
- ID *id = but->rnapoin.owner_id;
- if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) {
- skip_undo = true;
- }
- }
- }
+ /* fallback, else we don't get an undo! */
+ if (str == NULL || str[0] == '\0' || str_len_clip == 0) {
+ str = "Unknown Action";
+ str_len_clip = strlen(str);
+ }
- if (skip_undo == false) {
- /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo
- * steps to be written which cause lag: T71434. */
- if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) {
+ /* Optionally override undo when undo system doesn't support storing properties. */
+ if (but->rnapoin.owner_id) {
+ /* Exception for renaming ID data, we always need undo pushes in this case,
+ * because undo systems track data by their ID, see: T67002. */
+ /* Exception for active shape-key, since changing this in edit-mode updates
+ * the shape key from object mode data. */
+ if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) {
+ /* pass */
+ }
+ else {
+ ID *id = but->rnapoin.owner_id;
+ if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) {
skip_undo = true;
}
}
+ }
- if (skip_undo) {
- str = "";
+ if (skip_undo == false) {
+ /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo
+ * steps to be written which cause lag: T71434. */
+ if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) {
+ skip_undo = true;
}
+ }
- /* delayed, after all other funcs run, popups are closed, etc */
- uiAfterFunc *after = ui_afterfunc_new();
- BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr)));
+ if (skip_undo) {
+ str = "";
}
+
+ /* delayed, after all other funcs run, popups are closed, etc */
+ uiAfterFunc *after = ui_afterfunc_new();
+ BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr)));
}
static void ui_apply_but_autokey(bContext *C, uiBut *but)
@@ -961,21 +971,21 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but)
/* try autokey */
ui_but_anim_autokey(C, but, scene, scene->r.cfra);
- /* make a little report about what we've done! */
- if (but->rnaprop) {
- char *buf;
+ if (!but->rnaprop) {
+ return;
+ }
- if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) {
- return;
- }
+ if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) {
+ return;
+ }
- buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
- if (buf) {
- BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf);
- MEM_freeN(buf);
+ /* make a little report about what we've done! */
+ char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
+ if (buf) {
+ BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf);
+ MEM_freeN(buf);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
- }
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
}
}
@@ -1470,9 +1480,9 @@ static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *dat
}
}
- /* edit buttons proportionally to eachother
+ /* Edit buttons proportionally to each other.
* NOTE: if we mix buttons which are proportional and others which are not,
- * this may work a bit strangely */
+ * this may work a bit strangely. */
if ((but_active->rnaprop && (RNA_property_flag(but_active->rnaprop) & PROP_PROPORTIONAL)) ||
ELEM(but_active->unit_type, RNA_SUBTYPE_UNIT_VALUE(PROP_UNIT_LENGTH))) {
if (data->origvalue != 0.0) {
@@ -1625,29 +1635,34 @@ static bool ui_drag_toggle_set_xy_xy(
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
/* NOTE: ctrl is always true here because (at least for now)
* we always want to consider text control in this case, even when not embossed. */
- if (ui_but_is_interactive(but, true)) {
- if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
-
- /* execute the button */
- if (ui_drag_toggle_but_is_supported(but)) {
- /* is it pressed? */
- const int pushed_state_but = ui_drag_toggle_but_pushed_state(but);
- if (pushed_state_but != pushed_state) {
- UI_but_execute(C, region, but);
- if (do_check) {
- ui_but_update_edited(but);
- }
- if (U.runtime.is_dirty == false) {
- ui_but_update_preferences_dirty(but);
- }
- changed = true;
- }
- }
- /* done */
- }
+
+ if (!ui_but_is_interactive(but, true)) {
+ continue;
}
+ if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
+ continue;
+ }
+ if (!ui_drag_toggle_but_is_supported(but)) {
+ continue;
+ }
+ /* is it pressed? */
+ const int pushed_state_but = ui_drag_toggle_but_pushed_state(but);
+ if (pushed_state_but == pushed_state) {
+ continue;
+ }
+
+ /* execute the button */
+ UI_but_execute(C, region, but);
+ if (do_check) {
+ ui_but_update_edited(but);
+ }
+ if (U.runtime.is_dirty == false) {
+ ui_but_update_preferences_dirty(but);
+ }
+ changed = true;
}
}
+
if (changed) {
/* apply now, not on release (or if handlers are canceled for whatever reason) */
ui_apply_but_funcs_after(C);
@@ -1669,7 +1684,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
*/
if (drag_info->is_xy_lock_init == false) {
/* first store the buttons original coords */
- uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, NULL, NULL);
+ uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, false, NULL, NULL);
if (but) {
if (but->flag & UI_BUT_DRAG_LOCK) {
@@ -1739,7 +1754,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
if (done) {
wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, NULL, NULL);
+ uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, false, NULL, NULL);
if (but) {
ui_apply_but_undo(but);
@@ -2120,25 +2135,7 @@ static bool ui_but_drag_init(bContext *C,
}
}
else {
- wmDrag *drag = WM_event_start_drag(
- C,
- but->icon,
- but->dragtype,
- but->dragpoin,
- ui_but_value_get(but),
- (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : WM_DRAG_NOP);
- /* wmDrag has ownership over dragpoin now, stop messing with it. */
- but->dragpoin = NULL;
-
- if (but->imb) {
- WM_event_drag_image(drag, but->imb, but->imb_scale);
- }
-
- /* Special feature for assets: We add another drag item that supports multiple assets. It
- * gets the assets from context. */
- if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) {
- WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP);
- }
+ ui_but_drag_start(C, but);
}
return true;
}
@@ -2292,6 +2289,9 @@ static void ui_apply_but(
case UI_BTYPE_ROW:
ui_apply_but_ROW(C, block, but, data);
break;
+ case UI_BTYPE_GRID_TILE:
+ ui_apply_but_ROW(C, block, but, data);
+ break;
case UI_BTYPE_TREEROW:
ui_apply_but_TREEROW(C, block, but, data);
break;
@@ -2948,6 +2948,9 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR],
void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value)
{
+ /* Caller should check. */
+ BLI_assert((but->flag & UI_BUT_DISABLED) == 0);
+
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
ui_textedit_string_set(but, but->active, value);
@@ -3492,7 +3495,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT);
#ifdef WITH_INPUT_IME
- if (is_num_but == false && BLT_lang_is_ime_supported()) {
+ if (!is_num_but) {
ui_textedit_ime_begin(win, but);
}
#endif
@@ -3896,7 +3899,7 @@ static void ui_do_but_textedit(
if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
#ifdef WITH_INPUT_IME
- && !is_ime_composing && (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported())
+ && !is_ime_composing && !WM_event_is_ime_switch(event)
#endif
) {
char ascii = event->ascii;
@@ -4324,7 +4327,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
uiButtonActivateType activate_type)
{
ARegion *region = CTX_wm_region(C);
- uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, NULL, NULL);
+ uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, false, NULL, NULL);
if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
/* exit listrow */
@@ -4346,14 +4349,14 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
* \{ */
static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but,
- uiHandleButtonData *data,
+ ARegion *region,
const wmEvent *event)
{
float xmax = but->rect.xmax;
const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
int x = event->xy[0], y = event->xy[1];
- ui_window_to_block(data->region, but->block, &x, &y);
+ ui_window_to_block(region, but->block, &x, &y);
if (!BLI_rctf_isect_pt(&but->rect, x, y)) {
return NULL;
}
@@ -4382,7 +4385,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C,
uiHandleButtonData *data,
const wmEvent *event)
{
- uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data, event);
+ uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event);
if (!op_icon) {
return false;
@@ -4417,7 +4420,7 @@ static void ui_do_but_extra_operator_icons_mousemove(uiBut *but,
op_icon->highlighted = false;
}
- uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data, event);
+ uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event);
if (hovered) {
hovered->highlighted = true;
@@ -4501,10 +4504,14 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
uiHandleButtonData *data,
const wmEvent *event)
{
+ uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
+ BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT);
+
if (data->state == BUTTON_STATE_HIGHLIGHT) {
- if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY, EVT_BUT_OPEN) &&
+ (event->val == KM_PRESS)) {
but->drawstr[0] = 0;
- but->modifier_key = 0;
+ hotkey_but->modifier_key = 0;
button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
return WM_UI_HANDLER_BREAK;
}
@@ -4525,20 +4532,16 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
/* only cancel if click outside the button */
if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) {
- /* data->cancel doesn't work, this button opens immediate */
- if (but->flag & UI_BUT_IMMEDIATE) {
- ui_but_value_set(but, 0);
- }
- else {
- data->cancel = true;
- }
+ data->cancel = true;
+ /* Close the containing popup (if any). */
+ data->escapecancel = true;
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
}
/* always set */
- but->modifier_key = event->modifier;
+ hotkey_but->modifier_key = event->modifier;
ui_but_update(but);
ED_region_tag_redraw(data->region);
@@ -4656,7 +4659,7 @@ static int ui_do_but_TEX(
/* pass */
}
else {
- if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) {
+ if (!ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) {
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
}
return WM_UI_HANDLER_BREAK;
@@ -4782,7 +4785,7 @@ static int ui_do_but_TREEROW(bContext *C,
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)) {
+ if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) {
return WM_UI_HANDLER_BREAK;
}
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
@@ -4810,12 +4813,53 @@ static int ui_do_but_TREEROW(bContext *C,
return WM_UI_HANDLER_CONTINUE;
}
+static int ui_do_but_GRIDTILE(bContext *C,
+ uiBut *but,
+ uiHandleButtonData *data,
+ const wmEvent *event)
+{
+ BLI_assert(but->type == UI_BTYPE_GRID_TILE);
+
+ if (data->state == BUTTON_STATE_HIGHLIGHT) {
+ if (event->type == LEFTMOUSE) {
+ 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->region, 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;
+
+ case KM_CLICK:
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+
+ case KM_DBL_CLICK:
+ data->cancel = true;
+ // uiButGridTile *grid_tile_but = (uiButGridTile *)but;
+ // UI_tree_view_item_begin_rename(grid_tile_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;
+}
+
static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* First handle click on icon-drag type button. */
- if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && but->dragpoin) {
+ if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_drag_is_draggable(but)) {
if (ui_but_contains_point_px_icon(but, data->region, event)) {
/* tell the button to wait and keep checking further events to
@@ -4838,7 +4882,8 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
int ret = WM_UI_HANDLER_BREAK;
/* XXX: (a bit ugly) Special case handling for file-browser drag button. */
- if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) {
+ if (ui_but_drag_is_draggable(but) && but->imb &&
+ ui_but_contains_point_px_icon(but, data->region, event)) {
ret = WM_UI_HANDLER_CONTINUE;
}
/* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event
@@ -4850,6 +4895,10 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
ret = WM_UI_HANDLER_CONTINUE;
}
}
+ const uiBut *view_but = ui_view_item_find_mouse_over(data->region, event->xy);
+ if (view_but) {
+ ret = WM_UI_HANDLER_CONTINUE;
+ }
button_activate_state(C, but, BUTTON_STATE_EXIT);
return ret;
}
@@ -5992,7 +6041,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* First handle click on icon-drag type button. */
- if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) {
+ if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) {
if (ui_but_contains_point_px_icon(but, data->region, event)) {
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
data->dragstartx = event->xy[0];
@@ -6178,7 +6227,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* First handle click on icon-drag type button. */
- if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) {
+ if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) {
ui_palette_set_active(color_but);
if (ui_but_contains_point_px_icon(but, data->region, event)) {
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
@@ -6271,7 +6320,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target);
- IMB_colormanagement_srgb_to_scene_linear_v3(target);
+ IMB_colormanagement_srgb_to_scene_linear_v3(target, target);
}
else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target);
@@ -6288,7 +6337,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
}
else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color);
- IMB_colormanagement_scene_linear_to_srgb_v3(color);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color, color);
BKE_brush_color_set(scene, brush, color);
updated = true;
}
@@ -7393,8 +7442,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
if (snap) {
- float d[2] = {mx - data->dragstartx, data->dragstarty};
-
+ const float d[2] = {mx - data->dragstartx, data->dragstarty};
if (len_squared_v2(d) < (9.0f * U.dpi_fac)) {
snap = false;
}
@@ -7899,7 +7947,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
uiHandleButtonData *data = but->active;
int retval = WM_UI_HANDLER_CONTINUE;
- const bool is_disabled = but->flag & UI_BUT_DISABLED;
+ const bool is_disabled = but->flag & UI_BUT_DISABLED || data->disable_force;
/* if but->pointype is set, but->poin should be too */
BLI_assert(!but->pointype || but->poin);
@@ -8005,6 +8053,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_ROW:
retval = ui_do_but_TOG(C, but, data, event);
break;
+ case UI_BTYPE_GRID_TILE:
+ retval = ui_do_but_GRIDTILE(C, but, data, event);
+ break;
case UI_BTYPE_TREEROW:
retval = ui_do_but_TREEROW(C, but, data, event);
break;
@@ -8216,7 +8267,7 @@ static ARegion *ui_but_tooltip_init(
if (but) {
const wmWindow *win = CTX_wm_window(C);
uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get(
- but, but->active, win->eventstate);
+ but, but->active ? but->active->region : region, win->eventstate);
return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label);
}
@@ -8492,14 +8543,6 @@ static void button_activate_init(bContext *C,
}
button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
- /* activate right away */
- if (but->flag & UI_BUT_IMMEDIATE) {
- if (but->type == UI_BTYPE_HOTKEY_EVENT) {
- button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
- }
- /* .. more to be added here */
- }
-
if (type == BUTTON_ACTIVATE_OPEN) {
button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
@@ -8703,20 +8746,38 @@ static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_c
uiBut *but_found = NULL;
while (region) {
- uiBut *activebut = NULL;
+ /* Follow this exact priority (from highest to lowest priority):
+ * 1) Active-override button (#UI_BUT_ACTIVE_OVERRIDE).
+ * 2) The real active button.
+ * 3) The previously active button (#UI_BUT_LAST_ACTIVE).
+ */
+ uiBut *active_but_override = NULL;
+ uiBut *active_but_real = NULL;
+ uiBut *active_but_last = NULL;
/* find active button */
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ if (but->flag & UI_BUT_ACTIVE_OVERRIDE) {
+ active_but_override = but;
+ }
if (but->active) {
- activebut = but;
+ active_but_real = but;
}
- else if (!activebut && (but->flag & UI_BUT_LAST_ACTIVE)) {
- activebut = but;
+ if (but->flag & UI_BUT_LAST_ACTIVE) {
+ active_but_last = but;
}
}
}
+ uiBut *activebut = active_but_override;
+ if (!activebut) {
+ activebut = active_but_real;
+ }
+ if (!activebut) {
+ activebut = active_but_last;
+ }
+
if (activebut && (but_check_cb == NULL || but_check_cb(activebut))) {
uiHandleButtonData *data = activebut->active;
@@ -8947,7 +9008,12 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event)
static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region)
{
if (event->type == MOUSEMOVE) {
- uiBut *but = ui_but_find_mouse_over(region, event);
+ const bool labeledit = event->modifier & KM_CTRL;
+ /* Allow buttons to be activated to show the tool-tip,
+ * then force-disable them if they're not considered interactive
+ * so they don't swallow events but can still display tips. */
+ const bool for_tooltip = true;
+ uiBut *but = ui_but_find_mouse_over_ex(region, event->xy, labeledit, for_tooltip, NULL, NULL);
if (but) {
button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER);
@@ -8956,6 +9022,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg
* preferences. */
but->active->tooltip_force = true;
}
+
+ if (but->active && !ui_but_is_interactive(but, labeledit)) {
+ but->active->disable_force = true;
+ }
}
}
else if (event->type == EVT_BUT_OPEN) {
@@ -9435,7 +9505,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C,
int mouse_xy[2];
WM_event_drag_start_xy(event, mouse_xy);
- const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL);
+ const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, false, NULL, NULL);
if (list->dyn_data->custom_drag_optype) {
if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) {
@@ -9443,7 +9513,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C,
}
}
- return (hovered_but && hovered_but->dragpoin);
+ return (hovered_but && ui_but_drag_is_draggable(hovered_but));
}
static int ui_list_handle_click_drag(bContext *C,
@@ -9653,31 +9723,31 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
return retval;
}
-static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region)
+static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region)
{
- bool has_treerows = false;
+ bool has_view_item = false;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- /* Avoid unnecessary work: Tree-rows are assumed to be inside tree-views. */
+ /* Avoid unnecessary work: view item buttons are assumed to be inside views. */
if (BLI_listbase_is_empty(&block->views)) {
continue;
}
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
- if (but->type == UI_BTYPE_TREEROW) {
+ if (ui_but_is_view_item(but)) {
but->flag &= ~UI_ACTIVE;
- has_treerows = true;
+ has_view_item = true;
}
}
}
- if (!has_treerows) {
+ if (!has_view_item) {
/* Avoid unnecessary lookup. */
return WM_UI_HANDLER_CONTINUE;
}
- /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it.
+ /* Always highlight the hovered view item, even if the mouse hovers another button inside of it.
*/
- uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy);
+ uiBut *hovered_row_but = ui_view_item_find_mouse_over(region, event->xy);
if (hovered_row_but) {
hovered_row_but->flag |= UI_ACTIVE;
}
@@ -9685,6 +9755,21 @@ static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region)
return WM_UI_HANDLER_CONTINUE;
}
+static int ui_handle_view_item_event(bContext *C,
+ const wmEvent *event,
+ ARegion *region,
+ uiBut *view_but)
+{
+ BLI_assert(ui_but_is_view_item(view_but));
+ if (event->type == LEFTMOUSE) {
+ /* Will free active button if there already is one. */
+ ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER);
+ return ui_do_button(C, view_but->block, view_but, event);
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but)
{
uiHandleButtonData *data = but->active;
@@ -11285,9 +11370,15 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use
ui_blocks_set_tooltips(region, true);
}
- /* Always do this, to reliably update tree-row highlighting, even if the mouse hovers a button
- * inside the row (it's an overlapping layout). */
- ui_handle_tree_hover(event, region);
+ /* Always do this, to reliably update view item highlighting, even if the mouse hovers a button
+ * nested in the item (it's an overlapping layout). */
+ ui_handle_view_items_hover(event, region);
+ if (retval == WM_UI_HANDLER_CONTINUE) {
+ uiBut *view_item = ui_view_item_find_mouse_over(region, event->xy);
+ if (view_item) {
+ retval = ui_handle_view_item_event(C, event, region, view_item);
+ }
+ }
/* delayed apply callbacks */
ui_apply_but_funcs_after(C);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 332b9b44b0a..c19e842aad8 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -546,6 +546,7 @@ static void init_brush_icons(void)
INIT_BRUSH_ICON(ICON_BRUSH_MASK, mask);
INIT_BRUSH_ICON(ICON_BRUSH_MIX, mix);
INIT_BRUSH_ICON(ICON_BRUSH_NUDGE, nudge);
+ INIT_BRUSH_ICON(ICON_BRUSH_PAINT_SELECT, paint_select);
INIT_BRUSH_ICON(ICON_BRUSH_PINCH, pinch);
INIT_BRUSH_ICON(ICON_BRUSH_SCRAPE, scrape);
INIT_BRUSH_ICON(ICON_BRUSH_SMEAR, smear);
@@ -584,6 +585,19 @@ static void init_brush_icons(void)
INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard);
INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke);
+ /* Curves sculpt. */
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_ADD, curves_sculpt_add);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_COMB, curves_sculpt_comb);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_CUT, curves_sculpt_cut);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DELETE, curves_sculpt_delete);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DENSITY, curves_sculpt_density);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_GROW_SHRINK, curves_sculpt_grow_shrink);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PINCH, curves_sculpt_pinch);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PUFF, curves_sculpt_puff);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SLIDE, curves_sculpt_slide);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SMOOTH, curves_sculpt_smooth);
+ INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SNAKE_HOOK, curves_sculpt_snake_hook);
+
# undef INIT_BRUSH_ICON
}
@@ -2034,6 +2048,9 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
paint_mode = PAINT_MODE_TEXTURE_3D;
}
+ else if (ob->mode & OB_MODE_SCULPT_CURVES) {
+ paint_mode = PAINT_MODE_SCULPT_CURVES;
+ }
}
else if (space_type == SPACE_IMAGE) {
if (area->spacetype == space_type) {
diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c
index 00c1bcb5f6e..6ad5fe805ab 100644
--- a/source/blender/editors/interface/interface_icons_event.c
+++ b/source/blender/editors/interface/interface_icons_event.c
@@ -145,10 +145,10 @@ void icon_draw_rect_input(float x,
SNPRINTF(str, "F%d", 1 + (event_type - EVT_F1KEY));
icon_draw_rect_input_text(&rect, color, str, event_type > EVT_F9KEY ? 8.0f : 10.0f);
}
- else if (event_type == EVT_LEFTSHIFTKEY) {
+ else if (event_type == EVT_LEFTSHIFTKEY) { /* Right Shift has already been converted to left. */
icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x87, 0xa7, 0x0});
}
- else if (event_type == EVT_LEFTCTRLKEY) {
+ else if (event_type == EVT_LEFTCTRLKEY) { /* Right Shift has already been converted to left. */
if (platform == MACOS) {
icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x83, 0x0});
}
@@ -156,7 +156,7 @@ void icon_draw_rect_input(float x,
icon_draw_rect_input_text(&rect, color, "Ctrl", 9.0f);
}
}
- else if (event_type == EVT_LEFTALTKEY) {
+ else if (event_type == EVT_LEFTALTKEY) { /* Right Alt has already been converted to left. */
if (platform == MACOS) {
icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0xa5, 0x0});
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index c09ff68bbca..791e51b81a6 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -74,6 +74,12 @@ enum {
UI_SELECT_DRAW = (1 << 5),
/** Property search filter is active and the button does not match. */
UI_SEARCH_FILTER_NO_MATCH = (1 << 6),
+
+ /** Temporarily override the active button for lookups in context, regions, etc. (everything
+ * using #ui_context_button_active()). For example, so that operators normally acting on the
+ * active button can be polled on non-active buttons to (e.g. for disabling). */
+ UI_BUT_ACTIVE_OVERRIDE = (1 << 7),
+
/* WARNING: rest of #uiBut.flag in UI_interface.h */
};
@@ -223,7 +229,6 @@ struct uiBut {
bool changed;
/** so buttons can support unit systems which are not RNA */
uchar unit_type;
- short modifier_key;
short iconadd;
/** #UI_BTYPE_BLOCK data */
@@ -346,6 +351,13 @@ typedef struct uiButTreeRow {
int indentation;
} uiButTreeRow;
+/** Derived struct for #UI_BTYPE_GRID_TILE. */
+typedef struct uiButGridTile {
+ uiBut but;
+
+ uiGridViewItemHandle *view_item;
+} uiButGridTile;
+
/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
uiBut but;
@@ -375,6 +387,13 @@ typedef struct uiButCurveMapping {
eButGradientType gradient_type;
} uiButCurveMapping;
+/** Derived struct for #UI_BTYPE_HOTKEY_EVENT. */
+typedef struct uiButHotkeyEvent {
+ uiBut but;
+
+ short modifier_key;
+} uiButHotkeyEvent;
+
/**
* Additional, superimposed icon for a button, invoking an operator.
*/
@@ -1206,24 +1225,24 @@ typedef enum {
/**
* Helper call to draw a menu item without a button.
*
- * \param state: The state of the button,
- * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE.
+ * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically
+ * #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE.
* \param separator_type: The kind of separator which controls if and how the string is clipped.
- * \param r_xmax: The right hand position of the text, this takes into the icon,
- * padding and text clipping when there is not enough room to display the full text.
+ * \param r_xmax: The right hand position of the text, this takes into the icon, padding and text
+ * clipping when there is not enough room to display the full text.
*/
void ui_draw_menu_item(const struct uiFontStyle *fstyle,
rcti *rect,
const char *name,
int iconid,
- int state,
+ int but_flag,
uiMenuItemSeparatorType separator_type,
int *r_xmax);
void ui_draw_preview_item(const struct uiFontStyle *fstyle,
rcti *rect,
const char *name,
int iconid,
- int state,
+ int but_flag,
eFontStyle_Align text_align);
/**
* Version of #ui_draw_preview_item() that does not draw the menu background and item text based on
@@ -1309,6 +1328,12 @@ void ui_button_group_add_but(uiBlock *block, uiBut *but);
void ui_button_group_replace_but_ptr(uiBlock *block, const void *old_but_ptr, uiBut *new_but);
void ui_block_free_button_groups(uiBlock *block);
+/* interface_drag.cc */
+
+void ui_but_drag_free(uiBut *but);
+bool ui_but_drag_is_draggable(const uiBut *but);
+void ui_but_drag_start(struct bContext *C, uiBut *but);
+
/* interface_align.c */
bool ui_but_can_align(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -1347,11 +1372,13 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but);
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
+bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
/**
* Can we mouse over the button or is it hidden/disabled/layout.
* \note ctrl is kind of a hack currently,
* so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed.
*/
+bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip);
bool ui_but_is_interactive(const uiBut *but, bool labeledit) ATTR_WARN_UNUSED_RESULT;
bool ui_but_is_popover_once_compat(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
bool ui_but_has_array_value(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -1377,6 +1404,8 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2]
uiBut *ui_list_row_find_from_index(const struct ARegion *region,
int index,
uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2])
+ ATTR_NONNULL(1, 2);
uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2])
ATTR_NONNULL(1, 2);
uiBut *ui_tree_row_find_active(const struct ARegion *region);
@@ -1388,6 +1417,7 @@ typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
const int xy[2],
bool labeledit,
+ bool for_tooltip,
const uiButFindPollFn find_poll,
const void *find_custom_data)
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
@@ -1513,8 +1543,10 @@ void ui_interface_tag_script_reload_queries(void);
/* interface_view.cc */
void ui_block_free_views(struct uiBlock *block);
-uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
- const uiTreeViewHandle *new_view);
+uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block,
+ const uiTreeViewHandle *new_view);
+uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
+ const uiBlock *new_block, const uiGridViewHandle *new_view_handle);
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const uiTreeViewItemHandle *new_item_handle);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index c1bb2ed6d18..3465373c85d 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -961,11 +961,17 @@ static void ui_item_enum_expand_tabs(uiLayout *layout,
static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_v))
{
uiBut *but = but_v;
+ BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT);
+ const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
- RNA_int_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING);
- RNA_int_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING);
- RNA_int_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING);
- RNA_int_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING);
+ RNA_int_set(
+ &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING);
+ RNA_int_set(
+ &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING);
+ RNA_int_set(
+ &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING);
+ RNA_int_set(
+ &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING);
}
/**
@@ -1101,7 +1107,7 @@ static uiBut *ui_item_with_label(uiLayout *layout,
NULL);
UI_but_func_set(but, ui_keymap_but_cb, but, NULL);
if (flag & UI_ITEM_R_IMMEDIATE) {
- UI_but_flag_enable(but, UI_BUT_IMMEDIATE);
+ UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
}
}
else {
@@ -2332,7 +2338,14 @@ void uiItemFullR(uiLayout *layout,
/* property with separate label */
else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) {
but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
- but = ui_but_add_search(but, ptr, prop, NULL, NULL, false);
+ bool results_are_suggestions = false;
+ if (type == PROP_STRING) {
+ const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(prop);
+ if (search_flag & PROP_STRING_SEARCH_SUGGESTION) {
+ results_are_suggestions = true;
+ }
+ }
+ but = ui_but_add_search(but, ptr, prop, NULL, NULL, results_are_suggestions);
if (layout->redalert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
@@ -2705,11 +2718,16 @@ uiBut *ui_but_add_search(uiBut *but,
PropertyRNA *prop,
PointerRNA *searchptr,
PropertyRNA *searchprop,
- bool results_are_suggestions)
+ const bool results_are_suggestions)
{
/* for ID's we do automatic lookup */
+ bool has_search_fn = false;
+
PointerRNA sptr;
if (!searchprop) {
+ if (RNA_property_type(prop) == PROP_STRING) {
+ has_search_fn = (RNA_property_string_search_flag(prop) != 0);
+ }
if (RNA_property_type(prop) == PROP_POINTER) {
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
search_id_collection(ptype, &sptr, &searchprop);
@@ -2718,14 +2736,18 @@ uiBut *ui_but_add_search(uiBut *but,
}
/* turn button into search button */
- if (searchprop) {
+ if (has_search_fn || searchprop) {
uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
uiButSearch *search_but;
but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU);
search_but = (uiButSearch *)but;
- search_but->rnasearchpoin = *searchptr;
- search_but->rnasearchprop = searchprop;
+
+ if (searchptr) {
+ search_but->rnasearchpoin = *searchptr;
+ search_but->rnasearchprop = searchprop;
+ }
+
but->hardmax = MAX2(but->hardmax, 256.0f);
but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
if (RNA_property_is_unlink(prop)) {
@@ -2734,8 +2756,17 @@ uiBut *ui_but_add_search(uiBut *but,
coll_search->target_ptr = *ptr;
coll_search->target_prop = prop;
- coll_search->search_ptr = *searchptr;
- coll_search->search_prop = searchprop;
+
+ if (searchptr) {
+ coll_search->search_ptr = *searchptr;
+ coll_search->search_prop = searchprop;
+ }
+ else {
+ /* Rely on `has_search_fn`. */
+ coll_search->search_ptr = PointerRNA_NULL;
+ coll_search->search_prop = NULL;
+ }
+
coll_search->search_but = but;
coll_search->butstore_block = but->block;
coll_search->butstore = UI_butstore_create(coll_search->butstore_block);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 5b97a80d513..aafb56119ae 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1167,7 +1167,9 @@ static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
/* identifiers */
ot->name = "Copy to Selected";
ot->idname = "UI_OT_copy_to_selected_button";
- ot->description = "Copy property from this object to selected objects or bones";
+ ot->description =
+ "Copy the property's value from the active item to the same property of all selected items "
+ "if the same property exists";
/* callbacks */
ot->poll = copy_to_selected_button_poll;
@@ -1895,14 +1897,14 @@ static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
if (!gamma) {
- IMB_colormanagement_scene_linear_to_srgb_v3(color);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color, color);
}
RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
RNA_property_update(C, &but->rnapoin, but->rnaprop);
}
else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
if (gamma) {
- IMB_colormanagement_srgb_to_scene_linear_v3(color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(color, color);
}
RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
RNA_property_update(C, &but->rnapoin, but->rnaprop);
@@ -1940,6 +1942,24 @@ static void UI_OT_drop_color(wmOperatorType *ot)
/** \name Drop Name Operator
* \{ */
+static bool drop_name_poll(bContext *C)
+{
+ if (!ED_operator_regionactive(C)) {
+ return false;
+ }
+
+ const uiBut *but = UI_but_active_drop_name_button(C);
+ if (!but) {
+ return false;
+ }
+
+ if (but->flag & UI_BUT_DISABLED) {
+ return false;
+ }
+
+ return true;
+}
+
static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
uiBut *but = UI_but_active_drop_name_button(C);
@@ -1959,7 +1979,7 @@ static void UI_OT_drop_name(wmOperatorType *ot)
ot->idname = "UI_OT_drop_name";
ot->description = "Drop name to button";
- ot->poll = ED_operator_regionactive;
+ ot->poll = drop_name_poll;
ot->invoke = drop_name_invoke;
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -2142,11 +2162,8 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- if (!RNA_struct_property_is_set(op->ptr, "session_uuid")) {
- return OPERATOR_CANCELLED;
- }
- const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid");
- Material *ma = (Material *)BKE_libblock_find_session_uuid(bmain, ID_MA, session_uuid);
+ Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid(
+ bmain, op->ptr, ID_MA);
if (ma == NULL) {
return OPERATOR_CANCELLED;
}
@@ -2184,16 +2201,7 @@ static void UI_OT_drop_material(wmOperatorType *ot)
ot->exec = ui_drop_material_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- PropertyRNA *prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the data-block to assign",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ WM_operator_properties_id_lookup(ot, false);
}
/** \} */
diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc
index 337b2852d57..71cf60985df 100644
--- a/source/blender/editors/interface/interface_query.cc
+++ b/source/blender/editors/interface/interface_query.cc
@@ -58,12 +58,28 @@ bool ui_but_is_toggle(const uiBut *but)
UI_BTYPE_TREEROW);
}
-bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
+bool ui_but_is_view_item(const uiBut *but)
+{
+ return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE);
+}
+
+bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip)
{
/* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */
- if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) {
- return false;
+ if (but->type == UI_BTYPE_LABEL) {
+ if (for_tooltip) {
+ /* It's important labels are considered interactive for the purpose of showing tooltip. */
+ if (!ui_but_drag_is_draggable(but) && but->tip_func == nullptr) {
+ return false;
+ }
+ }
+ else {
+ if (!ui_but_drag_is_draggable(but)) {
+ return false;
+ }
+ }
}
+
if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) {
return false;
}
@@ -84,6 +100,11 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
return true;
}
+bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
+{
+ return ui_but_is_interactive_ex(but, labeledit, false);
+}
+
bool UI_but_is_utf8(const uiBut *but)
{
if (but->rnaprop) {
@@ -266,6 +287,7 @@ static uiBut *ui_but_find(const ARegion *region,
uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
const int xy[2],
const bool labeledit,
+ const bool for_tooltip,
const uiButFindPollFn find_poll,
const void *find_custom_data)
{
@@ -282,7 +304,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
if (find_poll && find_poll(but, find_custom_data) == false) {
continue;
}
- if (ui_but_is_interactive(but, labeledit)) {
+ if (ui_but_is_interactive_ex(but, labeledit, for_tooltip)) {
if (but->pie_dir != UI_RADIAL_NONE) {
if (ui_but_isect_pie_seg(block, but)) {
butover = but;
@@ -310,7 +332,8 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event)
{
- return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, nullptr, nullptr);
+ return ui_but_find_mouse_over_ex(
+ region, event->xy, event->modifier & KM_CTRL, false, nullptr, nullptr);
}
uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
@@ -414,7 +437,7 @@ static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata))
uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int xy[2])
{
- return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_listrow, nullptr);
+ return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_listrow, nullptr);
}
struct ListRowFindIndexData {
@@ -444,9 +467,19 @@ static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata))
return but->type == UI_BTYPE_TREEROW;
}
+static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata))
+{
+ return ui_but_is_view_item(but);
+}
+
+uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2])
+{
+ return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr);
+}
+
uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2])
{
- return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_treerow, nullptr);
+ return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr);
}
static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata)
diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc
index ab0a6039cdc..db1e5e653de 100644
--- a/source/blender/editors/interface/interface_region_color_picker.cc
+++ b/source/blender/editors/interface/interface_region_color_picker.cc
@@ -116,7 +116,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3])
* assuming it is more perceptually linear than the scene linear
* space for intuitive color picking. */
if (!ui_but_is_color_gamma(but)) {
- IMB_colormanagement_scene_linear_to_color_picking_v3(rgb);
+ IMB_colormanagement_scene_linear_to_color_picking_v3(rgb, rgb);
ui_color_picker_rgb_round(rgb);
}
}
@@ -124,7 +124,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3])
void ui_perceptual_to_scene_linear_space(uiBut *but, float rgb[3])
{
if (!ui_but_is_color_gamma(but)) {
- IMB_colormanagement_color_picking_to_scene_linear_v3(rgb);
+ IMB_colormanagement_color_picking_to_scene_linear_v3(rgb, rgb);
ui_color_picker_rgb_round(rgb);
}
}
@@ -208,7 +208,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but,
* (coming from other applications, web, etc) */
copy_v3_v3(rgb_hex, rgb_scene_linear);
if (from_but && !ui_but_is_color_gamma(from_but)) {
- IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
+ IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex);
ui_color_picker_rgb_round(rgb_hex);
}
@@ -291,7 +291,7 @@ static void ui_colorpicker_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexc
/* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
if (!ui_but_is_color_gamma(but)) {
- IMB_colormanagement_srgb_to_scene_linear_v3(rgb);
+ IMB_colormanagement_srgb_to_scene_linear_v3(rgb, rgb);
ui_color_picker_rgb_round(rgb);
}
@@ -777,7 +777,7 @@ static void ui_block_colorpicker(uiBlock *block,
copy_v3_v3(rgb_hex, rgba_scene_linear);
if (!ui_but_is_color_gamma(from_but)) {
- IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
+ IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex);
ui_color_picker_rgb_round(rgb_hex);
}
diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc
index e843a275d08..a22f7218203 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.cc
+++ b/source/blender/editors/interface/interface_region_menu_popup.cc
@@ -129,6 +129,12 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but)
/* get */
LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+ /* Prevent labels (typically headings), from being returned in the case the text
+ * happens to matches one of the menu items.
+ * Skip separators too as checking them is redundant. */
+ if (ELEM(but_iter->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
+ continue;
+ }
if (mem[hash_mod] ==
ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) {
return but_iter;
diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc
index bc497e2647c..81c0c29d09a 100644
--- a/source/blender/editors/interface/interface_region_search.cc
+++ b/source/blender/editors/interface/interface_region_search.cc
@@ -58,7 +58,7 @@ struct uiSearchItems {
char **names;
void **pointers;
int *icons;
- int *states;
+ int *but_flags;
uint8_t *name_prefix_offsets;
/** Is there any item with an icon? */
@@ -94,7 +94,7 @@ bool UI_search_item_add(uiSearchItems *items,
const char *name,
void *poin,
int iconid,
- int state,
+ const int but_flag,
const uint8_t name_prefix_offset)
{
/* hijack for autocomplete */
@@ -148,10 +148,10 @@ bool UI_search_item_add(uiSearchItems *items,
/* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set
* which will cause problems, add others as needed. */
- BLI_assert(
- (state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0);
- if (items->states) {
- items->states[items->totitem] = state;
+ BLI_assert((but_flag &
+ ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0);
+ if (items->but_flags) {
+ items->but_flags[items->totitem] = but_flag;
}
items->totitem++;
@@ -451,15 +451,16 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re
/* reset vars */
data->items.totitem = 0;
data->items.more = 0;
- if (reset == false) {
+ if (!reset) {
data->items.offset_i = data->items.offset;
}
else {
data->items.offset_i = data->items.offset = 0;
data->active = -1;
- /* handle active */
- if (search_but->items_update_fn && search_but->item_active) {
+ /* On init, find and center active item. */
+ const bool is_first_search = !search_but->but.changed;
+ if (is_first_search && search_but->items_update_fn && search_but->item_active) {
data->items.active = search_but->item_active;
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
data->items.active = nullptr;
@@ -556,7 +557,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
if (data->preview) {
/* draw items */
for (int a = 0; a < data->items.totitem; a++) {
- const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
+ const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a];
/* ensure icon is up-to-date */
ui_icon_ensure_deferred(C, data->items.icons[a], data->preview);
@@ -568,7 +569,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
&rect,
data->items.names[a],
data->items.icons[a],
- state,
+ but_flag,
UI_STYLE_TEXT_LEFT);
}
@@ -590,7 +591,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0;
/* draw items */
for (int a = 0; a < data->items.totitem; a++) {
- const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
+ const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a];
char *name = data->items.names[a];
int icon = data->items.icons[a];
char *name_sep_test = nullptr;
@@ -600,7 +601,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT;
}
/* Only set for displaying additional hint (e.g. library name of a linked data-block). */
- else if (state & UI_BUT_HAS_SEP_CHAR) {
+ else if (but_flag & UI_BUT_HAS_SEP_CHAR) {
separator_type = UI_MENU_ITEM_SEPARATOR_HINT;
}
@@ -615,7 +616,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
}
/* Simple menu item. */
- ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, nullptr);
+ ui_draw_menu_item(&data->fstyle, &rect, name, icon, but_flag, separator_type, nullptr);
}
else {
/* Split menu item, faded text before the separator. */
@@ -633,7 +634,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
&rect,
name,
0,
- state | UI_BUT_INACTIVE,
+ but_flag | UI_BUT_INACTIVE,
UI_MENU_ITEM_SEPARATOR_NONE,
&name_width);
*name_sep = name_sep_prev;
@@ -646,7 +647,8 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region)
}
/* The previous menu item draws the active selection. */
- ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, nullptr);
+ ui_draw_menu_item(
+ &data->fstyle, &rect, name_sep, icon, but_flag, separator_type, nullptr);
}
}
/* indicate more */
@@ -677,7 +679,7 @@ static void ui_searchbox_region_free_fn(ARegion *region)
MEM_freeN(data->items.names);
MEM_freeN(data->items.pointers);
MEM_freeN(data->items.icons);
- MEM_freeN(data->items.states);
+ MEM_freeN(data->items.but_flags);
if (data->items.name_prefix_offsets != nullptr) {
MEM_freeN(data->items.name_prefix_offsets);
@@ -847,7 +849,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C,
data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__);
data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__);
data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__);
- data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__);
+ data->items.but_flags = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__);
data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */
for (int i = 0; i < data->items.maxitem; i++) {
data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__);
@@ -913,7 +915,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
/* widget itself */
/* NOTE: i18n messages extracting tool does the same, please keep it in sync. */
{
- const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a];
+ const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a];
wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]);
char text_pre[128];
@@ -936,14 +938,14 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
&rect_pre,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre),
data->items.icons[a],
- state,
+ but_flag,
UI_MENU_ITEM_SEPARATOR_NONE,
nullptr);
ui_draw_menu_item(&data->fstyle,
&rect_post,
data->items.names[a],
0,
- state,
+ but_flag,
data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT :
UI_MENU_ITEM_SEPARATOR_NONE,
nullptr);
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index c7ebecb178b..82d4405e1b5 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -800,7 +800,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
.style = UI_TIP_STYLE_HEADER,
.color_id = UI_TIP_LC_NORMAL,
});
- field->text = BLI_sprintfN("%s", but_label.strinfo);
+ field->text = BLI_strdup(but_label.strinfo);
}
/* Tip */
diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc
index 0156a943015..904765f6dc4 100644
--- a/source/blender/editors/interface/interface_style.cc
+++ b/source/blender/editors/interface/interface_style.cc
@@ -376,25 +376,14 @@ void uiStyleInit(void)
{
const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first);
- /* recover from uninitialized dpi */
+ /* Recover from uninitialized DPI. */
if (U.dpi == 0) {
U.dpi = 72;
}
CLAMP(U.dpi, 48, 144);
- LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
- BLF_unload_id(font->blf_id);
- }
-
- if (blf_mono_font != -1) {
- BLF_unload_id(blf_mono_font);
- blf_mono_font = -1;
- }
-
- if (blf_mono_font_render != -1) {
- BLF_unload_id(blf_mono_font_render);
- blf_mono_font_render = -1;
- }
+ /* Needed so that custom fonts are always first. */
+ BLF_unload_all();
uiFont *font_first = static_cast<uiFont *>(U.uifonts.first);
@@ -498,6 +487,9 @@ void uiStyleInit(void)
const bool unique = true;
blf_mono_font_render = BLF_load_mono_default(unique);
}
+
+ /* Load the fallback fonts last. */
+ BLF_load_font_stack();
}
void UI_fontstyle_set(const uiFontStyle *fs)
diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc
index 384e9d1794e..4e587bd5338 100644
--- a/source/blender/editors/interface/interface_template_attribute_search.cc
+++ b/source/blender/editors/interface/interface_template_attribute_search.cc
@@ -24,14 +24,14 @@ using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo;
namespace blender::ui {
-static StringRef attribute_data_type_string(const CustomDataType type)
+static StringRef attribute_data_type_string(const eCustomDataType type)
{
const char *name = nullptr;
RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name);
return StringRef(IFACE_(name));
}
-static StringRef attribute_domain_string(const AttributeDomain domain)
+static StringRef attribute_domain_string(const eAttrDomain domain)
{
const char *name = nullptr;
RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name);
@@ -40,8 +40,8 @@ static StringRef attribute_domain_string(const AttributeDomain domain)
static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item)
{
- const StringRef data_type_name = attribute_data_type_string(item.data_type);
- const StringRef domain_name = attribute_domain_string(item.domain);
+ const StringRef data_type_name = attribute_data_type_string(*item.data_type);
+ const StringRef domain_name = attribute_domain_string(*item.domain);
std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR +
data_type_name;
@@ -91,6 +91,9 @@ void attribute_search_add_items(StringRefNull str,
if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) {
continue;
}
+ if (!bke::allow_procedural_attribute_access(item->name)) {
+ continue;
+ }
BLI_string_search_add(search, item->name.c_str(), (void *)item, 0);
}
diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc
index 68a699c652a..e0b6bbb34c4 100644
--- a/source/blender/editors/interface/interface_template_list.cc
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -945,13 +945,8 @@ static void ui_template_list_layout_draw(bContext *C,
const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0;
- /* TODO ED_fileselect_init_layout(). Share somehow? */
- float size_x = (96.0f / 20.0f) * UI_UNIT_X;
- float size_y = (96.0f / 20.0f) * UI_UNIT_Y;
-
- if (!show_names) {
- size_y -= UI_UNIT_Y;
- }
+ const int size_x = UI_preview_tile_size_x();
+ const int size_y = show_names ? UI_preview_tile_size_y() : UI_preview_tile_size_y_no_label();
const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1);
uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true);
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 4e6437e043a..05ae5299e58 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -742,7 +742,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
object_active->id.tag |= LIB_TAG_DOIT;
}
BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override);
+ bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false);
}
else if (object_active != NULL && !ID_IS_LINKED(object_active) &&
&object_active->instance_collection->id == id) {
@@ -754,7 +754,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
id,
&object_active->id,
&object_active->id,
- &id_override);
+ &id_override,
+ false);
}
break;
case ID_OB:
@@ -765,7 +766,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
object_active->id.tag |= LIB_TAG_DOIT;
}
BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override);
+ bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false);
}
break;
case ID_ME:
@@ -787,13 +788,20 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
if (object_active != NULL) {
object_active->id.tag |= LIB_TAG_DOIT;
}
- BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override);
+ BKE_lib_override_library_create(bmain,
+ scene,
+ view_layer,
+ NULL,
+ id,
+ &collection_active->id,
+ NULL,
+ &id_override,
+ false);
}
else {
object_active->id.tag |= LIB_TAG_DOIT;
BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override);
+ bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false);
}
}
break;
@@ -812,8 +820,11 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
*r_undo_push_label = "Make Library Override Hierarchy";
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- DEG_relations_tag_update(bmain);
+ /* Given `idptr` is re-assigned to owner property by caller to ensure proper updates etc. Here
+ * we also use it to ensure remapping of the owner property from the linked data to the newly
+ * created liboverride (note that in theory this remapping has already been done by code
+ * above). */
+ RNA_id_pointer_create(id_override, idptr);
}
}
@@ -1525,8 +1536,8 @@ static void template_ID_tabs(const bContext *C,
0.0f,
"");
UI_but_funcN_set(&tab->but, template_ID_set_property_exec_fn, MEM_dupallocN(template), id);
+ UI_but_drag_set_id(&tab->but, id);
tab->but.custom_data = (void *)id;
- tab->but.dragpoin = id;
tab->menu = mt;
UI_but_drawflag_enable(&tab->but, but_align);
diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc
index 993ccdf92f7..b7ca2d9aa11 100644
--- a/source/blender/editors/interface/interface_utils.cc
+++ b/source/blender/editors/interface/interface_utils.cc
@@ -24,6 +24,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_report.h"
@@ -516,71 +517,136 @@ void ui_rna_collection_search_update_fn(
StringSearch *search = skip_filter ? nullptr : BLI_string_search_new();
- /* build a temporary list of relevant items first */
- int item_index = 0;
- RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
+ if (data->search_prop != nullptr) {
+ /* build a temporary list of relevant items first */
+ int item_index = 0;
+ RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
- if (flag & PROP_ID_SELF_CHECK) {
- if (itemptr.data == data->target_ptr.owner_id) {
- continue;
+ if (flag & PROP_ID_SELF_CHECK) {
+ if (itemptr.data == data->target_ptr.owner_id) {
+ continue;
+ }
}
- }
- /* use filter */
- if (is_ptr_target) {
- if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
- continue;
+ /* use filter */
+ if (is_ptr_target) {
+ if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
+ continue;
+ }
}
- }
- int name_prefix_offset = 0;
- int iconid = ICON_NONE;
- bool has_sep_char = false;
- const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
+ int name_prefix_offset = 0;
+ int iconid = ICON_NONE;
+ bool has_sep_char = false;
+ const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
- if (is_id) {
- iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
- if (!ELEM(iconid, 0, ICON_BLANK1)) {
- has_id_icon = true;
- }
+ if (is_id) {
+ iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
+ if (!ELEM(iconid, 0, ICON_BLANK1)) {
+ has_id_icon = true;
+ }
- if (requires_exact_data_name) {
- name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
+ if (requires_exact_data_name) {
+ name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
+ }
+ else {
+ const ID *id = static_cast<ID *>(itemptr.data);
+ BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
+ BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
+ "Name string buffer should be big enough to hold full UI ID name");
+ name = name_buf;
+ has_sep_char = ID_IS_LINKED(id);
+ }
}
else {
- const ID *id = static_cast<ID *>(itemptr.data);
- BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
- BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
- "Name string buffer should be big enough to hold full UI ID name");
- name = name_buf;
- has_sep_char = ID_IS_LINKED(id);
+ name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
}
- }
- else {
- name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
- }
- if (name) {
- CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__);
- cis->data = itemptr.data;
- cis->name = BLI_strdup(name);
- cis->index = item_index;
- cis->iconid = iconid;
- cis->is_id = is_id;
- cis->name_prefix_offset = name_prefix_offset;
- cis->has_sep_char = has_sep_char;
- if (!skip_filter) {
- BLI_string_search_add(search, name, cis, 0);
+ if (name) {
+ CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__);
+ cis->data = itemptr.data;
+ cis->name = BLI_strdup(name);
+ cis->index = item_index;
+ cis->iconid = iconid;
+ cis->is_id = is_id;
+ cis->name_prefix_offset = name_prefix_offset;
+ cis->has_sep_char = has_sep_char;
+ if (!skip_filter) {
+ BLI_string_search_add(search, name, cis, 0);
+ }
+ BLI_addtail(items_list, cis);
+ if (name != name_buf) {
+ MEM_freeN(name);
+ }
}
- BLI_addtail(items_list, cis);
- if (name != name_buf) {
- MEM_freeN(name);
+
+ item_index++;
+ }
+ RNA_PROP_END;
+ }
+ else {
+ BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING);
+ const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(
+ data->target_prop);
+ BLI_assert(search_flag & PROP_STRING_SEARCH_SUPPORTED);
+
+ struct SearchVisitUserData {
+ StringSearch *search;
+ bool skip_filter;
+ int item_index;
+ ListBase *items_list;
+ const char *func_id;
+ } user_data = {nullptr};
+
+ user_data.search = search;
+ user_data.skip_filter = skip_filter;
+ user_data.items_list = items_list;
+ user_data.func_id = __func__;
+
+ RNA_property_string_search(
+ C,
+ &data->target_ptr,
+ data->target_prop,
+ str,
+ [](void *user_data, const StringPropertySearchVisitParams *visit_params) {
+ const bool show_extra_info = (G.debug_value == 102);
+
+ SearchVisitUserData *search_data = (struct SearchVisitUserData *)user_data;
+ CollItemSearch *cis = MEM_cnew<CollItemSearch>(search_data->func_id);
+ cis->data = nullptr;
+ if (visit_params->info && show_extra_info) {
+ cis->name = BLI_sprintfN(
+ "%s" UI_SEP_CHAR_S "%s", visit_params->text, visit_params->info);
+ }
+ else {
+ cis->name = BLI_strdup(visit_params->text);
+ }
+ cis->index = search_data->item_index;
+ cis->iconid = ICON_NONE;
+ cis->is_id = false;
+ cis->name_prefix_offset = 0;
+ cis->has_sep_char = visit_params->info != nullptr;
+ if (!search_data->skip_filter) {
+ BLI_string_search_add(search_data->search, visit_params->text, cis, 0);
+ }
+ BLI_addtail(search_data->items_list, cis);
+ search_data->item_index++;
+ },
+ (void *)&user_data);
+
+ if (search_flag & PROP_STRING_SEARCH_SORT) {
+ BLI_listbase_sort(items_list, [](const void *a_, const void *b_) -> int {
+ const CollItemSearch *cis_a = (const CollItemSearch *)a_;
+ const CollItemSearch *cis_b = (const CollItemSearch *)b_;
+ return BLI_strcasecmp_natural(cis_a->name, cis_b->name);
+ });
+ int i = 0;
+ LISTBASE_FOREACH (CollItemSearch *, cis, items_list) {
+ cis->index = i;
+ i++;
}
}
-
- item_index++;
}
- RNA_PROP_END;
if (skip_filter) {
LISTBASE_FOREACH (CollItemSearch *, cis, items_list) {
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
index 85e1dbe73a5..699ac0c2b53 100644
--- a/source/blender/editors/interface/interface_view.cc
+++ b/source/blender/editors/interface/interface_view.cc
@@ -10,15 +10,22 @@
*/
#include <memory>
+#include <type_traits>
#include <variant>
#include "DNA_screen_types.h"
+#include "BKE_screen.h"
+
#include "BLI_listbase.h"
+#include "ED_screen.h"
+
#include "interface_intern.h"
#include "UI_interface.hh"
+
+#include "UI_grid_view.hh"
#include "UI_tree_view.hh"
using namespace blender;
@@ -30,29 +37,51 @@ using namespace blender::ui;
*/
struct ViewLink : public Link {
using TreeViewPtr = std::unique_ptr<AbstractTreeView>;
+ using GridViewPtr = std::unique_ptr<AbstractGridView>;
std::string idname;
/* NOTE: Can't use std::get() on this until minimum macOS deployment target is 10.14. */
- std::variant<TreeViewPtr> view;
+ std::variant<TreeViewPtr, GridViewPtr> view;
};
+template<class T> constexpr void check_if_valid_view_type()
+{
+ static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>,
+ "Unsupported view type");
+}
+
template<class T> T *get_view_from_link(ViewLink &link)
{
auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view);
return t_uptr ? t_uptr->get() : nullptr;
}
-AbstractTreeView *UI_block_add_view(uiBlock &block,
- StringRef idname,
- std::unique_ptr<AbstractTreeView> tree_view)
+template<class T>
+static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view)
{
+ check_if_valid_view_type<T>();
+
ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
- view_link->view = std::move(tree_view);
+ view_link->view = std::move(view);
view_link->idname = idname;
- return get_view_from_link<AbstractTreeView>(*view_link);
+ return get_view_from_link<T>(*view_link);
+}
+
+AbstractGridView *UI_block_add_view(uiBlock &block,
+ StringRef idname,
+ std::unique_ptr<AbstractGridView> tree_view)
+{
+ return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(tree_view));
+}
+
+AbstractTreeView *UI_block_add_view(uiBlock &block,
+ StringRef idname,
+ std::unique_ptr<AbstractTreeView> tree_view)
+{
+ return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
}
void ui_block_free_views(uiBlock *block)
@@ -62,6 +91,26 @@ void ui_block_free_views(uiBlock *block)
}
}
+void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
+{
+ ARegion *region = listener_params->region;
+
+ LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
+ if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) {
+ if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view),
+ listener_params->notifier)) {
+ ED_region_tag_redraw(region);
+ }
+ }
+ else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) {
+ if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view),
+ listener_params->notifier)) {
+ ED_region_tag_redraw(region);
+ }
+ }
+ }
+}
+
uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2])
{
uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy);
@@ -82,11 +131,13 @@ uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
return tree_row_but->tree_item;
}
-static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
+template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view)
{
+ check_if_valid_view_type<T>();
+
/* First get the idname the of the view we're looking for. */
LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
- if (get_view_from_link<AbstractTreeView>(*view_link) == &view) {
+ if (get_view_from_link<T>(*view_link) == &view) {
return view_link->idname;
}
}
@@ -94,9 +145,11 @@ static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractT
return {};
}
-static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block,
- const AbstractTreeView &new_view)
+template<class T>
+static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view)
{
+ check_if_valid_view_type<T>();
+
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
return nullptr;
@@ -109,15 +162,15 @@ static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock
LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
if (old_view_link->idname == idname) {
- return get_view_from_link<AbstractTreeView>(*old_view_link);
+ return get_view_from_link<T>(*old_view_link);
}
}
return nullptr;
}
-uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
- const uiTreeViewHandle *new_view_handle)
+uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(
+ const uiBlock *new_block, const uiTreeViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle);
@@ -126,6 +179,16 @@ uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_bl
return reinterpret_cast<uiTreeViewHandle *>(old_view);
}
+uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
+ const uiBlock *new_block, const uiGridViewHandle *new_view_handle)
+{
+ BLI_assert(new_block && new_view_handle);
+ const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle);
+
+ AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view);
+ return reinterpret_cast<uiGridViewHandle *>(old_view);
+}
+
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const uiTreeViewItemHandle *new_item_handle)
{
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 98ecf91adbc..e2df2d77817 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -105,24 +105,25 @@ typedef enum {
UI_WTYPE_PROGRESSBAR,
UI_WTYPE_NODESOCKET,
UI_WTYPE_TREEROW,
+ UI_WTYPE_GRID_TILE,
} uiWidgetTypeEnum;
-/* Button state argument shares bits with 'uiBut.flag'.
- * reuse flags that aren't needed for drawing to avoid collision. */
-enum {
- /* Show that holding the button opens a menu. */
- UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY,
- UI_STATE_TEXT_INPUT = UI_BUT_UNDO,
- UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR,
- UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE,
- UI_STATE_TEXT_BEFORE_WIDGET = UI_BUT_IMMEDIATE,
-
- UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT |
- UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET),
-};
-/* Prevent accidental use. */
-#define UI_BUT_UPDATE_DELAY ((void)0)
-#define UI_BUT_UNDO ((void)0)
+/**
+ * The button's state information adapted for drawing. Use #STATE_INFO_NULL for empty state.
+ */
+typedef struct {
+ /** Copy of #uiBut.flag (possibly with overrides for drawing). */
+ int but_flag;
+ /** Copy of #uiBut.drawflag (possibly with overrides for drawing). */
+ int but_drawflag;
+
+ /** Show that holding the button opens a menu. */
+ bool has_hold_action : 1;
+ /** The button is in text input mode. */
+ bool is_text_input : 1;
+} uiWidgetStateInfo;
+
+static const uiWidgetStateInfo STATE_INFO_NULL = {0};
/** \} */
@@ -256,10 +257,21 @@ typedef struct uiWidgetType {
/* converted colors for state */
uiWidgetColors wcol;
- void (*state)(struct uiWidgetType *, int state, int drawflag, eUIEmbossType emboss);
- void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom);
- void (*custom)(
- uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom);
+ void (*state)(struct uiWidgetType *, const uiWidgetStateInfo *state, eUIEmbossType emboss)
+ ATTR_NONNULL();
+ void (*draw)(uiWidgetColors *,
+ rcti *,
+ const uiWidgetStateInfo *,
+ int roundboxalign,
+ const float zoom) ATTR_NONNULL();
+ void (*custom)(uiBut *,
+ uiWidgetColors *,
+ rcti *,
+ const uiWidgetStateInfo *,
+ int roundboxalign,
+ const float zoom) ATTR_NONNULL();
+ void (*draw_block)(
+ uiWidgetColors *, rcti *, int block_flag, int roundboxalign, const float zoom);
void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *);
} uiWidgetType;
@@ -1289,16 +1301,16 @@ static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
#define PREVIEW_PAD 4
-static float widget_alpha_factor(const int state)
+static float widget_alpha_factor(const uiWidgetStateInfo *state)
{
- if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) {
- if (state & UI_SEARCH_FILTER_NO_MATCH) {
+ if (state->but_flag & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) {
+ if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) {
return 0.25f;
}
return 0.5f;
}
- if (state & UI_SEARCH_FILTER_NO_MATCH) {
+ if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) {
return 0.5f;
}
@@ -1367,7 +1379,10 @@ static void widget_draw_icon(
}
}
else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) {
- alpha *= widget_alpha_factor(but->flag);
+ uiWidgetStateInfo state = {0};
+ state.but_flag = but->flag;
+ state.but_drawflag = but->drawflag;
+ alpha *= widget_alpha_factor(&state);
}
GPU_blend(GPU_BLEND_ALPHA);
@@ -1405,7 +1420,7 @@ static void widget_draw_icon(
const bool has_theme = UI_icon_get_theme_color(icon, color);
/* to indicate draggable */
- if (but->dragpoin && (but->flag & UI_ACTIVE)) {
+ if (ui_but_drag_is_draggable(but) && (but->flag & UI_ACTIVE)) {
UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme);
}
else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW))) {
@@ -2446,7 +2461,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
* \{ */
/* put all widget colors on half alpha, use local storage */
-static void ui_widget_color_disabled(uiWidgetType *wt, const int state)
+static void ui_widget_color_disabled(uiWidgetType *wt, const uiWidgetStateInfo *state)
{
static uiWidgetColors wcol_theme_s;
@@ -2473,8 +2488,7 @@ static void widget_active_color(uiWidgetColors *wcol)
}
static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state,
- int state,
- int drawflag,
+ const uiWidgetStateInfo *state,
const eUIEmbossType emboss)
{
/* Explicitly require #UI_EMBOSS_NONE_OR_STATUS for color blending with no emboss. */
@@ -2482,44 +2496,44 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco
return NULL;
}
- if (drawflag & UI_BUT_ANIMATED_CHANGED) {
+ if (state->but_drawflag & UI_BUT_ANIMATED_CHANGED) {
return wcol_state->inner_changed_sel;
}
- if (state & UI_BUT_ANIMATED_KEY) {
+ if (state->but_flag & UI_BUT_ANIMATED_KEY) {
return wcol_state->inner_key_sel;
}
- if (state & UI_BUT_ANIMATED) {
+ if (state->but_flag & UI_BUT_ANIMATED) {
return wcol_state->inner_anim_sel;
}
- if (state & UI_BUT_DRIVEN) {
+ if (state->but_flag & UI_BUT_DRIVEN) {
return wcol_state->inner_driven_sel;
}
- if (state & UI_BUT_OVERRIDDEN) {
+ if (state->but_flag & UI_BUT_OVERRIDDEN) {
return wcol_state->inner_overridden_sel;
}
return NULL;
}
/* copy colors from theme, and set changes in it based on state */
-static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss)
+static void widget_state(uiWidgetType *wt, const uiWidgetStateInfo *state, eUIEmbossType emboss)
{
uiWidgetStateColors *wcol_state = wt->wcol_state;
- if (state & UI_BUT_LIST_ITEM) {
+ if (state->but_flag & UI_BUT_LIST_ITEM) {
/* Override default widget's colors. */
bTheme *btheme = UI_GetTheme();
wt->wcol_theme = &btheme->tui.wcol_list_item;
- if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
+ if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
ui_widget_color_disabled(wt, state);
}
}
wt->wcol = *(wt->wcol_theme);
- const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss);
+ const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss);
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
if (color_blend != NULL) {
color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend);
@@ -2527,12 +2541,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp
copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
}
}
else {
- if (state & UI_BUT_ACTIVE_DEFAULT) {
+ if (state->but_flag & UI_BUT_ACTIVE_DEFAULT) {
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel);
}
@@ -2544,12 +2558,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp
* even if UI_SELECT. But currently this causes some flickering
* as buttons can be created and updated without respect to mouse
* position and so can draw without UI_ACTIVE set. See D6503. */
- if (state & UI_ACTIVE) {
+ if (state->but_flag & UI_ACTIVE) {
widget_active_color(&wt->wcol);
}
}
- if (state & UI_BUT_REDALERT) {
+ if (state->but_flag & UI_BUT_REDALERT) {
const uchar red[4] = {255, 0, 0};
if (wt->draw && emboss != UI_EMBOSS_NONE) {
color_blend_v3_v3(wt->wcol.inner, red, 0.4f);
@@ -2559,14 +2573,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp
}
}
- if (state & UI_BUT_DRAG_MULTI) {
+ if (state->but_flag & UI_BUT_DRAG_MULTI) {
/* the button isn't SELECT but we're editing this so draw with sel color */
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f);
}
- if (state & UI_BUT_NODE_ACTIVE) {
+ if (state->but_flag & UI_BUT_NODE_ACTIVE) {
const uchar blue[4] = {86, 128, 194};
color_blend_v3_v3(wt->wcol.inner, blue, 0.3f);
}
@@ -2600,14 +2614,16 @@ static float widget_radius_from_rcti(const rcti *rect, const uiWidgetColors *wco
* \{ */
/* sliders use special hack which sets 'item' as inner when drawing filling */
-static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss)
+static void widget_state_numslider(uiWidgetType *wt,
+ const uiWidgetStateInfo *state,
+ eUIEmbossType emboss)
{
uiWidgetStateColors *wcol_state = wt->wcol_state;
/* call this for option button */
- widget_state(wt, state, drawflag, emboss);
+ widget_state(wt, state, emboss);
- const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss);
+ const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss);
if (color_blend != NULL) {
/* Set the slider 'item' so that it reflects state settings too.
* De-saturate so the color of the slider doesn't conflict with the blend color,
@@ -2617,15 +2633,14 @@ static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eU
color_ensure_contrast_v3(wt->wcol.item, wt->wcol.inner, 30);
}
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
}
}
/* labels use theme colors for text */
static void widget_state_option_menu(uiWidgetType *wt,
- int state,
- int drawflag,
+ const uiWidgetStateInfo *state,
eUIEmbossType emboss)
{
const bTheme *btheme = UI_GetTheme();
@@ -2638,14 +2653,13 @@ static void widget_state_option_menu(uiWidgetType *wt,
copy_v3_v3_uchar(wcol_menu_option.text_sel, btheme->tui.wcol_menu_back.text_sel);
wt->wcol_theme = &wcol_menu_option;
- widget_state(wt, state, drawflag, emboss);
+ widget_state(wt, state, emboss);
wt->wcol_theme = old_wcol;
}
static void widget_state_nothing(uiWidgetType *wt,
- int UNUSED(state),
- int UNUSED(drawflag),
+ const uiWidgetStateInfo *UNUSED(state),
eUIEmbossType UNUSED(emboss))
{
wt->wcol = *(wt->wcol_theme);
@@ -2653,8 +2667,7 @@ static void widget_state_nothing(uiWidgetType *wt,
/* special case, button that calls pulldown */
static void widget_state_pulldown(uiWidgetType *wt,
- int UNUSED(state),
- int UNUSED(drawflag),
+ const uiWidgetStateInfo *UNUSED(state),
eUIEmbossType UNUSED(emboss))
{
wt->wcol = *(wt->wcol_theme);
@@ -2662,14 +2675,13 @@ static void widget_state_pulldown(uiWidgetType *wt,
/* special case, pie menu items */
static void widget_state_pie_menu_item(uiWidgetType *wt,
- int state,
- int UNUSED(drawflag),
+ const uiWidgetStateInfo *state,
eUIEmbossType UNUSED(emboss))
{
wt->wcol = *(wt->wcol_theme);
/* active and disabled (not so common) */
- if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
+ if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) {
color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f);
/* draw the backdrop at low alpha, helps navigating with keys
* when disabled items are active */
@@ -2678,18 +2690,18 @@ static void widget_state_pie_menu_item(uiWidgetType *wt,
}
else {
/* regular active */
- if (state & (UI_SELECT | UI_ACTIVE)) {
+ if (state->but_flag & (UI_SELECT | UI_ACTIVE)) {
copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
}
- else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+ else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
/* regular disabled */
color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f);
}
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
}
- else if (state & UI_ACTIVE) {
+ else if (state->but_flag & UI_ACTIVE) {
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item);
}
}
@@ -2697,14 +2709,13 @@ static void widget_state_pie_menu_item(uiWidgetType *wt,
/* special case, menu items */
static void widget_state_menu_item(uiWidgetType *wt,
- int state,
- int UNUSED(drawflag),
+ const uiWidgetStateInfo *state,
eUIEmbossType UNUSED(emboss))
{
wt->wcol = *(wt->wcol_theme);
/* active and disabled (not so common) */
- if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
+ if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) {
/* draw the backdrop at low alpha, helps navigating with keys
* when disabled items are active */
wt->wcol.text[3] = 128;
@@ -2713,15 +2724,15 @@ static void widget_state_menu_item(uiWidgetType *wt,
}
else {
/* regular active */
- if (state & UI_ACTIVE) {
+ if (state->but_flag & UI_ACTIVE) {
copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
}
- else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+ else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
/* regular disabled */
color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f);
}
- if (state & UI_ACTIVE) {
+ if (state->but_flag & UI_ACTIVE) {
copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
}
}
@@ -2787,7 +2798,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
}
static void widget_menu_back(
- uiWidgetColors *wcol, rcti *rect, int flag, int direction, const float zoom)
+ uiWidgetColors *wcol, rcti *rect, const int block_flag, const int direction, const float zoom)
{
uiWidgetBase wtb;
int roundboxalign = UI_CNR_ALL;
@@ -2795,7 +2806,7 @@ static void widget_menu_back(
widget_init(&wtb);
/* menu is 2nd level or deeper */
- if (flag & UI_BLOCK_POPUP) {
+ if (block_flag & UI_BLOCK_POPUP) {
// rect->ymin -= 4.0;
// rect->ymax += 4.0;
}
@@ -3321,13 +3332,17 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
#define NUM_BUT_PADDING_FACTOR 0.425f
-static void widget_numbut_draw(
- uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss)
+static void widget_numbut_draw(uiWidgetColors *wcol,
+ rcti *rect,
+ const float zoom,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ bool emboss)
{
const float rad = widget_radius_from_zoom(zoom, wcol);
const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f);
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
SWAP(short, wcol->shadetop, wcol->shadedown);
}
@@ -3343,7 +3358,7 @@ static void widget_numbut_draw(
}
/* decoration */
- if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) {
+ if ((state->but_flag & UI_ACTIVE) && !state->is_text_input) {
uiWidgetColors wcol_zone;
uiWidgetBase wtb_zone;
rcti rect_zone;
@@ -3356,7 +3371,7 @@ static void widget_numbut_draw(
wcol_zone = *wcol;
copy_v3_v3_uchar(wcol_zone.item, wcol->text);
- if (state & UI_STATE_ACTIVE_LEFT) {
+ if (state->but_drawflag & UI_BUT_ACTIVE_LEFT) {
widget_active_color(&wcol_zone);
}
@@ -3376,7 +3391,7 @@ static void widget_numbut_draw(
wcol_zone = *wcol;
copy_v3_v3_uchar(wcol_zone.item, wcol->text);
- if (state & UI_STATE_ACTIVE_RIGHT) {
+ if (state->but_drawflag & UI_BUT_ACTIVE_RIGHT) {
widget_active_color(&wcol_zone);
}
@@ -3395,7 +3410,7 @@ static void widget_numbut_draw(
wcol_zone = *wcol;
copy_v3_v3_uchar(wcol_zone.item, wcol->text);
- if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) {
+ if (!(state->but_drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT))) {
widget_active_color(&wcol_zone);
}
@@ -3414,7 +3429,7 @@ static void widget_numbut_draw(
widgetbase_draw(&wtb, wcol);
}
- if (!(state & UI_STATE_TEXT_INPUT)) {
+ if (!state->is_text_input) {
const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect);
rect->xmin += text_padding;
@@ -3422,14 +3437,20 @@ static void widget_numbut_draw(
}
}
-static void widget_numbut(
- uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_numbut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, false);
}
-static void widget_menubut(
- uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom)
+static void widget_menubut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *UNUSED(state),
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -3454,7 +3475,7 @@ static void widget_menubut(
static void widget_menubut_embossn(const uiBut *UNUSED(but),
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign))
{
uiWidgetBase wtb;
@@ -3476,7 +3497,7 @@ static void widget_menubut_embossn(const uiBut *UNUSED(but),
static void widget_numbut_embossn(const uiBut *UNUSED(but),
uiWidgetColors *wcol,
rcti *rect,
- int state,
+ const uiWidgetStateInfo *state,
int roundboxalign,
const float zoom)
{
@@ -3486,7 +3507,6 @@ static void widget_numbut_embossn(const uiBut *UNUSED(but),
void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
{
uiWidgetBase wtb;
- bool outline = false;
widget_init(&wtb);
@@ -3531,11 +3551,6 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s
/* draw */
wtb.draw_emboss = false; /* only emboss once */
- /* exception for progress bar */
- if (state & UI_SCROLL_NO_OUTLINE) {
- SWAP(bool, outline, wtb.draw_outline);
- }
-
round_box_edges(&wtb, UI_CNR_ALL, slider, rad);
if (state & UI_SCROLL_ARROWS) {
@@ -3563,17 +3578,13 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s
}
}
widgetbase_draw(&wtb, wcol);
-
- if (state & UI_SCROLL_NO_OUTLINE) {
- SWAP(bool, outline, wtb.draw_outline);
- }
}
}
static void widget_scroll(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int state,
+ const uiWidgetStateInfo *state,
int UNUSED(roundboxalign),
const float UNUSED(zoom))
{
@@ -3623,19 +3634,13 @@ static void widget_scroll(uiBut *but,
}
}
- if (state & UI_SELECT) {
- state = UI_SCROLL_PRESSED;
- }
- else {
- state = 0;
- }
- UI_draw_widget_scroll(wcol, rect, &rect1, state);
+ UI_draw_widget_scroll(wcol, rect, &rect1, (state->but_flag & UI_SELECT) ? UI_SCROLL_PRESSED : 0);
}
static void widget_progressbar(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int roundboxalign,
const float zoom)
{
@@ -3669,7 +3674,7 @@ static void widget_progressbar(uiBut *but,
static void widget_treerow_exec(uiWidgetColors *wcol,
rcti *rect,
- int state,
+ const uiWidgetStateInfo *state,
int UNUSED(roundboxalign),
int indentation,
const float zoom)
@@ -3682,7 +3687,7 @@ static void widget_treerow_exec(uiWidgetColors *wcol,
const float rad = widget_radius_from_zoom(zoom, wcol);
round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
- if ((state & UI_ACTIVE) || (state & UI_SELECT)) {
+ if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) {
widgetbase_draw(&wtb, wcol);
}
@@ -3690,18 +3695,32 @@ static void widget_treerow_exec(uiWidgetColors *wcol,
BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0);
}
-static void widget_treerow(
- uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_treerow(uiBut *but,
+ uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
uiButTreeRow *tree_row = (uiButTreeRow *)but;
BLI_assert(but->type == UI_BTYPE_TREEROW);
widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom);
}
+static void widget_gridtile(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
+{
+ /* TODO Reuse tree-row drawing. */
+ widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom);
+}
+
static void widget_nodesocket(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float UNUSED(zoom))
{
@@ -3737,8 +3756,12 @@ static void widget_nodesocket(uiBut *but,
copy_v3_v3_uchar(wcol->outline, old_outline);
}
-static void widget_numslider(
- uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_numslider(uiBut *but,
+ uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb, wtb1;
widget_init(&wtb);
@@ -3752,7 +3775,7 @@ static void widget_numslider(
widgetbase_draw(&wtb, wcol);
/* Draw slider part only when not in text editing. */
- if (!(state & UI_STATE_TEXT_INPUT)) {
+ if (!state->is_text_input) {
int roundboxalign_slider = roundboxalign;
uchar outline[3];
@@ -3760,7 +3783,7 @@ static void widget_numslider(
copy_v3_v3_uchar(wcol->outline, wcol->item);
copy_v3_v3_uchar(wcol->inner, wcol->item);
- if (!(state & UI_SELECT)) {
+ if (!(state->but_flag & UI_SELECT)) {
SWAP(short, wcol->shadetop, wcol->shadedown);
}
@@ -3828,7 +3851,7 @@ static void widget_numslider(
copy_v3_v3_uchar(wcol->outline, outline);
- if (!(state & UI_SELECT)) {
+ if (!(state->but_flag & UI_SELECT)) {
SWAP(short, wcol->shadetop, wcol->shadedown);
}
}
@@ -3840,7 +3863,7 @@ static void widget_numslider(
/* Add space at either side of the button so text aligns with number-buttons
* (which have arrow icons). */
- if (!(state & UI_STATE_TEXT_INPUT)) {
+ if (!state->is_text_input) {
const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect);
rect->xmax -= text_padding;
rect->xmin += text_padding;
@@ -3850,8 +3873,12 @@ static void widget_numslider(
/* I think 3 is sufficient border to indicate keyed status */
#define SWATCH_KEYED_BORDER 3
-static void widget_swatch(
- uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_swatch(uiBut *but,
+ uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
BLI_assert(but->type == UI_BTYPE_COLOR);
uiButColor *color_but = (uiButColor *)but;
@@ -3875,9 +3902,9 @@ static void widget_swatch(
ui_but_v3_get(but, col);
- if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN |
- UI_BUT_REDALERT)) ||
- (but->drawflag & UI_BUT_ANIMATED_CHANGED)) {
+ if ((state->but_flag & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN |
+ UI_BUT_OVERRIDDEN | UI_BUT_REDALERT)) ||
+ (state->but_drawflag & UI_BUT_ANIMATED_CHANGED)) {
/* draw based on state - color for keyed etc */
widgetbase_draw(&wtb, wcol);
@@ -3938,7 +3965,7 @@ static void widget_swatch(
static void widget_unitvec(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float zoom)
{
@@ -3946,10 +3973,15 @@ static void widget_unitvec(uiBut *but,
ui_draw_but_UNITVEC(but, wcol, rect, rad);
}
-static void widget_icon_has_anim(
- uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_icon_has_anim(uiBut *but,
+ uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
- if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) &&
+ if (state->but_flag &
+ (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) &&
but->emboss != UI_EMBOSS_NONE) {
uiWidgetBase wtb;
widget_init(&wtb);
@@ -3970,10 +4002,13 @@ static void widget_icon_has_anim(
}
}
-static void widget_textbut(
- uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_textbut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
SWAP(short, wcol->shadetop, wcol->shadedown);
}
@@ -3989,7 +4024,7 @@ static void widget_textbut(
static void widget_preview_tile(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float UNUSED(zoom))
{
@@ -3998,8 +4033,11 @@ static void widget_preview_tile(uiBut *but,
&style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER);
}
-static void widget_menuiconbut(
- uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom)
+static void widget_menuiconbut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *UNUSED(state),
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -4011,17 +4049,20 @@ static void widget_menuiconbut(
widgetbase_draw(&wtb, wcol);
}
-static void widget_pulldownbut(
- uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_pulldownbut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
float back[4];
UI_GetThemeColor4fv(TH_BACK, back);
- if ((state & UI_ACTIVE) || (back[3] < 1.0f)) {
+ if ((state->but_flag & UI_ACTIVE) || (back[3] < 1.0f)) {
uiWidgetBase wtb;
const float rad = widget_radius_from_zoom(zoom, wcol);
- if (state & UI_ACTIVE) {
+ if (state->but_flag & UI_ACTIVE) {
copy_v4_v4_uchar(wcol->inner, wcol->inner_sel);
copy_v3_v3_uchar(wcol->text, wcol->text_sel);
copy_v3_v3_uchar(wcol->outline, wcol->inner);
@@ -4042,7 +4083,7 @@ static void widget_pulldownbut(
static void widget_menu_itembut(uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float zoom)
{
@@ -4066,7 +4107,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol,
static void widget_menu_itembut_unpadded(uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float zoom)
{
@@ -4088,7 +4129,7 @@ static void widget_menu_itembut_unpadded(uiWidgetColors *wcol,
static void widget_menu_radial_itembut(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float zoom)
{
@@ -4114,7 +4155,7 @@ static void widget_menu_radial_itembut(uiBut *but,
static void widget_list_itembut(uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int UNUSED(roundboxalign),
const float zoom)
{
@@ -4131,11 +4172,13 @@ static void widget_list_itembut(uiWidgetColors *wcol,
static void widget_optionbut(uiWidgetColors *wcol,
rcti *rect,
- int state,
+ const uiWidgetStateInfo *state,
int UNUSED(roundboxalign),
const float UNUSED(zoom))
{
- const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET);
+ /* For a right aligned layout (signified by #UI_BUT_TEXT_RIGHT), draw the text on the left of the
+ * checkbox. */
+ const bool text_before_widget = (state->but_drawflag & UI_BUT_TEXT_RIGHT);
rcti recttemp = *rect;
uiWidgetBase wtb;
@@ -4160,7 +4203,7 @@ static void widget_optionbut(uiWidgetColors *wcol,
round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad);
/* decoration */
- if (state & UI_SELECT) {
+ if (state->but_flag & UI_SELECT) {
shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp);
}
@@ -4177,19 +4220,21 @@ static void widget_optionbut(uiWidgetColors *wcol,
}
/* labels use Editor theme colors for text */
-static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss)
+static void widget_state_label(uiWidgetType *wt,
+ const uiWidgetStateInfo *state,
+ eUIEmbossType emboss)
{
- if (state & UI_BUT_LIST_ITEM) {
+ if (state->but_flag & UI_BUT_LIST_ITEM) {
/* Override default label theme's colors. */
bTheme *btheme = UI_GetTheme();
wt->wcol_theme = &btheme->tui.wcol_list_item;
/* call this for option button */
- widget_state(wt, state, drawflag, emboss);
+ widget_state(wt, state, emboss);
}
else {
/* call this for option button */
- widget_state(wt, state, drawflag, emboss);
- if (state & UI_SELECT) {
+ widget_state(wt, state, emboss);
+ if (state->but_flag & UI_SELECT) {
UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text);
}
else {
@@ -4197,14 +4242,17 @@ static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmb
}
}
- if (state & UI_BUT_REDALERT) {
+ if (state->but_flag & UI_BUT_REDALERT) {
const uchar red[4] = {255, 0, 0};
color_blend_v3_v3(wt->wcol.text, red, 0.4f);
}
}
-static void widget_radiobut(
- uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom)
+static void widget_radiobut(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *UNUSED(state),
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -4218,7 +4266,7 @@ static void widget_radiobut(
static void widget_box(uiBut *but,
uiWidgetColors *wcol,
rcti *rect,
- int UNUSED(state),
+ const uiWidgetStateInfo *UNUSED(state),
int roundboxalign,
const float zoom)
{
@@ -4244,8 +4292,11 @@ static void widget_box(uiBut *but,
copy_v3_v3_uchar(wcol->inner, old_col);
}
-static void widget_but(
- uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom)
+static void widget_but(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *UNUSED(state),
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -4271,13 +4322,16 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
}
#endif
-static void widget_roundbut_exec(
- uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_roundbut_exec(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
- if (state & UI_STATE_HOLD_ACTION) {
+ if (state->has_hold_action) {
/* Show that keeping pressed performs another action (typically a menu). */
shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r');
}
@@ -4290,11 +4344,14 @@ static void widget_roundbut_exec(
widgetbase_draw(&wtb, wcol);
}
-static void widget_tab(
- uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom)
+static void widget_tab(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int roundboxalign,
+ const float zoom)
{
const float rad = widget_radius_from_zoom(zoom, wcol);
- const bool is_active = (state & UI_SELECT);
+ const bool is_active = (state->but_flag & UI_SELECT);
/* Draw shaded outline - Disabled for now,
* seems incorrect and also looks nicer without it IMHO ;). */
@@ -4442,7 +4499,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
case UI_WTYPE_TOOLTIP:
wt.wcol_theme = &btheme->tui.wcol_tooltip;
- wt.draw = widget_menu_back;
+ wt.draw_block = widget_menu_back;
break;
/* strings */
@@ -4498,7 +4555,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
case UI_WTYPE_MENU_BACK:
wt.wcol_theme = &btheme->tui.wcol_menu_back;
- wt.draw = widget_menu_back;
+ wt.draw_block = widget_menu_back;
break;
/* specials */
@@ -4552,9 +4609,15 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
break;
case UI_WTYPE_TREEROW:
+ wt.wcol_theme = &btheme->tui.wcol_view_item;
wt.custom = widget_treerow;
break;
+ case UI_WTYPE_GRID_TILE:
+ wt.wcol_theme = &btheme->tui.wcol_view_item;
+ wt.draw = widget_gridtile;
+ break;
+
case UI_WTYPE_NODESOCKET:
wt.custom = widget_nodesocket;
break;
@@ -4891,6 +4954,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
fstyle = &style->widgetlabel;
break;
+ case UI_BTYPE_GRID_TILE:
+ wt = widget_type(UI_WTYPE_GRID_TILE);
+ fstyle = &style->widgetlabel;
+ break;
+
case UI_BTYPE_SCROLL:
wt = widget_type(UI_WTYPE_SCROLL);
break;
@@ -4921,62 +4989,50 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
const int roundboxalign = widget_roundbox_set(but, rect);
- /* Mask out flags re-used for local state. */
- int state = but->flag & ~UI_STATE_FLAGS_ALL;
- const int drawflag = but->drawflag;
+ uiWidgetStateInfo state = {0};
+ state.but_flag = but->flag;
+ state.but_drawflag = but->drawflag;
- if (state & UI_SELECT_DRAW) {
- state |= UI_SELECT;
+ /* Override selected flag for drawing. */
+ if (but->flag & UI_SELECT_DRAW) {
+ state.but_flag |= UI_SELECT;
}
if ((but->editstr) ||
(UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) {
- state |= UI_STATE_TEXT_INPUT;
+ state.is_text_input = true;
}
if (but->hold_func) {
- state |= UI_STATE_HOLD_ACTION;
- }
-
- if (state & UI_ACTIVE) {
- if (but->drawflag & UI_BUT_ACTIVE_LEFT) {
- state |= UI_STATE_ACTIVE_LEFT;
- }
- else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) {
- state |= UI_STATE_ACTIVE_RIGHT;
- }
+ state.has_hold_action = true;
}
bool use_alpha_blend = false;
if (but->emboss != UI_EMBOSS_PULLDOWN) {
- if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
+ if (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
use_alpha_blend = true;
- ui_widget_color_disabled(wt, state);
+ ui_widget_color_disabled(wt, &state);
}
}
- if (drawflag & UI_BUT_TEXT_RIGHT) {
- state |= UI_STATE_TEXT_BEFORE_WIDGET;
- }
-
#ifdef USE_UI_POPOVER_ONCE
if (but->block->flag & UI_BLOCK_POPOVER_ONCE) {
- if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
- state |= UI_BUT_ACTIVE_DEFAULT;
+ if ((but->flag & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
+ state.but_flag |= UI_BUT_ACTIVE_DEFAULT;
}
}
#endif
if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) {
- state &= ~UI_BUT_OVERRIDDEN;
+ state.but_flag &= ~UI_BUT_OVERRIDDEN;
}
const float zoom = 1.0f / but->block->aspect;
- wt->state(wt, state, drawflag, but->emboss);
+ wt->state(wt, &state, but->emboss);
if (wt->custom) {
- wt->custom(but, &wt->wcol, rect, state, roundboxalign, zoom);
+ wt->custom(but, &wt->wcol, rect, &state, roundboxalign, zoom);
}
else if (wt->draw) {
- wt->draw(&wt->wcol, rect, state, roundboxalign, zoom);
+ wt->draw(&wt->wcol, rect, &state, roundboxalign, zoom);
}
if (wt->text) {
@@ -5017,13 +5073,13 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
{
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
- wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED);
+ wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED);
if (block) {
const float zoom = 1.0f / block->aspect;
- wt->draw(&wt->wcol, rect, block->flag, block->direction, zoom);
+ wt->draw_block(&wt->wcol, rect, block->flag, block->direction, zoom);
}
else {
- wt->draw(&wt->wcol, rect, 0, 0, 1.0f);
+ wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f);
}
ui_draw_clip_tri(block, rect, wt);
@@ -5118,8 +5174,8 @@ void ui_draw_popover_back(struct ARegion *region,
}
else {
const float zoom = 1.0f / block->aspect;
- wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED);
- wt->draw(&wt->wcol, rect, 0, 0, zoom);
+ wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED);
+ wt->draw_block(&wt->wcol, rect, 0, 0, zoom);
}
ui_draw_clip_tri(block, rect, wt);
@@ -5307,11 +5363,20 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type,
}
rcti rect_copy = *rect;
- wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED);
+ wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED);
if (color) {
rgba_float_to_uchar(wt->wcol.inner, color);
}
- wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f);
+
+ if (wt->draw_block) {
+ wt->draw_block(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f);
+ }
+ else if (wt->draw) {
+ wt->draw(&wt->wcol, &rect_copy, &STATE_INFO_NULL, UI_CNR_ALL, 1.0f);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
}
void ui_draw_widget_menu_back_color(const rcti *rect, bool use_shadow, const float color[4])
{
@@ -5326,16 +5391,16 @@ void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow)
void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect)
{
uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
- wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED);
- /* wt->draw ends up using same function to draw the tooltip as menu_back */
- wt->draw(&wt->wcol, rect, 0, 0, 1.0f);
+ wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED);
+ /* wt->draw_block ends up using same function to draw the tooltip as menu_back */
+ wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f);
}
void ui_draw_menu_item(const uiFontStyle *fstyle,
rcti *rect,
const char *name,
int iconid,
- int state,
+ int but_flag,
uiMenuItemSeparatorType separator_type,
int *r_xmax)
{
@@ -5346,8 +5411,11 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
int padding = 0.25f * row_height;
char *cpoin = NULL;
- wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
- wt->draw(&wt->wcol, rect, 0, 0, 1.0f);
+ uiWidgetStateInfo state = {0};
+ state.but_flag = but_flag;
+
+ wt->state(wt, &state, UI_EMBOSS_UNDEFINED);
+ wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f);
UI_fontstyle_set(fstyle);
@@ -5442,8 +5510,12 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
/* part text right aligned */
if (separator_type != UI_MENU_ITEM_SEPARATOR_NONE) {
if (cpoin) {
+ /* State info for the hint drawing. */
+ uiWidgetStateInfo hint_state = state;
/* Set inactive state for grayed out text. */
- wt->state(wt, state | UI_BUT_INACTIVE, 0, UI_EMBOSS_UNDEFINED);
+ hint_state.but_flag |= UI_BUT_INACTIVE;
+
+ wt->state(wt, &hint_state, UI_EMBOSS_UNDEFINED);
char hint_drawstr[UI_MAX_DRAW_STR];
{
@@ -5528,14 +5600,17 @@ void ui_draw_preview_item(const uiFontStyle *fstyle,
rcti *rect,
const char *name,
int iconid,
- int state,
+ int but_flag,
eFontStyle_Align text_align)
{
uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM_UNPADDED);
+ uiWidgetStateInfo state = {0};
+ state.but_flag = but_flag;
+
/* drawing button background */
- wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
- wt->draw(&wt->wcol, rect, 0, 0, 1.0f);
+ wt->state(wt, &state, UI_EMBOSS_UNDEFINED);
+ wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f);
ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align);
}
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
index bf756fb5838..f86d1c4d8bc 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/tree_view.cc
@@ -68,6 +68,12 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
+bool AbstractTreeView::listen(const wmNotifier &) const
+{
+ /* Nothing by default. */
+ return false;
+}
+
bool AbstractTreeView::is_renaming() const
{
return rename_buffer_ != nullptr;
@@ -82,7 +88,7 @@ void AbstractTreeView::update_from_old(uiBlock &new_block)
return;
}
- uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block(
+ uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiTreeViewHandle *>(this));
if (old_view_handle == nullptr) {
is_reconstructed_ = true;
@@ -805,6 +811,13 @@ class TreeViewItemAPIWrapper {
using namespace blender::ui;
+bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle,
+ const wmNotifier *notifier)
+{
+ const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle);
+ return view.listen(*notifier);
+}
+
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
{
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc
index 66171dc13e4..6ece7eb4ffa 100644
--- a/source/blender/editors/interface/view2d.cc
+++ b/source/blender/editors/interface/view2d.cc
@@ -2103,21 +2103,12 @@ void UI_view2d_text_cache_draw(ARegion *region)
col_pack_prev = v2s->col.pack;
}
- if (v2s->rect.xmin >= v2s->rect.xmax) {
- BLF_draw_default((float)(v2s->mval[0] + xofs),
- (float)(v2s->mval[1] + yofs),
- 0.0,
- v2s->str,
- BLF_DRAW_STR_DUMMY_MAX);
- }
- else {
- BLF_enable(font_id, BLF_CLIPPING);
- BLF_clipping(
- font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
- BLF_draw_default(
- v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX);
- BLF_disable(font_id, BLF_CLIPPING);
- }
+ BLF_enable(font_id, BLF_CLIPPING);
+ BLF_clipping(
+ font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
+ BLF_draw_default(
+ v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX);
+ BLF_disable(font_id, BLF_CLIPPING);
}
g_v2d_strings = nullptr;
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index 418a399db28..a716c00d5d9 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -9,9 +9,11 @@ set(INC
../../depsgraph
../../io/alembic
../../io/collada
+ ../../io/common
../../io/gpencil
../../io/usd
../../io/wavefront_obj
+ ../../io/stl
../../makesdna
../../makesrna
../../windowmanager
@@ -32,6 +34,7 @@ set(SRC
io_obj.c
io_ops.c
io_usd.c
+ io_stl_ops.c
io_alembic.h
io_cache.h
@@ -40,12 +43,12 @@ set(SRC
io_obj.h
io_ops.h
io_usd.h
+ io_stl_ops.h
)
set(LIB
bf_blenkernel
bf_blenlib
- bf_wavefront_obj
)
if(WITH_OPENCOLLADA)
@@ -55,6 +58,27 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
+if(WITH_IO_WAVEFRONT_OBJ)
+ list(APPEND LIB
+ bf_wavefront_obj
+ )
+ add_definitions(-DWITH_IO_WAVEFRONT_OBJ)
+endif()
+
+if(WITH_IO_STL)
+ list(APPEND LIB
+ bf_stl
+ )
+ add_definitions(-DWITH_IO_STL)
+endif()
+
+if(WITH_IO_GPENCIL)
+ list(APPEND LIB
+ bf_gpencil
+ )
+ add_definitions(-DWITH_IO_GPENCIL)
+endif()
+
if(WITH_ALEMBIC)
list(APPEND LIB
bf_alembic
@@ -77,6 +101,4 @@ if(WITH_HARU)
add_definitions(-DWITH_HARU)
endif()
-list(APPEND LIB bf_gpencil)
-
blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 87923d9fdf8..0e8e0f83597 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -282,7 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->ui = wm_alembic_export_draw;
ot->check = wm_alembic_export_check;
- ot->flag |= OPTYPE_PRESET;
+ ot->flag = OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c
index 624b85358f5..969049313c9 100644
--- a/source/blender/editors/io/io_cache.c
+++ b/source/blender/editors/io/io_cache.c
@@ -75,17 +75,17 @@ static void open_cancel(bContext *UNUSED(C), wmOperator *op)
static int cachefile_open_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_report(op->reports, RPT_ERROR, "No filename given");
+ BKE_report(op->reports, RPT_ERROR, "No filepath given");
return OPERATOR_CANCELLED;
}
- char filename[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", filename);
+ char filepath[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filepath);
Main *bmain = CTX_data_main(C);
- CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename), 0);
- BLI_strncpy(cache_file->filepath, filename, FILE_MAX);
+ CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filepath), 0);
+ BLI_strncpy(cache_file->filepath, filepath, FILE_MAX);
DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE);
/* Will be set when running invoke, not exec directly. */
@@ -182,7 +182,7 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven
static int cachefile_layer_add_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_report(op->reports, RPT_ERROR, "No filename given");
+ BKE_report(op->reports, RPT_ERROR, "No filepath given");
return OPERATOR_CANCELLED;
}
@@ -192,10 +192,10 @@ static int cachefile_layer_add_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- char filename[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", filename);
+ char filepath[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filepath);
- CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filename);
+ CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filepath);
if (!layer) {
WM_report(RPT_ERROR, "Could not add a layer to the cache file");
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index dc62212bf53..c491e7a5815 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -468,7 +468,7 @@ void WM_OT_collada_export(wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->check = wm_collada_export_check;
- ot->flag |= OPTYPE_PRESET;
+ ot->flag = OPTYPE_PRESET;
ot->ui = wm_collada_export_draw;
@@ -786,7 +786,7 @@ void WM_OT_collada_import(wmOperatorType *ot)
ot->exec = wm_collada_import_exec;
ot->poll = WM_operator_winactive;
- // ot->flag |= OPTYPE_PRESET;
+ // ot->flag = OPTYPE_PRESET;
ot->ui = wm_collada_import_draw;
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 7ac05fcca3e..6e5ae9f3cba 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -5,36 +5,38 @@
* \ingroup editor/io
*/
-#include "BLI_path_util.h"
-#include "BLI_string.h"
+#ifdef WITH_IO_GPENCIL
-#include "DNA_gpencil_types.h"
-#include "DNA_space_types.h"
+# include "BLI_path_util.h"
+# include "BLI_string.h"
-#include "BKE_gpencil.h"
-#include "BKE_main.h"
-#include "BKE_report.h"
-#include "BKE_screen.h"
+# include "DNA_gpencil_types.h"
+# include "DNA_space_types.h"
-#include "BLT_translation.h"
+# include "BKE_gpencil.h"
+# include "BKE_main.h"
+# include "BKE_report.h"
+# include "BKE_screen.h"
-#include "RNA_access.h"
-#include "RNA_define.h"
+# include "BLT_translation.h"
-#include "UI_interface.h"
-#include "UI_resources.h"
+# include "RNA_access.h"
+# include "RNA_define.h"
-#include "WM_api.h"
-#include "WM_types.h"
+# include "UI_interface.h"
+# include "UI_resources.h"
-#include "DEG_depsgraph.h"
-#include "DEG_depsgraph_query.h"
+# include "WM_api.h"
+# include "WM_types.h"
-#include "io_gpencil.h"
+# include "DEG_depsgraph.h"
+# include "DEG_depsgraph_query.h"
-#include "gpencil_io.h"
+# include "io_gpencil.h"
-#if defined(WITH_PUGIXML) || defined(WITH_HARU)
+# include "gpencil_io.h"
+
+# if defined(WITH_PUGIXML) || defined(WITH_HARU)
/* Definition of enum elements to export. */
/* Common props for exporting. */
static void gpencil_export_common_props_definition(wmOperatorType *ot)
@@ -87,10 +89,10 @@ static void set_export_filepath(bContext *C, wmOperator *op, const char *extensi
RNA_string_set(op->ptr, "filepath", filepath);
}
}
-#endif
+# endif
/* <-------- SVG single frame export. --------> */
-#ifdef WITH_PUGIXML
+# ifdef WITH_PUGIXML
static bool wm_gpencil_export_svg_common_check(bContext *UNUSED(C), wmOperator *op)
{
char filepath[FILE_MAX];
@@ -241,10 +243,10 @@ void WM_OT_gpencil_export_svg(wmOperatorType *ot)
"Clip Camera",
"Clip drawings to camera size when export in camera view");
}
-#endif
+# endif
/* <-------- PDF single frame export. --------> */
-#ifdef WITH_HARU
+# ifdef WITH_HARU
static bool wm_gpencil_export_pdf_common_check(bContext *UNUSED(C), wmOperator *op)
{
@@ -406,4 +408,6 @@ void WM_OT_gpencil_export_pdf(wmOperatorType *ot)
"Frames",
"Which frames to include in the export");
}
-#endif
+# endif /* WITH_HARU */
+
+#endif /* WITH_IO_GPENCIL */
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index 8bed32ad6c3..45f5441616f 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -5,34 +5,36 @@
* \ingroup editor/io
*/
-#include "BLI_path_util.h"
+#ifdef WITH_IO_GPENCIL
-#include "DNA_gpencil_types.h"
-#include "DNA_space_types.h"
+# include "BLI_path_util.h"
-#include "BKE_context.h"
-#include "BKE_gpencil.h"
-#include "BKE_report.h"
+# include "DNA_gpencil_types.h"
+# include "DNA_space_types.h"
-#include "BLT_translation.h"
+# include "BKE_context.h"
+# include "BKE_gpencil.h"
+# include "BKE_report.h"
-#include "RNA_access.h"
-#include "RNA_define.h"
+# include "BLT_translation.h"
-#include "UI_interface.h"
-#include "UI_resources.h"
+# include "RNA_access.h"
+# include "RNA_define.h"
-#include "WM_api.h"
-#include "WM_types.h"
+# include "UI_interface.h"
+# include "UI_resources.h"
-#include "DEG_depsgraph.h"
-#include "DEG_depsgraph_query.h"
+# include "WM_api.h"
+# include "WM_types.h"
-#include "ED_gpencil.h"
+# include "DEG_depsgraph.h"
+# include "DEG_depsgraph_query.h"
-#include "io_gpencil.h"
+# include "ED_gpencil.h"
-#include "gpencil_io.h"
+# include "io_gpencil.h"
+
+# include "gpencil_io.h"
/* <-------- SVG single frame import. --------> */
static bool wm_gpencil_import_svg_common_check(bContext *UNUSED(C), wmOperator *op)
@@ -174,3 +176,5 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot)
0.001f,
100.0f);
}
+
+#endif /* WITH_IO_GPENCIL */
diff --git a/source/blender/editors/io/io_gpencil_utils.c b/source/blender/editors/io/io_gpencil_utils.c
index fa5fcd79b96..9a88daef1a1 100644
--- a/source/blender/editors/io/io_gpencil_utils.c
+++ b/source/blender/editors/io/io_gpencil_utils.c
@@ -5,14 +5,16 @@
* \ingroup editor/io
*/
-#include "DNA_space_types.h"
+#ifdef WITH_IO_GPENCIL
-#include "BKE_context.h"
-#include "BKE_screen.h"
+# include "DNA_space_types.h"
-#include "WM_api.h"
+# include "BKE_context.h"
+# include "BKE_screen.h"
-#include "io_gpencil.h"
+# include "WM_api.h"
+
+# include "io_gpencil.h"
ARegion *get_invoke_region(bContext *C)
{
@@ -46,3 +48,5 @@ View3D *get_invoke_view3d(bContext *C)
return NULL;
}
+
+#endif /* WITH_IO_GPENCIL */
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 3345d422bd1..53fa4788d0e 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -4,51 +4,39 @@
* \ingroup editor/io
*/
-#include "DNA_space_types.h"
+#ifdef WITH_IO_WAVEFRONT_OBJ
-#include "BKE_context.h"
-#include "BKE_main.h"
-#include "BKE_report.h"
+# include "DNA_space_types.h"
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
+# include "BKE_context.h"
+# include "BKE_main.h"
+# include "BKE_report.h"
-#include "BLT_translation.h"
+# include "BLI_path_util.h"
+# include "BLI_string.h"
+# include "BLI_utildefines.h"
-#include "MEM_guardedalloc.h"
+# include "BLT_translation.h"
-#include "RNA_access.h"
-#include "RNA_define.h"
+# include "ED_outliner.h"
-#include "UI_interface.h"
-#include "UI_resources.h"
+# include "MEM_guardedalloc.h"
-#include "WM_api.h"
-#include "WM_types.h"
+# include "RNA_access.h"
+# include "RNA_define.h"
-#include "DEG_depsgraph.h"
+# include "UI_interface.h"
+# include "UI_resources.h"
-#include "IO_wavefront_obj.h"
-#include "io_obj.h"
+# include "WM_api.h"
+# include "WM_types.h"
-static const EnumPropertyItem io_obj_transform_axis_forward[] = {
- {OBJ_AXIS_X_FORWARD, "X_FORWARD", 0, "X", "Positive X axis"},
- {OBJ_AXIS_Y_FORWARD, "Y_FORWARD", 0, "Y", "Positive Y axis"},
- {OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"},
- {OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"},
- {OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"},
- {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"},
- {0, NULL, 0, NULL, NULL}};
+# include "DEG_depsgraph.h"
-static const EnumPropertyItem io_obj_transform_axis_up[] = {
- {OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"},
- {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"},
- {OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"},
- {OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"},
- {OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"},
- {OBJ_AXIS_NEGATIVE_Z_UP, "NEGATIVE_Z_UP", 0, "-Z", "Negative Z axis"},
- {0, NULL, 0, NULL, NULL}};
+# include "IO_orientation.h"
+# include "IO_path_util_types.h"
+# include "IO_wavefront_obj.h"
+# include "io_obj.h"
static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
{DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"},
@@ -59,6 +47,15 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
"Export objects as they appear in the viewport"},
{0, NULL, 0, NULL, NULL}};
+static const EnumPropertyItem io_obj_path_mode[] = {
+ {PATH_REFERENCE_AUTO, "AUTO", 0, "Auto", "Use Relative paths with subdirectories only"},
+ {PATH_REFERENCE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Always write absolute paths"},
+ {PATH_REFERENCE_RELATIVE, "RELATIVE", 0, "Relative", "Write relative paths where possible"},
+ {PATH_REFERENCE_MATCH, "MATCH", 0, "Match", "Match Absolute/Relative setting with input path"},
+ {PATH_REFERENCE_STRIP, "STRIP", 0, "Strip", "Write filename only"},
+ {PATH_REFERENCE_COPY, "COPY", 0, "Copy", "Copy the file to the destination path"},
+ {0, NULL, 0, NULL, NULL}};
+
static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
@@ -87,6 +84,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
struct OBJExportParams export_params;
+ export_params.file_base_for_tests[0] = '\0';
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.blen_filepath = CTX_data_main(C)->filepath;
export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation");
@@ -102,7 +100,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects");
export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv");
export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals");
+ export_params.export_colors = RNA_boolean_get(op->ptr, "export_colors");
export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials");
+ export_params.path_mode = RNA_enum_get(op->ptr, "path_mode");
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs");
@@ -119,9 +119,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
{
-
const bool export_animation = RNA_boolean_get(imfptr, "export_animation");
const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups");
+ const bool export_materials = RNA_boolean_get(imfptr, "export_materials");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -150,6 +150,9 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE);
uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE);
uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE);
+ sub = uiLayoutColumn(sub, false);
+ uiLayoutSetEnabled(sub, export_materials);
+ uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE);
/* Options for what to write. */
box = uiLayoutBox(layout);
@@ -158,10 +161,12 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE);
uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE);
+ uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE);
uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE);
uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE);
uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE);
+ /* Grouping options. */
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Grouping"), ICON_GROUP);
col = uiLayoutColumn(box, false);
@@ -231,6 +236,8 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op)
void WM_OT_obj_export(struct wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
ot->name = "Export Wavefront OBJ";
ot->description = "Save the scene to a Wavefront OBJ file";
ot->idname = "WM_OT_obj_export";
@@ -241,10 +248,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
ot->ui = wm_obj_export_draw;
ot->check = wm_obj_export_check;
- ot->flag |= OPTYPE_PRESET;
+ ot->flag = OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
- FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
+ FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
@@ -276,13 +283,9 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
INT_MIN,
INT_MAX);
/* Object transform options. */
- RNA_def_enum(ot->srna,
- "forward_axis",
- io_obj_transform_axis_forward,
- OBJ_AXIS_NEGATIVE_Z_FORWARD,
- "Forward Axis",
- "");
- RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
+ RNA_def_enum(
+ ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
+ RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
RNA_def_float(ot->srna,
"scaling_factor",
1.0f,
@@ -314,12 +317,19 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
"Export Normals",
"Export per-face normals if the face is flat-shaded, per-face-per-loop "
"normals if smooth-shaded");
+ RNA_def_boolean(ot->srna, "export_colors", false, "Export Colors", "Export per-vertex colors");
RNA_def_boolean(ot->srna,
"export_materials",
true,
"Export Materials",
"Export MTL library. There must be a Principled-BSDF node for image textures to "
"be exported to the MTL file");
+ RNA_def_enum(ot->srna,
+ "path_mode",
+ io_obj_path_mode,
+ PATH_REFERENCE_AUTO,
+ "Path Mode",
+ "Method used to reference paths");
RNA_def_boolean(ot->srna,
"export_triangulated_mesh",
false,
@@ -358,6 +368,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
"Every smooth-shaded face is assigned group \"1\" and every flat-shaded face \"off\"");
RNA_def_boolean(
ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", "");
+
+ /* Only show .obj or .mtl files by default. */
+ prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
}
static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
@@ -378,10 +392,17 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
import_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
+ import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups");
import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
OBJ_import(C, &import_params);
+ Scene *scene = CTX_data_scene(C);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -402,6 +423,7 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Options"), ICON_EXPORT);
col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "import_vertex_groups", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
}
@@ -415,9 +437,12 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op)
void WM_OT_obj_import(struct wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
ot->name = "Import Wavefront OBJ";
ot->description = "Load a Wavefront OBJ scene";
ot->idname = "WM_OT_obj_import";
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->invoke = wm_obj_import_invoke;
ot->exec = wm_obj_import_exec;
@@ -425,7 +450,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
ot->ui = wm_obj_import_draw;
WM_operator_properties_filesel(ot,
- FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
+ FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
@@ -441,16 +466,23 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
"Resize the objects to keep bounding box under this value. Value 0 disables clamping",
0.0f,
1000.0f);
- RNA_def_enum(ot->srna,
- "forward_axis",
- io_obj_transform_axis_forward,
- OBJ_AXIS_NEGATIVE_Z_FORWARD,
- "Forward Axis",
- "");
- RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
+ RNA_def_enum(
+ ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
+ RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
+ RNA_def_boolean(ot->srna,
+ "import_vertex_groups",
+ false,
+ "Vertex Groups",
+ "Import OBJ groups as vertex groups");
RNA_def_boolean(ot->srna,
"validate_meshes",
false,
"Validate Meshes",
"Check imported mesh objects for invalid data (slow)");
+
+ /* Only show .obj or .mtl files by default. */
+ prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
}
+
+#endif /* WITH_IO_WAVEFRONT_OBJ */
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index 094f89d1540..0340d0598d5 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -24,6 +24,7 @@
#include "io_cache.h"
#include "io_gpencil.h"
#include "io_obj.h"
+#include "io_stl_ops.h"
void ED_operatortypes_io(void)
{
@@ -41,14 +42,14 @@ void ED_operatortypes_io(void)
WM_operatortype_append(WM_OT_usd_export);
#endif
+#ifdef WITH_IO_GPENCIL
WM_operatortype_append(WM_OT_gpencil_import_svg);
-
-#ifdef WITH_PUGIXML
+# ifdef WITH_PUGIXML
WM_operatortype_append(WM_OT_gpencil_export_svg);
-#endif
-
-#ifdef WITH_HARU
+# endif
+# ifdef WITH_HARU
WM_operatortype_append(WM_OT_gpencil_export_pdf);
+# endif
#endif
WM_operatortype_append(CACHEFILE_OT_open);
@@ -58,6 +59,12 @@ void ED_operatortypes_io(void)
WM_operatortype_append(CACHEFILE_OT_layer_remove);
WM_operatortype_append(CACHEFILE_OT_layer_move);
+#ifdef WITH_IO_WAVEFRONT_OBJ
WM_operatortype_append(WM_OT_obj_export);
WM_operatortype_append(WM_OT_obj_import);
+#endif
+
+#ifdef WITH_IO_STL
+ WM_operatortype_append(WM_OT_stl_import);
+#endif
}
diff --git a/source/blender/editors/io/io_stl_ops.c b/source/blender/editors/io/io_stl_ops.c
new file mode 100644
index 00000000000..7db32cd6f18
--- /dev/null
+++ b/source/blender/editors/io/io_stl_ops.c
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup editor/io
+ */
+
+#ifdef WITH_IO_STL
+
+# include "BKE_context.h"
+# include "BKE_report.h"
+
+# include "WM_api.h"
+# include "WM_types.h"
+
+# include "DNA_space_types.h"
+
+# include "ED_outliner.h"
+
+# include "RNA_access.h"
+# include "RNA_define.h"
+
+# include "IO_stl.h"
+# include "io_stl_ops.h"
+
+static int wm_stl_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return WM_operator_filesel(C, op, event);
+}
+
+static int wm_stl_import_execute(bContext *C, wmOperator *op)
+{
+ struct STLImportParams params;
+ params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
+ params.up_axis = RNA_enum_get(op->ptr, "up_axis");
+ params.use_facet_normal = RNA_boolean_get(op->ptr, "use_facet_normal");
+ params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit");
+ params.global_scale = RNA_float_get(op->ptr, "global_scale");
+ params.use_mesh_validate = RNA_boolean_get(op->ptr, "use_mesh_validate");
+
+ int files_len = RNA_collection_length(op->ptr, "files");
+
+ if (files_len) {
+ PointerRNA fileptr;
+ PropertyRNA *prop;
+ char dir_only[FILE_MAX], file_only[FILE_MAX];
+
+ RNA_string_get(op->ptr, "directory", dir_only);
+ prop = RNA_struct_find_property(op->ptr, "files");
+ for (int i = 0; i < files_len; i++) {
+ RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr);
+ RNA_string_get(&fileptr, "name", file_only);
+ BLI_join_dirfile(params.filepath, sizeof(params.filepath), dir_only, file_only);
+ STL_import(C, &params);
+ }
+ }
+ else if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ RNA_string_get(op->ptr, "filepath", params.filepath);
+ STL_import(C, &params);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
+
+ Scene *scene = CTX_data_scene(C);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool wm_stl_import_check(bContext *UNUSED(C), wmOperator *op)
+{
+ const int num_axes = 3;
+ /* Both forward and up axes cannot be the same (or same except opposite sign). */
+ if (RNA_enum_get(op->ptr, "forward_axis") % num_axes ==
+ (RNA_enum_get(op->ptr, "up_axis") % num_axes)) {
+ RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % num_axes + 1);
+ return true;
+ }
+ return false;
+}
+
+void WM_OT_stl_import(struct wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Import STL";
+ ot->description = "Import an STL file as an object";
+ ot->idname = "WM_OT_stl_import";
+
+ ot->invoke = wm_stl_import_invoke;
+ ot->exec = wm_stl_import_execute;
+ ot->poll = WM_operator_winactive;
+ ot->check = wm_stl_import_check;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER,
+ FILE_BLENDER,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY |
+ WM_FILESEL_SHOW_PROPS,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_ALPHA);
+
+ RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
+ RNA_def_boolean(ot->srna,
+ "use_scene_unit",
+ false,
+ "Scene Unit",
+ "Apply current scene's unit (as defined by unit scale) to imported data");
+ RNA_def_boolean(ot->srna,
+ "use_facet_normal",
+ false,
+ "Facet Normals",
+ "Use (import) facet normals (note that this will still give flat shading)");
+ RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", "");
+ RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", "");
+ RNA_def_boolean(ot->srna,
+ "use_mesh_validate",
+ false,
+ "Validate Mesh",
+ "Validate and correct imported mesh (slow)");
+
+ /* Only show .stl files by default. */
+ prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+
+#endif /* WITH_IO_STL */
diff --git a/source/blender/editors/io/io_stl_ops.h b/source/blender/editors/io/io_stl_ops.h
new file mode 100644
index 00000000000..8f548f75985
--- /dev/null
+++ b/source/blender/editors/io/io_stl_ops.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup editor/io
+ */
+
+#pragma once
+
+struct wmOperatorType;
+
+void WM_OT_stl_export(struct wmOperatorType *ot);
+void WM_OT_stl_import(struct wmOperatorType *ot);
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index e0616a0cec3..a59cdf60243 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -57,6 +57,20 @@ const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_usd_mtl_name_collision_mode_items[] = {
+ {USD_MTL_NAME_COLLISION_MAKE_UNIQUE,
+ "MAKE_UNIQUE",
+ 0,
+ "Make Unique",
+ "Import each USD material as a unique Blender material"},
+ {USD_MTL_NAME_COLLISION_REFERENCE_EXISTING,
+ "REFERENCE_EXISTING",
+ 0,
+ "Reference Existing",
+ "If a material with the same name already exists, reference that instead of importing"},
+ {0, NULL, 0, NULL, NULL},
+};
+
/* Stored in the wmOperator's customdata field to indicate it should run as a background job.
* This is set when the operator is invoked, and not set when it is only executed. */
enum { AS_BACKGROUND_JOB = 1 };
@@ -118,7 +132,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface");
const bool export_textures = RNA_boolean_get(op->ptr, "export_textures");
const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures");
- const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths");
+ const bool relative_paths = RNA_boolean_get(op->ptr, "relative_paths");
struct USDExportParams params = {
export_animation,
@@ -133,7 +147,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
generate_preview_surface,
export_textures,
overwrite_textures,
- relative_texture_paths,
+ relative_paths,
};
bool ok = USD_export(C, filename, &params, as_background_job);
@@ -181,15 +195,29 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
const bool export_tex = RNA_boolean_get(ptr, "export_textures");
uiLayoutSetActive(row, export_mtl && preview && export_tex);
- row = uiLayoutRow(col, true);
- uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE);
- uiLayoutSetActive(row, export_mtl && preview);
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("File References"));
+ uiItemR(col, ptr, "relative_paths", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Experimental"), ICON_NONE);
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
}
+static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op)
+{
+ char filepath[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filepath);
+
+ if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", NULL)) {
+ BLI_path_extension_ensure(filepath, FILE_MAX, ".usdc");
+ RNA_string_set(op->ptr, "filepath", filepath);
+ return true;
+ }
+
+ return false;
+}
+
void WM_OT_usd_export(struct wmOperatorType *ot)
{
ot->name = "Export USD";
@@ -200,8 +228,9 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
ot->exec = wm_usd_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_export_draw;
+ ot->check = wm_usd_export_check;
- ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_PRESET; /* No UNDO possible. */
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_USD,
@@ -282,10 +311,11 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
"Allow overwriting existing texture files when exporting textures");
RNA_def_boolean(ot->srna,
- "relative_texture_paths",
+ "relative_paths",
true,
- "Relative Texture Paths",
- "Make texture asset paths relative to the USD file");
+ "Relative Paths",
+ "Use relative paths to reference external files (i.e. textures, volumes) in "
+ "USD, otherwise use absolute paths");
}
/* ====== USD Import ====== */
@@ -355,6 +385,9 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale");
+ const eUSDMtlNameCollisionMode mtl_name_collision_mode = RNA_enum_get(op->ptr,
+ "mtl_name_collision_mode");
+
/* TODO(makowalski): Add support for sequences. */
const bool is_sequence = false;
int offset = 0;
@@ -393,7 +426,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.use_instancing = use_instancing,
.import_usd_preview = import_usd_preview,
.set_material_blend = set_material_blend,
- .light_intensity_scale = light_intensity_scale};
+ .light_intensity_scale = light_intensity_scale,
+ .mtl_name_collision_mode = mtl_name_collision_mode};
const bool ok = USD_import(C, filename, &params, as_background_job);
@@ -436,6 +470,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE);
uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE);
+ uiItemR(box, ptr, "mtl_name_collision_mode", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental"));
@@ -559,6 +594,14 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
"Scale for the intensity of imported lights",
0.0001f,
1000.0f);
+
+ RNA_def_enum(
+ ot->srna,
+ "mtl_name_collision_mode",
+ rna_enum_usd_mtl_name_collision_mode_items,
+ USD_MTL_NAME_COLLISION_MAKE_UNIQUE,
+ "Material Name Collision",
+ "Behavior when the name of an imported material conflicts with an existing material");
}
#endif /* WITH_USD */
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 0b9261eac4f..37c1815fca3 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -31,7 +31,9 @@
#include "mask_intern.h" /* own include */
-/******************** add vertex *********************/
+/* -------------------------------------------------------------------- */
+/** \name Add Vertex
+ * \{ */
static void setup_vertex_point(Mask *mask,
MaskSpline *spline,
@@ -160,7 +162,11 @@ static void setup_vertex_point(Mask *mask,
ED_mask_select_flush_all(mask);
}
-/* **** add extrude vertex **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Extrude Vertex
+ * \{ */
static void finSelectedSplinePoint(MaskLayer *mask_layer,
MaskSpline **spline,
@@ -206,7 +212,11 @@ static void finSelectedSplinePoint(MaskLayer *mask_layer,
}
}
-/* **** add subdivide vertex **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Subdivide Vertex
+ * \{ */
static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
{
@@ -230,7 +240,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
- const float threshold = 9;
+ const float threshold = 12;
float tangent[2];
float u;
@@ -390,7 +400,7 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer,
MaskSplinePoint *new_point = NULL, *ref_point = NULL;
if (!mask_layer) {
- /* if there's no mask layer currently operationg on, create new one */
+ /* If there's no mask layer currently operating on, create new one. */
mask_layer = BKE_mask_layer_new(mask, "");
mask->masklay_act = mask->masklay_tot - 1;
}
@@ -492,6 +502,12 @@ static int add_vertex_handle_cyclic(
return OPERATOR_PASS_THROUGH;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Vertex Operator
+ * \{ */
+
static int add_vertex_exec(bContext *C, wmOperator *op)
{
MaskViewLockState lock_state;
@@ -567,7 +583,7 @@ void MASK_OT_add_vertex(wmOperatorType *ot)
/* api callbacks */
ot->exec = add_vertex_exec;
ot->invoke = add_vertex_invoke;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -585,7 +601,11 @@ void MASK_OT_add_vertex(wmOperatorType *ot)
1.0f);
}
-/******************** add feather vertex *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Feather Vertex Operator
+ * \{ */
static int add_feather_vertex_exec(bContext *C, wmOperator *op)
{
@@ -593,7 +613,7 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op)
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
- const float threshold = 9;
+ const float threshold = 12;
float co[2], u;
RNA_float_get_array(op->ptr, "location", co);
@@ -677,7 +697,11 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot)
1.0f);
}
-/******************** common primitive functions *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Common Primitive Functions
+ * \{ */
static BezTriple *points_to_bezier(const float (*points)[2],
const int num_points,
@@ -812,7 +836,11 @@ static void define_primitive_add_properties(wmOperatorType *ot)
FLT_MAX);
}
-/******************** primitive add circle *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Primitive Add Circle Operator
+ * \{ */
static int primitive_circle_add_exec(bContext *C, wmOperator *op)
{
@@ -834,7 +862,7 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = primitive_circle_add_exec;
ot->invoke = primitive_add_invoke;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -843,7 +871,11 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot)
define_primitive_add_properties(ot);
}
-/******************** primitive add suqare *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Primitive Add Suqare Operator
+ * \{ */
static int primitive_square_add_exec(bContext *C, wmOperator *op)
{
@@ -865,7 +897,7 @@ void MASK_OT_primitive_square_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = primitive_square_add_exec;
ot->invoke = primitive_add_invoke;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -873,3 +905,5 @@ void MASK_OT_primitive_square_add(wmOperatorType *ot)
/* properties */
define_primitive_add_properties(ot);
}
+
+/** \} */
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index aab4007854f..a18419ad422 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -663,6 +663,7 @@ void ED_mask_draw_region(
const char draw_flag,
const char draw_type,
const eMaskOverlayMode overlay_mode,
+ const float blend_factor,
/* convert directly into aspect corrected vars */
const int width_i,
const int height_i,
@@ -721,12 +722,14 @@ void ED_mask_draw_region(
}
if (draw_flag & MASK_DRAWFLAG_OVERLAY) {
- const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+ float buf_col[4] = {1.0f, 0.0f, 0.0f, 0.0f};
float *buffer = mask_rasterize(mask_eval, width, height);
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
/* More blending types could be supported in the future. */
- GPU_blend(GPU_BLEND_MULTIPLY);
+ GPU_blend(GPU_BLEND_ALPHA);
+ buf_col[0] = -1.0f;
+ buf_col[3] = 1.0f;
}
GPU_matrix_push();
@@ -737,10 +740,18 @@ void ED_mask_draw_region(
}
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_uniform_vector(
- state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTexTiled(
- &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL);
+ state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, buf_col);
+ if (overlay_mode == MASK_OVERLAY_COMBINED) {
+ const float blend_col[4] = {0.0f, 0.0f, 0.0f, blend_factor};
+
+ immDrawPixelsTexTiled(
+ &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, blend_col);
+ }
+ else {
+ immDrawPixelsTexTiled(
+ &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL);
+ }
GPU_matrix_pop();
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
@@ -765,7 +776,9 @@ void ED_mask_draw_region(
}
/* draw! */
- draw_mask_layers(C, mask_eval, draw_flag, draw_type, width, height);
+ if (draw_flag & MASK_DRAWFLAG_SPLINE) {
+ draw_mask_layers(C, mask_eval, draw_flag, draw_type, width, height);
+ }
if (do_draw_cb) {
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c
index b2d49bcc642..915f90a1537 100644
--- a/source/blender/editors/mask/mask_edit.c
+++ b/source/blender/editors/mask/mask_edit.c
@@ -42,6 +42,22 @@ bool ED_maskedit_poll(bContext *C)
return false;
}
+bool ED_maskedit_visible_splines_poll(bContext *C)
+{
+ ScrArea *area = CTX_wm_area(C);
+ if (area) {
+ switch (area->spacetype) {
+ case SPACE_CLIP:
+ return ED_space_clip_maskedit_visible_splines_poll(C);
+ case SPACE_SEQ:
+ return ED_space_sequencer_maskedit_poll(C);
+ case SPACE_IMAGE:
+ return ED_space_image_maskedit_visible_splines_poll(C);
+ }
+ }
+ return false;
+}
+
bool ED_maskedit_mask_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
@@ -58,6 +74,22 @@ bool ED_maskedit_mask_poll(bContext *C)
return false;
}
+bool ED_maskedit_mask_visible_splines_poll(bContext *C)
+{
+ const ScrArea *area = CTX_wm_area(C);
+ if (area) {
+ switch (area->spacetype) {
+ case SPACE_CLIP:
+ return ED_space_clip_maskedit_mask_visible_splines_poll(C);
+ case SPACE_SEQ:
+ return ED_space_sequencer_maskedit_mask_poll(C);
+ case SPACE_IMAGE:
+ return ED_space_image_maskedit_mask_visible_splines_poll(C);
+ }
+ }
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h
index c620d781c7f..2e99b45f215 100644
--- a/source/blender/editors/mask/mask_intern.h
+++ b/source/blender/editors/mask/mask_intern.h
@@ -86,9 +86,6 @@ void ED_mask_select_flush_all(struct Mask *mask);
/* mask_editor.c */
-bool ED_maskedit_poll(struct bContext *C);
-bool ED_maskedit_mask_poll(struct bContext *C);
-
/* Generalized solution for preserving editor viewport when making changes while lock-to-selection
* is enabled.
* Any mask operator can use this API, without worrying that some editors do not have an idea of
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 3c0e7ee399c..14976d860f6 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -113,7 +113,7 @@ void MASK_OT_new(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_new_exec;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_poll;
/* properties */
RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask");
@@ -146,7 +146,7 @@ void MASK_OT_layer_new(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_layer_new_exec;
- ot->poll = ED_maskedit_poll;
+ ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -181,7 +181,7 @@ void MASK_OT_layer_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_layer_remove_exec;
- ot->poll = ED_maskedit_poll;
+ ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -907,7 +907,7 @@ void MASK_OT_slide_point(wmOperatorType *ot)
/* api callbacks */
ot->invoke = slide_point_invoke;
ot->modal = slide_point_modal;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1297,7 +1297,7 @@ void MASK_OT_slide_spline_curvature(wmOperatorType *ot)
/* api callbacks */
ot->invoke = slide_spline_curvature_invoke;
ot->modal = slide_spline_curvature_modal;
- ot->poll = ED_operator_mask;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1336,7 +1336,7 @@ void MASK_OT_cyclic_toggle(wmOperatorType *ot)
/* api callbacks */
ot->exec = cyclic_toggle_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1493,7 +1493,7 @@ void MASK_OT_delete(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_operator_confirm;
ot->exec = delete_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1551,7 +1551,7 @@ void MASK_OT_switch_direction(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_switch_direction_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1613,7 +1613,7 @@ void MASK_OT_normals_make_consistent(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_normals_make_consistent_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1693,7 +1693,7 @@ void MASK_OT_handle_type_set(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = set_handle_type_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1849,7 +1849,7 @@ void MASK_OT_feather_weight_clear(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_feather_weight_clear_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2043,7 +2043,7 @@ void MASK_OT_duplicate(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_duplicate_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2084,7 +2084,7 @@ void MASK_OT_copy_splines(wmOperatorType *ot)
static bool paste_splines_poll(bContext *C)
{
- if (ED_maskedit_mask_poll(C)) {
+ if (ED_maskedit_mask_visible_splines_poll(C)) {
return BKE_mask_clipboard_is_empty() == false;
}
diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c
index afe457a8502..02e1524e23e 100644
--- a/source/blender/editors/mask/mask_query.c
+++ b/source/blender/editors/mask/mask_query.c
@@ -45,6 +45,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C,
float *r_u,
float *r_score)
{
+ const float threshold_sq = threshold * threshold;
+
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -139,7 +141,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C,
}
}
- if (point && dist_best_sq < threshold) {
+ if (point && dist_best_sq < threshold_sq) {
if (r_mask_layer) {
*r_mask_layer = point_mask_layer;
}
@@ -613,7 +615,7 @@ bool ED_mask_selected_minmax(const bContext *C,
/* Use evaluated mask to take animation into account.
* The animation of splies is not "flushed" back to original, so need to explicitly
- * sue evaluated datablock here. */
+ * use evaluated datablock here. */
Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask->id);
INIT_MINMAX2(min, max);
diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c
index 9d8b84de66b..1f175ce51fc 100644
--- a/source/blender/editors/mask/mask_relationships.c
+++ b/source/blender/editors/mask/mask_relationships.c
@@ -21,6 +21,7 @@
#include "WM_types.h"
#include "ED_clip.h" /* frame remapping functions */
+#include "ED_mask.h"
#include "ED_screen.h"
#include "mask_intern.h" /* own include */
@@ -61,7 +62,7 @@ void MASK_OT_parent_clear(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_parent_clear_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index 0bd054e1b89..4bd80208b9e 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -222,7 +222,7 @@ void MASK_OT_select_all(wmOperatorType *ot)
/* api callbacks */
ot->exec = select_all_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -364,12 +364,9 @@ static int select_exec(bContext *C, wmOperator *op)
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
if (deselect_all) {
- /* For clip editor tracks, leave deselect all to clip editor. */
- if (!ED_clip_can_select(C)) {
- ED_mask_deselect_all(C);
- ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
- return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
- }
+ ED_mask_deselect_all(C);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+ return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
return OPERATOR_PASS_THROUGH;
@@ -401,7 +398,8 @@ void MASK_OT_select(wmOperatorType *ot)
/* api callbacks */
ot->exec = select_exec;
ot->invoke = select_invoke;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
+ ot->get_name = ED_select_pick_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
@@ -506,7 +504,7 @@ void MASK_OT_select_box(wmOperatorType *ot)
ot->invoke = WM_gesture_box_invoke;
ot->exec = box_select_exec;
ot->modal = WM_gesture_box_modal;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
@@ -629,7 +627,7 @@ void MASK_OT_select_lasso(wmOperatorType *ot)
ot->invoke = WM_gesture_lasso_invoke;
ot->modal = WM_gesture_lasso_modal;
ot->exec = clip_lasso_select_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
ot->cancel = WM_gesture_lasso_cancel;
/* flags */
@@ -747,7 +745,8 @@ void MASK_OT_select_circle(wmOperatorType *ot)
ot->invoke = WM_gesture_circle_invoke;
ot->modal = WM_gesture_circle_modal;
ot->exec = circle_select_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -810,7 +809,7 @@ void MASK_OT_select_linked_pick(wmOperatorType *ot)
/* api callbacks */
ot->invoke = mask_select_linked_pick_invoke;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -865,7 +864,7 @@ void MASK_OT_select_linked(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_select_linked_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -964,7 +963,7 @@ void MASK_OT_select_more(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_select_more_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -984,7 +983,7 @@ void MASK_OT_select_less(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_select_less_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index dd54d84a90b..55145f27012 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -67,7 +67,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_shape_key_insert_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -113,7 +113,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_shape_key_clear_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -197,7 +197,7 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_shape_key_feather_reset_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -356,7 +356,7 @@ void MASK_OT_shape_key_rekey(wmOperatorType *ot)
/* api callbacks */
ot->exec = mask_shape_key_rekey_exec;
- ot->poll = ED_maskedit_mask_poll;
+ ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index ed09e5a6334..28ac913a3e3 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -24,7 +24,7 @@ set(INC
)
set(SRC
- editface.c
+ editface.cc
editmesh_add.c
editmesh_add_gizmo.c
editmesh_automerge.c
@@ -51,10 +51,10 @@ set(SRC
editmesh_tools.c
editmesh_undo.c
editmesh_utils.c
- mesh_data.c
+ mesh_data.cc
mesh_mirror.c
mesh_ops.c
- meshtools.c
+ meshtools.cc
mesh_intern.h
)
diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.cc
index a5c6adaa43e..69fe69fe117 100644
--- a/source/blender/editors/mesh/editface.c
+++ b/source/blender/editors/mesh/editface.cc
@@ -36,16 +36,16 @@
/* own include */
-void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
+void paintface_flush_flags(bContext *C, Object *ob, short flag)
{
Mesh *me = BKE_mesh_from_object(ob);
MPoly *polys, *mp_orig;
- const int *index_array = NULL;
+ const int *index_array = nullptr;
int totpoly;
BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0);
- if (me == NULL) {
+ if (me == nullptr) {
return;
}
@@ -60,7 +60,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
- if (ob_eval == NULL) {
+ if (ob_eval == nullptr) {
return;
}
@@ -68,7 +68,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval;
bool updated = false;
- if (me_orig != NULL && me_eval != NULL && me_orig->totpoly == me->totpoly) {
+ if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) {
/* Update the COW copy of the mesh. */
for (int i = 0; i < me->totpoly; i++) {
me_orig->mpoly[i].flag = me->mpoly[i].flag;
@@ -79,7 +79,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
updated = true;
}
/* Mesh polys => Final derived polys */
- else if ((index_array = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
+ else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
polys = me_eval->mpoly;
totpoly = me_eval->totpoly;
@@ -104,10 +104,10 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_SELECT_PAINT);
}
- DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
}
else {
- DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
}
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
@@ -115,18 +115,13 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag)
void paintface_hide(bContext *C, Object *ob, const bool unselected)
{
- Mesh *me;
- MPoly *mpoly;
- int a;
-
- me = BKE_mesh_from_object(ob);
- if (me == NULL || me->totpoly == 0) {
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr || me->totpoly == 0) {
return;
}
- mpoly = me->mpoly;
- a = me->totpoly;
- while (a--) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &me->mpoly[i];
if ((mpoly->flag & ME_HIDE) == 0) {
if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) {
mpoly->flag |= ME_HIDE;
@@ -136,8 +131,6 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected)
if (mpoly->flag & ME_HIDE) {
mpoly->flag &= ~ME_FACE_SEL;
}
-
- mpoly++;
}
BKE_mesh_flush_hidden_from_polys(me);
@@ -147,23 +140,17 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected)
void paintface_reveal(bContext *C, Object *ob, const bool select)
{
- Mesh *me;
- MPoly *mpoly;
- int a;
-
- me = BKE_mesh_from_object(ob);
- if (me == NULL || me->totpoly == 0) {
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr || me->totpoly == 0) {
return;
}
- mpoly = me->mpoly;
- a = me->totpoly;
- while (a--) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &me->mpoly[i];
if (mpoly->flag & ME_HIDE) {
SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL);
mpoly->flag &= ~ME_HIDE;
}
- mpoly++;
}
BKE_mesh_flush_hidden_from_polys(me);
@@ -171,13 +158,10 @@ void paintface_reveal(bContext *C, Object *ob, const bool select)
paintface_flush_flags(C, ob, SELECT | ME_HIDE);
}
-/* Set tface seams based on edge data, uses hash table to find seam edges. */
+/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */
static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select)
{
- MPoly *mp;
- MLoop *ml;
- int a, b;
bool do_it = true;
bool mark = false;
@@ -186,20 +170,20 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
if (index != (uint)-1) {
/* only put face under cursor in array */
- mp = &me->mpoly[index];
+ MPoly *mp = &me->mpoly[index];
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
BLI_BITMAP_ENABLE(poly_tag, index);
}
else {
/* fill array by selection */
- mp = me->mpoly;
- for (a = 0; a < me->totpoly; a++, mp++) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mp = &me->mpoly[i];
if (mp->flag & ME_HIDE) {
/* pass */
}
else if (mp->flag & ME_FACE_SEL) {
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
- BLI_BITMAP_ENABLE(poly_tag, a);
+ BLI_BITMAP_ENABLE(poly_tag, i);
}
}
}
@@ -208,17 +192,17 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
do_it = false;
/* expand selection */
- mp = me->mpoly;
- for (a = 0; a < me->totpoly; a++, mp++) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mp = &me->mpoly[i];
if (mp->flag & ME_HIDE) {
continue;
}
- if (!BLI_BITMAP_TEST(poly_tag, a)) {
+ if (!BLI_BITMAP_TEST(poly_tag, i)) {
mark = false;
- ml = me->mloop + mp->loopstart;
- for (b = 0; b < mp->totloop; b++, ml++) {
+ MLoop *ml = me->mloop + mp->loopstart;
+ for (int b = 0; b < mp->totloop; b++, ml++) {
if ((me->medge[ml->e].flag & ME_SEAM) == 0) {
if (BLI_BITMAP_TEST(edge_tag, ml->e)) {
mark = true;
@@ -228,7 +212,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
}
if (mark) {
- BLI_BITMAP_ENABLE(poly_tag, a);
+ BLI_BITMAP_ENABLE(poly_tag, i);
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
do_it = true;
}
@@ -238,8 +222,9 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
MEM_freeN(edge_tag);
- for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) {
- if (BLI_BITMAP_TEST(poly_tag, a)) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mp = &me->mpoly[i];
+ if (BLI_BITMAP_TEST(poly_tag, i)) {
SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL);
}
}
@@ -249,11 +234,10 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select)
{
- Mesh *me;
uint index = (uint)-1;
- me = BKE_mesh_from_object(ob);
- if (me == NULL || me->totpoly == 0) {
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr || me->totpoly == 0) {
return;
}
@@ -270,34 +254,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
{
- Mesh *me;
- MPoly *mpoly;
- int a;
-
- me = BKE_mesh_from_object(ob);
- if (me == NULL) {
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr) {
return false;
}
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
- mpoly = me->mpoly;
- a = me->totpoly;
- while (a--) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &me->mpoly[i];
if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) {
action = SEL_DESELECT;
break;
}
- mpoly++;
}
}
bool changed = false;
- mpoly = me->mpoly;
- a = me->totpoly;
- while (a--) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &me->mpoly[i];
if ((mpoly->flag & ME_HIDE) == 0) {
switch (action) {
case SEL_SELECT:
@@ -318,7 +295,6 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
break;
}
}
- mpoly++;
}
if (changed) {
@@ -331,30 +307,25 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
{
- const Mesh *me;
- const MPoly *mp;
- const MLoop *ml;
- const MVert *mvert;
- int a, b;
bool ok = false;
float vec[3], bmat[3][3];
- me = BKE_mesh_from_object(ob);
+ const Mesh *me = BKE_mesh_from_object(ob);
if (!me || !me->mloopuv) {
return ok;
}
+ const MVert *mvert = me->mvert;
copy_m3_m4(bmat, ob->obmat);
- mvert = me->mvert;
- mp = me->mpoly;
- for (a = me->totpoly; a > 0; a--, mp++) {
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mp = &me->mpoly[i];
if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) {
continue;
}
- ml = me->mloop + mp->loopstart;
- for (b = 0; b < mp->totloop; b++, ml++) {
+ const MLoop *ml = me->mloop + mp->loopstart;
+ for (int b = 0; b < mp->totloop; b++, ml++) {
mul_v3_m3v3(vec, bmat, mvert[ml->v].co);
add_v3_v3v3(vec, vec, ob->obmat[3]);
minmax_v3v3_v3(r_min, r_max, vec);
@@ -366,19 +337,18 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
return ok;
}
-bool paintface_mouse_select(struct bContext *C,
+bool paintface_mouse_select(bContext *C,
const int mval[2],
- const struct SelectPick_Params *params,
+ const SelectPick_Params *params,
Object *ob)
{
- Mesh *me;
- MPoly *mpoly_sel = NULL;
+ MPoly *mpoly_sel = nullptr;
uint index;
bool changed = false;
bool found = false;
/* Get the face under the cursor */
- me = BKE_mesh_from_object(ob);
+ Mesh *me = BKE_mesh_from_object(ob);
if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
if (index < me->totpoly) {
@@ -444,11 +414,11 @@ void paintvert_flush_flags(Object *ob)
Mesh *me = BKE_mesh_from_object(ob);
Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
MVert *mvert_eval, *mv;
- const int *index_array = NULL;
+ const int *index_array = nullptr;
int totvert;
int i;
- if (me == NULL) {
+ if (me == nullptr) {
return;
}
@@ -456,11 +426,11 @@ void paintvert_flush_flags(Object *ob)
* since this could become slow for realtime updates (circle-select for eg) */
BKE_mesh_flush_select_from_verts(me);
- if (me_eval == NULL) {
+ if (me_eval == nullptr) {
return;
}
- index_array = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
+ index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
mvert_eval = me_eval->mvert;
totvert = me_eval->totvert;
@@ -485,41 +455,34 @@ void paintvert_flush_flags(Object *ob)
BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL);
}
-void paintvert_tag_select_update(struct bContext *C, struct Object *ob)
+void paintvert_tag_select_update(bContext *C, Object *ob)
{
- DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
}
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
{
- Mesh *me;
- MVert *mvert;
- int a;
-
- me = BKE_mesh_from_object(ob);
- if (me == NULL) {
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr) {
return false;
}
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
- mvert = me->mvert;
- a = me->totvert;
- while (a--) {
+ for (int i = 0; i < me->totvert; i++) {
+ MVert *mvert = &me->mvert[i];
if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) {
action = SEL_DESELECT;
break;
}
- mvert++;
}
}
bool changed = false;
- mvert = me->mvert;
- a = me->totvert;
- while (a--) {
+ for (int i = 0; i < me->totvert; i++) {
+ MVert *mvert = &me->mvert[i];
if ((mvert->flag & ME_HIDE) == 0) {
switch (action) {
case SEL_SELECT:
@@ -540,7 +503,6 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
break;
}
}
- mvert++;
}
if (changed) {
@@ -565,11 +527,8 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
{
Mesh *me = BKE_mesh_from_object(ob);
- MVert *mv;
- MDeformVert *dv;
- int a, tot;
- if (me == NULL || me->dvert == NULL) {
+ if (me == nullptr || me->dvert == nullptr) {
return;
}
@@ -577,12 +536,11 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
}
- dv = me->dvert;
- tot = me->totvert;
-
- for (a = 0, mv = me->mvert; a < tot; a++, mv++, dv++) {
+ for (int i = 0; i < me->totvert; i++) {
+ MVert *mv = &me->mvert[i];
+ MDeformVert *dv = &me->dvert[i];
if ((mv->flag & ME_HIDE) == 0) {
- if (dv->dw == NULL) {
+ if (dv->dw == nullptr) {
/* if null weight then not grouped */
mv->flag |= SELECT;
}
diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c
index b2f33428b21..57e0d04727c 100644
--- a/source/blender/editors/mesh/editmesh_add.c
+++ b/source/blender/editors/mesh/editmesh_add.c
@@ -113,7 +113,7 @@ static int add_primitive_plane_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(
@@ -178,7 +178,7 @@ static int add_primitive_cube_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(em,
@@ -252,7 +252,7 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(
@@ -324,7 +324,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(em,
@@ -400,7 +400,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(em,
@@ -476,7 +476,7 @@ static int add_primitive_grid_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(
@@ -553,7 +553,7 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(em,
@@ -614,7 +614,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(
@@ -682,7 +682,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op)
em = BKE_editmesh_from_object(obedit);
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(
diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c
index d0f37314661..f5090c0143d 100644
--- a/source/blender/editors/mesh/editmesh_add_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_add_gizmo.c
@@ -328,7 +328,7 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op)
const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
if (calc_uvs) {
- ED_mesh_uv_texture_ensure(obedit->data, NULL);
+ ED_mesh_uv_ensure(obedit->data, NULL);
}
if (!EDBM_op_call_and_selectf(em,
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 969d5b5912c..e7891450bd6 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -669,7 +669,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
short etype = event->type;
short eval = event->val;
- /* When activated from toolbar, need to convert leftmouse release to confirm */
+ /* When activated from toolbar, need to convert left-mouse release to confirm. */
if (ELEM(etype, LEFTMOUSE, opdata->launch_event) && (eval == KM_RELEASE) &&
RNA_boolean_get(op->ptr, "release_confirm")) {
etype = EVT_MODAL_MAP;
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 2642a613e92..166eb40a7db 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -99,8 +99,8 @@ static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_selec
BM_edge_select_set(em->bm, e, true);
}
}
+ EDBM_selectmode_flush(em);
}
- EDBM_select_flush(em);
}
EDBM_update(me,
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index ee40431c101..5680865ae67 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -190,6 +190,22 @@ typedef struct KnifeBVH {
} KnifeBVH;
+/** Additional per-object data. */
+typedef struct KnifeObjectInfo {
+ const float (*cagecos)[3];
+
+ /**
+ * Optionally allocate triangle indices, these are needed for non-interactive knife
+ * projection as multiple cuts are made without the BVH being updated.
+ * Using these indices the it's possible to access `cagecos` even if the face has been cut
+ * and the loops in `em->looptris` no longer refer to the original triangles, see: T97153.
+ */
+ const int (*tri_indices)[3];
+
+ /** Only assigned for convenient access. */
+ BMEditMesh *em;
+} KnifeObjectInfo;
+
/* struct for properties used while drawing */
typedef struct KnifeTool_OpData {
ARegion *region; /* Region that knifetool was activated in. */
@@ -203,6 +219,9 @@ typedef struct KnifeTool_OpData {
Object **objects;
uint objects_len;
+ /** Array `objects_len` length of additional per-object data. */
+ KnifeObjectInfo *objects_info;
+
MemArena *arena;
/* Reused for edge-net filling. */
@@ -220,7 +239,6 @@ typedef struct KnifeTool_OpData {
GHash *facetrimap;
KnifeBVH bvh;
- const float (**cagecos)[3];
BLI_mempool *kverts;
BLI_mempool *kedges;
@@ -359,45 +377,45 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3],
kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL);
/* Ray-cast all planes. */
+ float ray_dir[3];
+ float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
+ float lambda_best[2] = {-FLT_MAX, FLT_MAX};
+ int i;
+
{
- float ray_dir[3];
- float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
- float lambda_best[2] = {-FLT_MAX, FLT_MAX};
- int i;
+ float curr_cage_adjust[3];
+ float co_depth[3];
- {
- float curr_cage_adjust[3];
- float co_depth[3];
+ copy_v3_v3(co_depth, kcd->prev.cage);
+ ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
- copy_v3_v3(co_depth, kcd->prev.cage);
- ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
+ sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
+ }
- sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
+ for (i = 0; i < 4; i++) {
+ float ray_hit[3];
+ float lambda_test;
+ if (!isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
+ continue;
}
- for (i = 0; i < 4; i++) {
- float ray_hit[3];
- float lambda_test;
- if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
- madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
- if (lambda_test < 0.0f) {
- if (lambda_test > lambda_best[0]) {
- copy_v3_v3(ray_hit_best[0], ray_hit);
- lambda_best[0] = lambda_test;
- }
- }
- else {
- if (lambda_test < lambda_best[1]) {
- copy_v3_v3(ray_hit_best[1], ray_hit);
- lambda_best[1] = lambda_test;
- }
- }
+ madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
+ if (lambda_test < 0.0f) {
+ if (lambda_test > lambda_best[0]) {
+ copy_v3_v3(ray_hit_best[0], ray_hit);
+ lambda_best[0] = lambda_test;
+ }
+ }
+ else {
+ if (lambda_test < lambda_best[1]) {
+ copy_v3_v3(ray_hit_best[1], ray_hit);
+ lambda_best[1] = lambda_test;
}
}
-
- copy_v3_v3(r_v1, ray_hit_best[0]);
- copy_v3_v3(r_v2, ray_hit_best[1]);
}
+
+ copy_v3_v3(r_v1, ray_hit_best[0]);
+ copy_v3_v3(r_v2, ray_hit_best[1]);
}
static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
@@ -422,43 +440,45 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd)
{
- if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) {
- float v1[3], v2[3];
+ if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) {
+ return;
+ }
- /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */
- knifetool_raycast_planes(kcd, v1, v2);
+ float v1[3], v2[3];
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */
+ knifetool_raycast_planes(kcd, v1, v2);
- switch (kcd->constrain_axis) {
- case KNF_CONSTRAIN_AXIS_X: {
- immUniformColor3ubv(kcd->colors.xaxis);
- break;
- }
- case KNF_CONSTRAIN_AXIS_Y: {
- immUniformColor3ubv(kcd->colors.yaxis);
- break;
- }
- case KNF_CONSTRAIN_AXIS_Z: {
- immUniformColor3ubv(kcd->colors.zaxis);
- break;
- }
- default: {
- immUniformColor3ubv(kcd->colors.axis_extra);
- break;
- }
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+
+ switch (kcd->constrain_axis) {
+ case KNF_CONSTRAIN_AXIS_X: {
+ immUniformColor3ubv(kcd->colors.xaxis);
+ break;
}
+ case KNF_CONSTRAIN_AXIS_Y: {
+ immUniformColor3ubv(kcd->colors.yaxis);
+ break;
+ }
+ case KNF_CONSTRAIN_AXIS_Z: {
+ immUniformColor3ubv(kcd->colors.zaxis);
+ break;
+ }
+ default: {
+ immUniformColor3ubv(kcd->colors.axis_extra);
+ break;
+ }
+ }
- GPU_line_width(2.0);
+ GPU_line_width(2.0);
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, v1);
- immVertex3fv(pos, v2);
- immEnd();
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, v1);
+ immVertex3fv(pos, v2);
+ immEnd();
- immUnbindProgram();
- }
+ immUnbindProgram();
}
static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
@@ -1134,6 +1154,52 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Knife Object Info Accessors (#KnifeObjectInfo)
+ * \{ */
+
+static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd,
+ int base_index,
+ int tri_index,
+ int tri_index_buf[3])
+{
+ const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index];
+ if (obinfo->tri_indices) {
+ return obinfo->tri_indices[tri_index];
+ }
+ for (int i = 0; i < 3; i++) {
+ tri_index_buf[i] = BM_elem_index_get(obinfo->em->looptris[tri_index][i]->v);
+ }
+ return tri_index_buf;
+}
+
+static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd,
+ int base_index,
+ int tri_index,
+ float cos[3][3])
+{
+ const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index];
+ int tri_ind_buf[3];
+ const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf);
+ for (int i = 0; i < 3; i++) {
+ copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]);
+ }
+}
+
+static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd,
+ int base_index,
+ int tri_index,
+ float cos[3][3])
+{
+ knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos);
+ const Object *ob = kcd->objects[base_index];
+ for (int i = 0; i < 3; i++) {
+ mul_m4_v3(ob->obmat, cos[i]);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Knife BVH Utils
* \{ */
@@ -1162,7 +1228,6 @@ static void knife_bvh_init(KnifeTool_OpData *kcd)
}
/* Construct BVH Tree. */
- float cos[3][3];
const float epsilon = FLT_EPSILON * 2.0f;
int tottri = 0;
int ob_tottri = 0;
@@ -1220,16 +1285,9 @@ static void knife_bvh_init(KnifeTool_OpData *kcd)
continue;
}
- copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]);
- copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]);
- copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]);
-
- /* Convert to world-space. */
- mul_m4_v3(ob->obmat, cos[0]);
- mul_m4_v3(ob->obmat, cos[1]);
- mul_m4_v3(ob->obmat, cos[2]);
-
- BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3);
+ float tri_cos[3][3];
+ knife_bm_tri_cagecos_get_worldspace(kcd, b, i, tri_cos);
+ BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3);
}
tottri += em->tottri;
@@ -1252,6 +1310,10 @@ static void knife_bvh_raycast_cb(void *userdata,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
+ if (index == -1) {
+ return;
+ }
+
KnifeTool_OpData *kcd = userdata;
BMLoop **ltri;
Object *ob;
@@ -1260,65 +1322,55 @@ static void knife_bvh_raycast_cb(void *userdata,
float dist, uv[2];
bool isect;
int tottri;
- float tri_cos[3][3];
- if (index != -1) {
- tottri = 0;
- uint b = 0;
- for (; b < kcd->objects_len; b++) {
- index -= tottri;
- ob = kcd->objects[b];
- em = BKE_editmesh_from_object(ob);
- tottri = em->tottri;
- if (index < tottri) {
- ltri = em->looptris[index];
- break;
- }
+ tottri = 0;
+ uint b = 0;
+ for (; b < kcd->objects_len; b++) {
+ index -= tottri;
+ ob = kcd->objects[b];
+ em = BKE_editmesh_from_object(ob);
+ tottri = em->tottri;
+ if (index < tottri) {
+ ltri = em->looptris[index];
+ break;
}
+ }
- if (kcd->bvh.filter_cb) {
- if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) {
- return;
- }
+ if (kcd->bvh.filter_cb) {
+ if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) {
+ return;
}
+ }
- copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]);
- copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]);
- copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]);
- mul_m4_v3(ob->obmat, tri_cos[0]);
- mul_m4_v3(ob->obmat, tri_cos[1]);
- mul_m4_v3(ob->obmat, tri_cos[2]);
-
- isect =
- (ray->radius > 0.0f ?
- isect_ray_tri_epsilon_v3(ray->origin,
- ray->direction,
- tri_cos[0],
- tri_cos[1],
- tri_cos[2],
- &dist,
- uv,
- ray->radius) :
+ float tri_cos[3][3];
+ knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos);
+ isect = (ray->radius > 0.0f ?
+ isect_ray_tri_epsilon_v3(
+ ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv, ray->radius) :
#ifdef USE_KDOPBVH_WATERTIGHT
- isect_ray_tri_watertight_v3(
- ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv));
+ isect_ray_tri_watertight_v3(
+ ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, uv));
#else
- isect_ray_tri_v3(
- ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv));
+ isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv);
#endif
- if (isect && dist < hit->dist) {
- hit->dist = dist;
- hit->index = index;
+ if (isect && dist < hit->dist) {
+ madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
- copy_v3_v3(hit->no, ltri[0]->f->no);
+ /* Discard clipped points. */
+ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
+ ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false)) {
+ return;
+ }
- madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
+ hit->dist = dist;
+ hit->index = index;
- kcd->bvh.looptris = em->looptris;
- copy_v2_v2(kcd->bvh.uv, uv);
- kcd->bvh.base_index = b;
- }
+ copy_v3_v3(hit->no, ltri[0]->f->no);
+
+ kcd->bvh.looptris = em->looptris;
+ copy_v2_v2(kcd->bvh.uv, uv);
+ kcd->bvh.base_index = b;
}
}
@@ -1333,7 +1385,6 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd,
uint *r_base_index)
{
BMFace *face;
- BMLoop **ltri;
BVHTreeRayHit hit;
const float dist = r_dist ? *r_dist : FLT_MAX;
hit.dist = dist;
@@ -1347,8 +1398,9 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd,
/* Hits returned in world space. */
if (r_hitout) {
- ltri = kcd->bvh.looptris[hit.index];
- interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv);
+ float tri_cos[3][3];
+ knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos);
+ interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv);
if (r_cagehit) {
copy_v3_v3(r_cagehit, hit.co);
@@ -1384,7 +1436,6 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd,
kcd->bvh.filter_data = filter_userdata;
BMFace *face;
- BMLoop **ltri;
BVHTreeRayHit hit;
const float dist = r_dist ? *r_dist : FLT_MAX;
hit.dist = dist;
@@ -1401,8 +1452,9 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd,
/* Hits returned in world space. */
if (r_hitout) {
- ltri = kcd->bvh.looptris[hit.index];
- interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv);
+ float tri_cos[3][3];
+ knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos);
+ interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv);
if (r_cagehit) {
copy_v3_v3(r_cagehit, hit.co);
@@ -1684,7 +1736,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob
BMFace *f;
if (BM_elem_index_get(v) >= 0) {
- cageco = kcd->cagecos[base_index][BM_elem_index_get(v)];
+ cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)];
}
else {
cageco = v->co;
@@ -1928,29 +1980,30 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd)
* Also remove all but one of a series of vertex hits for the same vertex. */
for (int i = 0; i < n; i++) {
KnifeLineHit *lhi = &linehits[i];
- if (lhi->v) {
- for (int j = i - 1; j >= 0; j--) {
- KnifeLineHit *lhj = &linehits[j];
- if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
- fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
- break;
- }
+ if (lhi->v == NULL) {
+ continue;
+ }
- if (lhi->kfe == lhj->kfe) {
- lhj->l = -1.0f;
- is_double = true;
- }
+ for (int j = i - 1; j >= 0; j--) {
+ KnifeLineHit *lhj = &linehits[j];
+ if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
+ fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
+ break;
}
- for (int j = i + 1; j < n; j++) {
- KnifeLineHit *lhj = &linehits[j];
- if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
- fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
- break;
- }
- if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) {
- lhj->l = -1.0f;
- is_double = true;
- }
+
+ if (lhi->kfe == lhj->kfe) {
+ lhj->l = -1.0f;
+ is_double = true;
+ }
+ }
+ for (int j = i + 1; j < n; j++) {
+ KnifeLineHit *lhj = &linehits[j];
+ if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
+ break;
+ }
+ if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) {
+ lhj->l = -1.0f;
+ is_double = true;
}
}
}
@@ -2222,11 +2275,12 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, Li
/* Remove dangling edges, not essential - but nice for users. */
for (i = 0; i < edge_array_len_orig; i++) {
- if (kfe_array[i]) {
- if (BM_edge_is_wire(kfe_array[i]->e)) {
- BM_edge_kill(bm, kfe_array[i]->e);
- kfe_array[i]->e = NULL;
- }
+ if (kfe_array[i] == NULL) {
+ continue;
+ }
+ if (BM_edge_is_wire(kfe_array[i]->e)) {
+ BM_edge_kill(bm, kfe_array[i]->e);
+ kfe_array[i]->e = NULL;
}
}
@@ -2538,34 +2592,30 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd,
BLI_assert(tri_i >= 0 && tri_i < tottri);
for (; tri_i < tottri; tri_i++) {
- float lv[3][3];
+ float tri_cos[3][3];
float ray_tri_uv[2];
tri = em->looptris[tri_i];
if (tri[0]->f != f) {
break;
}
- copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]);
- copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]);
- copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]);
- mul_m4_v3(ob->obmat, lv[0]);
- mul_m4_v3(ob->obmat, lv[1]);
- mul_m4_v3(ob->obmat, lv[2]);
+
+ knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, tri_cos);
/* Using epsilon test in case ray is directly through an internal
* tessellation edge and might not hit either tessellation tri with
* an exact test;
* We will exclude hits near real edges by a later test. */
if (isect_ray_tri_epsilon_v3(
- v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) {
+ v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS)) {
/* Check if line coplanar with tri. */
- normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]);
- plane_from_point_normal_v3(tri_plane, lv[0], tri_norm);
+ normal_tri_v3(tri_norm, UNPACK3(tri_cos));
+ plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm);
if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) &&
(dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) {
return false;
}
- interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv);
+ interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv);
/* Now check that far enough away from verts and edges. */
list = knife_get_face_kedges(kcd, ob, base_index, f);
for (ref = list->first; ref; ref = ref->next) {
@@ -2605,9 +2655,10 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd)
ob = kcd->objects[b];
em = BKE_editmesh_from_object(ob);
- if (kcd->cagecos[b]) {
+ const float(*cagecos)[3] = kcd->objects_info[b].cagecos;
+ if (cagecos) {
for (int i = 0; i < em->bm->totvert; i++) {
- copy_v3_v3(ws, kcd->cagecos[b][i]);
+ copy_v3_v3(ws, cagecos[i]);
mul_m4_v3(ob->obmat, ws);
minmax_v3v3_v3(min, max, ws);
}
@@ -3763,7 +3814,7 @@ static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd)
* If scene orientation is set to anything other than global it takes priority.
* Otherwise kcd->constrain_axis_mode is used.
*/
-static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd)
+static void knife_constrain_axis(KnifeTool_OpData *kcd)
{
/* Obtain current mouse position in world space. */
float curr_cage_adjusted[3];
@@ -3772,7 +3823,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd)
/* Constrain axes. */
Scene *scene = kcd->scene;
- ViewLayer *view_layer = CTX_data_view_layer(C);
+ ViewLayer *view_layer = kcd->vc.view_layer;
Object *obedit = (kcd->prev.ob) ? kcd->prev.ob : kcd->vc.obedit;
RegionView3D *rv3d = kcd->region->regiondata;
const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
@@ -3824,7 +3875,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd)
* In this case the selection-buffer is used to select the face,
* then the closest `vert` or `edge` is set, and those will enable `is_co_set`.
*/
-static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2])
+static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
copy_v2_v2(kcd->curr.mval, mval);
@@ -3847,7 +3898,7 @@ static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, cons
}
if (kcd->axis_constrained) {
- knife_constrain_axis(C, kcd);
+ knife_constrain_axis(kcd);
}
}
@@ -3961,10 +4012,13 @@ static void knifetool_undo(KnifeTool_OpData *kcd)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name #KnifeTool_OpData (#op->customdata) Init and Free
+/** \name #KnifeObjectInfo (#kcd->objects_info) Init and Free
* \{ */
-static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index)
+static void knifetool_init_obinfo(KnifeTool_OpData *kcd,
+ Object *ob,
+ uint base_index,
+ bool use_tri_indices)
{
Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
@@ -3973,18 +4027,36 @@ static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_
BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT);
- kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
+ KnifeObjectInfo *obinfo = &kcd->objects_info[base_index];
+ obinfo->em = em_eval;
+ obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL);
+
+ if (use_tri_indices) {
+ BMLoop *(*looptris)[3] = em_eval->looptris;
+ int(*tri_indices)[3] = MEM_mallocN(sizeof(int[3]) * em_eval->tottri, __func__);
+ for (int i = 0; i < em_eval->tottri; i++) {
+ BMLoop **tri = looptris[i];
+ tri_indices[i][0] = BM_elem_index_get(tri[0]->v);
+ tri_indices[i][1] = BM_elem_index_get(tri[1]->v);
+ tri_indices[i][2] = BM_elem_index_get(tri[2]->v);
+ }
+ obinfo->tri_indices = tri_indices;
+ }
}
-static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index)
+static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index)
{
- if (kcd->cagecos[base_index]) {
- MEM_freeN((void *)kcd->cagecos[base_index]);
- kcd->cagecos[base_index] = NULL;
- }
+ MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos);
+ MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #KnifeTool_OpData (#op->customdata) Init and Free
+ * \{ */
+
static void knife_init_colors(KnifeColors *colors)
{
/* Possible BMESH_TODO: add explicit themes or calculate these by
@@ -4007,8 +4079,7 @@ static void knife_init_colors(KnifeColors *colors)
}
/* called when modal loop selection gets set up... */
-static void knifetool_init(bContext *C,
- ViewContext *vc,
+static void knifetool_init(ViewContext *vc,
KnifeTool_OpData *kcd,
const bool only_select,
const bool cut_through,
@@ -4018,6 +4089,10 @@ static void knifetool_init(bContext *C,
const float angle_snapping_increment,
const bool is_interactive)
{
+ /* Needed so multiple non-interactive cuts (also called knife-project)
+ * doesn't access indices of loops that were created by cutting, see: T97153. */
+ bool use_tri_indices = !is_interactive;
+
kcd->vc = *vc;
Scene *scene = vc->scene;
@@ -4027,15 +4102,15 @@ static void knifetool_init(bContext *C,
kcd->region = vc->region;
kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- CTX_data_view_layer(C), CTX_wm_view3d(C), &kcd->objects_len);
+ vc->view_layer, vc->v3d, &kcd->objects_len);
Object *ob;
BMEditMesh *em;
- kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos");
+ kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos");
for (uint b = 0; b < kcd->objects_len; b++) {
ob = kcd->objects[b];
em = BKE_editmesh_from_object(ob);
- knifetool_init_cagecos(kcd, ob, b);
+ knifetool_init_obinfo(kcd, ob, b, use_tri_indices);
/* Can't usefully select resulting edges in face mode. */
kcd->select_result = (em->selectmode != SCE_SELECT_FACE);
@@ -4139,9 +4214,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd)
/* Knife BVH cleanup. */
for (int i = 0; i < kcd->objects_len; i++) {
- knifetool_free_cagecos(kcd, i);
+ knifetool_free_obinfo(kcd, i);
}
- MEM_freeN((void *)kcd->cagecos);
+ MEM_freeN((void *)kcd->objects_info);
knife_bvh_free(kcd);
/* Line-hits cleanup. */
@@ -4169,14 +4244,14 @@ static void knifetool_exit(wmOperator *op)
/** \name Mouse-Moving Event Updates
* \{ */
-/* Update active knife edge/vert pointers. */
-static int knife_update_active(bContext *C, KnifeTool_OpData *kcd)
+/** Update active knife edge/vert pointers. */
+static int knife_update_active(KnifeTool_OpData *kcd)
{
/* If no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
* Note that drawing lines in `free-space` isn't properly supported
* but there's no guarantee (0, 0, 0) has any geometry either - campbell */
- if (!knife_snap_update_from_mval(C, kcd, kcd->mval)) {
+ if (!knife_snap_update_from_mval(kcd, kcd->mval)) {
float origin[3];
float origin_ofs[3];
@@ -4197,20 +4272,20 @@ static int knife_update_active(bContext *C, KnifeTool_OpData *kcd)
return 1;
}
-static void knifetool_update_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2])
+static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_recalc_ortho(kcd);
copy_v2_v2(kcd->mval, mval);
- if (knife_update_active(C, kcd)) {
+ if (knife_update_active(kcd)) {
ED_region_tag_redraw(kcd->region);
}
}
-static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const int mval_i[2])
+static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2])
{
const float mval[2] = {UNPACK2(mval_i)};
- knifetool_update_mval(C, kcd, mval);
+ knifetool_update_mval(kcd, mval);
}
/** \} */
@@ -4219,33 +4294,18 @@ static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const in
/** \name Finalization
* \{ */
-/* Called on tool confirmation. */
-static void knifetool_finish_ex(KnifeTool_OpData *kcd)
+static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob)
{
- Object *ob;
- BMEditMesh *em;
- for (uint b = 0; b < kcd->objects_len; b++) {
- ob = kcd->objects[b];
- em = BKE_editmesh_from_object(ob);
-
- knife_make_cuts(kcd, ob);
-
- EDBM_selectmode_flush(em);
- EDBM_update(ob->data,
- &(const struct EDBMUpdate_Params){
- .calc_looptri = true,
- .calc_normals = true,
- .is_destructive = true,
- });
- }
+ knife_make_cuts(kcd, ob);
}
-static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint UNUSED(base_index))
+/**
+ * A post version is needed to to delay recalculating tessellation after making cuts.
+ * Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349.
+ */
+static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob)
{
- knife_make_cuts(kcd, ob);
-
BMEditMesh *em = BKE_editmesh_from_object(ob);
-
EDBM_selectmode_flush(em);
EDBM_update(ob->data,
&(const struct EDBMUpdate_Params){
@@ -4255,6 +4315,16 @@ static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint U
});
}
+/* Called on tool confirmation. */
+static void knifetool_finish_ex(KnifeTool_OpData *kcd)
+{
+ for (uint b = 0; b < kcd->objects_len; b++) {
+ Object *ob = kcd->objects[b];
+ knifetool_finish_single_pre(kcd, ob);
+ knifetool_finish_single_post(kcd, ob);
+ }
+}
+
static void knifetool_finish(wmOperator *op)
{
KnifeTool_OpData *kcd = op->customdata;
@@ -4373,7 +4443,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) {
kcd->angle_snapping_increment = snapping_increment_temp;
}
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
knife_update_header(C, op, kcd);
ED_region_tag_redraw(kcd->region);
return OPERATOR_RUNNING_MODAL;
@@ -4391,7 +4461,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_workspace_status_text(C, NULL);
return OPERATOR_CANCELLED;
- case KNF_MODAL_CONFIRM:
+ case KNF_MODAL_CONFIRM: {
+ const bool changed = (kcd->totkvert != 0);
/* finish */
ED_region_tag_redraw(kcd->region);
@@ -4400,11 +4471,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_workspace_status_text(C, NULL);
/* Cancel to prevent undo push for empty cuts. */
- if (kcd->totkvert == 0) {
+ if (!changed) {
return OPERATOR_CANCELLED;
}
-
return OPERATOR_FINISHED;
+ }
case KNF_MODAL_UNDO:
if (BLI_stack_is_empty(kcd->undostack)) {
ED_region_tag_redraw(kcd->region);
@@ -4413,7 +4484,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
knifetool_undo(kcd);
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
ED_region_tag_redraw(kcd->region);
handled = true;
break;
@@ -4421,7 +4492,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
kcd->snap_midpoints = true;
knife_recalc_ortho(kcd);
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
knife_update_header(C, op, kcd);
ED_region_tag_redraw(kcd->region);
do_refresh = true;
@@ -4431,7 +4502,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
kcd->snap_midpoints = false;
knife_recalc_ortho(kcd);
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
knife_update_header(C, op, kcd);
ED_region_tag_redraw(kcd->region);
do_refresh = true;
@@ -4465,7 +4536,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
RNA_float_get(op->ptr, "angle_snapping_increment"));
knifetool_disable_orientation_locking(kcd);
knife_reset_snap_angle_input(kcd);
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
knife_update_header(C, op, kcd);
ED_region_tag_redraw(kcd->region);
do_refresh = true;
@@ -4550,7 +4621,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
kcd->is_drag_undo = false;
/* Needed because the last face 'hit' is ignored when dragging. */
- knifetool_update_mval(C, kcd, kcd->curr.mval);
+ knifetool_update_mval(kcd, kcd->curr.mval);
}
ED_region_tag_redraw(kcd->region);
@@ -4563,14 +4634,14 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (kcd->is_drag_hold) {
kcd->is_drag_hold = false;
kcd->is_drag_undo = false;
- knifetool_update_mval(C, kcd, kcd->curr.mval);
+ knifetool_update_mval(kcd, kcd->curr.mval);
}
kcd->prev = kcd->curr;
kcd->curr = kcd->init;
knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval);
- knifetool_update_mval(C, kcd, kcd->curr.mval);
+ knifetool_update_mval(kcd, kcd->curr.mval);
knife_add_cut(kcd);
@@ -4606,7 +4677,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_PASS_THROUGH;
case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */
if (kcd->mode != MODE_PANNING) {
- knifetool_update_mval_i(C, kcd, event->mval);
+ knifetool_update_mval_i(kcd, event->mval);
+ knife_update_header(C, op, kcd);
if (kcd->is_drag_hold) {
if (kcd->totlinehit >= 2) {
@@ -4633,7 +4705,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) {
kcd->angle_snapping_increment = snapping_increment_temp;
}
- knife_update_active(C, kcd);
+ knife_update_active(kcd);
knife_update_header(C, op, kcd);
ED_region_tag_redraw(kcd->region);
return OPERATOR_RUNNING_MODAL;
@@ -4688,7 +4760,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (do_refresh) {
/* We don't really need to update mval,
* but this happens to be the best way to refresh at the moment. */
- knifetool_update_mval_i(C, kcd, event->mval);
+ knifetool_update_mval_i(kcd, event->mval);
+ knife_update_header(C, op, kcd);
}
/* Keep going until the user confirms. */
@@ -4714,8 +4787,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* alloc new customdata */
kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
- knifetool_init(C,
- &vc,
+ knifetool_init(&vc,
kcd,
only_select,
cut_through,
@@ -4751,7 +4823,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
WM_event_add_modal_handler(C, op);
- knifetool_update_mval_i(C, kcd, event->mval);
+ knifetool_update_mval_i(kcd, event->mval);
if (wait_for_input == false) {
/* Avoid copy-paste logic. */
@@ -4869,7 +4941,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
return false;
}
-void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through)
+void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through)
{
KnifeTool_OpData *kcd;
@@ -4884,8 +4956,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
- knifetool_init(C,
- vc,
+ knifetool_init(vc,
kcd,
only_select,
cut_through,
@@ -4911,7 +4982,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
int i;
for (i = 0; i < mval_tot; i++) {
- knifetool_update_mval(C, kcd, mval_fl[i]);
+ knifetool_update_mval(kcd, mval_fl[i]);
if (i == 0) {
knife_start_cut(kcd);
kcd->mode = MODE_DRAGGING;
@@ -4939,7 +5010,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
}
- knifetool_finish_single_ex(kcd, ob, b);
+ knifetool_finish_single_pre(kcd, ob);
/* Tag faces inside! */
if (use_tag) {
@@ -4963,17 +5034,19 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
/* Tag all faces linked to cut edges. */
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
/* Check are we tagged?, then we are an original face. */
- if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) {
- BMFace *f;
- BMIter fiter;
- BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
- float cent[3], cent_ss[2];
- BM_face_calc_point_in_face(f, cent);
- mul_m4_v3(ob->obmat, cent);
- knife_project_v2(kcd, cent, cent_ss);
- if (edbm_mesh_knife_point_isect(polys, cent_ss)) {
- BM_elem_flag_enable(f, BM_ELEM_TAG);
- }
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ BMFace *f;
+ BMIter fiter;
+ BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
+ float cent[3], cent_ss[2];
+ BM_face_calc_point_in_face(f, cent);
+ mul_m4_v3(ob->obmat, cent);
+ knife_project_v2(kcd, cent, cent_ss);
+ if (edbm_mesh_knife_point_isect(polys, cent_ss)) {
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
}
}
}
@@ -4983,43 +5056,45 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
BMFace *f;
keep_search = false;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) {
- /* Am I connected to a tagged face via an un-tagged edge
- * (ie, not across a cut)? */
- BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
- BMLoop *l_iter = l_first;
- bool found = false;
-
- do {
- if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) {
- /* Now check if the adjacent faces is tagged. */
- BMLoop *l_radial_iter = l_iter->radial_next;
- if (l_radial_iter != l_iter) {
- do {
- if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
- found = true;
- }
- } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter &&
- (found == false));
- }
- }
- } while ((l_iter = l_iter->next) != l_first && (found == false));
-
- if (found) {
- float cent[3], cent_ss[2];
- BM_face_calc_point_in_face(f, cent);
- mul_m4_v3(ob->obmat, cent);
- knife_project_v2(kcd, cent, cent_ss);
- if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) &&
- edbm_mesh_knife_point_isect(polys, cent_ss)) {
- BM_elem_flag_enable(f, BM_ELEM_TAG);
- keep_search = true;
- }
- else {
- /* Don't lose time on this face again, set it as outside. */
- F_ISECT_SET_OUTSIDE(f);
+ if (BM_elem_flag_test(f, BM_ELEM_TAG) || !F_ISECT_IS_UNKNOWN(f)) {
+ continue;
+ }
+
+ /* Am I connected to a tagged face via an un-tagged edge
+ * (ie, not across a cut)? */
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
+ bool found = false;
+
+ do {
+ if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) {
+ /* Now check if the adjacent faces is tagged. */
+ BMLoop *l_radial_iter = l_iter->radial_next;
+ if (l_radial_iter != l_iter) {
+ do {
+ if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
+ found = true;
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter &&
+ (found == false));
}
}
+ } while ((l_iter = l_iter->next) != l_first && (found == false));
+
+ if (found) {
+ float cent[3], cent_ss[2];
+ BM_face_calc_point_in_face(f, cent);
+ mul_m4_v3(ob->obmat, cent);
+ knife_project_v2(kcd, cent, cent_ss);
+ if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) &&
+ edbm_mesh_knife_point_isect(polys, cent_ss)) {
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
+ keep_search = true;
+ }
+ else {
+ /* Don't lose time on this face again, set it as outside. */
+ F_ISECT_SET_OUTSIDE(f);
+ }
}
}
} while (keep_search);
@@ -5028,6 +5103,10 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag
#undef F_ISECT_SET_UNKNOWN
#undef F_ISECT_SET_OUTSIDE
}
+
+ /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and
+ * the doc-string for #knifetool_finish_single_post. */
+ knifetool_finish_single_post(kcd, ob);
}
knifetool_exit_ex(kcd);
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index bce46dd7cf7..c32b1fa99c0 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -142,7 +142,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op)
ED_view3d_viewcontext_init_object(&vc, obedit);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- EDBM_mesh_knife(C, &vc, polys, true, cut_through);
+ EDBM_mesh_knife(&vc, polys, true, cut_through);
/* select only tagged faces */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index 3f05c27e8fa..c931cb4948b 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -1370,8 +1370,14 @@ static bool edbm_select_similar_poll_property(const bContext *UNUSED(C),
const char *prop_id = RNA_property_identifier(prop);
const int type = RNA_enum_get(op->ptr, "type");
+ /* Only show compare when it is used. */
+ if (STREQ(prop_id, "compare")) {
+ if (type == SIMVERT_VGROUP) {
+ return false;
+ }
+ }
/* Only show threshold when it is used. */
- if (STREQ(prop_id, "threshold")) {
+ else if (STREQ(prop_id, "threshold")) {
if (!ELEM(type,
SIMVERT_NORMAL,
SIMEDGE_BEVEL,
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index cafcc4ec578..1febc429edc 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3092,8 +3092,8 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op)
BMOperator bmop;
- Mesh *me = BKE_object_get_original_mesh(ob);
- CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
+ const Mesh *me = BKE_object_get_original_mesh(ob);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) {
continue;
@@ -3144,8 +3144,8 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op)
continue;
}
- Mesh *me = BKE_object_get_original_mesh(obedit);
- CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
+ const Mesh *me = BKE_object_get_original_mesh(obedit);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) {
continue;
@@ -7032,6 +7032,14 @@ static void sort_bmelem_flag(bContext *C,
}
BM_mesh_remap(em->bm, map[0], map[1], map[2]);
+
+ EDBM_update(ob->data,
+ &(const struct EDBMUpdate_Params){
+ .calc_looptri = (totelem[2] != 0),
+ .calc_normals = false,
+ .is_destructive = true,
+ });
+
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index ecc5f8f8ef5..d75c92f963f 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -72,7 +72,7 @@ static CLG_LogRef LOG = {"ed.undo.mesh"};
/* Single linked list of layers stored per type */
typedef struct BArrayCustomData {
struct BArrayCustomData *next;
- CustomDataType type;
+ eCustomDataType type;
int states_len; /* number of layers for each type */
BArrayState *states[0];
} BArrayCustomData;
@@ -149,7 +149,7 @@ static void um_arraystore_cd_compact(struct CustomData *cdata,
const BArrayCustomData *bcd_reference_current = bcd_reference;
BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL;
for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) {
- const CustomDataType type = cdata->layers[layer_start].type;
+ const eCustomDataType type = cdata->layers[layer_start].type;
/* Perform a full copy on dynamic layers.
*
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index c3d5f33705c..04030583f5c 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -9,6 +9,7 @@
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_alloca.h"
@@ -19,6 +20,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_editmesh_bvh.h"
#include "BKE_global.h"
@@ -1639,21 +1641,22 @@ void EDBM_project_snap_verts(
float mval[2], co_proj[3];
if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- if (ED_transform_snap_object_project_view3d(snap_context,
- depsgraph,
- region,
- CTX_wm_view3d(C),
- SCE_SNAP_MODE_FACE,
- &(const struct SnapObjectParams){
- .snap_select = SNAP_NOT_ACTIVE,
- .edit_mode_type = SNAP_GEOM_FINAL,
- .use_occlusion_test = true,
- },
- mval,
- NULL,
- NULL,
- co_proj,
- NULL)) {
+ if (ED_transform_snap_object_project_view3d(
+ snap_context,
+ depsgraph,
+ region,
+ CTX_wm_view3d(C),
+ SCE_SNAP_MODE_FACE,
+ &(const struct SnapObjectParams){
+ .snap_target_select = SCE_SNAP_TARGET_NOT_ACTIVE,
+ .edit_mode_type = SNAP_GEOM_FINAL,
+ .use_occlusion_test = true,
+ },
+ mval,
+ NULL,
+ NULL,
+ co_proj,
+ NULL)) {
mul_v3_m4v3(eve->co, obedit->imat, co_proj);
}
}
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.cc
index d11f0b490c1..67834bf05ce 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.cc
@@ -13,7 +13,7 @@
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -43,10 +43,12 @@
#include "mesh_intern.h" /* own include */
+using blender::Array;
+
static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot)
{
CustomData *data;
- BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : NULL;
+ BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : nullptr;
int tot;
switch (htype) {
@@ -93,7 +95,7 @@ static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_t
default:
BLI_assert(0);
tot = 0;
- data = NULL;
+ data = nullptr;
break;
}
@@ -172,7 +174,7 @@ static void mesh_uv_reset_array(float **fuv, const int len)
static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset)
{
- float **fuv = BLI_array_alloca(fuv, f->len);
+ Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(f->len);
BMIter liter;
BMLoop *l;
int i;
@@ -181,21 +183,21 @@ static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset)
fuv[i] = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv;
}
- mesh_uv_reset_array(fuv, f->len);
+ mesh_uv_reset_array(fuv.data(), f->len);
}
static void mesh_uv_reset_mface(MPoly *mp, MLoopUV *mloopuv)
{
- float **fuv = BLI_array_alloca(fuv, mp->totloop);
+ Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(mp->totloop);
for (int i = 0; i < mp->totloop; i++) {
fuv[i] = mloopuv[mp->loopstart + i].uv;
}
- mesh_uv_reset_array(fuv, mp->totloop);
+ mesh_uv_reset_array(fuv.data(), mp->totloop);
}
-void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum)
+void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum)
{
BMEditMesh *em = me->edit_mesh;
@@ -219,7 +221,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum)
else {
/* Collect Mesh UVs */
BLI_assert(CustomData_has_layer(&me->ldata, CD_MLOOPUV));
- MLoopUV *mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum);
+ MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum);
for (int i = 0; i < me->totpoly; i++) {
mesh_uv_reset_mface(&me->mpoly[i], mloopuv);
@@ -229,7 +231,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum)
DEG_id_tag_update(&me->id, 0);
}
-void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me)
+void ED_mesh_uv_loop_reset(bContext *C, Mesh *me)
{
/* could be ldata or pdata */
CustomData *ldata = GET_CD_DATA(me, ldata);
@@ -239,7 +241,7 @@ void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me)
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
}
-int ED_mesh_uv_texture_add(
+int ED_mesh_uv_add(
Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
{
/* NOTE: keep in sync with #ED_mesh_color_add. */
@@ -284,7 +286,7 @@ int ED_mesh_uv_texture_add(
is_init = true;
}
else {
- CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name);
+ CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, name);
}
if (active_set || layernum_dst == 0) {
@@ -305,7 +307,7 @@ int ED_mesh_uv_texture_add(
return layernum_dst;
}
-void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name)
+void ED_mesh_uv_ensure(Mesh *me, const char *name)
{
BMEditMesh *em;
int layernum_dst;
@@ -315,25 +317,25 @@ void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name)
layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPUV);
if (layernum_dst == 0) {
- ED_mesh_uv_texture_add(me, name, true, true, NULL);
+ ED_mesh_uv_add(me, name, true, true, nullptr);
}
}
else {
layernum_dst = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
if (layernum_dst == 0) {
- ED_mesh_uv_texture_add(me, name, true, true, NULL);
+ ED_mesh_uv_add(me, name, true, true, nullptr);
}
}
}
-bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n)
+bool ED_mesh_uv_remove_index(Mesh *me, const int n)
{
CustomData *ldata = GET_CD_DATA(me, ldata);
CustomDataLayer *cdlu;
int index;
index = CustomData_get_layer_index_n(ldata, CD_MLOOPUV, n);
- cdlu = (index == -1) ? NULL : &ldata->layers[index];
+ cdlu = (index == -1) ? nullptr : &ldata->layers[index];
if (!cdlu) {
return false;
@@ -346,24 +348,22 @@ bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n)
return true;
}
-bool ED_mesh_uv_texture_remove_active(Mesh *me)
+bool ED_mesh_uv_remove_active(Mesh *me)
{
- /* texpoly/uv are assumed to be in sync */
CustomData *ldata = GET_CD_DATA(me, ldata);
const int n = CustomData_get_active_layer(ldata, CD_MLOOPUV);
if (n != -1) {
- return ED_mesh_uv_texture_remove_index(me, n);
+ return ED_mesh_uv_remove_index(me, n);
}
return false;
}
-bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name)
+bool ED_mesh_uv_remove_named(Mesh *me, const char *name)
{
- /* texpoly/uv are assumed to be in sync */
CustomData *ldata = GET_CD_DATA(me, ldata);
const int n = CustomData_get_named_layer(ldata, CD_MLOOPUV, name);
if (n != -1) {
- return ED_mesh_uv_texture_remove_index(me, n);
+ return ED_mesh_uv_remove_index(me, n);
}
return false;
}
@@ -371,7 +371,7 @@ bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name)
int ED_mesh_color_add(
Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
{
- /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */
+ /* NOTE: keep in sync with #ED_mesh_uv_add. */
BMEditMesh *em;
int layernum;
@@ -409,7 +409,7 @@ int ED_mesh_color_add(
}
else {
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name);
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name);
}
if (active_set || layernum == 0) {
@@ -425,14 +425,14 @@ int ED_mesh_color_add(
return layernum;
}
-bool ED_mesh_color_ensure(struct Mesh *me, const char *name)
+bool ED_mesh_color_ensure(Mesh *me, const char *name)
{
- BLI_assert(me->edit_mesh == NULL);
+ BLI_assert(me->edit_mesh == nullptr);
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
if (!layer) {
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name);
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name);
layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
BKE_id_attributes_active_color_set(&me->id, layer);
@@ -441,7 +441,7 @@ bool ED_mesh_color_ensure(struct Mesh *me, const char *name)
DEG_id_tag_update(&me->id, 0);
- return (layer != NULL);
+ return (layer != nullptr);
}
bool ED_mesh_color_remove_index(Mesh *me, const int n)
@@ -451,7 +451,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n)
int index;
index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n);
- cdl = (index == -1) ? NULL : &ldata->layers[index];
+ cdl = (index == -1) ? nullptr : &ldata->layers[index];
if (!cdl) {
return false;
@@ -487,7 +487,7 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
static bool layers_poll(bContext *C)
{
Object *ob = ED_object_context(C);
- ID *data = (ob) ? ob->data : NULL;
+ ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr;
return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data &&
!ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
}
@@ -501,7 +501,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C)
}
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
CustomData *vdata = GET_CD_DATA(me, vdata);
const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
if (active != -1) {
@@ -514,7 +514,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C)
int ED_mesh_sculpt_color_add(
Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
{
- /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */
+ /* NOTE: keep in sync with #ED_mesh_uv_add. */
BMEditMesh *em;
int layernum;
@@ -549,12 +549,14 @@ int ED_mesh_sculpt_color_add(
}
if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) {
- MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
+ const MPropCol *color_data = (const MPropCol *)CustomData_get_layer(&me->vdata,
+ CD_PROP_COLOR);
CustomData_add_layer_named(
- &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name);
+ &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, (MPropCol *)color_data, me->totvert, name);
}
else {
- CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+ CustomData_add_layer_named(
+ &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name);
}
if (active_set || layernum == 0) {
@@ -570,18 +572,18 @@ int ED_mesh_sculpt_color_add(
return layernum;
}
-bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name)
+bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name)
{
- BLI_assert(me->edit_mesh == NULL);
+ BLI_assert(me->edit_mesh == nullptr);
if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
- CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+ CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name);
BKE_mesh_update_customdata_pointers(me, true);
}
DEG_id_tag_update(&me->id, 0);
- return (me->mloopcol != NULL);
+ return (me->mloopcol != nullptr);
}
bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
@@ -591,7 +593,7 @@ bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
int index;
index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n);
- cdl = (index == -1) ? NULL : &vdata->layers[index];
+ cdl = (index == -1) ? nullptr : &vdata->layers[index];
if (!cdl) {
return false;
@@ -631,7 +633,7 @@ static bool uv_texture_remove_poll(bContext *C)
}
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
CustomData *ldata = GET_CD_DATA(me, ldata);
const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV);
if (active != -1) {
@@ -644,16 +646,16 @@ static bool uv_texture_remove_poll(bContext *C)
static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
- if (ED_mesh_uv_texture_add(me, NULL, true, true, op->reports) == -1) {
+ if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) {
return OPERATOR_CANCELLED;
}
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
- ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
+ WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
}
return OPERATOR_FINISHED;
@@ -677,16 +679,16 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot)
static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
- if (!ED_mesh_uv_texture_remove_active(me)) {
+ if (!ED_mesh_uv_remove_active(me)) {
return OPERATOR_CANCELLED;
}
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
- ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
+ WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
}
return OPERATOR_FINISHED;
@@ -716,7 +718,7 @@ static bool vertex_color_remove_poll(bContext *C)
}
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
CustomData *ldata = GET_CD_DATA(me, ldata);
const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR);
if (active != -1) {
@@ -729,9 +731,9 @@ static bool vertex_color_remove_poll(bContext *C)
static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
- if (ED_mesh_color_add(me, NULL, true, true, op->reports) == -1) {
+ if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) {
return OPERATOR_CANCELLED;
}
@@ -756,7 +758,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot)
static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (!ED_mesh_color_remove_active(me)) {
return OPERATOR_CANCELLED;
@@ -785,9 +787,9 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
- if (ED_mesh_sculpt_color_add(me, NULL, true, true, op->reports) == -1) {
+ if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) {
return OPERATOR_CANCELLED;
}
@@ -812,7 +814,7 @@ void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot)
static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (!ED_mesh_sculpt_color_remove_active(me)) {
return OPERATOR_CANCELLED;
@@ -868,7 +870,7 @@ static bool mesh_customdata_mask_clear_poll(bContext *C)
{
Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
/* special case - can't run this if we're in sculpt mode */
if (ob->mode & OB_MODE_SCULPT) {
@@ -925,7 +927,7 @@ static int mesh_customdata_skin_state(bContext *C)
Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) {
CustomData *data = GET_CD_DATA(me, vdata);
return CustomData_has_layer(data, CD_MVERT_SKIN);
@@ -942,7 +944,7 @@ static bool mesh_customdata_skin_add_poll(bContext *C)
static int mesh_customdata_skin_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BKE_mesh_ensure_skin_customdata(me);
@@ -1025,7 +1027,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator
me->smoothresh);
}
- CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop);
+ CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, nullptr, me->totloop);
}
DEG_id_tag_update(&me->id, 0);
@@ -1057,7 +1059,7 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato
if (BKE_mesh_has_custom_loop_normals(me)) {
BMEditMesh *em = me->edit_mesh;
- if (em != NULL && em->bm->lnor_spacearr != NULL) {
+ if (em != nullptr && em->bm->lnor_spacearr != nullptr) {
BKE_lnor_spacearr_clear(em->bm->lnor_spacearr);
}
return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL);
@@ -1114,7 +1116,7 @@ static void mesh_add_verts(Mesh *mesh, int len)
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
if (!CustomData_has_layer(&vdata, CD_MVERT)) {
- CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+ CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert);
}
CustomData_free(&mesh->vdata, mesh->totvert);
@@ -1152,7 +1154,7 @@ static void mesh_add_edges(Mesh *mesh, int len)
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
- CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
+ CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
}
CustomData_free(&mesh->edata, mesh->totedge);
@@ -1186,7 +1188,7 @@ static void mesh_add_loops(Mesh *mesh, int len)
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
- CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
+ CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop);
}
BKE_mesh_runtime_clear_cache(mesh);
@@ -1215,7 +1217,7 @@ static void mesh_add_polys(Mesh *mesh, int len)
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
- CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly);
+ CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly);
}
CustomData_free(&mesh->pdata, mesh->totpoly);
@@ -1413,21 +1415,21 @@ void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail)
ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX);
}
-Mesh *ED_mesh_context(struct bContext *C)
+Mesh *ED_mesh_context(bContext *C)
{
- Mesh *mesh = CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data;
- if (mesh != NULL) {
+ Mesh *mesh = static_cast<Mesh *>(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data);
+ if (mesh != nullptr) {
return mesh;
}
Object *ob = ED_object_active_context(C);
- if (ob == NULL) {
- return NULL;
+ if (ob == nullptr) {
+ return nullptr;
}
ID *data = (ID *)ob->data;
- if (data == NULL || GS(data->name) != ID_ME) {
- return NULL;
+ if (data == nullptr || GS(data->name) != ID_ME) {
+ return nullptr;
}
return (Mesh *)data;
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index b39a0b90f6d..303234df48c 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -9,6 +9,10 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct BMEditMesh;
struct BMElem;
struct BMOperator;
@@ -170,8 +174,7 @@ void MESH_OT_knife_project(struct wmOperatorType *ot);
/**
* \param use_tag: When set, tag all faces inside the polylines.
*/
-void EDBM_mesh_knife(struct bContext *C,
- struct ViewContext *vc,
+void EDBM_mesh_knife(struct ViewContext *vc,
struct LinkNode *polys,
bool use_tag,
bool cut_through);
@@ -301,7 +304,7 @@ void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot);
void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot);
#endif
-/* *** mesh_data.c *** */
+/* *** mesh_data.cc *** */
void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
@@ -314,3 +317,7 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.cc
index 9575fde68f0..9e28e1bafdd 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.cc
@@ -21,10 +21,8 @@
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
-#include "BLI_blenlib.h"
-#include "BLI_math.h"
-
#include "BKE_context.h"
+#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_key.h"
@@ -91,7 +89,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
{
int a, b;
- Mesh *me = ob_src->data;
+ Mesh *me = static_cast<Mesh *>(ob_src->data);
MVert *mvert = *mvert_pp;
MEdge *medge = *medge_pp;
MLoop *mloop = *mloop_pp;
@@ -106,12 +104,13 @@ static void join_mesh_single(Depsgraph *depsgraph,
CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert);
/* vertex groups */
- MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT);
- MDeformVert *dvert_src = CustomData_get(&me->vdata, 0, CD_MDEFORMVERT);
+ MDeformVert *dvert = (MDeformVert *)CustomData_get(vdata, *vertofs, CD_MDEFORMVERT);
+ const MDeformVert *dvert_src = (const MDeformVert *)CustomData_get(
+ &me->vdata, 0, CD_MDEFORMVERT);
/* Remap to correct new vgroup indices, if needed. */
if (dvert_src) {
- BLI_assert(dvert != NULL);
+ BLI_assert(dvert != nullptr);
/* Build src to merged mapping of vgroup indices. */
int *vgroup_index_map;
@@ -120,7 +119,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
ob_src, ob_dst, &vgroup_index_map_len);
BKE_object_defgroup_index_map_apply(
dvert, me->totvert, vgroup_index_map, vgroup_index_map_len);
- if (vgroup_index_map != NULL) {
+ if (vgroup_index_map != nullptr) {
MEM_freeN(vgroup_index_map);
}
}
@@ -150,11 +149,11 @@ static void join_mesh_single(Depsgraph *depsgraph,
float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs;
/* Check if this mesh has such a shape-key. */
- KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL;
+ KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : nullptr;
if (okb) {
/* copy this mesh's shape-key to the destination shape-key
* (need to transform first) */
- float(*ocos)[3] = okb->data;
+ float(*ocos)[3] = static_cast<float(*)[3]>(okb->data);
for (a = 0; a < me->totvert; a++, cos++, ocos++) {
copy_v3_v3(*cos, *ocos);
mul_m4_v3(cmat, *cos);
@@ -180,10 +179,10 @@ static void join_mesh_single(Depsgraph *depsgraph,
float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs;
/* Check if this was one of the original shape-keys. */
- KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL;
+ KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : nullptr;
if (okb) {
/* copy this mesh's shape-key to the destination shape-key */
- float(*ocos)[3] = okb->data;
+ float(*ocos)[3] = static_cast<float(*)[3]>(okb->data);
for (a = 0; a < me->totvert; a++, cos++, ocos++) {
copy_v3_v3(*cos, *ocos);
}
@@ -254,17 +253,17 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
/* Face maps. */
- int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP);
- int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP);
+ int *fmap = (int *)CustomData_get(pdata, *polyofs, CD_FACEMAP);
+ const int *fmap_src = (const int *)CustomData_get(&me->pdata, 0, CD_FACEMAP);
/* Remap to correct new face-map indices, if needed. */
if (fmap_src) {
- BLI_assert(fmap != NULL);
+ BLI_assert(fmap != nullptr);
int *fmap_index_map;
int fmap_index_map_len;
fmap_index_map = BKE_object_facemap_index_map_create(ob_src, ob_dst, &fmap_index_map_len);
BKE_object_facemap_index_map_apply(fmap, me->totpoly, fmap_index_map, fmap_index_map_len);
- if (fmap_index_map != NULL) {
+ if (fmap_index_map != nullptr) {
MEM_freeN(fmap_index_map);
}
}
@@ -290,7 +289,7 @@ static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset
return;
}
- int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ int *face_sets = (int *)CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
if (!face_sets) {
return;
}
@@ -317,20 +316,18 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
- Material **matar = NULL, *ma;
+ Material **matar = nullptr, *ma;
Mesh *me;
- MVert *mvert = NULL;
- MEdge *medge = NULL;
- MPoly *mpoly = NULL;
- MLoop *mloop = NULL;
- Key *key, *nkey = NULL;
- KeyBlock *kb, *kbn;
+ MVert *mvert = nullptr;
+ MEdge *medge = nullptr;
+ MPoly *mpoly = nullptr;
+ MLoop *mloop = nullptr;
+ Key *key, *nkey = nullptr;
float imat[4][4];
int a, b, totcol, totmat = 0, totedge = 0, totvert = 0;
- int totloop = 0, totpoly = 0, vertofs, *matmap = NULL;
+ int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr;
int i, haskey = 0, edgeofs, loopofs, polyofs;
bool ok = false, join_parent = false;
- bDeformGroup *dg, *odg;
CustomData vdata, edata, fdata, ldata, pdata;
if (ob->mode & OB_MODE_EDIT) {
@@ -349,7 +346,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
/* count & check */
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
if (ob_iter->type == OB_MESH) {
- me = ob_iter->data;
+ me = static_cast<Mesh *>(ob_iter->data);
totvert += me->totvert;
totedge += me->totedge;
@@ -361,7 +358,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
ok = true;
}
- if ((ob->parent != NULL) && (ob_iter == ob->parent)) {
+ if ((ob->parent != nullptr) && (ob_iter == ob->parent)) {
join_parent = true;
}
@@ -376,7 +373,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
/* Apply parent transform if the active object's parent was joined to it.
* NOTE: This doesn't apply recursive parenting. */
if (join_parent) {
- ob->parent = NULL;
+ ob->parent = nullptr;
BKE_object_apply_mat4_ex(ob, ob->obmat, ob->parent, ob->parentinv, false);
}
@@ -416,12 +413,12 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
/* new material indices and material array */
if (totmat) {
- matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar");
- matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap");
+ matar = static_cast<Material **>(MEM_callocN(sizeof(*matar) * totmat, __func__));
+ matmap = static_cast<int *>(MEM_callocN(sizeof(*matmap) * totmat, __func__));
}
totcol = ob->totcol;
- /* obact materials in new main array, is nicer start! */
+ /* Active object materials in new main array, is nicer start! */
for (a = 0; a < ob->totcol; a++) {
matar[a] = BKE_object_material_get(ob, a + 1);
id_us_plus((ID *)matar[a]);
@@ -438,7 +435,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
nkey = (Key *)BKE_id_copy(bmain, &key->id);
/* for all keys in old block, clear data-arrays */
- for (kb = key->block.first; kb; kb = kb->next) {
+ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
if (kb->data) {
MEM_freeN(kb->data);
}
@@ -462,13 +459,14 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
/* only act if a mesh, and not the one we're joining to */
if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) {
- me = ob_iter->data;
+ me = static_cast<Mesh *>(ob_iter->data);
/* Join this object's vertex groups to the base one's */
- for (dg = me->vertex_group_names.first; dg; dg = dg->next) {
+ LISTBASE_FOREACH (bDeformGroup *, dg, &me->vertex_group_names) {
/* See if this group exists in the object (if it doesn't, add it to the end) */
if (!BKE_object_defgroup_find_name(ob, dg->name)) {
- odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup");
+ bDeformGroup *odg = static_cast<bDeformGroup *>(
+ MEM_mallocN(sizeof(bDeformGroup), __func__));
memcpy(odg, dg, sizeof(bDeformGroup));
BLI_addtail(&mesh_active->vertex_group_names, odg);
}
@@ -481,8 +479,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
/* Join this object's face maps to the base one's. */
LISTBASE_FOREACH (bFaceMap *, fmap, &ob_iter->fmaps) {
/* See if this group exists in the object (if it doesn't, add it to the end) */
- if (BKE_object_facemap_find_name(ob, fmap->name) == NULL) {
- bFaceMap *fmap_new = MEM_mallocN(sizeof(bFaceMap), "join faceMap");
+ if (BKE_object_facemap_find_name(ob, fmap->name) == nullptr) {
+ bFaceMap *fmap_new = static_cast<bFaceMap *>(MEM_mallocN(sizeof(bFaceMap), __func__));
memcpy(fmap_new, fmap, sizeof(bFaceMap));
BLI_addtail(&ob->fmaps, fmap_new);
}
@@ -522,13 +520,15 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
* check if destination mesh already has matching entries too. */
if (me->key && key) {
/* for remapping KeyBlock.relative */
- int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__);
- KeyBlock **kb_map = MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__);
+ int *index_map = static_cast<int *>(
+ MEM_mallocN(sizeof(int) * me->key->totkey, __func__));
+ KeyBlock **kb_map = static_cast<KeyBlock **>(
+ MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__));
- for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) {
+ LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) {
BLI_assert(i < me->key->totkey);
- kbn = BKE_keyblock_find_name(key, kb->name);
+ KeyBlock *kbn = BKE_keyblock_find_name(key, kb->name);
/* if key doesn't exist in destination mesh, add it */
if (kbn) {
index_map[i] = BLI_findindex(&key->block, kbn);
@@ -549,7 +549,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
}
/* remap relative index values */
- for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) {
+ LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) {
/* sanity check, should always be true */
if (LIKELY(kb->relative < me->key->totkey)) {
kb_map[i]->relative = index_map[kb->relative];
@@ -571,10 +571,10 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
CustomData_reset(&ldata);
CustomData_reset(&pdata);
- mvert = CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
- medge = CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
- mloop = CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
- mpoly = CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly);
+ mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert);
+ medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
+ mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop);
+ mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly);
vertofs = 0;
edgeofs = 0;
@@ -661,7 +661,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
CTX_DATA_END;
/* return to mesh we're merging to */
- me = ob->data;
+ me = static_cast<Mesh *>(ob->data);
CustomData_free(&me->vdata, me->totvert);
CustomData_free(&me->edata, me->totedge);
@@ -703,8 +703,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
if (totcol) {
me->mat = matar;
- ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar");
- ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits");
+ ob->mat = static_cast<Material **>(MEM_callocN(sizeof(*ob->mat) * totcol, __func__));
+ ob->matbits = static_cast<char *>(MEM_callocN(sizeof(*ob->matbits) * totcol, __func__));
MEM_freeN(matmap);
}
@@ -751,8 +751,8 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
Object *ob_active = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mesh *me = (Mesh *)ob_active->data;
- Mesh *selme = NULL;
- Mesh *me_deformed = NULL;
+ Mesh *selme = nullptr;
+ Mesh *me_deformed = nullptr;
Key *key = me->key;
KeyBlock *kb;
bool ok = false, nonequal_verts = false;
@@ -769,7 +769,7 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
ok = true;
}
else {
- nonequal_verts = 1;
+ nonequal_verts = true;
}
}
}
@@ -787,12 +787,12 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (key == NULL) {
+ if (key == nullptr) {
key = me->key = BKE_key_add(bmain, (ID *)me);
key->type = KEY_RELATIVE;
/* first key added, so it was the basis. initialize it with the existing mesh */
- kb = BKE_keyblock_add(key, NULL);
+ kb = BKE_keyblock_add(key, nullptr);
BKE_keyblock_convert_from_mesh(me, key, kb);
}
@@ -835,21 +835,21 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
/** \name Mesh Topology Mirror API
* \{ */
-static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1};
+static MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false};
BLI_INLINE void mesh_mirror_topo_table_get_meshes(Object *ob,
Mesh *me_eval,
Mesh **r_me_mirror,
BMEditMesh **r_em_mirror)
{
- Mesh *me_mirror = NULL;
- BMEditMesh *em_mirror = NULL;
+ Mesh *me_mirror = nullptr;
+ BMEditMesh *em_mirror = nullptr;
- Mesh *me = ob->data;
- if (me_eval != NULL) {
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ if (me_eval != nullptr) {
me_mirror = me_eval;
}
- else if (me->edit_mesh != NULL) {
+ else if (me->edit_mesh != nullptr) {
em_mirror = me->edit_mesh;
}
else {
@@ -892,7 +892,7 @@ static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval)
static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
MVert *mvert = me_eval ? me_eval->mvert : me->mvert;
float vec[3];
@@ -901,7 +901,7 @@ static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index)
vec[1] = mvert->co[1];
vec[2] = mvert->co[2];
- return ED_mesh_mirror_spatial_table_lookup(ob, NULL, me_eval, vec);
+ return ED_mesh_mirror_spatial_table_lookup(ob, nullptr, me_eval, vec);
}
static int mesh_get_x_mirror_vert_topo(Object *ob, Mesh *mesh, int index)
@@ -928,28 +928,25 @@ static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, c
/* ignore nan verts */
if ((isfinite(co[0]) == false) || (isfinite(co[1]) == false) || (isfinite(co[2]) == false)) {
- return NULL;
+ return nullptr;
}
vec[0] = -co[0];
vec[1] = co[1];
vec[2] = co[2];
- i = ED_mesh_mirror_spatial_table_lookup(ob, em, NULL, vec);
+ i = ED_mesh_mirror_spatial_table_lookup(ob, em, nullptr, vec);
if (i != -1) {
return BM_vert_at_index(em->bm, i);
}
- return NULL;
+ return nullptr;
}
-static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob,
- struct BMEditMesh *em,
- BMVert *eve,
- int index)
+static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, BMEditMesh *em, BMVert *eve, int index)
{
intptr_t poinval;
- if (!ed_mesh_mirror_topo_table_update(ob, NULL)) {
- return NULL;
+ if (!ed_mesh_mirror_topo_table_update(ob, nullptr)) {
+ return nullptr;
}
if (index == -1) {
@@ -965,7 +962,7 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob,
}
if (index == em->bm->totvert) {
- return NULL;
+ return nullptr;
}
}
@@ -974,15 +971,11 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob,
if (poinval != -1) {
return (BMVert *)(poinval);
}
- return NULL;
+ return nullptr;
}
-BMVert *editbmesh_get_x_mirror_vert(Object *ob,
- struct BMEditMesh *em,
- BMVert *eve,
- const float co[3],
- int index,
- const bool use_topology)
+BMVert *editbmesh_get_x_mirror_vert(
+ Object *ob, BMEditMesh *em, BMVert *eve, const float co[3], int index, const bool use_topology)
{
if (use_topology) {
return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index);
@@ -992,7 +985,7 @@ BMVert *editbmesh_get_x_mirror_vert(Object *ob,
int ED_mesh_mirror_get_vert(Object *ob, int index)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
int index_mirr;
@@ -1004,7 +997,7 @@ int ED_mesh_mirror_get_vert(Object *ob, int index)
index_mirr = eve_mirr ? BM_elem_index_get(eve_mirr) : -1;
}
else {
- index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, use_topology);
+ index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, use_topology);
}
return index_mirr;
@@ -1021,7 +1014,7 @@ static float *editmesh_get_mirror_uv(
/* ignore nan verts */
if (isnan(uv[0]) || !isfinite(uv[0]) || isnan(uv[1]) || !isfinite(uv[1])) {
- return NULL;
+ return nullptr;
}
if (axis) {
@@ -1061,14 +1054,14 @@ static float *editmesh_get_mirror_uv(
}
}
- return NULL;
+ return nullptr;
}
#endif
static uint mirror_facehash(const void *ptr)
{
- const MFace *mf = ptr;
+ const MFace *mf = static_cast<const MFace *>(ptr);
uint v0, v1;
if (mf->v4) {
@@ -1121,21 +1114,21 @@ static bool mirror_facecmp(const void *a, const void *b)
int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
MVert *mv, *mvert;
MFace mirrormf, *mf, *hashmf, *mface;
GHash *fhash;
int *mirrorverts, *mirrorfaces;
- BLI_assert(em == NULL); /* Does not work otherwise, currently... */
+ BLI_assert(em == nullptr); /* Does not work otherwise, currently... */
const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
const int totvert = me_eval ? me_eval->totvert : me->totvert;
const int totface = me_eval ? me_eval->totface : me->totface;
int a;
- mirrorverts = MEM_callocN(sizeof(int) * totvert, "MirrorVerts");
- mirrorfaces = MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces");
+ mirrorverts = static_cast<int *>(MEM_callocN(sizeof(int) * totvert, "MirrorVerts"));
+ mirrorfaces = static_cast<int *>(MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces"));
mvert = me_eval ? me_eval->mvert : me->mvert;
mface = me_eval ? me_eval->mface : me->mface;
@@ -1165,7 +1158,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
SWAP(uint, mirrormf.v2, mirrormf.v4);
}
- hashmf = BLI_ghash_lookup(fhash, &mirrormf);
+ hashmf = static_cast<MFace *>(BLI_ghash_lookup(fhash, &mirrormf));
if (hashmf) {
mirrorfaces[a * 2] = hashmf - mface;
mirrorfaces[a * 2 + 1] = mirror_facerotation(&mirrormf, hashmf);
@@ -1175,7 +1168,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
}
}
- BLI_ghash_free(fhash, NULL, NULL);
+ BLI_ghash_free(fhash, nullptr, nullptr);
MEM_freeN(mirrorverts);
return mirrorfaces;
@@ -1186,7 +1179,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index)
{
ViewContext vc;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BLI_assert(me && GS(me->id.name) == ID_ME);
@@ -1199,8 +1192,8 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px,
ED_view3d_select_id_validate(&vc);
if (dist_px) {
- /* sample rect to increase chances of selecting, so that when clicking
- * on an edge in the backbuf, we can still select a face */
+ /* Sample rect to increase chances of selecting, so that when clicking
+ * on an edge in the back-buffer, we can still select a face. */
*r_index = DRW_select_buffer_find_nearest_to_point(
vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totpoly + 1, &dist_px);
}
@@ -1220,7 +1213,7 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px,
static void ed_mesh_pick_face_vert__mpoly_find(
/* context */
- struct ARegion *region,
+ ARegion *region,
const float mval[2],
/* mesh data (evaluated) */
const MPoly *mp,
@@ -1250,14 +1243,14 @@ bool ED_mesh_pick_face_vert(
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
uint poly_index;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BLI_assert(me && GS(me->id.name) == ID_ME);
if (ED_mesh_pick_face(C, ob, mval, dist_px, &poly_index)) {
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
- struct ARegion *region = CTX_wm_region(C);
+ ARegion *region = CTX_wm_region(C);
/* derived mesh to find deformed locations */
Mesh *me_eval = mesh_get_eval_final(
@@ -1266,14 +1259,13 @@ bool ED_mesh_pick_face_vert(
int v_idx_best = ORIGINDEX_NONE;
/* find the vert closest to 'mval' */
- const float mval_f[2] = {UNPACK2(mval)};
+ const float mval_f[2] = {(float)mval[0], (float)mval[1]};
float len_best = FLT_MAX;
MPoly *me_eval_mpoly;
MLoop *me_eval_mloop;
MVert *me_eval_mvert;
uint me_eval_mpoly_len;
- const int *index_mp_to_orig;
me_eval_mpoly = me_eval->mpoly;
me_eval_mloop = me_eval->mloop;
@@ -1281,7 +1273,7 @@ bool ED_mesh_pick_face_vert(
me_eval_mpoly_len = me_eval->totpoly;
- index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
+ const int *index_mp_to_orig = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
/* tag all verts using this face */
if (index_mp_to_orig) {
@@ -1313,8 +1305,8 @@ bool ED_mesh_pick_face_vert(
/* map 'dm -> me' r_index if possible */
if (v_idx_best != ORIGINDEX_NONE) {
- const int *index_mv_to_orig;
- index_mv_to_orig = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
+ const int *index_mv_to_orig = (const int *)CustomData_get_layer(&me_eval->vdata,
+ CD_ORIGINDEX);
if (index_mv_to_orig) {
v_idx_best = index_mv_to_orig[v_idx_best];
}
@@ -1335,7 +1327,7 @@ bool ED_mesh_pick_face_vert(
*
* \return boolean true == Found
*/
-typedef struct VertPickData {
+struct VertPickData {
const MVert *mvert;
const float *mval_f; /* [2] */
ARegion *region;
@@ -1343,14 +1335,14 @@ typedef struct VertPickData {
/* runtime */
float len_best;
int v_idx_best;
-} VertPickData;
+};
static void ed_mesh_pick_vert__mapFunc(void *userData,
int index,
const float co[3],
const float UNUSED(no[3]))
{
- VertPickData *data = userData;
+ VertPickData *data = static_cast<VertPickData *>(userData);
if ((data->mvert[index].flag & ME_HIDE) == 0) {
float sco[2];
@@ -1368,7 +1360,7 @@ bool ED_mesh_pick_vert(
bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index)
{
ViewContext vc;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BLI_assert(me && GS(me->id.name) == ID_ME);
@@ -1382,8 +1374,8 @@ bool ED_mesh_pick_vert(
if (use_zbuf) {
if (dist_px > 0) {
- /* sample rect to increase chances of selecting, so that when clicking
- * on an face in the backbuf, we can still select a vert */
+ /* Sample rectangle to increase chances of selecting, so that when clicking
+ * on an face in the back-buffer, we can still select a vert. */
*r_index = DRW_select_buffer_find_nearest_to_point(
vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totvert + 1, &dist_px);
}
@@ -1405,16 +1397,16 @@ bool ED_mesh_pick_vert(
/* derived mesh to find deformed locations */
Mesh *me_eval = mesh_get_eval_final(vc.depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
ARegion *region = vc.region;
- RegionView3D *rv3d = region->regiondata;
+ RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
/* find the vert closest to 'mval' */
const float mval_f[2] = {(float)mval[0], (float)mval[1]};
- VertPickData data = {NULL};
+ VertPickData data = {nullptr};
ED_view3d_init_mats_rv3d(ob, rv3d);
- if (me_eval == NULL) {
+ if (me_eval == nullptr) {
return false;
}
@@ -1440,7 +1432,7 @@ bool ED_mesh_pick_vert(
MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve)
{
if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (!BLI_listbase_is_empty(&me->vertex_group_names)) {
BMesh *bm = me->edit_mesh->bm;
const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
@@ -1452,27 +1444,27 @@ MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve)
if (r_eve) {
*r_eve = eve;
}
- return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ return static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
}
}
}
}
if (r_eve) {
- *r_eve = NULL;
+ *r_eve = nullptr;
}
- return NULL;
+ return nullptr;
}
MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
int index = BKE_mesh_mselect_active_get(me, ME_VSEL);
if (r_index) {
*r_index = index;
}
- if (index == -1 || me->dvert == NULL) {
- return NULL;
+ if (index == -1 || me->dvert == nullptr) {
+ return nullptr;
}
return me->dvert + index;
}
@@ -1481,14 +1473,14 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
{
if (ob->type == OB_MESH) {
if (ob->mode & OB_MODE_EDIT) {
- return ED_mesh_active_dvert_get_em(ob, NULL);
+ return ED_mesh_active_dvert_get_em(ob, nullptr);
}
- return ED_mesh_active_dvert_get_ob(ob, NULL);
+ return ED_mesh_active_dvert_get_ob(ob, nullptr);
}
- return NULL;
+ return nullptr;
}
-void EDBM_mesh_stats_multi(struct Object **objects,
+void EDBM_mesh_stats_multi(Object **objects,
const uint objects_len,
int totelem[3],
int totelem_sel[3])
diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc
index db8860efdd8..041a1383b28 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -621,9 +621,16 @@ Object *ED_object_add_type_with_obdata(bContext *C,
else {
ob = BKE_object_add(bmain, view_layer, type, name);
}
- BASACT(view_layer)->local_view_bits = local_view_bits;
- /* editor level activate, notifiers */
- ED_object_base_activate(C, view_layer->basact);
+
+ Base *ob_base_act = BASACT(view_layer);
+ /* While not getting a valid base is not a good thing, it can happen in convoluted corner cases,
+ * better not crash on it in releases. */
+ BLI_assert(ob_base_act != nullptr);
+ if (ob_base_act != nullptr) {
+ ob_base_act->local_view_bits = local_view_bits;
+ /* editor level activate, notifiers */
+ ED_object_base_activate(C, ob_base_act);
+ }
/* more editor stuff */
ED_object_base_init_transform_on_add(ob, loc, rot);
@@ -1265,9 +1272,9 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot)
"Relative Path",
"Select the file relative to the blend file");
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
- prop = RNA_def_string(
- ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign");
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+
+ WM_operator_properties_id_lookup(ot, true);
+
ED_object_add_generic_props(ot, false);
}
@@ -1651,19 +1658,12 @@ static std::optional<CollectionAddInfo> collection_add_info_get_from_op(bContext
Main *bmain = CTX_data_main(C);
PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location");
- PropertyRNA *prop_session_uuid = RNA_struct_find_property(op->ptr, "session_uuid");
- PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name");
+
+ add_info.collection = reinterpret_cast<Collection *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR));
bool update_location_if_necessary = false;
- if (prop_name && RNA_property_is_set(op->ptr, prop_name)) {
- char name[MAX_ID_NAME - 2];
- RNA_property_string_get(op->ptr, prop_name, name);
- add_info.collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name);
- update_location_if_necessary = true;
- }
- else if (RNA_property_is_set(op->ptr, prop_session_uuid)) {
- const uint32_t session_uuid = (uint32_t)RNA_property_int_get(op->ptr, prop_session_uuid);
- add_info.collection = (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid);
+ if (add_info.collection) {
update_location_if_necessary = true;
}
else {
@@ -1736,8 +1736,7 @@ static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent
RNA_int_set(op->ptr, "drop_y", event->xy[1]);
}
- if (!RNA_struct_property_is_set(op->ptr, "name") &&
- !RNA_struct_property_is_set(op->ptr, "session_uuid")) {
+ if (!WM_operator_properties_id_lookup_is_set(op->ptr)) {
return WM_enum_search_invoke(C, op, event);
}
return op->type->exec(C, op);
@@ -1769,16 +1768,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot)
ot->prop = prop;
ED_object_add_generic_props(ot, false);
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the collection to add",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
+ WM_operator_properties_id_lookup(ot, false);
object_add_drop_xy_props(ot);
}
@@ -1875,20 +1865,11 @@ void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
/* properties */
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the collection to add",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
+ WM_operator_properties_id_lookup(ot, false);
ED_object_add_generic_props(ot, false);
- /* Important: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE).
+ /* IMPORTANT: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE).
*/
RNA_def_boolean(ot->srna,
"use_instance",
@@ -1920,18 +1901,12 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op)
ushort local_view_bits;
float loc[3], rot[3];
- PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name");
PropertyRNA *prop_type = RNA_struct_find_property(op->ptr, "type");
PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location");
- /* These shouldn't fail when created by outliner dropping as it checks the ID is valid. */
- if (!RNA_property_is_set(op->ptr, prop_name) || !RNA_property_is_set(op->ptr, prop_type)) {
- return OPERATOR_CANCELLED;
- }
const short id_type = RNA_property_enum_get(op->ptr, prop_type);
- char name[MAX_ID_NAME - 2];
- RNA_property_string_get(op->ptr, prop_name, name);
- id = BKE_libblock_find_name(bmain, id_type, name);
+ id = WM_operator_properties_id_lookup_from_name_or_session_uuid(
+ bmain, op->ptr, (ID_Type)id_type);
if (id == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -1974,7 +1949,7 @@ void OBJECT_OT_data_instance_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_string(ot->srna, "name", "Name", MAX_ID_NAME - 2, "Name", "ID name to add");
+ WM_operator_properties_id_lookup(ot, true);
PropertyRNA *prop = RNA_def_enum(ot->srna, "type", rna_enum_id_type_items, 0, "Type", "");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
ED_object_add_generic_props(ot, false);
@@ -2111,11 +2086,33 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
Curves *curves_id = static_cast<Curves *>(object->data);
curves_id->surface = surface_ob;
id_us_plus(&surface_ob->id);
+
+ Mesh *surface_mesh = static_cast<Mesh *>(surface_ob->data);
+ const char *uv_name = CustomData_get_active_layer_name(&surface_mesh->ldata, CD_MLOOPUV);
+ if (uv_name != nullptr) {
+ curves_id->surface_uv_map = BLI_strdup(uv_name);
+ }
}
return OPERATOR_FINISHED;
}
+static bool object_curves_empty_hair_add_poll(bContext *C)
+{
+ if (!U.experimental.use_new_curves_type) {
+ return false;
+ }
+ if (!ED_operator_objectmode(C)) {
+ return false;
+ }
+ Object *ob = CTX_data_active_object(C);
+ if (ob == nullptr || ob->type != OB_MESH) {
+ CTX_wm_operator_poll_msg_set(C, "No active mesh object");
+ return false;
+ }
+ return true;
+}
+
void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot)
{
ot->name = "Add Empty Curves";
@@ -2123,7 +2120,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot)
ot->idname = "OBJECT_OT_curves_empty_hair_add";
ot->exec = object_curves_empty_hair_add_exec;
- ot->poll = object_curves_add_poll;
+ ot->poll = object_curves_empty_hair_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2762,9 +2759,32 @@ static const EnumPropertyItem convert_target_items[] = {
"Point Cloud",
"Point Cloud from Mesh objects"},
#endif
+ {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"},
{0, nullptr, 0, nullptr, nullptr},
};
+static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ EnumPropertyItem *items = nullptr;
+ int items_num = 0;
+ for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) {
+ if (item->value == OB_CURVES) {
+ if (U.experimental.use_new_curves_type) {
+ RNA_enum_item_add(&items, &items_num, item);
+ }
+ }
+ else {
+ RNA_enum_item_add(&items, &items_num, item);
+ }
+ }
+ RNA_enum_item_end(&items, &items_num);
+ *r_free = true;
+ return items;
+}
+
static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
if (ob->runtime.curve_cache == nullptr) {
@@ -3065,6 +3085,50 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
ob_gpencil->actcol = actcol;
}
+ else if (target == OB_CURVES) {
+ ob->flag |= OB_DONE;
+
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
+ GeometrySet geometry;
+ if (ob_eval->runtime.geometry_set_eval != nullptr) {
+ geometry = *ob_eval->runtime.geometry_set_eval;
+ }
+
+ if (geometry.has_curves()) {
+ if (keep_original) {
+ basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
+ newob = basen->object;
+
+ /* Decrement original curve's usage count. */
+ Curve *legacy_curve = static_cast<Curve *>(newob->data);
+ id_us_min(&legacy_curve->id);
+
+ /* Make a copy of the curve. */
+ newob->data = BKE_id_copy(bmain, &legacy_curve->id);
+ }
+ else {
+ newob = ob;
+ }
+
+ const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>();
+ const Curves *curves_eval = curve_component.get_for_read();
+ Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2));
+
+ newob->data = new_curves;
+ newob->type = OB_CURVES;
+
+ blender::bke::CurvesGeometry::wrap(
+ new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry);
+ BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id);
+
+ BKE_object_free_derived_caches(newob);
+ BKE_object_free_modifiers(newob, 0);
+ }
+ else {
+ BKE_reportf(
+ op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2);
+ }
+ }
else if (ob->type == OB_MESH && target == OB_POINTCLOUD) {
ob->flag |= OB_DONE;
@@ -3480,6 +3544,7 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(
ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to");
+ RNA_def_enum_funcs(ot->prop, convert_target_items_fn);
RNA_def_boolean(ot->srna,
"keep_original",
false,
@@ -3718,15 +3783,13 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *basen;
- Object *ob;
const bool linked = RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag;
- char name[MAX_ID_NAME - 2];
- /* find object, create fake base */
- RNA_string_get(op->ptr, "name", name);
- ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
+ /* Find object, create fake base. */
+
+ Object *ob = reinterpret_cast<Object *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
if (ob == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Object not found");
@@ -3734,7 +3797,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
- basen = object_add_duplicate_internal(
+ Base *basen = object_add_duplicate_internal(
bmain,
scene,
view_layer,
@@ -3814,7 +3877,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
"Linked",
"Duplicate object but not object data, linking to the original data");
- RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add");
+ WM_operator_properties_id_lookup(ot, true);
prop = RNA_def_float_matrix(
ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
@@ -3836,14 +3899,11 @@ 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 {
+ Object *ob = reinterpret_cast<Object *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
+
+ if (!ob) {
ob = OBACT(view_layer);
}
@@ -3922,12 +3982,25 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
- RNA_def_string(ot->srna,
- "name",
- nullptr,
- MAX_ID_NAME - 2,
- "Name",
- "Object name to place (when unset use the active object)");
+ prop = RNA_def_string(
+ ot->srna,
+ "name",
+ nullptr,
+ MAX_ID_NAME - 2,
+ "Name",
+ "Object name to place (uses the active object when this and 'session_uuid' are unset)");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
+ prop = RNA_def_int(ot->srna,
+ "session_uuid",
+ 0,
+ INT32_MIN,
+ INT32_MAX,
+ "Session UUID",
+ "Session UUID of the object to place (uses the active object when this and "
+ "'name' are unset)",
+ INT32_MIN,
+ INT32_MAX);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
prop = RNA_def_float_matrix(
ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index 1483c24ac70..effbde41c38 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -220,22 +220,22 @@ static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *l
MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
Mesh *me = (Mesh *)ob->data;
MultiresModifierData tmp_mmd = *mmd;
- DerivedMesh *cddm = CDDM_from_mesh(me);
- DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
+ *lvl = mmd->lvl;
if (mmd->lvl == 0) {
- dm = CDDM_copy(cddm);
- }
- else {
- tmp_mmd.lvl = mmd->lvl;
- tmp_mmd.sculptlvl = mmd->lvl;
- dm = multires_make_derived_from_derived(cddm, &tmp_mmd, scene, ob, 0);
+ DerivedMesh *cddm = CDDM_from_mesh(me);
+ DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
+ return cddm;
}
- cddm->release(cddm);
+ DerivedMesh *cddm = CDDM_from_mesh(me);
+ DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
+ tmp_mmd.lvl = mmd->lvl;
+ tmp_mmd.sculptlvl = mmd->lvl;
+ dm = multires_make_derived_from_derived(cddm, &tmp_mmd, scene, ob, 0);
- *lvl = mmd->lvl;
+ cddm->release(cddm);
return dm;
}
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index a7379d7e492..58daf753679 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -21,6 +21,8 @@
#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BKE_attribute.h"
+#include "BKE_callbacks.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
@@ -446,14 +448,11 @@ static bool bake_object_check(ViewLayer *view_layer,
}
if (target == R_BAKE_TARGET_VERTEX_COLORS) {
- MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
- const bool mcol_valid = (mcol != NULL);
- MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol == NULL && !mcol_valid) {
+ if (BKE_id_attributes_active_color_get(&me->id) == NULL) {
BKE_reportf(reports,
RPT_ERROR,
- "No vertex colors layer found in the object \"%s\"",
- ob->id.name + 2);
+ "Mesh does not have an active color attribute \"%s\"",
+ me->id.name + 2);
return false;
}
}
@@ -937,17 +936,13 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, ReportList *reports)
{
if (ob->type != OB_MESH) {
- BKE_report(
- reports, RPT_ERROR, "Vertex color baking not support with object types other than mesh");
+ BKE_report(reports, RPT_ERROR, "Color attribute baking is only supported for mesh objects");
return false;
}
Mesh *me = ob->data;
- MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
- const bool mcol_valid = (mcol != NULL);
- MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol == NULL && !mcol_valid) {
- BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to");
+ if (BKE_id_attributes_active_color_get(&me->id) == NULL) {
+ BKE_report(reports, RPT_ERROR, "No active color attribute to bake to");
return false;
}
@@ -996,10 +991,10 @@ static int find_original_loop(const Mesh *me_orig,
return ORIGINDEX_NONE;
}
-static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
- Object *ob,
- Mesh *me_eval,
- BakePixel *pixel_array)
+static void bake_targets_populate_pixels_color_attributes(BakeTargets *targets,
+ Object *ob,
+ Mesh *me_eval,
+ BakePixel *pixel_array)
{
Mesh *me = ob->data;
const int pixels_num = targets->pixels_num;
@@ -1094,19 +1089,42 @@ static void bake_result_add_to_rgba(float rgba[4], const float *result, const in
}
}
+static void convert_float_color_to_byte_color(const MPropCol *float_colors,
+ const int num,
+ const bool is_noncolor,
+ MLoopCol *byte_colors)
+{
+ if (is_noncolor) {
+ for (int i = 0; i < num; i++) {
+ unit_float_to_uchar_clamp_v4(&byte_colors->r, float_colors[i].color);
+ }
+ }
+ else {
+ for (int i = 0; i < num; i++) {
+ linearrgb_to_srgb_uchar4(&byte_colors[i].r, float_colors[i].color);
+ }
+ }
+}
+
static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
{
Mesh *me = ob->data;
- MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
- const bool mcol_valid = (mcol != NULL);
- MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR);
+ CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id);
+ BLI_assert(active_color_layer != NULL);
+ const eAttrDomain domain = BKE_id_attribute_domain(&me->id, active_color_layer);
+
const int channels_num = targets->channels_num;
+ const bool is_noncolor = targets->is_noncolor;
const float *result = targets->result;
- if (mcol_valid) {
+ if (domain == ATTR_DOMAIN_POINT) {
const int totvert = me->totvert;
const int totloop = me->totloop;
+ MPropCol *mcol = active_color_layer->type == CD_PROP_COLOR ?
+ active_color_layer->data :
+ MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__);
+
/* Accumulate float vertex colors in scene linear color space. */
int *num_loops_for_vertex = MEM_callocN(sizeof(int) * me->totvert, "num_loops_for_vertex");
memset(mcol, 0, sizeof(MPropCol) * me->totvert);
@@ -1125,25 +1143,35 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
}
}
+ if (mcol != active_color_layer->data) {
+ convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data);
+ MEM_freeN(mcol);
+ }
+
MEM_SAFE_FREE(num_loops_for_vertex);
}
- else {
- /* Byte loop colors in sRGB colors space. */
- MLoop *mloop = me->mloop;
- const int totloop = me->totloop;
- const bool is_noncolor = targets->is_noncolor;
-
- for (int i = 0; i < totloop; i++, mloop++, mloopcol++) {
- float rgba[4];
- zero_v4(rgba);
- bake_result_add_to_rgba(rgba, &result[i * channels_num], channels_num);
-
- if (is_noncolor) {
- unit_float_to_uchar_clamp_v4(&mloopcol->r, rgba);
+ else if (domain == ATTR_DOMAIN_CORNER) {
+ switch (active_color_layer->type) {
+ case CD_PROP_COLOR: {
+ MPropCol *colors = active_color_layer->data;
+ for (int i = 0; i < me->totloop; i++) {
+ zero_v4(colors[i].color);
+ bake_result_add_to_rgba(colors[i].color, &result[i * channels_num], channels_num);
+ }
+ break;
}
- else {
- linearrgb_to_srgb_uchar4(&mloopcol->r, rgba);
+ case CD_PROP_BYTE_COLOR: {
+ MLoopCol *colors = active_color_layer->data;
+ for (int i = 0; i < me->totloop; i++) {
+ MPropCol color;
+ zero_v4(color.color);
+ bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num);
+ convert_float_color_to_byte_color(&color, 1, is_noncolor, &colors[i]);
+ }
+ break;
}
+ default:
+ BLI_assert_unreachable();
}
}
@@ -1197,7 +1225,7 @@ static void bake_targets_populate_pixels(const BakeAPIRender *bkr,
BakePixel *pixel_array)
{
if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- bake_targets_populate_pixels_vertex_colors(targets, ob, me_eval, pixel_array);
+ bake_targets_populate_pixels_color_attributes(targets, ob, me_eval, pixel_array);
}
else {
RE_bake_pixels_populate(me_eval, pixel_array, targets->pixels_num, targets, bkr->uv_layer);
@@ -1535,7 +1563,7 @@ static int bake(const BakeAPIRender *bkr,
ob_low_eval->obmat);
}
else {
- /* from multiresolution */
+ /* From multi-resolution. */
Mesh *me_nores = NULL;
ModifierData *md = NULL;
int mode;
@@ -1806,6 +1834,17 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, floa
RE_SetReports(bkr->render, NULL);
}
+static void bake_job_complete(void *bkv)
+{
+ BakeAPIRender *bkr = (BakeAPIRender *)bkv;
+ BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_COMPLETE);
+}
+static void bake_job_canceled(void *bkv)
+{
+ BakeAPIRender *bkr = (BakeAPIRender *)bkv;
+ BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_CANCEL);
+}
+
static void bake_freejob(void *bkv)
{
BakeAPIRender *bkr = (BakeAPIRender *)bkv;
@@ -1941,6 +1980,7 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
/* init bake render */
bake_init_api_data(op, C, bkr);
+ BKE_callback_exec_id(CTX_data_main(C), &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_PRE);
re = bkr->render;
/* setup new render */
@@ -1958,7 +1998,8 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
/* TODO: only draw bake image, can we enforce this. */
WM_jobs_timer(
wm_job, 0.5, (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) ? NC_GEOM | ND_DATA : NC_IMAGE, 0);
- WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL);
+ WM_jobs_callbacks_ex(
+ wm_job, bake_startjob, NULL, NULL, NULL, bake_job_complete, bake_job_canceled);
G.is_break = false;
G.is_rendering = true;
diff --git a/source/blender/editors/object/object_collection.c b/source/blender/editors/object/object_collection.c
index 054c9e1de46..39951c2ab6e 100644
--- a/source/blender/editors/object/object_collection.c
+++ b/source/blender/editors/object/object_collection.c
@@ -526,7 +526,7 @@ void OBJECT_OT_collection_link(wmOperatorType *ot)
ot->prop = prop;
}
-static int collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
+static int collection_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_context(C);
@@ -535,6 +535,12 @@ static int collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
if (!ob || !collection) {
return OPERATOR_CANCELLED;
}
+ if (ID_IS_LINKED(collection) || ID_IS_OVERRIDE_LIBRARY(collection)) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Cannot remove an object from a linked or library override collection");
+ return OPERATOR_CANCELLED;
+ }
BKE_collection_object_remove(bmain, collection, ob, false);
@@ -561,7 +567,7 @@ void OBJECT_OT_collection_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-static int collection_unlink_exec(bContext *C, wmOperator *UNUSED(op))
+static int collection_unlink_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Collection *collection = CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data;
@@ -569,6 +575,14 @@ static int collection_unlink_exec(bContext *C, wmOperator *UNUSED(op))
if (!collection) {
return OPERATOR_CANCELLED;
}
+ if (ID_IS_OVERRIDE_LIBRARY(collection) &&
+ collection->id.override_library->hierarchy_root != &collection->id) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Cannot unlink a library override collection which is not the root of its override "
+ "hierarchy");
+ return OPERATOR_CANCELLED;
+ }
BKE_id_delete(bmain, collection);
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index d982d86fe77..bf3b71178e8 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -245,13 +245,11 @@ static void set_constraint_nth_target(bConstraint *con,
const char subtarget[],
int index)
{
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
int num_targets, i;
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
+ if (BKE_constraint_targets_get(con, &targets)) {
num_targets = BLI_listbase_count(&targets);
if (index < 0) {
@@ -274,9 +272,7 @@ static void set_constraint_nth_target(bConstraint *con,
}
}
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
- }
+ BKE_constraint_targets_flush(con, &targets, 0);
}
}
@@ -289,7 +285,6 @@ static void set_constraint_nth_target(bConstraint *con,
static void test_constraint(
Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
{
- const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
bool check_targets = true;
@@ -465,14 +460,7 @@ static void test_constraint(
}
/* Check targets for constraints */
- if (check_targets && cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
- /* constraints with empty target list that actually require targets */
- if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
- con->flag |= CONSTRAINT_DISABLE;
- }
-
+ if (check_targets && BKE_constraint_targets_get(con, &targets)) {
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
@@ -543,8 +531,12 @@ static void test_constraint(
}
/* free any temporary targets */
- if (cti->flush_constraint_targets) {
- cti->flush_constraint_targets(con, &targets, 0);
+ BKE_constraint_targets_flush(con, &targets, 0);
+ }
+ else if (check_targets) {
+ /* constraints with empty target list that actually require targets */
+ if (ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
+ con->flag |= CONSTRAINT_DISABLE;
}
}
}
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index dfe858e5bd9..4837b538bf6 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -45,7 +45,7 @@
* Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */
/* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */
static const EnumPropertyItem DT_layer_items[] = {
- {0, "", 0, "Vertex Data", ""},
+ RNA_ENUM_ITEM_HEADING("Vertex Data", NULL),
{DT_TYPE_MDEFORMVERT,
"VGROUP_WEIGHTS",
0,
@@ -60,7 +60,8 @@ static const EnumPropertyItem DT_layer_items[] = {
{DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"},
#endif
{DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"},
- {0, "", 0, "Edge Data", ""},
+
+ RNA_ENUM_ITEM_HEADING("Edge Data", NULL),
{DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"},
{DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"},
{DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"},
@@ -70,11 +71,13 @@ static const EnumPropertyItem DT_layer_items[] = {
0,
"Freestyle Mark",
"Transfer Freestyle edge mark"},
- {0, "", 0, "Face Corner Data", ""},
+
+ RNA_ENUM_ITEM_HEADING("Face Corner Data", NULL),
{DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"},
{DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, "VCOL", 0, "Colors", "Color Attributes"},
{DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"},
- {0, "", 0, "Face Data", ""},
+
+ RNA_ENUM_ITEM_HEADING("Face Data", NULL),
{DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"},
{DT_TYPE_FREESTYLE_FACE,
"FREESTYLE_FACE",
@@ -85,14 +88,14 @@ static const EnumPropertyItem DT_layer_items[] = {
};
static void dt_add_vcol_layers(CustomData *cdata,
- CustomDataMask mask,
+ eCustomDataMask mask,
EnumPropertyItem **r_item,
int *r_totitem)
{
int types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR};
for (int i = 0; i < 2; i++) {
- CustomDataType type = types[i];
+ eCustomDataType type = types[i];
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index cb0e76c11e4..e5dd9fb2c8b 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -17,6 +17,7 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
+#include "BLI_math_rotation.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -86,6 +87,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_types.h"
#include "UI_interface_icons.h"
@@ -1241,6 +1243,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op)
ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
/* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL);
+ /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side.
+ * If further testing shows it's not necessary (for both bones and objects) removal is fine. */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL);
return OPERATOR_FINISHED;
@@ -1310,6 +1315,9 @@ static int object_update_paths_exec(bContext *C, wmOperator *op)
ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
/* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL);
+ /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side.
+ * If further testing shows it's not necessary (for both bones and objects) removal is fine. */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL);
return OPERATOR_FINISHED;
@@ -1461,6 +1469,8 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot)
static int shade_smooth_exec(bContext *C, wmOperator *op)
{
const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth");
+ const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth");
+ const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle");
bool changed_multi = false;
bool has_linked_data = false;
@@ -1508,6 +1518,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
bool changed = false;
if (ob->type == OB_MESH) {
BKE_mesh_smooth_flag_set(ob->data, use_smooth);
+ BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle);
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
changed = true;
}
@@ -1577,6 +1588,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(
+ ot->srna,
+ "use_auto_smooth",
+ false,
+ "Auto Smooth",
+ "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
+ RNA_def_property_float_default(prop, DEG2RADF(30.0f));
+ RNA_def_property_ui_text(prop,
+ "Angle",
+ "Maximum angle between face normals that will be considered as smooth"
+ "(unused if custom split normals data are available)");
}
/** \} */
@@ -1811,6 +1841,11 @@ static int move_to_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (ID_IS_OVERRIDE_LIBRARY(collection)) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot add objects to a library override collection");
+ return OPERATOR_CANCELLED;
+ }
+
ListBase objects = selected_objects_get(C);
if (is_new) {
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index fdf2cae026d..0055cdf9ea1 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -458,7 +458,7 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base
return false;
}
- bool mode_transfered = false;
+ bool mode_transferred = false;
ED_undo_group_begin(C);
@@ -480,11 +480,11 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_toolsystem_update_from_context_view3d(C);
- mode_transfered = true;
+ mode_transferred = true;
}
ED_undo_group_end(C);
- return mode_transfered;
+ return mode_transferred;
}
static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -493,8 +493,8 @@ static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEven
const eObjectMode src_mode = (eObjectMode)ob_src->mode;
Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
- const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
- if (!mode_transfered) {
+ const bool mode_transferred = object_transfer_mode_to_base(C, op, base_dst);
+ if (!mode_transferred) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc
index 3328fe65f2e..202c6d96a47 100644
--- a/source/blender/editors/object/object_modifier.cc
+++ b/source/blender/editors/object/object_modifier.cc
@@ -515,12 +515,12 @@ void ED_object_modifier_copy_to_object(bContext *C,
DEG_relations_tag_update(bmain);
}
-bool ED_object_modifier_convert(ReportList *UNUSED(reports),
- Main *bmain,
- Depsgraph *depsgraph,
- ViewLayer *view_layer,
- Object *ob,
- ModifierData *md)
+bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports),
+ Main *bmain,
+ Depsgraph *depsgraph,
+ ViewLayer *view_layer,
+ Object *ob,
+ ModifierData *md)
{
int cvert = 0;
@@ -740,7 +740,13 @@ static bool modifier_apply_obdata(
}
else {
Mesh *mesh_applied = modifier_apply_create_mesh_for_modifier(
- depsgraph, ob, md_eval, true, true);
+ depsgraph,
+ ob,
+ md_eval,
+ /* It's important not to apply virtual modifiers (e.g. shape-keys) because they're kept,
+ * causing them to be applied twice, see: T97758. */
+ false,
+ true);
if (!mesh_applied) {
BKE_report(reports, RPT_ERROR, "Modifier returned error, skipping apply");
return false;
@@ -1462,7 +1468,7 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
return OPERATOR_CANCELLED;
}
- if (do_merge_customdata &&
+ if (ob->type == OB_MESH && do_merge_customdata &&
(mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) {
BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data);
}
@@ -1525,12 +1531,12 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
edit_modifier_properties(ot);
edit_modifier_report_property(ot);
- RNA_def_boolean(
- ot->srna,
- "merge_customdata",
- true,
- "Merge UV's",
- "Merge UV coordinates that share a vertex to account for imprecision in some modifiers");
+ RNA_def_boolean(ot->srna,
+ "merge_customdata",
+ true,
+ "Merge UV's",
+ "For mesh objects, merge UV coordinates that share a vertex to account for "
+ "imprecision in some modifiers");
PropertyRNA *prop = RNA_def_boolean(ot->srna,
"single_user",
false,
@@ -1602,7 +1608,7 @@ void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
-/** \name Convert Modifier Operator
+/** \name Convert Particle System Modifier to Mesh Operator
* \{ */
static int modifier_convert_exec(bContext *C, wmOperator *op)
@@ -1612,18 +1618,12 @@ static int modifier_convert_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
- const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata");
- if (!md || !ED_object_modifier_convert(op->reports, bmain, depsgraph, view_layer, ob, md)) {
+ if (!md || !ED_object_modifier_convert_psys_to_mesh(
+ op->reports, bmain, depsgraph, view_layer, ob, md)) {
return OPERATOR_CANCELLED;
}
- if (do_merge_customdata &&
- (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) {
- BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data);
- }
-
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
@@ -1640,7 +1640,7 @@ static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent *U
void OBJECT_OT_modifier_convert(wmOperatorType *ot)
{
- ot->name = "Convert Modifier";
+ ot->name = "Convert Particles to Mesh";
ot->description = "Convert particles to a mesh object";
ot->idname = "OBJECT_OT_modifier_convert";
@@ -1651,13 +1651,6 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
-
- RNA_def_boolean(
- ot->srna,
- "merge_customdata",
- true,
- "Merge UV's",
- "Merge UV coordinates that share a vertex to account for imprecision in some modifiers");
}
/** \} */
@@ -3393,6 +3386,7 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o
nmd->node_group = new_tree;
id_us_min(&tree->id);
+ DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 1067c2d6a95..8a0d380ff2f 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -281,7 +281,7 @@ void ED_operatormacros_object(void)
ot = WM_operatortype_append_macro("OBJECT_OT_duplicate_move",
"Duplicate Objects",
- "Duplicate selected objects and move them",
+ "Duplicate the selected objects and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
if (ot) {
WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
@@ -289,11 +289,11 @@ void ED_operatormacros_object(void)
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
}
- /* grr, should be able to pass options on... */
- ot = WM_operatortype_append_macro("OBJECT_OT_duplicate_move_linked",
- "Duplicate Linked",
- "Duplicate selected objects and move them",
- OPTYPE_UNDO | OPTYPE_REGISTER);
+ ot = WM_operatortype_append_macro(
+ "OBJECT_OT_duplicate_move_linked",
+ "Duplicate Linked",
+ "Duplicate the selected objects, but not their object data, and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
if (ot) {
otmacro = WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
RNA_boolean_set(otmacro->ptr, "linked", true);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 5ef8e573e27..f55ffdf0fcd 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1653,7 +1653,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot)
"Link Instance Collection",
"Replace assigned Collection Instance"},
{MAKE_LINKS_FONTS, "FONTS", 0, "Link Fonts to Text", "Replace Text object Fonts"},
- {0, "", 0, NULL, NULL},
+ RNA_ENUM_ITEM_SEPR,
{MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Copy Modifiers", "Replace Modifiers"},
{MAKE_LINKS_SHADERFX,
"EFFECTS",
@@ -1964,7 +1964,7 @@ static void single_objectdata_action_users(
ID *id_act = (ID *)adt->action;
if (single_data_needs_duplication(id_act)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID);
+ BKE_animdata_duplicate_id_action(bmain, id_obdata, USER_DUP_ACT | USER_DUP_LINKED_ID);
}
}
}
@@ -2258,49 +2258,6 @@ static bool make_override_library_object_overridable_check(Main *bmain, Object *
return false;
}
-/* Set the object to override. */
-static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- Object *obact = ED_object_active_context(C);
-
- /* Sanity checks. */
- if (!scene || ID_IS_LINKED(scene) || !obact) {
- return OPERATOR_CANCELLED;
- }
-
- if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
- ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) ||
- make_override_library_object_overridable_check(bmain, obact)) {
- uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
- uiLayout *layout = UI_popup_menu_layout(pup);
-
- /* Create operator menu item with relevant properties filled in. */
- PointerRNA opptr_dummy;
- uiItemFullO_ptr(
- layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy);
-
- /* Present the menu and be done... */
- UI_popup_menu_end(C, pup);
-
- /* This invoke just calls another instance of this operator... */
- return OPERATOR_INTERFACE;
- }
-
- if (ID_IS_LINKED(obact)) {
- /* Show menu with list of directly linked collections containing the active object. */
- WM_enum_search_invoke(C, op, event);
- return OPERATOR_CANCELLED;
- }
-
- /* Error.. cannot continue. */
- BKE_report(op->reports,
- RPT_ERROR,
- "Can only make library override for a referenced object or collection");
- return OPERATOR_CANCELLED;
-}
-
static int make_override_library_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -2310,8 +2267,12 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
ID *id_root = NULL;
bool is_override_instancing_object = false;
- GSet *user_overrides_objects_uids = BLI_gset_new(
- BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__);
+ const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable");
+
+ GSet *user_overrides_objects_uids = do_fully_editable ? NULL :
+ BLI_gset_new(BLI_ghashutil_inthash_p,
+ BLI_ghashutil_intcmp,
+ __func__);
bool user_overrides_from_selected_objects = false;
if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
@@ -2329,27 +2290,19 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
user_overrides_from_selected_objects = false;
}
else if (!make_override_library_object_overridable_check(bmain, obact)) {
- const int i = RNA_property_enum_get(op->ptr, op->type->prop);
- const uint collection_session_uuid = *((uint *)&i);
+ const int i = RNA_property_int_get(op->ptr, op->type->prop);
+ const uint collection_session_uuid = *((const uint *)&i);
if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) {
BKE_reportf(op->reports,
RPT_ERROR_INVALID_INPUT,
- "Active object '%s' is not overridable",
+ "Could not find an overridable root hierarchy for object '%s'",
obact->id.name + 2);
return OPERATOR_CANCELLED;
}
-
Collection *collection = BLI_listbase_bytes_find(&bmain->collections,
&collection_session_uuid,
sizeof(collection_session_uuid),
offsetof(ID, session_uuid));
- if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) {
- BKE_reportf(op->reports,
- RPT_ERROR_INVALID_INPUT,
- "Could not find an overridable collection containing object '%s'",
- obact->id.name + 2);
- return OPERATOR_CANCELLED;
- }
id_root = &collection->id;
user_overrides_from_selected_objects = true;
}
@@ -2359,7 +2312,10 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
user_overrides_from_selected_objects = true;
}
- if (user_overrides_from_selected_objects) {
+ if (do_fully_editable) {
+ /* Pass. */
+ }
+ else if (user_overrides_from_selected_objects) {
/* Only selected objects can be 'user overrides'. */
FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) {
BLI_gset_add(user_overrides_objects_uids, POINTER_FROM_UINT(ob_iter->id.session_uuid));
@@ -2379,30 +2335,65 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
ID *id_root_override;
- const bool success = BKE_lib_override_library_create(
- bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, &id_root_override);
-
- /* Define liboverrides from selected/validated objects as user defined. */
- ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
- id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
- continue;
- }
- if (BLI_gset_haskey(user_overrides_objects_uids,
- POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) {
- id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ const bool success = BKE_lib_override_library_create(bmain,
+ scene,
+ view_layer,
+ NULL,
+ id_root,
+ id_root,
+ &obact->id,
+ &id_root_override,
+ do_fully_editable);
+
+ if (!do_fully_editable) {
+ /* Define liboverrides from selected/validated objects as user defined. */
+ ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
+ id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
+ continue;
+ }
+ if (BLI_gset_haskey(user_overrides_objects_uids,
+ POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) {
+ id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
}
- }
- FOREACH_MAIN_ID_END;
+ FOREACH_MAIN_ID_END;
- BLI_gset_free(user_overrides_objects_uids, NULL);
+ BLI_gset_free(user_overrides_objects_uids, NULL);
+ }
- /* Remove the instance empty from this scene, the items now have an overridden collection
- * instead. */
- if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, obact);
+ if (success) {
+ if (is_override_instancing_object) {
+ /* Remove the instance empty from this scene, the items now have an overridden collection
+ * instead. */
+ ED_object_base_free_and_unlink(bmain, scene, obact);
+ }
+ else {
+ /* Remove the found root ID from the view layer. */
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Collection *collection_root = (Collection *)id_root;
+ LISTBASE_FOREACH_MUTABLE (
+ CollectionParent *, collection_parent, &collection_root->parents) {
+ if (ID_IS_LINKED(collection_parent->collection) ||
+ !BKE_view_layer_has_collection(view_layer, collection_parent->collection)) {
+ continue;
+ }
+ BKE_collection_child_remove(bmain, collection_parent->collection, collection_root);
+ }
+ break;
+ }
+ case ID_OB: {
+ /* TODO: Not sure how well we can handle this case, when we don't have the collections as
+ * reference containers... */
+ break;
+ }
+ default:
+ break;
+ }
+ }
}
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
@@ -2411,46 +2402,68 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
-static bool make_override_library_poll(bContext *C)
+/* Set the object to override. */
+static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- Object *obact = CTX_data_active_object(C);
-
- /* Object must be directly linked to be overridable. */
- return (ED_operator_objectmode(C) && obact != NULL &&
- (ID_IS_LINKED(obact) || (obact->instance_collection != NULL &&
- ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) &&
- !ID_IS_OVERRIDE_LIBRARY(obact))));
-}
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *obact = ED_object_active_context(C);
-static const EnumPropertyItem *make_override_collections_of_linked_object_itemf(
- bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
-{
- EnumPropertyItem item_tmp = {0}, *item = NULL;
- int totitem = 0;
+ /* Sanity checks. */
+ if (!scene || ID_IS_LINKED(scene) || !obact) {
+ return OPERATOR_CANCELLED;
+ }
- Object *object = ED_object_active_context(C);
- Main *bmain = CTX_data_main(C);
+ if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) ||
+ make_override_library_object_overridable_check(bmain, obact)) {
+ return make_override_library_exec(C, op);
+ }
- if (!object || !ID_IS_LINKED(object)) {
- return DummyRNA_DEFAULT_items;
+ if (!ID_IS_LINKED(obact)) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot make library override from a local object");
+ return OPERATOR_CANCELLED;
}
+ int potential_root_collections_num = 0;
+ uint collection_session_uuid = MAIN_ID_SESSION_UUID_UNSET;
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
/* Only check for directly linked collections. */
- if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) {
+ if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0 ||
+ !BKE_view_layer_has_collection(view_layer, collection)) {
continue;
}
- if (BKE_collection_has_object_recursive(collection, object)) {
- item_tmp.identifier = item_tmp.name = collection->id.name + 2;
- item_tmp.value = *((int *)&collection->id.session_uuid);
- RNA_enum_item_add(&item, &totitem, &item_tmp);
+ if (BKE_collection_has_object_recursive(collection, obact)) {
+ if (potential_root_collections_num == 0) {
+ collection_session_uuid = collection->id.session_uuid;
+ }
+ potential_root_collections_num++;
}
}
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
+ if (potential_root_collections_num <= 1) {
+ RNA_property_int_set(op->ptr, op->type->prop, *((int *)&collection_session_uuid));
+ return make_override_library_exec(C, op);
+ }
- return item;
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Too many potential root collections (%d) for the override hierarchy, "
+ "please use the Outliner instead",
+ potential_root_collections_num);
+ return OPERATOR_CANCELLED;
+}
+
+static bool make_override_library_poll(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+
+ /* Object must be directly linked to be overridable. */
+ return (ED_operator_objectmode(C) && obact != NULL &&
+ (ID_IS_LINKED(obact) || (obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) &&
+ !ID_IS_OVERRIDE_LIBRARY(obact))));
}
void OBJECT_OT_make_override_library(wmOperatorType *ot)
@@ -2470,16 +2483,25 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
- prop = RNA_def_enum(ot->srna,
- "collection",
- DummyRNA_DEFAULT_items,
- MAIN_ID_SESSION_UUID_UNSET,
- "Override Collection",
- "Name of directly linked collection containing the selected object, to make "
- "an override from");
- RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf);
- RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
+ prop = RNA_def_int(ot->srna,
+ "collection",
+ MAIN_ID_SESSION_UUID_UNSET,
+ INT_MIN,
+ INT_MAX,
+ "Override Collection",
+ "Session UUID of the directly linked collection containing the selected "
+ "object, to make an override from",
+ INT_MIN,
+ INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
ot->prop = prop;
+
+ prop = RNA_def_boolean(ot->srna,
+ "do_fully_editable",
+ false,
+ "Create Fully Editable",
+ "Make all created override data-blocks fully editable");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/** \} */
@@ -2594,9 +2616,7 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot)
/** \name Drop Named Material on Object Operator
* \{ */
-char *ED_object_ot_drop_named_material_tooltip(bContext *C,
- PointerRNA *properties,
- const int mval[2])
+char *ED_object_ot_drop_named_material_tooltip(bContext *C, const char *name, const int mval[2])
{
int mat_slot = 0;
Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot);
@@ -2605,9 +2625,6 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C,
}
mat_slot = max_ii(mat_slot, 1);
- char name[MAX_ID_NAME - 2];
- RNA_string_get(properties, "name", name);
-
Material *prev_mat = BKE_object_material_get(ob, mat_slot);
char *result;
@@ -2629,11 +2646,9 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent
Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot);
mat_slot = max_ii(mat_slot, 1);
- Material *ma;
- char name[MAX_ID_NAME - 2];
+ Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid(
+ bmain, op->ptr, ID_MA);
- RNA_string_get(op->ptr, "name", name);
- ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name);
if (ob == NULL || ma == NULL) {
return OPERATOR_CANCELLED;
}
@@ -2663,7 +2678,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
/* properties */
- RNA_def_string(ot->srna, "name", "Material", MAX_ID_NAME - 2, "Name", "Material name to assign");
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc
index ba2efa6e517..8a7138b25ac 100644
--- a/source/blender/editors/object/object_remesh.cc
+++ b/source/blender/editors/object/object_remesh.cc
@@ -344,7 +344,7 @@ static void voxel_size_edit_draw(const bContext *C, ARegion *UNUSED(ar), void *a
BKE_unit_value_as_string(str,
VOXEL_SIZE_EDIT_MAX_STR_LEN,
(double)(cd->voxel_size * unit->scale_length),
- 4,
+ -3,
B_UNIT_LENGTH,
unit,
true);
@@ -415,15 +415,15 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve
}
if (event->modifier & KM_CTRL) {
- /* Linear mode, enables jumping to any voxel size. */
- d = d * 0.0005f;
- }
- else {
/* Multiply d by the initial voxel size to prevent uncontrollable speeds when using low voxel
* sizes. */
/* When the voxel size is slower, it needs more precision. */
d = d * min_ff(pow2f(cd->init_voxel_size), 0.1f) * 0.05f;
}
+ else {
+ /* Linear mode, enables jumping to any voxel size. */
+ d = d * 0.0005f;
+ }
if (cd->slow_mode) {
cd->voxel_size = cd->slow_voxel_size + d * 0.05f;
}
@@ -592,7 +592,8 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
ED_region_tag_redraw(region);
const char *status_str = TIP_(
- "Move the mouse to change the voxel size. LMB: confirm size, ESC/RMB: cancel");
+ "Move the mouse to change the voxel size. CTRL: Relative Scale, SHIFT: Precision Mode, "
+ "ENTER/LMB: Confirm Size, ESC/RMB: Cancel");
ED_workspace_status_text(C, status_str);
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index da3df159c33..82e67231ec1 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -1125,7 +1125,7 @@ static int object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEv
bool can_handle_multiuser = apply_objects_internal_can_multiuser(C);
bool need_single_user = can_handle_multiuser && apply_objects_internal_need_single_user(C);
- if ((ob->data != nullptr) && need_single_user) {
+ if ((ob != nullptr) && (ob->data != nullptr) && need_single_user) {
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "isolate_users");
if (!RNA_property_is_set(op->ptr, prop)) {
RNA_property_boolean_set(op->ptr, prop, true);
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 492ece0b40e..9ad36cacc7d 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -4266,7 +4266,6 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
Mesh *me = ob->data;
BMEditMesh *em = me->edit_mesh;
- float weight_act;
int i;
if (em) {
@@ -4278,18 +4277,15 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
if (dvert_act == NULL) {
return;
}
- weight_act = BKE_defvert_find_weight(dvert_act, def_nr);
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && (eve != eve_act)) {
- MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
- MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr);
- if (dw) {
- dw->weight = weight_act;
+ MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
- if (me->symmetry & ME_SYMMETRY_X) {
- ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset);
- }
+ BKE_defvert_copy_index(dvert_dst, def_nr, dvert_act, def_nr);
+
+ if (me->symmetry & ME_SYMMETRY_X) {
+ ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset);
}
}
}
@@ -4306,17 +4302,15 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
if (dvert_act == NULL) {
return;
}
- weight_act = BKE_defvert_find_weight(dvert_act, def_nr);
dv = me->dvert;
for (i = 0; i < me->totvert; i++, dv++) {
if ((me->mvert[i].flag & SELECT) && (dv != dvert_act)) {
- MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr);
- if (dw) {
- dw->weight = weight_act;
- if (me->symmetry & ME_SYMMETRY_X) {
- ED_mesh_defvert_mirror_update_ob(ob, -1, i);
- }
+
+ BKE_defvert_copy_index(dv, def_nr, dvert_act, def_nr);
+
+ if (me->symmetry & ME_SYMMETRY_X) {
+ ED_mesh_defvert_mirror_update_ob(ob, -1, i);
}
}
}
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index a56f513e98f..addcedc4481 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -1306,7 +1306,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush)
{
static const int flags = IB_rect | IB_multilayer | IB_metadata;
- char path[FILE_MAX];
+ char filepath[FILE_MAX];
const char *folder;
if (!(brush->icon_imbuf)) {
@@ -1315,22 +1315,22 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush)
if (brush->icon_filepath[0]) {
/* First use the path directly to try and load the file. */
- BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath));
- BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id));
+ BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath));
+ BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id));
/* Use default color-spaces for brushes. */
- brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr);
+ brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr);
/* otherwise lets try to find it in other directories */
if (!(brush->icon_imbuf)) {
folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons");
BLI_make_file_string(
- BKE_main_blendfile_path_from_global(), path, folder, brush->icon_filepath);
+ BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath);
- if (path[0]) {
+ if (filepath[0]) {
/* Use default color spaces. */
- brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr);
+ brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr);
}
}
@@ -1821,13 +1821,13 @@ void PreviewLoadJob::run_fn(void *customdata,
const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview));
const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]);
- const char *path = &deferred_data[1];
+ const char *filepath = &deferred_data[1];
- // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path);
+ // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, filepath);
- IMB_thumb_path_lock(path);
- ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source);
- IMB_thumb_path_unlock(path);
+ IMB_thumb_path_lock(filepath);
+ ImBuf *thumb = IMB_thumb_manage(filepath, THB_LARGE, source);
+ IMB_thumb_path_unlock(filepath);
if (thumb) {
/* PreviewImage assumes premultiplied alpha... */
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index a13177942a8..57a9e6be917 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -67,7 +67,10 @@ static Scene *scene_add(Main *bmain, Scene *scene_old, eSceneCopyMethod method)
}
/** Add a new scene in the sequence editor. */
-static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod method)
+Scene *ED_scene_sequencer_add(Main *bmain,
+ bContext *C,
+ eSceneCopyMethod method,
+ const bool assign_strip)
{
Sequence *seq = NULL;
Scene *scene_active = CTX_data_scene(C);
@@ -88,6 +91,11 @@ static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod
Scene *scene_new = scene_add(bmain, scene_strip, method);
+ /* If don't need assign the scene to the strip, nothing else to do. */
+ if (!assign_strip) {
+ return scene_new;
+ }
+
/* As the scene is created in sequencer, do not set the new scene as active.
* This is useful for story-boarding where we want to keep actual scene active.
* The new scene is linked to the active strip and the viewport updated. */
@@ -291,7 +299,7 @@ static int scene_new_sequencer_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
int type = RNA_enum_get(op->ptr, "type");
- if (ED_scene_sequencer_add(bmain, C, type) == NULL) {
+ if (ED_scene_sequencer_add(bmain, C, type, true) == NULL) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index ad815f0d998..f58baa0e25c 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -145,6 +145,10 @@ void ED_region_do_listen(wmRegionListenerParams *params)
region->type->listener(params);
}
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ UI_block_views_listen(block, params);
+ }
+
LISTBASE_FOREACH (uiList *, list, &region->ui_lists) {
if (list->type && list->type->listener) {
list->type->listener(list, params);
@@ -3454,9 +3458,9 @@ ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int x
if (!area) {
/* Check all windows except the active one. */
int scr_pos[2];
- wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos);
- if (r_win && r_win != win) {
- win = r_win;
+ wmWindow *win_other = WM_window_find_under_cursor(win, xy, scr_pos);
+ if (win_other && win_other != win) {
+ win = win_other;
screen = WM_window_get_active_screen(win);
area = BKE_screen_find_area_xy(screen, spacetype, scr_pos);
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index d5385181055..08c8c863729 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -626,8 +626,8 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
screen_geom_vertices_scale(win, screen);
ED_screen_areas_iter (win, screen, area) {
- /* set spacetype and region callbacks, calls init() */
- /* sets subwindows for regions, adds handlers */
+ /* Set space-type and region callbacks, calls init() */
+ /* Sets sub-windows for regions, adds handlers. */
ED_area_init(wm, win, area);
}
@@ -1315,7 +1315,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *area)
else {
ED_screen_state_toggle(C, win, area, state);
}
- /* warning: 'area' may be freed */
+ /* WARNING: 'area' may be freed */
}
/* otherwise just tile the area again */
else {
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index d7cf09ca89a..212fb846b43 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -659,36 +659,10 @@ bool ED_operator_editmball(bContext *C)
return false;
}
-bool ED_operator_mask(bContext *C)
-{
- ScrArea *area = CTX_wm_area(C);
- if (area && area->spacedata.first) {
- switch (area->spacetype) {
- case SPACE_CLIP: {
- SpaceClip *screen = area->spacedata.first;
- return ED_space_clip_check_show_maskedit(screen);
- }
- case SPACE_SEQ: {
- SpaceSeq *sseq = area->spacedata.first;
- Scene *scene = CTX_data_scene(C);
- return ED_space_sequencer_check_show_maskedit(sseq, scene);
- }
- case SPACE_IMAGE: {
- SpaceImage *sima = area->spacedata.first;
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
- return ED_space_image_check_show_maskedit(sima, obedit);
- }
- }
- }
-
- return false;
-}
-
-bool ED_operator_camera(bContext *C)
+bool ED_operator_camera_poll(bContext *C)
{
struct Camera *cam = CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data;
- return (cam != NULL);
+ return (cam != NULL && !ID_IS_LINKED(cam));
}
/** \} */
@@ -1477,7 +1451,7 @@ static int area_close_exec(bContext *C, wmOperator *op)
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
- /* This operator is scriptable, so the area passed could be invalid. */
+ /* This operator is script-able, so the area passed could be invalid. */
if (BLI_findindex(&screen->areabase, area) == -1) {
BKE_report(op->reports, RPT_ERROR, "Area not found in the active screen");
return OPERATOR_CANCELLED;
@@ -2490,6 +2464,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY:
sd->do_snap = event->val == KM_PRESS;
update_factor = true;
break;
@@ -2722,7 +2697,7 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event
rmd->region->sizey = rmd->region->winy;
}
- /* now copy to regionmovedata */
+ /* Now copy to region-move-data. */
if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) {
rmd->origval = rmd->region->sizex;
}
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index d3bf28798c4..8879161d2af 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -10,6 +10,7 @@ set(INC
../../depsgraph
../../draw
../../functions
+ ../../geometry
../../gpu
../../imbuf
../../makesdna
@@ -33,6 +34,8 @@ set(SRC
curves_sculpt_delete.cc
curves_sculpt_grow_shrink.cc
curves_sculpt_ops.cc
+ curves_sculpt_selection.cc
+ curves_sculpt_selection_paint.cc
curves_sculpt_snake_hook.cc
paint_canvas.cc
paint_cursor.c
@@ -49,13 +52,13 @@ set(SRC
paint_stroke.c
paint_utils.c
paint_vertex.cc
- paint_vertex_color_ops.c
+ paint_vertex_color_ops.cc
paint_vertex_color_utils.c
paint_vertex_proj.c
paint_vertex_weight_ops.c
paint_vertex_weight_utils.c
sculpt.c
- sculpt_automasking.c
+ sculpt_automasking.cc
sculpt_boundary.c
sculpt_brush_types.c
sculpt_cloth.c
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
index 1fdecf47bbd..bac03de77e7 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
@@ -18,11 +18,13 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_paint.h"
#include "BKE_report.h"
-#include "BKE_spline.hh"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
@@ -36,8 +38,12 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "GEO_add_curves_on_mesh.hh"
+
+#include "WM_api.h"
+
/**
- * The code below uses a prefix naming convention to indicate the coordinate space:
+ * The code below uses a suffix naming convention to indicate the coordinate space:
* cu: Local space of the curves object that is being edited.
* su: Local space of the surface object.
* wo: World space.
@@ -63,80 +69,52 @@ class AddOperation : public CurvesSculptStrokeOperation {
}
}
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
-static void initialize_straight_curve_positions(const float3 &p1,
- const float3 &p2,
- MutableSpan<float3> r_positions)
-{
- const float step = 1.0f / (float)(r_positions.size() - 1);
- for (const int i : r_positions.index_range()) {
- r_positions[i] = math::interpolate(p1, p2, i * step);
- }
-}
-
/**
* Utility class that actually executes the update when the stroke is updated. That's useful
* because it avoids passing a very large number of parameters between functions.
*/
struct AddOperationExecutor {
AddOperation *self_ = nullptr;
- Depsgraph *depsgraph_ = nullptr;
- Scene *scene_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
Object *object_ = nullptr;
- ARegion *region_ = nullptr;
- View3D *v3d_ = nullptr;
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
Object *surface_ob_ = nullptr;
Mesh *surface_ = nullptr;
Span<MLoopTri> surface_looptris_;
- Span<float3> corner_normals_su_;
- CurvesSculpt *curves_sculpt_ = nullptr;
- Brush *brush_ = nullptr;
- BrushCurvesSculptSettings *brush_settings_ = nullptr;
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ const BrushCurvesSculptSettings *brush_settings_ = nullptr;
+ int add_amount_;
+ bool use_front_face_;
float brush_radius_re_;
float2 brush_pos_re_;
- bool use_front_face_;
- bool interpolate_length_;
- bool interpolate_shape_;
- bool use_interpolation_;
- float new_curve_length_;
- int add_amount_;
- int points_per_curve_;
-
- /** Various matrices to convert between coordinate spaces. */
- float4x4 curves_to_world_mat_;
- float4x4 world_to_curves_mat_;
- float4x4 world_to_surface_mat_;
- float4x4 surface_to_world_mat_;
- float4x4 surface_to_curves_mat_;
- float4x4 surface_to_curves_normal_mat_;
+ CurvesSculptTransforms transforms_;
BVHTreeFromMesh surface_bvh_;
- int tot_old_curves_;
- int tot_old_points_;
-
struct AddedPoints {
Vector<float3> positions_cu;
Vector<float3> bary_coords;
Vector<int> looptri_indices;
};
- void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ AddOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
self_ = &self;
- depsgraph_ = CTX_data_depsgraph_pointer(C);
- scene_ = CTX_data_scene(C);
- object_ = CTX_data_active_object(C);
- region_ = CTX_wm_region(C);
- v3d_ = CTX_wm_view3d(C);
+ object_ = CTX_data_active_object(&C);
curves_id_ = static_cast<Curves *>(object_->data);
curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
@@ -145,41 +123,21 @@ struct AddOperationExecutor {
return;
}
- curves_to_world_mat_ = object_->obmat;
- world_to_curves_mat_ = curves_to_world_mat_.inverted();
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
surface_ob_ = curves_id_->surface;
surface_ = static_cast<Mesh *>(surface_ob_->data);
- surface_to_world_mat_ = surface_ob_->obmat;
- world_to_surface_mat_ = surface_to_world_mat_.inverted();
- surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_;
- surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed();
-
- if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
- BKE_mesh_calc_normals_split(surface_);
- }
- corner_normals_su_ = {
- reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
- surface_->totloop};
- curves_sculpt_ = scene_->toolsettings->curves_sculpt;
- brush_ = BKE_paint_brush(&curves_sculpt_->paint);
+ curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
brush_settings_ = brush_->curves_sculpt_settings;
- brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension);
brush_pos_re_ = stroke_extension.mouse_position;
use_front_face_ = brush_->flag & BRUSH_FRONTFACE;
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
add_amount_ = std::max(0, brush_settings_->add_amount);
- points_per_curve_ = std::max(2, brush_settings_->points_per_curve);
- interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
- interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE;
- use_interpolation_ = interpolate_length_ || interpolate_shape_;
- new_curve_length_ = brush_settings_->curve_length;
-
- tot_old_curves_ = curves_->curves_num();
- tot_old_points_ = curves_->points_num();
if (add_amount_ == 0) {
return;
@@ -215,31 +173,56 @@ struct AddOperationExecutor {
return;
}
- if (use_interpolation_) {
- this->ensure_curve_roots_kdtree();
+ /* Find UV map. */
+ VArray_Span<float2> surface_uv_map;
+ if (curves_id_->surface_uv_map != nullptr) {
+ MeshComponent surface_component;
+ surface_component.replace(surface_, GeometryOwnershipType::ReadOnly);
+ surface_uv_map = surface_component
+ .attribute_try_get_for_read(curves_id_->surface_uv_map,
+ ATTR_DOMAIN_CORNER)
+ .typed<float2>();
}
- const int tot_added_curves = added_points.bary_coords.size();
- const int tot_added_points = tot_added_curves * points_per_curve_;
+ /* Find normals. */
+ if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_);
+ }
+ const Span<float3> corner_normals_su = {
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
+ surface_->totloop};
- curves_->resize(curves_->points_num() + tot_added_points,
- curves_->curves_num() + tot_added_curves);
+ geometry::AddCurvesOnMeshInputs add_inputs;
+ add_inputs.root_positions_cu = added_points.positions_cu;
+ add_inputs.bary_coords = added_points.bary_coords;
+ add_inputs.looptri_indices = added_points.looptri_indices;
+ add_inputs.interpolate_length = brush_settings_->flag &
+ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
+ add_inputs.interpolate_shape = brush_settings_->flag &
+ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE;
+ add_inputs.interpolate_point_count = brush_settings_->flag &
+ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT;
+ add_inputs.fallback_curve_length = brush_settings_->curve_length;
+ add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
+ add_inputs.surface = surface_;
+ add_inputs.surface_bvh = &surface_bvh_;
+ add_inputs.surface_looptris = surface_looptris_;
+ add_inputs.surface_uv_map = surface_uv_map;
+ add_inputs.corner_normals_su = corner_normals_su;
+ add_inputs.curves_to_surface_mat = transforms_.curves_to_surface;
+ add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal;
+
+ if (add_inputs.interpolate_length || add_inputs.interpolate_shape ||
+ add_inputs.interpolate_point_count) {
+ this->ensure_curve_roots_kdtree();
+ add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
+ }
- threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); },
- [&]() { this->initialize_attributes(added_points); });
+ geometry::add_curves_on_mesh(*curves_, add_inputs);
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region_);
- }
-
- float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const
- {
- const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co;
- const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co;
- const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co;
- float3 bary_coords;
- interp_weights_tri_v3(bary_coords, v0, v1, v2, position);
- return bary_coords;
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
}
/**
@@ -249,16 +232,16 @@ struct AddOperationExecutor {
{
float3 ray_start_wo, ray_end_wo;
ED_view3d_win_to_segment_clipped(
- depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true);
- const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo;
- const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo;
+ ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true);
+ const float3 ray_start_cu = transforms_.world_to_curves * ray_start_wo;
+ const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->sample_in_center(
- r_added_points, brush_transform * ray_start_su, brush_transform * ray_end_su);
+ const float4x4 transform = transforms_.curves_to_surface * brush_transform;
+ this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu);
}
}
@@ -285,10 +268,10 @@ struct AddOperationExecutor {
const int looptri_index = ray_hit.index;
const float3 brush_pos_su = ray_hit.co;
- const float3 bary_coords = this->get_bary_coords(
+ const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
*surface_, surface_looptris_[looptri_index], brush_pos_su);
- const float3 brush_pos_cu = surface_to_curves_mat_ * brush_pos_su;
+ const float3 brush_pos_cu = transforms_.surface_to_curves * brush_pos_su;
r_added_points.positions_cu.append(brush_pos_cu);
r_added_points.bary_coords.append(bary_coords);
@@ -312,57 +295,37 @@ struct AddOperationExecutor {
const float4x4 &brush_transform)
{
const int old_amount = r_added_points.bary_coords.size();
- const int max_iterations = std::max(100'000, add_amount_ * 10);
+ const int max_iterations = 100;
int current_iteration = 0;
while (r_added_points.bary_coords.size() < old_amount + add_amount_) {
if (current_iteration++ >= max_iterations) {
break;
}
-
- const float r = brush_radius_re_ * std::sqrt(rng.get_float());
- const float angle = rng.get_float() * 2.0f * M_PI;
- const float2 pos_re = brush_pos_re_ + r * float2(std::cos(angle), std::sin(angle));
-
- float3 ray_start_wo, ray_end_wo;
- ED_view3d_win_to_segment_clipped(
- depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true);
- const float3 ray_start_su = brush_transform * (world_to_surface_mat_ * ray_start_wo);
- const float3 ray_end_su = brush_transform * (world_to_surface_mat_ * ray_end_wo);
- const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
-
- BVHTreeRayHit ray_hit;
- ray_hit.dist = FLT_MAX;
- ray_hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh_.tree,
- ray_start_su,
- ray_direction_su,
- 0.0f,
- &ray_hit,
- surface_bvh_.raycast_callback,
- &surface_bvh_);
-
- if (ray_hit.index == -1) {
- continue;
- }
-
- if (use_front_face_) {
- const float3 normal_su = ray_hit.no;
- if (math::dot(ray_direction_su, normal_su) >= 0.0f) {
- continue;
- }
+ const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size();
+ const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
+ rng,
+ *surface_,
+ surface_bvh_,
+ brush_pos_re_,
+ brush_radius_re_,
+ [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
+ float3 start_wo, end_wo;
+ ED_view3d_win_to_segment_clipped(
+ ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
+ const float3 start_cu = brush_transform * (transforms_.world_to_curves * start_wo);
+ const float3 end_cu = brush_transform * (transforms_.world_to_curves * end_wo);
+ r_start_su = transforms_.curves_to_surface * start_cu;
+ r_end_su = transforms_.curves_to_surface * end_cu;
+ },
+ use_front_face_,
+ add_amount_,
+ missing_amount,
+ r_added_points.bary_coords,
+ r_added_points.looptri_indices,
+ r_added_points.positions_cu);
+ for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) {
+ pos = transforms_.surface_to_curves * pos;
}
-
- const int looptri_index = ray_hit.index;
- const float3 pos_su = ray_hit.co;
-
- const float3 bary_coords = this->get_bary_coords(
- *surface_, surface_looptris_[looptri_index], pos_su);
-
- const float3 pos_cu = surface_to_curves_mat_ * pos_su;
-
- r_added_points.positions_cu.append(pos_cu);
- r_added_points.bary_coords.append(bary_coords);
- r_added_points.looptri_indices.append(looptri_index);
}
}
@@ -371,66 +334,47 @@ struct AddOperationExecutor {
*/
void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points)
{
- /* Find ray that starts in the center of the brush. */
- float3 brush_ray_start_wo, brush_ray_end_wo;
- ED_view3d_win_to_segment_clipped(
- depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true);
- const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo;
- const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo;
-
- /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius
- * in 3D. */
- float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo;
- ED_view3d_win_to_segment_clipped(depsgraph_,
- region_,
- v3d_,
- brush_pos_re_ + float2(brush_radius_re_, 0),
- brush_radius_ray_start_wo,
- brush_radius_ray_end_wo,
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ transforms_,
+ surface_bvh_,
+ brush_pos_re_,
+ brush_radius_re_);
+ if (!brush_3d.has_value()) {
+ return;
+ }
+
+ float3 view_ray_start_wo, view_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(ctx_.depsgraph,
+ ctx_.region,
+ ctx_.v3d,
+ brush_pos_re_,
+ view_ray_start_wo,
+ view_ray_end_wo,
true);
- const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo;
- const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo;
+ const float3 view_direction_su = math::normalize(
+ transforms_.world_to_surface * view_ray_end_wo -
+ transforms_.world_to_surface * view_ray_start_wo);
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->sample_spherical(rng,
- r_added_points,
- brush_transform * brush_ray_start_su,
- brush_transform * brush_ray_end_su,
- brush_transform * brush_radius_ray_start_su,
- brush_transform * brush_radius_ray_end_su);
+ const float4x4 transform = transforms_.curves_to_surface * brush_transform;
+ const float3 brush_pos_su = transform * brush_3d->position_cu;
+ const float brush_radius_su = transform_brush_radius(
+ transform, brush_3d->position_cu, brush_3d->radius_cu);
+ this->sample_spherical(
+ rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su);
}
}
void sample_spherical(RandomNumberGenerator &rng,
AddedPoints &r_added_points,
- const float3 &brush_ray_start_su,
- const float3 &brush_ray_end_su,
- const float3 &brush_radius_ray_start_su,
- const float3 &brush_radius_ray_end_su)
+ const float3 &brush_pos_su,
+ const float brush_radius_su,
+ const float3 &view_direction_su)
{
- const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su);
-
- BVHTreeRayHit ray_hit;
- ray_hit.dist = FLT_MAX;
- ray_hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh_.tree,
- brush_ray_start_su,
- brush_ray_direction_su,
- 0.0f,
- &ray_hit,
- surface_bvh_.raycast_callback,
- &surface_bvh_);
-
- if (ray_hit.index == -1) {
- return;
- }
-
- /* Compute brush radius. */
- const float3 brush_pos_su = ray_hit.co;
- const float brush_radius_su = dist_to_line_v3(
- brush_pos_su, brush_radius_ray_start_su, brush_radius_ray_end_su);
const float brush_radius_sq_su = pow2f(brush_radius_su);
/* Find surface triangles within brush radius. */
@@ -447,7 +391,7 @@ struct AddOperationExecutor {
const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
float3 normal_su;
normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
- if (math::dot(normal_su, brush_ray_direction_su) >= 0.0f) {
+ if (math::dot(normal_su, view_direction_su) >= 0.0f) {
return;
}
looptri_indices.append(index);
@@ -469,9 +413,6 @@ struct AddOperationExecutor {
const float brush_plane_area_su = M_PI * brush_radius_sq_su;
const float approximate_density_su = add_amount_ / brush_plane_area_su;
- /* Used for switching between two triangle sampling strategies. */
- const float area_threshold = brush_plane_area_su;
-
/* Usually one or two iterations should be enough. */
const int max_iterations = 5;
int current_iteration = 0;
@@ -481,78 +422,18 @@ struct AddOperationExecutor {
if (current_iteration++ >= max_iterations) {
break;
}
-
- for (const int looptri_index : looptri_indices) {
- const MLoopTri &looptri = surface_looptris_[looptri_index];
-
- const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
- const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
- const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
-
- const float looptri_area_su = area_tri_v3(v0_su, v1_su, v2_su);
-
- if (looptri_area_su < area_threshold) {
- /* The triangle is small compared to the brush radius. Sample by generating random
- * barycentric coordinates. */
- const int amount = rng.round_probabilistic(approximate_density_su * looptri_area_su);
- for ([[maybe_unused]] const int i : IndexRange(amount)) {
- const float3 bary_coord = rng.get_barycentric_coordinates();
- const float3 point_pos_su = attribute_math::mix3(bary_coord, v0_su, v1_su, v2_su);
- const float distance_to_brush_sq_su = math::distance_squared(point_pos_su,
- brush_pos_su);
- if (distance_to_brush_sq_su > brush_radius_sq_su) {
- continue;
- }
-
- r_added_points.bary_coords.append(bary_coord);
- r_added_points.looptri_indices.append(looptri_index);
- r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su);
- }
- }
- else {
- /* The triangle is large compared to the brush radius. Sample by generating random points
- * on the triangle plane within the brush radius. */
- float3 normal_su;
- normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
-
- float3 brush_pos_proj_su = brush_pos_su;
- project_v3_plane(brush_pos_proj_su, normal_su, v0_su);
-
- const float proj_distance_sq_su = math::distance_squared(brush_pos_proj_su,
- brush_pos_su);
- const float brush_radius_factor_sq = 1.0f -
- std::min(1.0f,
- proj_distance_sq_su / brush_radius_sq_su);
- const float radius_proj_sq_su = brush_radius_sq_su * brush_radius_factor_sq;
- const float radius_proj_su = std::sqrt(radius_proj_sq_su);
- const float circle_area_su = M_PI * radius_proj_su;
-
- const int amount = rng.round_probabilistic(approximate_density_su * circle_area_su);
-
- const float3 axis_1_su = math::normalize(v1_su - v0_su) * radius_proj_su;
- const float3 axis_2_su = math::normalize(math::cross(
- axis_1_su, math::cross(axis_1_su, v2_su - v0_su))) *
- radius_proj_su;
-
- for ([[maybe_unused]] const int i : IndexRange(amount)) {
- const float r = std::sqrt(rng.get_float());
- const float angle = rng.get_float() * 2.0f * M_PI;
- const float x = r * std::cos(angle);
- const float y = r * std::sin(angle);
- const float3 point_pos_su = brush_pos_proj_su + axis_1_su * x + axis_2_su * y;
- if (!isect_point_tri_prism_v3(point_pos_su, v0_su, v1_su, v2_su)) {
- /* Sampled point is not in the triangle. */
- continue;
- }
-
- float3 bary_coord;
- interp_weights_tri_v3(bary_coord, v0_su, v1_su, v2_su, point_pos_su);
-
- r_added_points.bary_coords.append(bary_coord);
- r_added_points.looptri_indices.append(looptri_index);
- r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su);
- }
- }
+ const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
+ rng,
+ *surface_,
+ looptri_indices,
+ brush_pos_su,
+ brush_radius_su,
+ approximate_density_su,
+ r_added_points.bary_coords,
+ r_added_points.looptri_indices,
+ r_added_points.positions_cu);
+ for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) {
+ pos = transforms_.surface_to_curves * pos;
}
}
@@ -577,271 +458,20 @@ struct AddOperationExecutor {
BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
}
}
-
- void initialize_curve_offsets(const int tot_added_curves)
- {
- MutableSpan<int> offsets = curves_->offsets_for_write();
- threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) {
- for (const int i : range) {
- const int curve_i = tot_old_curves_ + i;
- offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_;
- }
- });
- }
-
- struct NeighborInfo {
- /* Curve index of the neighbor. */
- int index;
- /* The weights of all neighbors of a new curve add up to 1. */
- float weight;
- };
- static constexpr int max_neighbors = 5;
- using NeighborsVector = Vector<NeighborInfo, max_neighbors>;
-
- void initialize_attributes(const AddedPoints &added_points)
- {
- Array<NeighborsVector> neighbors_per_curve;
- if (use_interpolation_) {
- neighbors_per_curve = this->find_curve_neighbors(added_points);
- }
-
- Array<float> new_lengths_cu(added_points.bary_coords.size());
- if (interpolate_length_) {
- this->interpolate_lengths(neighbors_per_curve, new_lengths_cu);
- }
- else {
- new_lengths_cu.fill(new_curve_length_);
- }
-
- Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points);
- this->initialize_surface_attachment(added_points);
-
- if (interpolate_shape_) {
- this->initialize_position_with_interpolation(
- added_points, neighbors_per_curve, new_normals_su, new_lengths_cu);
- }
- else {
- this->initialize_position_without_interpolation(
- added_points, new_lengths_cu, new_normals_su);
- }
- }
-
- Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points)
- {
- const int tot_added_curves = added_points.bary_coords.size();
- Array<NeighborsVector> neighbors_per_curve(tot_added_curves);
- threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) {
- for (const int i : range) {
- const float3 root_cu = added_points.positions_cu[i];
- std::array<KDTreeNearest_3d, max_neighbors> nearest_n;
- const int found_neighbors = BLI_kdtree_3d_find_nearest_n(
- self_->curve_roots_kdtree_, root_cu, nearest_n.data(), max_neighbors);
- float tot_weight = 0.0f;
- for (const int neighbor_i : IndexRange(found_neighbors)) {
- KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
- const float weight = 1.0f / std::max(nearest.dist, 0.00001f);
- tot_weight += weight;
- neighbors_per_curve[i].append({nearest.index, weight});
- }
- /* Normalize weights. */
- for (NeighborInfo &neighbor : neighbors_per_curve[i]) {
- neighbor.weight /= tot_weight;
- }
- }
- });
- return neighbors_per_curve;
- }
-
- void interpolate_lengths(const Span<NeighborsVector> neighbors_per_curve,
- MutableSpan<float> r_lengths)
- {
- const Span<float3> positions_cu = curves_->positions();
-
- threading::parallel_for(r_lengths.index_range(), 128, [&](const IndexRange range) {
- for (const int added_curve_i : range) {
- const Span<NeighborInfo> neighbors = neighbors_per_curve[added_curve_i];
- float length_sum = 0.0f;
- for (const NeighborInfo &neighbor : neighbors) {
- const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index);
- float neighbor_length = 0.0f;
- const int tot_segments = neighbor_points.size() - 1;
- for (const int segment_i : IndexRange(tot_segments)) {
- const float3 &p1 = positions_cu[neighbor_points[segment_i]];
- const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1];
- neighbor_length += math::distance(p1, p2);
- }
- length_sum += neighbor.weight * neighbor_length;
- }
- const float length = neighbors.is_empty() ? new_curve_length_ : length_sum;
- r_lengths[added_curve_i] = length;
- }
- });
- }
-
- float3 compute_point_normal_su(const int looptri_index, const float3 &bary_coord)
- {
- const MLoopTri &looptri = surface_looptris_[looptri_index];
- const int l0 = looptri.tri[0];
- const int l1 = looptri.tri[1];
- const int l2 = looptri.tri[2];
-
- const float3 &l0_normal_su = corner_normals_su_[l0];
- const float3 &l1_normal_su = corner_normals_su_[l1];
- const float3 &l2_normal_su = corner_normals_su_[l2];
-
- const float3 normal_su = math::normalize(
- attribute_math::mix3(bary_coord, l0_normal_su, l1_normal_su, l2_normal_su));
- return normal_su;
- }
-
- Array<float3> compute_normals_for_added_curves_su(const AddedPoints &added_points)
- {
- Array<float3> normals_su(added_points.bary_coords.size());
- threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) {
- for (const int i : range) {
- const int looptri_index = added_points.looptri_indices[i];
- const float3 &bary_coord = added_points.bary_coords[i];
- normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord);
- }
- });
- return normals_su;
- }
-
- void initialize_surface_attachment(const AddedPoints &added_points)
- {
- MutableSpan<int> surface_triangle_indices = curves_->surface_triangle_indices_for_write();
- MutableSpan<float2> surface_triangle_coords = curves_->surface_triangle_coords_for_write();
- threading::parallel_for(
- added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) {
- for (const int i : range) {
- const int curve_i = tot_old_curves_ + i;
- surface_triangle_indices[curve_i] = added_points.looptri_indices[i];
- surface_triangle_coords[curve_i] = float2(added_points.bary_coords[i]);
- }
- });
- }
-
- /**
- * Initialize new curves so that they are just a straight line in the normal direction.
- */
- void initialize_position_without_interpolation(const AddedPoints &added_points,
- const Span<float> lengths_cu,
- const MutableSpan<float3> normals_su)
- {
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
-
- threading::parallel_for(
- added_points.bary_coords.index_range(), 256, [&](const IndexRange range) {
- for (const int i : range) {
- const int first_point_i = tot_old_points_ + i * points_per_curve_;
- const float3 &root_cu = added_points.positions_cu[i];
- const float length = lengths_cu[i];
- const float3 &normal_su = normals_su[i];
- const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su);
- const float3 tip_cu = root_cu + length * normal_cu;
-
- initialize_straight_curve_positions(
- root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_));
- }
- });
- }
-
- /**
- * Use neighboring curves to determine the shape.
- */
- void initialize_position_with_interpolation(const AddedPoints &added_points,
- const Span<NeighborsVector> neighbors_per_curve,
- const Span<float3> new_normals_su,
- const Span<float> new_lengths_cu)
- {
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
- const VArray_Span<int> surface_triangle_indices{curves_->surface_triangle_indices()};
- const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords();
-
- threading::parallel_for(
- added_points.bary_coords.index_range(), 256, [&](const IndexRange range) {
- for (const int i : range) {
- const Span<NeighborInfo> neighbors = neighbors_per_curve[i];
-
- const float length_cu = new_lengths_cu[i];
- const float3 &normal_su = new_normals_su[i];
- const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su);
-
- const float3 &root_cu = added_points.positions_cu[i];
- const int first_point_i = tot_old_points_ + i * points_per_curve_;
-
- if (neighbors.is_empty()) {
- /* If there are no neighbors, just make a straight line. */
- const float3 tip_cu = root_cu + length_cu * normal_cu;
- initialize_straight_curve_positions(
- root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_));
- continue;
- }
-
- positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu);
-
- for (const NeighborInfo &neighbor : neighbors) {
- const int neighbor_curve_i = neighbor.index;
- const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i];
-
- float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]};
- neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y;
-
- const float3 neighbor_normal_su = this->compute_point_normal_su(
- neighbor_looptri_index, neighbor_bary_coord);
- const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ *
- neighbor_normal_su);
-
- /* The rotation matrix used to transform relative coordinates of the neighbor curve
- * to the new curve. */
- float normal_rotation_cu[3][3];
- rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu);
-
- const IndexRange neighbor_points = curves_->points_for_curve(neighbor_curve_i);
- const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]];
-
- /* Use a temporary #PolySpline, because that's the easiest way to resample an
- * existing curve right now. Resampling is necessary if the length of the new curve
- * does not match the length of the neighbors or the number of handle points is
- * different. */
- PolySpline neighbor_spline;
- neighbor_spline.resize(neighbor_points.size());
- neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points));
- neighbor_spline.mark_cache_invalid();
-
- const float neighbor_length_cu = neighbor_spline.length();
- const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
-
- const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor;
- for (const int j : IndexRange(points_per_curve_)) {
- const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor(
- j * resample_factor);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- neighbor_spline.sample_with_index_factors<float3>(
- neighbor_spline.positions(), {&index_factor, 1}, {&p, 1});
- const float3 relative_coord = p - neighbor_root_cu;
- float3 rotated_relative_coord = relative_coord;
- mul_m3_v3(normal_rotation_cu, rotated_relative_coord);
- positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord;
- }
- }
- }
- });
- }
};
-void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
{
- AddOperationExecutor executor;
+ AddOperationExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports)
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
+ ReportList *reports)
{
- Object &ob_active = *CTX_data_active_object(&C);
+ const Object &ob_active = *CTX_data_active_object(&C);
BLI_assert(ob_active.type == OB_CURVES);
- Curves &curves_id = *static_cast<Curves *>(ob_active.data);
+ const Curves &curves_id = *static_cast<Curves *>(ob_active.data);
if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) {
BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh");
return {};
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
index 89470772e1c..8c6ef34ef26 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
@@ -4,15 +4,22 @@
#include "curves_sculpt_intern.hh"
+#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "DNA_meshdata_types.h"
+
#include "ED_view3d.h"
#include "UI_interface.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_length_parameterize.hh"
#include "BLI_task.hh"
/**
@@ -41,9 +48,9 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
const float3 &ray_start_cu,
const float3 &ray_end_cu,
const float brush_radius_re,
- ARegion &region,
- RegionView3D &rv3d,
- Object &object)
+ const ARegion &region,
+ const RegionView3D &rv3d,
+ const Object &object)
{
/* This value might have to be adjusted based on user feedback. */
const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f);
@@ -94,11 +101,30 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
- const int tot_segments = points.size() - 1;
- for (const int segment_i : IndexRange(tot_segments)) {
- const float3 &p1_cu = positions[points[segment_i]];
- const float3 &p2_cu = positions[points[segment_i] + 1];
+ if (points.size() == 1) {
+ const float3 &pos_cu = positions[points.first()];
+
+ const float depth_sq_cu = math::distance_squared(ray_start_cu, pos_cu);
+ if (depth_sq_cu > max_depth_sq_cu) {
+ continue;
+ }
+
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(&region, pos_cu, pos_re, projection.values);
+
+ BrushPositionCandidate candidate;
+ candidate.position_cu = pos_cu;
+ candidate.depth_sq_cu = depth_sq_cu;
+ candidate.distance_sq_re = math::distance_squared(brush_pos_re, pos_re);
+
+ update_if_better(best_candidate, candidate);
+ continue;
+ }
+
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions[segment_i];
+ const float3 &p2_cu = positions[segment_i + 1];
float2 p1_re, p2_re;
ED_view3d_project_float_v2_m4(&region, p1_cu, p1_re, projection.values);
@@ -141,23 +167,21 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
return best_candidate.position_cu;
}
-std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
- Object &curves_object,
+std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
+ const ARegion &region,
+ const View3D &v3d,
+ const RegionView3D &rv3d,
+ const Object &curves_object,
const float2 &brush_pos_re,
const float brush_radius_re)
{
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
- ARegion *region = CTX_wm_region(&C);
- View3D *v3d = CTX_wm_view3d(&C);
- RegionView3D *rv3d = CTX_wm_region_view3d(&C);
-
- Curves &curves_id = *static_cast<Curves *>(curves_object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- Object *surface_object = curves_id.surface;
+ const Curves &curves_id = *static_cast<Curves *>(curves_object.data);
+ const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ const Object *surface_object = curves_id.surface;
float3 center_ray_start_wo, center_ray_end_wo;
ED_view3d_win_to_segment_clipped(
- depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true);
+ &depsgraph, &region, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true);
/* Shorten ray when the surface object is hit. */
if (surface_object != nullptr) {
@@ -205,8 +229,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
center_ray_start_cu,
center_ray_end_cu,
brush_radius_re,
- *region,
- *rv3d,
+ region,
+ rv3d,
curves_object);
if (!brush_position_optional_cu.has_value()) {
/* Nothing found. */
@@ -216,9 +240,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
/* Determine the 3D brush radius. */
float3 radius_ray_start_wo, radius_ray_end_wo;
- ED_view3d_win_to_segment_clipped(depsgraph,
- region,
- v3d,
+ ED_view3d_win_to_segment_clipped(&depsgraph,
+ &region,
+ &v3d,
brush_pos_re + float2(brush_radius_re, 0.0f),
radius_ray_start_wo,
radius_ray_end_wo,
@@ -232,6 +256,55 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
return brush_3d;
}
+std::optional<CurvesBrush3D> sample_curves_surface_3d_brush(
+ const Depsgraph &depsgraph,
+ const ARegion &region,
+ const View3D &v3d,
+ const CurvesSculptTransforms &transforms,
+ const BVHTreeFromMesh &surface_bvh,
+ const float2 &brush_pos_re,
+ const float brush_radius_re)
+{
+ float3 brush_ray_start_wo, brush_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ &depsgraph, &region, &v3d, brush_pos_re, brush_ray_start_wo, brush_ray_end_wo, true);
+ const float3 brush_ray_start_su = transforms.world_to_surface * brush_ray_start_wo;
+ const float3 brush_ray_end_su = transforms.world_to_surface * brush_ray_end_wo;
+
+ const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su);
+
+ BVHTreeRayHit ray_hit;
+ ray_hit.dist = FLT_MAX;
+ ray_hit.index = -1;
+ BLI_bvhtree_ray_cast(surface_bvh.tree,
+ brush_ray_start_su,
+ brush_ray_direction_su,
+ 0.0f,
+ &ray_hit,
+ surface_bvh.raycast_callback,
+ const_cast<void *>(static_cast<const void *>(&surface_bvh)));
+ if (ray_hit.index == -1) {
+ return std::nullopt;
+ }
+
+ float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(&depsgraph,
+ &region,
+ &v3d,
+ brush_pos_re + float2(brush_radius_re, 0),
+ brush_radius_ray_start_wo,
+ brush_radius_ray_end_wo,
+ true);
+ const float3 brush_radius_ray_start_cu = transforms.world_to_curves * brush_radius_ray_start_wo;
+ const float3 brush_radius_ray_end_cu = transforms.world_to_curves * brush_radius_ray_end_wo;
+
+ const float3 brush_pos_su = ray_hit.co;
+ const float3 brush_pos_cu = transforms.surface_to_curves * brush_pos_su;
+ const float brush_radius_cu = dist_to_line_v3(
+ brush_pos_cu, brush_radius_ray_start_cu, brush_radius_ray_end_cu);
+ return CurvesBrush3D{brush_pos_cu, brush_radius_cu};
+}
+
Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
{
Vector<float4x4> matrices;
@@ -260,4 +333,68 @@ Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetr
return matrices;
}
+float transform_brush_radius(const float4x4 &transform,
+ const float3 &brush_position,
+ const float old_radius)
+{
+ const float3 offset_position = brush_position + float3(old_radius, 0.0f, 0.0f);
+ const float3 new_position = transform * brush_position;
+ const float3 new_offset_position = transform * offset_position;
+ return math::distance(new_position, new_offset_position);
+}
+
+void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position)
+{
+ /* Find the accumulated length of each point in the original curve,
+ * treating it as a poly curve for performance reasons and simplicity. */
+ Array<float> orig_lengths(length_parameterize::lengths_num(positions.size(), false));
+ length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths);
+ const float orig_total_length = orig_lengths.last();
+
+ /* Find the factor by which the new curve is shorter or longer than the original. */
+ const float new_last_segment_length = math::distance(positions.last(1), new_last_position);
+ const float new_total_length = orig_lengths.last(1) + new_last_segment_length;
+ const float length_factor = safe_divide(new_total_length, orig_total_length);
+
+ /* Calculate the lengths to sample the original curve with by scaling the original lengths. */
+ Array<float> new_lengths(positions.size() - 1);
+ new_lengths.first() = 0.0f;
+ for (const int i : new_lengths.index_range().drop_front(1)) {
+ new_lengths[i] = orig_lengths[i - 1] * length_factor;
+ }
+
+ Array<int> indices(positions.size() - 1);
+ Array<float> factors(positions.size() - 1);
+ length_parameterize::create_samples_from_sorted_lengths(
+ orig_lengths, new_lengths, false, indices, factors);
+
+ Array<float3> new_positions(positions.size() - 1);
+ length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
+ positions.drop_back(1).copy_from(new_positions);
+ positions.last() = new_last_position;
+}
+
+CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C)
+{
+ this->depsgraph = CTX_data_depsgraph_pointer(&C);
+ this->scene = CTX_data_scene(&C);
+ this->region = CTX_wm_region(&C);
+ this->v3d = CTX_wm_view3d(&C);
+ this->rv3d = CTX_wm_region_view3d(&C);
+}
+
+CurvesSculptTransforms::CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob)
+{
+ this->curves_to_world = curves_ob.obmat;
+ this->world_to_curves = this->curves_to_world.inverted();
+
+ if (surface_ob != nullptr) {
+ this->surface_to_world = surface_ob->obmat;
+ this->world_to_surface = this->surface_to_world.inverted();
+ this->surface_to_curves = this->world_to_curves * this->surface_to_world;
+ this->curves_to_surface = this->world_to_surface * this->curves_to_world;
+ this->surface_to_curves_normal = this->surface_to_curves.inverted().transposed();
+ }
+}
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
index 28258a26f74..541bf9d8253 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
@@ -37,6 +37,8 @@
#include "UI_interface.h"
+#include "WM_api.h"
+
/**
* The code below uses a prefix naming convention to indicate the coordinate space:
* cu: Local space of the curves object that is being edited.
@@ -67,7 +69,7 @@ class CombOperation : public CurvesSculptStrokeOperation {
friend struct CombOperationExecutor;
public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
/**
@@ -76,61 +78,47 @@ class CombOperation : public CurvesSculptStrokeOperation {
*/
struct CombOperationExecutor {
CombOperation *self_ = nullptr;
- bContext *C_ = nullptr;
- Depsgraph *depsgraph_ = nullptr;
- Scene *scene_ = nullptr;
- Object *object_ = nullptr;
- ARegion *region_ = nullptr;
- View3D *v3d_ = nullptr;
- RegionView3D *rv3d_ = nullptr;
+ CurvesSculptCommonContext ctx_;
- CurvesSculpt *curves_sculpt_ = nullptr;
- Brush *brush_ = nullptr;
- float brush_radius_re_;
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
float brush_strength_;
eBrushFalloffShape falloff_shape_;
+ Object *object_ = nullptr;
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
- const Object *surface_ob_ = nullptr;
- const Mesh *surface_ = nullptr;
- Span<MLoopTri> surface_looptris_;
+ VArray<float> point_factors_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
float2 brush_pos_prev_re_;
float2 brush_pos_re_;
float2 brush_pos_diff_re_;
- float brush_pos_diff_length_re_;
- float4x4 curves_to_world_mat_;
- float4x4 world_to_curves_mat_;
- float4x4 surface_to_world_mat_;
- float4x4 world_to_surface_mat_;
+ CurvesSculptTransforms transforms_;
- BVHTreeFromMesh surface_bvh_;
+ CombOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
- void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
self_ = &self;
BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; });
- C_ = C;
- depsgraph_ = CTX_data_depsgraph_pointer(C);
- scene_ = CTX_data_scene(C);
- object_ = CTX_data_active_object(C);
- region_ = CTX_wm_region(C);
- v3d_ = CTX_wm_view3d(C);
- rv3d_ = CTX_wm_region_view3d(C);
-
- curves_sculpt_ = scene_->toolsettings->curves_sculpt;
- brush_ = BKE_paint_brush(&curves_sculpt_->paint);
- brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
- brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
+ object_ = CTX_data_active_object(&C);
- curves_to_world_mat_ = object_->obmat;
- world_to_curves_mat_ = curves_to_world_mat_.inverted();
+ curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
+ brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
+ brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
+ brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
@@ -140,26 +128,14 @@ struct CombOperationExecutor {
return;
}
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+
+ point_factors_ = get_point_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
brush_pos_prev_re_ = self_->brush_pos_last_re_;
brush_pos_re_ = stroke_extension.mouse_position;
brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
- brush_pos_diff_length_re_ = math::length(brush_pos_diff_re_);
-
- surface_ob_ = curves_id_->surface;
- if (surface_ob_ != nullptr) {
- surface_ = static_cast<const Mesh *>(surface_ob_->data);
- surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
- BKE_mesh_runtime_looptri_len(surface_)};
- surface_to_world_mat_ = surface_ob_->obmat;
- world_to_surface_mat_ = surface_to_world_mat_.inverted();
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- }
-
- BLI_SCOPED_DEFER([&]() {
- if (surface_ob_ != nullptr) {
- free_bvhtree_from_mesh(&surface_bvh_);
- }
- });
if (stroke_extension.is_first) {
if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
@@ -186,7 +162,8 @@ struct CombOperationExecutor {
curves_->tag_positions_changed();
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region_);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
}
/**
@@ -209,13 +186,14 @@ struct CombOperationExecutor {
MutableSpan<float3> positions_cu = curves_->positions_for_write();
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
- const float brush_radius_sq_re = pow2f(brush_radius_re_);
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
- threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<int> &local_changed_curves = r_changed_curves.local();
- for (const int curve_i : curves_range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
const IndexRange points = curves_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
@@ -223,7 +201,7 @@ struct CombOperationExecutor {
/* Find the position of the point in screen space. */
float2 old_pos_re;
- ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2(
old_pos_re, brush_pos_prev_re_, brush_pos_re_);
@@ -235,17 +213,21 @@ struct CombOperationExecutor {
const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re);
/* A falloff that is based on how far away the point is from the stroke. */
const float radius_falloff = BKE_brush_curve_strength(
- brush_, distance_to_brush_re, brush_radius_re_);
+ brush_, distance_to_brush_re, brush_radius_re);
/* Combine the falloff and brush strength. */
- const float weight = brush_strength_ * radius_falloff;
+ const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
- /* Offset the old point position in screen space and transform it back into 3D space. */
+ /* Offset the old point position in screen space and transform it back into 3D space.
+ */
const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
float3 new_position_wo;
- ED_view3d_win_to_3d(
- v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo);
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * old_pos_cu,
+ new_position_re,
+ new_position_wo);
const float3 new_position_cu = brush_transform *
- (world_to_curves_mat_ * new_position_wo);
+ (transforms_.world_to_curves * new_position_wo);
positions_cu[point_i] = new_position_cu;
curve_changed = true;
@@ -263,23 +245,23 @@ struct CombOperationExecutor {
void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
float3 brush_start_wo, brush_end_wo;
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_prev_re_,
brush_start_wo);
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_re_,
brush_end_wo);
- const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
- const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+ const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo;
+ const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo;
- const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
@@ -300,9 +282,9 @@ struct CombOperationExecutor {
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
- threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<int> &local_changed_curves = r_changed_curves.local();
- for (const int curve_i : curves_range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
const IndexRange points = curves_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
@@ -322,7 +304,7 @@ struct CombOperationExecutor {
const float radius_falloff = BKE_brush_curve_strength(
brush_, distance_to_brush_cu, brush_radius_cu);
/* Combine the falloff and brush strength. */
- const float weight = brush_strength_ * radius_falloff;
+ const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
/* Update the point position. */
positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu;
@@ -340,8 +322,13 @@ struct CombOperationExecutor {
*/
void initialize_spherical_brush_reference_point()
{
- std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
- *C_, *object_, brush_pos_re_, brush_radius_re_);
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
if (brush_3d.has_value()) {
self_->brush_3d_ = *brush_3d;
}
@@ -380,11 +367,11 @@ struct CombOperationExecutor {
threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : changed_curves.as_span().slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
- for (const int segment_i : IndexRange(points.size() - 1)) {
- const float3 &p1_cu = positions_cu[points[segment_i]];
- float3 &p2_cu = positions_cu[points[segment_i] + 1];
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions_cu[segment_i];
+ float3 &p2_cu = positions_cu[segment_i + 1];
const float3 direction = math::normalize(p2_cu - p1_cu);
- const float expected_length_cu = expected_lengths_cu[points[segment_i]];
+ const float expected_length_cu = expected_lengths_cu[segment_i];
p2_cu = p1_cu + direction * expected_length_cu;
}
}
@@ -393,9 +380,9 @@ struct CombOperationExecutor {
}
};
-void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
{
- CombOperationExecutor executor;
+ CombOperationExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
index 3f3c48ecbb6..eab7dabcd22 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
@@ -35,6 +35,8 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "WM_api.h"
+
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
* cu: Local space of the curves object that is being edited.
@@ -48,65 +50,57 @@ using blender::bke::CurvesGeometry;
class DeleteOperation : public CurvesSculptStrokeOperation {
private:
- float2 brush_pos_prev_re_;
-
CurvesBrush3D brush_3d_;
friend struct DeleteOperationExecutor;
public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
struct DeleteOperationExecutor {
DeleteOperation *self_ = nullptr;
- bContext *C_ = nullptr;
- Depsgraph *depsgraph_ = nullptr;
- Scene *scene_ = nullptr;
- Object *object_ = nullptr;
- ARegion *region_ = nullptr;
- View3D *v3d_ = nullptr;
- RegionView3D *rv3d_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+ Object *object_ = nullptr;
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
- CurvesSculpt *curves_sculpt_ = nullptr;
- Brush *brush_ = nullptr;
- float brush_radius_re_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
float2 brush_pos_re_;
- float2 brush_pos_prev_re_;
- float4x4 curves_to_world_mat_;
- float4x4 world_to_curves_mat_;
+ CurvesSculptTransforms transforms_;
- void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ DeleteOperationExecutor(const bContext &C) : ctx_(C)
{
- BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; });
+ }
+ void execute(DeleteOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
+ {
self_ = &self;
- C_ = C;
- depsgraph_ = CTX_data_depsgraph_pointer(C);
- scene_ = CTX_data_scene(C);
- object_ = CTX_data_active_object(C);
- region_ = CTX_wm_region(C);
- v3d_ = CTX_wm_view3d(C);
- rv3d_ = CTX_wm_region_view3d(C);
+ object_ = CTX_data_active_object(&C);
curves_id_ = static_cast<Curves *>(object_->data);
curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
- curves_sculpt_ = scene_->toolsettings->curves_sculpt;
- brush_ = BKE_paint_brush(&curves_sculpt_->paint);
- brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ selected_curve_indices_.clear();
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
+ curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
+ brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
+ brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
brush_pos_re_ = stroke_extension.mouse_position;
- brush_pos_prev_re_ = stroke_extension.is_first ? stroke_extension.mouse_position :
- self.brush_pos_prev_re_;
- curves_to_world_mat_ = object_->obmat;
- world_to_curves_mat_ = curves_to_world_mat_.inverted();
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
@@ -137,7 +131,8 @@ struct DeleteOperationExecutor {
curves_->remove_curves(mask);
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region_);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
}
void delete_projected_with_symmetry(MutableSpan<bool> curves_to_delete)
@@ -145,32 +140,47 @@ struct DeleteOperationExecutor {
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->delete_projected(curves_to_delete, brush_transform);
+ this->delete_projected(brush_transform, curves_to_delete);
}
}
- void delete_projected(MutableSpan<bool> curves_to_delete, const float4x4 &brush_transform)
+ void delete_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_delete)
{
const float4x4 brush_transform_inv = brush_transform.inverted();
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
Span<float3> positions_cu = curves_->positions();
- threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) {
- for (const int curve_i : curve_range) {
- const IndexRange point_range = curves_->points_for_curve(curve_i);
- for (const int segment_i : IndexRange(point_range.size() - 1)) {
- const float3 pos1_cu = brush_transform_inv * positions_cu[point_range[segment_i]];
- const float3 pos2_cu = brush_transform_inv * positions_cu[point_range[segment_i + 1]];
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
+
+ threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ if (points.size() == 1) {
+ const float3 pos_cu = brush_transform_inv * positions_cu[points.first()];
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+
+ if (math::distance_squared(brush_pos_re_, pos_re) <= brush_radius_sq_re) {
+ curves_to_delete[curve_i] = true;
+ }
+ continue;
+ }
+
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i];
+ const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1];
float2 pos1_re, pos2_re;
- ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values);
- ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, pos2_cu, pos2_re, projection.values);
- const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_);
- if (dist <= brush_radius_re_) {
+ const float dist_sq_re = dist_squared_to_line_segment_v2(
+ brush_pos_re_, pos1_re, pos2_re);
+ if (dist_sq_re <= brush_radius_sq_re) {
curves_to_delete[curve_i] = true;
break;
}
@@ -182,57 +192,50 @@ struct DeleteOperationExecutor {
void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete)
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
-
- float3 brush_start_wo, brush_end_wo;
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
- brush_pos_prev_re_,
- brush_start_wo);
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ float3 brush_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_re_,
- brush_end_wo);
- const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
- const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+ brush_wo);
+ const float3 brush_cu = transforms_.world_to_curves * brush_wo;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->delete_spherical(
- curves_to_delete, brush_transform * brush_start_cu, brush_transform * brush_end_cu);
+ this->delete_spherical(brush_transform * brush_cu, curves_to_delete);
}
}
- void delete_spherical(MutableSpan<bool> curves_to_delete,
- const float3 &brush_start_cu,
- const float3 &brush_end_cu)
+ void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete)
{
Span<float3> positions_cu = curves_->positions();
- const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
- threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) {
- for (const int curve_i : curve_range) {
+ threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
- for (const int segment_i : IndexRange(points.size() - 1)) {
- const float3 pos1_cu = positions_cu[points[segment_i]];
- const float3 pos2_cu = positions_cu[points[segment_i] + 1];
-
- float3 closest_segment_cu, closest_brush_cu;
- isect_seg_seg_v3(pos1_cu,
- pos2_cu,
- brush_start_cu,
- brush_end_cu,
- closest_segment_cu,
- closest_brush_cu);
- const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu,
- closest_brush_cu);
- if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
+
+ if (points.size() == 1) {
+ const float3 &pos_cu = positions_cu[points.first()];
+ const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu);
+ if (distance_sq_cu < brush_radius_sq_cu) {
+ curves_to_delete[curve_i] = true;
+ }
+ continue;
+ }
+
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 &pos1_cu = positions_cu[segment_i];
+ const float3 &pos2_cu = positions_cu[segment_i + 1];
+
+ const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu);
+ if (distance_sq_cu > brush_radius_sq_cu) {
continue;
}
curves_to_delete[curve_i] = true;
@@ -244,17 +247,23 @@ struct DeleteOperationExecutor {
void initialize_spherical_brush_reference_point()
{
- std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
- *C_, *object_, brush_pos_re_, brush_radius_re_);
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
if (brush_3d.has_value()) {
self_->brush_3d_ = *brush_3d;
}
}
};
-void DeleteOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+void DeleteOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
{
- DeleteOperationExecutor executor;
+ DeleteOperationExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
index 4ac4559cbbc..cf893f09fc6 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
@@ -2,8 +2,6 @@
#include <algorithm>
-#include "curves_sculpt_intern.hh"
-
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_float4x4.hh"
#include "BLI_kdtree.h"
@@ -36,6 +34,8 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "WM_api.h"
+
#include "curves_sculpt_intern.hh"
/**
@@ -68,10 +68,10 @@ class CurvesEffect {
*/
class ShrinkCurvesEffect : public CurvesEffect {
private:
- Brush &brush_;
+ const Brush &brush_;
public:
- ShrinkCurvesEffect(Brush &brush) : brush_(brush)
+ ShrinkCurvesEffect(const Brush &brush) : brush_(brush)
{
}
@@ -151,50 +151,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point);
const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu;
- this->move_last_point_and_resample(positions_cu, curve_points, new_last_pos_cu);
+ move_last_point_and_resample(positions_cu.slice(curve_points), new_last_pos_cu);
}
});
}
-
- void move_last_point_and_resample(MutableSpan<float3> positions,
- const IndexRange curve_points,
- const float3 &new_last_point_position) const
- {
- Vector<float> old_lengths;
- old_lengths.append(0.0f);
- /* Used to (1) normalize the segment sizes over time and (2) support making zero-length
- * segments */
- const float extra_length = 0.001f;
- for (const int segment_i : IndexRange(curve_points.size() - 1)) {
- const float3 &p1 = positions[curve_points[segment_i]];
- const float3 &p2 = positions[curve_points[segment_i] + 1];
- const float length = math::distance(p1, p2);
- old_lengths.append(old_lengths.last() + length + extra_length);
- }
- Vector<float> point_factors;
- for (float &old_length : old_lengths) {
- point_factors.append(old_length / old_lengths.last());
- }
-
- PolySpline new_spline;
- new_spline.resize(curve_points.size());
- MutableSpan<float3> new_spline_positions = new_spline.positions();
- for (const int i : IndexRange(curve_points.size() - 1)) {
- new_spline_positions[i] = positions[curve_points[i]];
- }
- new_spline_positions.last() = new_last_point_position;
- new_spline.mark_cache_invalid();
-
- for (const int i : IndexRange(curve_points.size())) {
- const float factor = point_factors[i];
- const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- new_spline.sample_with_index_factors<float3>(
- new_spline_positions, {&index_factor, 1}, {&p, 1});
- positions[curve_points[i]] = p;
- }
- }
};
/**
@@ -203,10 +163,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
class ScaleCurvesEffect : public CurvesEffect {
private:
bool scale_up_;
- Brush &brush_;
+ const Brush &brush_;
public:
- ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush)
+ ScaleCurvesEffect(bool scale_up, const Brush &brush) : scale_up_(scale_up), brush_(brush)
{
}
@@ -261,7 +221,7 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation {
{
}
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
/**
@@ -270,24 +230,24 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation {
*/
struct CurvesEffectOperationExecutor {
CurvesEffectOperation *self_ = nullptr;
- Depsgraph *depsgraph_ = nullptr;
- Scene *scene_ = nullptr;
- Object *object_ = nullptr;
- ARegion *region_ = nullptr;
- View3D *v3d_ = nullptr;
- RegionView3D *rv3d_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+ Object *object_ = nullptr;
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
- Brush *brush_ = nullptr;
- float brush_radius_re_;
- float brush_radius_sq_re_;
+ VArray<float> curve_selection_factors_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
float brush_strength_;
+
eBrushFalloffShape falloff_shape_;
- float4x4 curves_to_world_mat_;
- float4x4 world_to_curves_mat_;
+ CurvesSculptTransforms transforms_;
float2 brush_pos_start_re_;
float2 brush_pos_end_re_;
@@ -297,17 +257,18 @@ struct CurvesEffectOperationExecutor {
Vector<float> move_distances_cu;
};
- void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ CurvesEffectOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(CurvesEffectOperation &self,
+ const bContext &C,
+ const StrokeExtension &stroke_extension)
{
BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; });
self_ = &self;
- depsgraph_ = CTX_data_depsgraph_pointer(C);
- scene_ = CTX_data_scene(C);
- object_ = CTX_data_active_object(C);
- region_ = CTX_wm_region(C);
- v3d_ = CTX_wm_view3d(C);
- rv3d_ = CTX_wm_region_view3d(C);
+ object_ = CTX_data_active_object(&C);
curves_id_ = static_cast<Curves *>(object_->data);
curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
@@ -315,15 +276,20 @@ struct CurvesEffectOperationExecutor {
return;
}
- CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt;
- brush_ = BKE_paint_brush(&curves_sculpt.paint);
- brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
- brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
- brush_radius_sq_re_ = pow2f(brush_radius_re_);
+ curve_selection_factors_ = get_curves_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
+ const CurvesSculpt &curves_sculpt = *ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint);
+ brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
+
+ brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
+ brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
+ brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
+
falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape);
- curves_to_world_mat_ = object_->obmat;
- world_to_curves_mat_ = curves_to_world_mat_.inverted();
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
brush_pos_start_re_ = self.last_mouse_position_;
brush_pos_end_re_ = stroke_extension.mouse_position;
@@ -331,7 +297,13 @@ struct CurvesEffectOperationExecutor {
if (stroke_extension.is_first) {
if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
- *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) {
+ *ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ stroke_extension.mouse_position,
+ brush_radius_base_re_)) {
self.brush_3d_ = *brush_3d;
}
}
@@ -356,7 +328,8 @@ struct CurvesEffectOperationExecutor {
curves_->tag_positions_changed();
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region_);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
}
void gather_influences_projected(
@@ -365,7 +338,7 @@ struct CurvesEffectOperationExecutor {
const Span<float3> positions_cu = curves_->positions();
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
@@ -374,22 +347,26 @@ struct CurvesEffectOperationExecutor {
symmetry_brush_transforms_inv.append(brush_transform.inverted());
}
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
+
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
Influences &local_influences = influences_for_thread.local();
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
- const int tot_segments = points.size() - 1;
- float max_move_distance_cu = 0.0f;
+ const float curve_selection_factor = curve_selection_factors_[curve_i];
+
+ float max_move_distance_cu = 0.0f;
for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) {
- for (const int segment_i : IndexRange(tot_segments)) {
- const float3 &p1_cu = brush_transform_inv * positions_cu[points[segment_i]];
- const float3 &p2_cu = brush_transform_inv * positions_cu[points[segment_i] + 1];
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 p1_cu = brush_transform_inv * positions_cu[segment_i];
+ const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1];
float2 p1_re, p2_re;
- ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values);
- ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, p2_cu, p2_re, projection.values);
float2 closest_on_brush_re;
float2 closest_on_segment_re;
@@ -404,31 +381,31 @@ struct CurvesEffectOperationExecutor {
p1_re,
p2_re);
- if (dist_to_brush_sq_re > brush_radius_sq_re_) {
+ if (dist_to_brush_sq_re > brush_radius_sq_re) {
continue;
}
const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
const float radius_falloff = BKE_brush_curve_strength(
- brush_, dist_to_brush_re, brush_radius_re_);
- const float weight = brush_strength_ * radius_falloff;
+ brush_, dist_to_brush_re, brush_radius_re);
+ const float weight = brush_strength_ * radius_falloff * curve_selection_factor;
const float3 closest_on_segment_cu = math::interpolate(
p1_cu, p2_cu, lambda_on_segment);
float3 brush_start_pos_wo, brush_end_pos_wo;
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * closest_on_segment_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * closest_on_segment_cu,
brush_pos_start_re_,
brush_start_pos_wo);
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * closest_on_segment_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * closest_on_segment_cu,
brush_pos_end_re_,
brush_end_pos_wo);
- const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo;
- const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo;
+ const float3 brush_start_pos_cu = transforms_.world_to_curves * brush_start_pos_wo;
+ const float3 brush_end_pos_cu = transforms_.world_to_curves * brush_end_pos_wo;
const float move_distance_cu = weight *
math::distance(brush_start_pos_cu, brush_end_pos_cu);
@@ -449,21 +426,21 @@ struct CurvesEffectOperationExecutor {
const Span<float3> positions_cu = curves_->positions();
float3 brush_pos_start_wo, brush_pos_end_wo;
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_start_re_,
brush_pos_start_wo);
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_end_re_,
brush_pos_end_wo);
- const float3 brush_pos_start_cu = world_to_curves_mat_ * brush_pos_start_wo;
- const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo;
+ const float3 brush_pos_start_cu = transforms_.world_to_curves * brush_pos_start_wo;
+ const float3 brush_pos_end_cu = transforms_.world_to_curves * brush_pos_end_wo;
const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu;
const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu);
- const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
@@ -474,15 +451,18 @@ struct CurvesEffectOperationExecutor {
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
- const int tot_segments = points.size() - 1;
+
float max_move_distance_cu = 0.0f;
+
+ const float curve_selection_factor = curve_selection_factors_[curve_i];
+
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu;
const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu;
- for (const int segment_i : IndexRange(tot_segments)) {
- const float3 &p1_cu = positions_cu[points[segment_i]];
- const float3 &p2_cu = positions_cu[points[segment_i] + 1];
+ for (const int segment_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions_cu[segment_i];
+ const float3 &p2_cu = positions_cu[segment_i + 1];
float3 closest_on_segment_cu;
float3 closest_on_brush_cu;
@@ -502,7 +482,7 @@ struct CurvesEffectOperationExecutor {
const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
const float radius_falloff = BKE_brush_curve_strength(
brush_, dist_to_brush_cu, brush_radius_cu);
- const float weight = brush_strength_ * radius_falloff;
+ const float weight = brush_strength_ * radius_falloff * curve_selection_factor;
const float move_distance_cu = weight * brush_pos_diff_length_cu;
max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu);
@@ -517,18 +497,18 @@ struct CurvesEffectOperationExecutor {
}
};
-void CurvesEffectOperation::on_stroke_extended(bContext *C,
+void CurvesEffectOperation::on_stroke_extended(const bContext &C,
const StrokeExtension &stroke_extension)
{
- CurvesEffectOperationExecutor executor;
+ CurvesEffectOperationExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation(
- const BrushStrokeMode brush_mode, bContext *C)
+ const BrushStrokeMode brush_mode, const bContext &C)
{
- Scene &scene = *CTX_data_scene(C);
- Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint);
+ const Scene &scene = *CTX_data_scene(&C);
+ const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint);
const bool use_scale_uniform = brush.curves_sculpt_settings->flag &
BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM;
const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index 9d000649d62..ad3871bee45 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -8,12 +8,20 @@
#include "paint_intern.h"
#include "BLI_math_vector.hh"
+#include "BLI_vector.hh"
+#include "BLI_virtual_array.hh"
+#include "BKE_attribute.h"
#include "BKE_curves.hh"
struct ARegion;
struct RegionView3D;
+struct Depsgraph;
+struct View3D;
struct Object;
+struct Brush;
+struct Scene;
+struct BVHTreeFromMesh;
namespace blender::ed::sculpt_paint {
@@ -22,23 +30,44 @@ using bke::CurvesGeometry;
struct StrokeExtension {
bool is_first;
float2 mouse_position;
+ float pressure;
};
+float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension);
+float brush_radius_get(const Scene &scene,
+ const Brush &brush,
+ const StrokeExtension &stroke_extension);
+
+float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension);
+float brush_strength_get(const Scene &scene,
+ const Brush &brush,
+ const StrokeExtension &stroke_extension);
+
/**
* Base class for stroke based operations in curves sculpt mode.
*/
class CurvesSculptStrokeOperation {
public:
virtual ~CurvesSculptStrokeOperation() = default;
- virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0;
+ virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0;
};
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports);
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
+ ReportList *reports);
std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation(
- const BrushStrokeMode brush_mode, bContext *C);
+ const BrushStrokeMode brush_mode, const bContext &C);
+std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation(
+ const BrushStrokeMode brush_mode, const bContext &C);
+std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode,
+ const bContext &C);
+std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation(
+ const BrushStrokeMode brush_mode, const bContext &C);
+std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation();
struct CurvesBrush3D {
float3 position_cu;
@@ -48,11 +77,69 @@ struct CurvesBrush3D {
/**
* Find 3d brush position based on cursor position for curves sculpting.
*/
-std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
- Object &curves_object,
+std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
+ const ARegion &region,
+ const View3D &v3d,
+ const RegionView3D &rv3d,
+ const Object &curves_object,
const float2 &brush_pos_re,
- float brush_radius_re);
+ const float brush_radius_re);
Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry);
+/**
+ * Get the floating point selection on the curve domain, averaged from points if necessary.
+ */
+VArray<float> get_curves_selection(const Curves &curves_id);
+
+/**
+ * Get the floating point selection on the curve domain, copied from curves if necessary.
+ */
+VArray<float> get_point_selection(const Curves &curves_id);
+
+/**
+ * Find curves that have any point selected (a selection factor greater than zero),
+ * or curves that have their own selection factor greater than zero.
+ */
+IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position);
+
+class CurvesSculptCommonContext {
+ public:
+ const Depsgraph *depsgraph = nullptr;
+ const Scene *scene = nullptr;
+ ARegion *region = nullptr;
+ const View3D *v3d = nullptr;
+ const RegionView3D *rv3d = nullptr;
+
+ CurvesSculptCommonContext(const bContext &C);
+};
+
+struct CurvesSculptTransforms {
+ float4x4 curves_to_world;
+ float4x4 curves_to_surface;
+ float4x4 world_to_curves;
+ float4x4 world_to_surface;
+ float4x4 surface_to_world;
+ float4x4 surface_to_curves;
+ float4x4 surface_to_curves_normal;
+
+ CurvesSculptTransforms() = default;
+ CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob);
+};
+
+std::optional<CurvesBrush3D> sample_curves_surface_3d_brush(
+ const Depsgraph &depsgraph,
+ const ARegion &region,
+ const View3D &v3d,
+ const CurvesSculptTransforms &transforms,
+ const BVHTreeFromMesh &surface_bvh,
+ const float2 &brush_pos_re,
+ const float brush_radius_re);
+
+float transform_brush_radius(const float4x4 &transform,
+ const float3 &brush_position,
+ const float old_radius);
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index d8713c8eb1d..5d6ffa67005 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -18,6 +18,7 @@
#include "WM_toolsystem.h"
#include "ED_curves_sculpt.h"
+#include "ED_image.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_view3d.h"
@@ -49,7 +50,7 @@
bool CURVES_SCULPT_mode_poll(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
return ob && ob->mode & OB_MODE_SCULPT_CURVES;
}
@@ -74,14 +75,44 @@ using blender::bke::CurvesGeometry;
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
+float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
+{
+ if (BKE_brush_use_size_pressure(&brush)) {
+ return stroke_extension.pressure;
+ }
+ return 1.0f;
+}
+
+float brush_radius_get(const Scene &scene,
+ const Brush &brush,
+ const StrokeExtension &stroke_extension)
+{
+ return BKE_brush_size_get(&scene, &brush) * brush_radius_factor(brush, stroke_extension);
+}
+
+float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension)
+{
+ if (BKE_brush_use_alpha_pressure(&brush)) {
+ return stroke_extension.pressure;
+ }
+ return 1.0f;
+}
+
+float brush_strength_get(const Scene &scene,
+ const Brush &brush,
+ const StrokeExtension &stroke_extension)
+{
+ return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension);
+}
+
static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C,
wmOperator &op)
{
const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode"));
- Scene &scene = *CTX_data_scene(&C);
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ const Scene &scene = *CTX_data_scene(&C);
+ const CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ const Brush &brush = *BKE_paint_brush_for_read(&curves_sculpt.paint);
switch (brush.curves_sculpt_tool) {
case CURVES_SCULPT_TOOL_COMB:
return new_comb_operation();
@@ -92,7 +123,9 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
case CURVES_SCULPT_TOOL_ADD:
return new_add_operation(C, op.reports);
case CURVES_SCULPT_TOOL_GROW_SHRINK:
- return new_grow_shrink_operation(mode, &C);
+ return new_grow_shrink_operation(mode, C);
+ case CURVES_SCULPT_TOOL_SELECTION_PAINT:
+ return new_selection_paint_operation(mode, C);
}
BLI_assert_unreachable();
return {};
@@ -128,6 +161,7 @@ static void stroke_update_step(bContext *C,
StrokeExtension stroke_extension;
RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
+ stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
if (!op_data->operation) {
stroke_extension.is_first = true;
@@ -138,7 +172,7 @@ static void stroke_update_step(bContext *C,
}
if (op_data->operation) {
- op_data->operation->on_stroke_extended(C, stroke_extension);
+ op_data->operation->on_stroke_extended(*C, stroke_extension);
}
}
@@ -149,8 +183,8 @@ static void stroke_done(const bContext *C, PaintStroke *stroke)
static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- Paint *paint = BKE_paint_get_active_from_context(C);
- Brush *brush = BKE_paint_brush(paint);
+ const Paint *paint = BKE_paint_get_active_from_context(C);
+ const Brush *brush = BKE_paint_brush_for_read(paint);
if (brush == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -219,7 +253,7 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
static bool curves_sculptmode_toggle_poll(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
if (ob == nullptr) {
return false;
}
@@ -238,7 +272,7 @@ static void curves_sculptmode_enter(bContext *C)
ob->mode = OB_MODE_SCULPT_CURVES;
- paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d);
+ ED_paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d);
/* Update for mode change. */
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
new file mode 100644
index 00000000000..f620fed5761
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_index_mask_ops.hh"
+
+#include "BKE_curves.hh"
+
+#include "curves_sculpt_intern.hh"
+
+namespace blender::ed::sculpt_paint {
+
+static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_CURVE:
+ return curves.selection_curve_float();
+ case ATTR_DOMAIN_POINT:
+ return curves.adapt_domain(
+ curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+VArray<float> get_curves_selection(const Curves &curves_id)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).curves_num());
+ }
+ return get_curves_selection(CurvesGeometry::wrap(curves_id.geometry),
+ eAttrDomain(curves_id.selection_domain));
+}
+
+static VArray<float> get_point_selection(const CurvesGeometry &curves, const eAttrDomain domain)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_CURVE:
+ return curves.adapt_domain(
+ curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ case ATTR_DOMAIN_POINT:
+ return curves.selection_point_float();
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+VArray<float> get_point_selection(const Curves &curves_id)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).points_num());
+ }
+ return get_point_selection(CurvesGeometry::wrap(curves_id.geometry),
+ eAttrDomain(curves_id.selection_domain));
+}
+
+static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
+ const eAttrDomain domain,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT: {
+ const VArray<float> selection = curves.selection_point_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ IndexMask(curves.curves_num());
+ }
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.curves_range(), 512, r_indices, [&](const int curve_i) {
+ for (const int i : curves.points_for_curve(curve_i)) {
+ if (selection[i] > 0.0f) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+ case ATTR_DOMAIN_CURVE: {
+ const VArray<float> selection = curves.selection_curve_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ IndexMask(curves.curves_num());
+ }
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.curves_range(), 2048, r_indices, [&](const int i) {
+ return selection[i] > 0.0f;
+ });
+ }
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return CurvesGeometry::wrap(curves_id.geometry).curves_range();
+ }
+ return retrieve_selected_curves(CurvesGeometry::wrap(curves_id.geometry),
+ eAttrDomain(curves_id.selection_domain),
+ r_indices);
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
new file mode 100644
index 00000000000..b40aebcaaf1
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+#include <numeric>
+
+#include "BLI_memory_utils.hh"
+#include "BLI_task.hh"
+
+#include "DNA_brush_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "WM_api.h"
+
+#include "curves_sculpt_intern.hh"
+
+/**
+ * The code below uses a suffix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+using bke::CurvesGeometry;
+
+class SelectionPaintOperation : public CurvesSculptStrokeOperation {
+ private:
+ bool use_select_;
+ bool clear_selection_;
+
+ CurvesBrush3D brush_3d_;
+
+ friend struct SelectionPaintOperationExecutor;
+
+ public:
+ SelectionPaintOperation(const bool use_select, const bool clear_selection)
+ : use_select_(use_select), clear_selection_(clear_selection)
+ {
+ }
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+struct SelectionPaintOperationExecutor {
+ SelectionPaintOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *object_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
+ float brush_strength_;
+
+ float selection_goal_;
+
+ float2 brush_pos_re_;
+
+ CurvesSculptTransforms transforms_;
+
+ SelectionPaintOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(SelectionPaintOperation &self,
+ const bContext &C,
+ const StrokeExtension &stroke_extension)
+ {
+ self_ = &self;
+ object_ = CTX_data_active_object(&C);
+
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ curves_id_->flag |= CV_SCULPT_SELECTION_ENABLED;
+ if (curves_->curves_num() == 0) {
+ return;
+ }
+
+ brush_ = BKE_paint_brush_for_read(&ctx_.scene->toolsettings->curves_sculpt->paint);
+ brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
+ brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
+ brush_strength_ = BKE_brush_alpha_get(ctx_.scene, brush_);
+
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ if (self.clear_selection_) {
+ if (stroke_extension.is_first) {
+ if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) {
+ curves_->selection_point_float_for_write().fill(0.0f);
+ }
+ else if (curves_id_->selection_domain == ATTR_DOMAIN_CURVE) {
+ curves_->selection_curve_float_for_write().fill(0.0f);
+ }
+ }
+ }
+
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+
+ selection_goal_ = self_->use_select_ ? 1.0f : 0.0f;
+
+ if (stroke_extension.is_first) {
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->initialize_spherical_brush_reference_point();
+ }
+ }
+
+ if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) {
+ MutableSpan<float> selection = curves_->selection_point_float_for_write();
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->paint_point_selection_projected_with_symmetry(selection);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->paint_point_selection_spherical_with_symmetry(selection);
+ }
+ }
+ else {
+ MutableSpan<float> selection = curves_->selection_curve_float_for_write();
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->paint_curve_selection_projected_with_symmetry(selection);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->paint_curve_selection_spherical_with_symmetry(selection);
+ }
+ }
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because
+ * selection is handled as a generic attribute for now. */
+ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
+ }
+
+ void paint_point_selection_projected_with_symmetry(MutableSpan<float> selection)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->paint_point_selection_projected(brush_transform, selection);
+ }
+ }
+
+ void paint_point_selection_projected(const float4x4 &brush_transform,
+ MutableSpan<float> selection)
+ {
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ Span<float3> positions_cu = curves_->positions();
+
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
+
+ threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
+ for (const int point_i : point_range) {
+ const float3 pos_cu = brush_transform_inv * positions_cu[point_i];
+
+ /* Find the position of the point in screen space. */
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+
+ const float distance_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_);
+ if (distance_to_brush_sq_re > brush_radius_sq_re) {
+ /* Ignore the point because it's too far away. */
+ continue;
+ }
+
+ const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re);
+ /* A falloff that is based on how far away the point is from the stroke. */
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_re, brush_radius_re);
+ /* Combine the falloff and brush strength. */
+ const float weight = brush_strength_ * radius_falloff;
+
+ selection[point_i] = math::interpolate(selection[point_i], selection_goal_, weight);
+ }
+ });
+ }
+
+ void paint_point_selection_spherical_with_symmetry(MutableSpan<float> selection)
+ {
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ float3 brush_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_wo);
+ const float3 brush_cu = transforms_.world_to_curves * brush_wo;
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->paint_point_selection_spherical(selection, brush_transform * brush_cu);
+ }
+ }
+
+ void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
+ {
+ Span<float3> positions_cu = curves_->positions();
+
+ const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
+ for (const int i : point_range) {
+ const float3 pos_old_cu = positions_cu[i];
+
+ /* Compute distance to the brush. */
+ const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu);
+ if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
+ /* Ignore the point because it's too far away. */
+ continue;
+ }
+
+ const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
+
+ /* A falloff that is based on how far away the point is from the stroke. */
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_cu, brush_radius_cu);
+ /* Combine the falloff and brush strength. */
+ const float weight = brush_strength_ * radius_falloff;
+
+ selection[i] = math::interpolate(selection[i], selection_goal_, weight);
+ }
+ });
+ }
+
+ void paint_curve_selection_projected_with_symmetry(MutableSpan<float> selection)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->paint_curve_selection_projected(brush_transform, selection);
+ }
+ }
+
+ void paint_curve_selection_projected(const float4x4 &brush_transform,
+ MutableSpan<float> selection)
+ {
+ const Span<float3> positions_cu = curves_->positions();
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
+
+ threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const float max_weight = threading::parallel_reduce(
+ curves_->points_for_curve(curve_i).drop_back(1),
+ 1024,
+ 0.0f,
+ [&](const IndexRange segment_range, const float init) {
+ float max_weight = init;
+ for (const int segment_i : segment_range) {
+ const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i];
+ const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1];
+
+ float2 pos1_re;
+ float2 pos2_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, pos2_cu, pos2_re, projection.values);
+
+ const float distance_sq_re = dist_squared_to_line_segment_v2(
+ brush_pos_re_, pos1_re, pos2_re);
+ if (distance_sq_re > brush_radius_sq_re) {
+ continue;
+ }
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, std::sqrt(distance_sq_re), brush_radius_re);
+ const float weight = brush_strength_ * radius_falloff;
+ max_weight = std::max(max_weight, weight);
+ }
+ return max_weight;
+ },
+ [](float a, float b) { return std::max(a, b); });
+ selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight);
+ }
+ });
+ }
+
+ void paint_curve_selection_spherical_with_symmetry(MutableSpan<float> selection)
+ {
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ float3 brush_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_wo);
+ const float3 brush_cu = transforms_.world_to_curves * brush_wo;
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->paint_curve_selection_spherical(selection, brush_transform * brush_cu);
+ }
+ }
+
+ void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
+ {
+ const Span<float3> positions_cu = curves_->positions();
+
+ const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const float max_weight = threading::parallel_reduce(
+ curves_->points_for_curve(curve_i).drop_back(1),
+ 1024,
+ 0.0f,
+ [&](const IndexRange segment_range, const float init) {
+ float max_weight = init;
+ for (const int segment_i : segment_range) {
+ const float3 &pos1_cu = positions_cu[segment_i];
+ const float3 &pos2_cu = positions_cu[segment_i + 1];
+
+ const float distance_sq_cu = dist_squared_to_line_segment_v3(
+ brush_cu, pos1_cu, pos2_cu);
+ if (distance_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, std::sqrt(distance_sq_cu), brush_radius_cu);
+ const float weight = brush_strength_ * radius_falloff;
+ max_weight = std::max(max_weight, weight);
+ }
+ return max_weight;
+ },
+ [](float a, float b) { return std::max(a, b); });
+ selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight);
+ }
+ });
+ }
+
+ void initialize_spherical_brush_reference_point()
+ {
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
+ if (brush_3d.has_value()) {
+ self_->brush_3d_ = *brush_3d;
+ }
+ }
+};
+
+void SelectionPaintOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
+{
+ SelectionPaintOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation(
+ const BrushStrokeMode brush_mode, const bContext &C)
+{
+ Scene &scene = *CTX_data_scene(&C);
+ Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint);
+ const bool use_select = ELEM(brush_mode, BRUSH_STROKE_INVERT) ==
+ ((brush.flag & BRUSH_DIR_IN) != 0);
+ const bool clear_selection = use_select && brush_mode != BRUSH_STROKE_SMOOTH;
+
+ return std::make_unique<SelectionPaintOperation>(use_select, clear_selection);
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
index d2814c17682..b63e5a7756b 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -23,7 +23,6 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
-#include "BKE_spline.hh"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
@@ -37,6 +36,8 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "WM_api.h"
+
/**
* The code below uses a prefix naming convention to indicate the coordinate space:
* - `cu`: Local space of the curves object that is being edited.
@@ -61,7 +62,7 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation {
friend struct SnakeHookOperatorExecutor;
public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
/**
@@ -70,49 +71,51 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation {
*/
struct SnakeHookOperatorExecutor {
SnakeHookOperation *self_ = nullptr;
- bContext *C_ = nullptr;
- Scene *scene_ = nullptr;
- Object *object_ = nullptr;
- ARegion *region_ = nullptr;
- View3D *v3d_ = nullptr;
- RegionView3D *rv3d_ = nullptr;
+ CurvesSculptCommonContext ctx_;
- CurvesSculpt *curves_sculpt_ = nullptr;
- Brush *brush_ = nullptr;
- float brush_radius_re_;
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
float brush_strength_;
+
eBrushFalloffShape falloff_shape_;
+ Object *object_ = nullptr;
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
- float4x4 curves_to_world_mat_;
- float4x4 world_to_curves_mat_;
+ VArray<float> curve_factors_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ CurvesSculptTransforms transforms_;
float2 brush_pos_prev_re_;
float2 brush_pos_re_;
float2 brush_pos_diff_re_;
- void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ SnakeHookOperatorExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(SnakeHookOperation &self,
+ const bContext &C,
+ const StrokeExtension &stroke_extension)
{
BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; });
self_ = &self;
- C_ = C;
- scene_ = CTX_data_scene(C);
- object_ = CTX_data_active_object(C);
- region_ = CTX_wm_region(C);
- v3d_ = CTX_wm_view3d(C);
- rv3d_ = CTX_wm_region_view3d(C);
-
- curves_sculpt_ = scene_->toolsettings->curves_sculpt;
- brush_ = BKE_paint_brush(&curves_sculpt_->paint);
- brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
- brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
- falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
+ object_ = CTX_data_active_object(&C);
+
+ curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
+
+ brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
+ brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
+ brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
- curves_to_world_mat_ = object_->obmat;
- world_to_curves_mat_ = curves_to_world_mat_.inverted();
+ falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
curves_id_ = static_cast<Curves *>(object_->data);
curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
@@ -120,14 +123,24 @@ struct SnakeHookOperatorExecutor {
return;
}
+ transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+
+ curve_factors_ = get_curves_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
brush_pos_prev_re_ = self.last_mouse_position_re_;
brush_pos_re_ = stroke_extension.mouse_position;
brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
if (stroke_extension.is_first) {
if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
- std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
- *C_, *object_, brush_pos_re_, brush_radius_re_);
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
if (brush_3d.has_value()) {
self_->brush_3d_ = *brush_3d;
}
@@ -147,7 +160,8 @@ struct SnakeHookOperatorExecutor {
curves_->tag_positions_changed();
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region_);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ ED_region_tag_redraw(ctx_.region);
}
void projected_snake_hook_with_symmetry()
@@ -166,7 +180,10 @@ struct SnakeHookOperatorExecutor {
MutableSpan<float3> positions_cu = curves_->positions_for_write();
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
@@ -175,24 +192,29 @@ struct SnakeHookOperatorExecutor {
const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i];
float2 old_pos_re;
- ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values);
+ ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
- const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_);
- if (distance_to_brush_re > brush_radius_re_) {
+ const float distance_to_brush_sq_re = math::distance_squared(old_pos_re,
+ brush_pos_prev_re_);
+ if (distance_to_brush_sq_re > brush_radius_sq_re) {
continue;
}
const float radius_falloff = BKE_brush_curve_strength(
- brush_, distance_to_brush_re, brush_radius_re_);
- const float weight = brush_strength_ * radius_falloff;
+ brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re);
+ const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
float3 new_position_wo;
- ED_view3d_win_to_3d(
- v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo);
- const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo);
-
- this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * old_pos_cu,
+ new_position_re,
+ new_position_wo);
+ const float3 new_position_cu = brush_transform *
+ (transforms_.world_to_curves * new_position_wo);
+
+ move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
}
});
}
@@ -200,23 +222,23 @@ struct SnakeHookOperatorExecutor {
void spherical_snake_hook_with_symmetry()
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
float3 brush_start_wo, brush_end_wo;
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_prev_re_,
brush_start_wo);
- ED_view3d_win_to_3d(v3d_,
- region_,
- curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
brush_pos_re_,
brush_end_wo);
- const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
- const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+ const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo;
+ const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo;
- const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
@@ -250,51 +272,20 @@ struct SnakeHookOperatorExecutor {
const float radius_falloff = BKE_brush_curve_strength(
brush_, distance_to_brush_cu, brush_radius_cu);
- const float weight = brush_strength_ * radius_falloff;
+ const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu;
- this->move_last_point_and_resample(positions_cu.slice(points), new_pos_cu);
+ move_last_point_and_resample(positions_cu.slice(points), new_pos_cu);
}
});
}
-
- void move_last_point_and_resample(MutableSpan<float3> positions,
- const float3 &new_last_position) const
- {
- /* Find the accumulated length of each point in the original curve,
- * treating it as a poly curve for performance reasons and simplicity. */
- Array<float> orig_lengths(length_parameterize::lengths_num(positions.size(), false));
- length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths);
- const float orig_total_length = orig_lengths.last();
-
- /* Find the factor by which the new curve is shorter or longer than the original. */
- const float new_last_segment_length = math::distance(positions.last(1), new_last_position);
- const float new_total_length = orig_lengths.last(1) + new_last_segment_length;
- const float length_factor = new_total_length / orig_total_length;
-
- /* Calculate the lengths to sample the original curve with by scaling the original lengths. */
- Array<float> new_lengths(positions.size() - 1);
- new_lengths.first() = 0.0f;
- for (const int i : new_lengths.index_range().drop_front(1)) {
- new_lengths[i] = orig_lengths[i - 1] * length_factor;
- }
-
- Array<int> indices(positions.size() - 1);
- Array<float> factors(positions.size() - 1);
- length_parameterize::create_samples_from_sorted_lengths(
- orig_lengths, new_lengths, false, indices, factors);
-
- Array<float3> new_positions(positions.size() - 1);
- length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
- positions.drop_back(1).copy_from(new_positions);
- positions.last() = new_last_position;
- }
};
-void SnakeHookOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+void SnakeHookOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
{
- SnakeHookOperatorExecutor executor;
+ SnakeHookOperatorExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 861da185975..c5ebcf870a3 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -38,6 +38,7 @@
#include "IMB_imbuf_types.h"
+#include "ED_image.h"
#include "ED_view3d.h"
#include "DEG_depsgraph.h"
@@ -1358,7 +1359,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon
ViewContext *vc = &pcontext->vc;
SculptCursorGeometryInfo gi;
- const float mouse[2] = {
+ const float mval_fl[2] = {
pcontext->x - pcontext->region->winrct.xmin,
pcontext->y - pcontext->region->winrct.ymin,
};
@@ -1368,7 +1369,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon
pcontext->prev_active_vertex_index = ss->active_vertex_index;
if (!ups->stroke_active) {
pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update(
- C, &gi, mouse, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE));
+ C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE));
copy_v3_v3(pcontext->location, gi.location);
copy_v3_v3(pcontext->normal, gi.normal);
}
@@ -1932,7 +1933,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
/* Public API */
-void paint_cursor_start(Paint *p, bool (*poll)(bContext *C))
+void ED_paint_cursor_start(Paint *p, bool (*poll)(bContext *C))
{
if (p && !p->paint_cursor) {
p->paint_cursor = WM_paint_cursor_activate(
diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc
index 572e5b78b74..56cd4751881 100644
--- a/source/blender/editors/sculpt_paint/paint_image.cc
+++ b/source/blender/editors/sculpt_paint/paint_image.cc
@@ -287,7 +287,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
return false;
}
-bool image_paint_poll(bContext *C)
+bool ED_image_tools_paint_poll(bContext *C)
{
return image_paint_poll_ex(C, true);
}
@@ -301,7 +301,7 @@ static bool image_paint_2d_clone_poll(bContext *C)
{
Brush *brush = image_paint_brush(C);
- if (!CTX_wm_region_view3d(C) && image_paint_poll(C)) {
+ if (!CTX_wm_region_view3d(C) && ED_image_tools_paint_poll(C)) {
if (brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE)) {
if (brush->clone.image) {
return true;
@@ -359,9 +359,7 @@ void paint_brush_color_get(struct Scene *scene,
}
/* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA.
* Brush colors are expected to be in sRGB though. */
- IMB_colormanagement_scene_linear_to_srgb_v3(color_gr);
-
- copy_v3_v3(color, color_gr);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color, color_gr);
}
else {
copy_v3_v3(color, BKE_brush_color_get(scene, br));
@@ -432,7 +430,7 @@ static void toggle_paint_cursor(Scene *scene, bool enable)
paint_cursor_delete_textures();
}
else if (enable) {
- paint_cursor_start(p, image_paint_poll);
+ ED_paint_cursor_start(p, ED_image_tools_paint_poll);
}
}
@@ -457,7 +455,7 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene)
if (enabled) {
BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_2D, PAINT_CURSOR_TEXTURE_PAINT);
- paint_cursor_start(&imapaint->paint, image_paint_poll);
+ ED_paint_cursor_start(&imapaint->paint, ED_image_tools_paint_poll);
}
else {
paint_cursor_delete_textures();
@@ -738,7 +736,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
ot->poll = sample_color_poll;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER;
/* properties */
PropertyRNA *prop;
@@ -927,7 +925,7 @@ static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op))
static bool brush_colors_flip_poll(bContext *C)
{
- if (image_paint_poll(C)) {
+ if (ED_image_tools_paint_poll(C)) {
Brush *br = image_paint_brush(C);
if (ELEM(br->imagepaint_tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) {
return true;
@@ -956,7 +954,7 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
ot->poll = brush_colors_flip_poll;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER;
}
void ED_imapaint_bucket_fill(struct bContext *C,
@@ -993,7 +991,7 @@ static bool texture_paint_poll(bContext *C)
bool image_texture_paint_poll(bContext *C)
{
- return (texture_paint_poll(C) || image_paint_poll(C));
+ return (texture_paint_poll(C) || ED_image_tools_paint_poll(C));
}
bool facemask_paint_poll(bContext *C)
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index d30aa4dfab1..fae2e6863fa 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -621,39 +621,38 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter,
}
static void brush_painter_2d_tex_mapping(ImagePaintState *s,
- ImBuf *canvas,
+ ImagePaintTile *tile,
const int diameter,
- const float startpos[2],
const float pos[2],
const float mouse[2],
int mapmode,
rctf *mapping)
{
- float invw = 1.0f / (float)canvas->x;
- float invh = 1.0f / (float)canvas->y;
- int xmin, ymin, xmax, ymax;
- int ipos[2];
+ float invw = 1.0f / (float)tile->canvas->x;
+ float invh = 1.0f / (float)tile->canvas->y;
+ float start[2];
/* find start coordinate of brush in canvas */
- ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f);
- ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f);
+ start[0] = pos[0] - diameter / 2.0f;
+ start[1] = pos[1] - diameter / 2.0f;
if (mapmode == MTEX_MAP_MODE_STENCIL) {
/* map from view coordinates of brush to region coordinates */
- UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin);
- UI_view2d_view_to_region(
- s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax);
+ float xmin, ymin, xmax, ymax;
+ UI_view2d_view_to_region_fl(s->v2d, start[0] * invw, start[1] * invh, &xmin, &ymin);
+ UI_view2d_view_to_region_fl(
+ s->v2d, (start[0] + diameter) * invw, (start[1] + diameter) * invh, &xmax, &ymax);
/* output mapping from brush ibuf x/y to region coordinates */
- mapping->xmin = xmin;
- mapping->ymin = ymin;
mapping->xmax = (xmax - xmin) / (float)diameter;
mapping->ymax = (ymax - ymin) / (float)diameter;
+ mapping->xmin = xmin + (tile->uv_origin[0] * tile->size[0] * mapping->xmax);
+ mapping->ymin = ymin + (tile->uv_origin[1] * tile->size[1] * mapping->ymax);
}
else if (mapmode == MTEX_MAP_MODE_3D) {
/* 3D mapping, just mapping to canvas 0..1. */
- mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f);
- mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f);
+ mapping->xmin = 2.0f * (start[0] * invw - 0.5f);
+ mapping->ymin = 2.0f * (start[1] * invh - 0.5f);
mapping->xmax = 2.0f * invw;
mapping->ymax = 2.0f * invh;
}
@@ -665,8 +664,10 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s,
mapping->ymax = 1.0f;
}
else /* if (mapmode == MTEX_MAP_MODE_TILED) */ {
- mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]);
- mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]);
+ mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) -
+ (int)floorf(tile->start_paintpos[0]);
+ mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) -
+ (int)floorf(tile->start_paintpos[1]);
mapping->xmax = 1.0f;
mapping->ymax = 1.0f;
}
@@ -711,14 +712,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
do_partial_update = true;
}
- brush_painter_2d_tex_mapping(s,
- tile->canvas,
- diameter,
- tile->start_paintpos,
- pos,
- mouse,
- brush->mtex.brush_map_mode,
- &painter->tex_mapping);
+ brush_painter_2d_tex_mapping(
+ s, tile, diameter, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping);
}
if (cache->is_maskbrush) {
@@ -745,14 +740,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
renew_maxmask) {
MEM_SAFE_FREE(cache->tex_mask);
- brush_painter_2d_tex_mapping(s,
- tile->canvas,
- diameter,
- tile->start_paintpos,
- pos,
- mouse,
- brush->mask_mtex.brush_map_mode,
- &painter->mask_mapping);
+ brush_painter_2d_tex_mapping(
+ s, tile, diameter, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping);
if (do_partial_update_mask) {
brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter);
diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
index 8ddf1614e41..a671c24c514 100644
--- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
+++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
@@ -34,6 +34,8 @@
#include "WM_toolsystem.h"
#include "WM_types.h"
+#include "ED_image.h"
+
#include "paint_intern.h"
namespace blender::ed::sculpt_paint::image::ops::paint {
@@ -316,7 +318,7 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo
if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
pop->cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, ED_image_tools_paint_poll, gradient_draw_line, pop);
}
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
@@ -520,7 +522,7 @@ void PAINT_OT_image_paint(wmOperatorType *ot)
ot->invoke = paint_invoke;
ot->modal = paint_modal;
ot->exec = paint_exec;
- ot->poll = image_paint_poll;
+ ot->poll = ED_image_tools_paint_poll;
ot->cancel = paint_cancel;
/* flags */
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 1303da71435..50480b8aef0 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -7,6 +7,7 @@
*/
#include <float.h>
+#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
@@ -35,12 +36,17 @@
#include "IMB_imbuf_types.h"
#include "DNA_brush_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_defs.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
+#include "DNA_object_enums.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_camera.h"
#include "BKE_colorband.h"
@@ -61,6 +67,8 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -76,12 +84,18 @@
#include "GPU_capabilities.h"
#include "GPU_init_exit.h"
+#include "NOD_shader.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_types.h"
#include "IMB_colormanagement.h"
@@ -3118,7 +3132,7 @@ static void project_paint_face_init(const ProjPaintState *ps,
}
}
- /* Is this UV visible from the view? - raytrace */
+ /* Is this UV visible from the view? - ray-trace */
/* project_paint_PickFace is less complex, use for testing */
// if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == tri_index) {
if ((ps->do_occlude == false) ||
@@ -3208,7 +3222,7 @@ static void project_paint_face_init(const ProjPaintState *ps,
float seam_subsection[4][2];
float fac1, fac2;
- /* Pixelspace UVs. */
+ /* Pixel-space UV's. */
float lt_puv[3][2];
lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x;
@@ -4068,7 +4082,7 @@ typedef struct {
static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone)
{
- MLoopUV *mloopuv_clone_base = NULL;
+ const MLoopUV *mloopuv_clone_base = NULL;
/* use clone mtface? */
if (ps->do_layer_clone) {
@@ -4478,7 +4492,8 @@ static void project_paint_begin(const bContext *C,
}
}
- /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */
+ /* when using sub-surface or multi-resolution,
+ * mesh-data arrays are thrown away, we need to keep a copy. */
if (ps->is_shared_user == false) {
proj_paint_state_cavity_init(ps);
}
@@ -6422,6 +6437,17 @@ static const EnumPropertyItem layer_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static Material *get_or_create_current_material(bContext *C, Object *ob)
+{
+ Material *ma = BKE_object_material_get(ob, ob->actcol);
+ if (!ma) {
+ Main *bmain = CTX_data_main(C);
+ ma = BKE_material_add(bmain, "Material");
+ BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF);
+ }
+ return ma;
+}
+
static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
{
Image *ima;
@@ -6459,55 +6485,97 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
return ima;
}
-static void proj_paint_default_color(wmOperator *op, int type, Material *ma)
+static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob)
{
- if (RNA_struct_property_is_set(op->ptr, "color")) {
- return;
+ char name[MAX_NAME] = "";
+ float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ eAttrDomain domain = ATTR_DOMAIN_POINT;
+ eCustomDataType type = CD_PROP_COLOR;
+
+ if (op) {
+ RNA_string_get(op->ptr, "name", name);
+ RNA_float_get_array(op->ptr, "color", color);
+ domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain");
+ type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type");
}
- bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED);
- if (in_node == NULL) {
- return;
+ ID *id = (ID *)ob->data;
+ CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports);
+
+ if (!layer) {
+ return NULL;
}
- float color[4];
+ BKE_id_attributes_active_color_set(id, layer);
- if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) {
- /* Copy color from node, so result is unchanged after assigning textures. */
- bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name);
+ if (!BKE_id_attributes_render_color_get(id)) {
+ BKE_id_attributes_render_color_set(id, layer);
+ }
- switch (in_sock->type) {
- case SOCK_FLOAT: {
- bNodeSocketValueFloat *socket_data = in_sock->default_value;
- copy_v3_fl(color, socket_data->value);
- color[3] = 1.0f;
- break;
+ BKE_object_attributes_active_color_fill(ob, color, false);
+
+ return layer;
+}
+
+/**
+ * Get a default color for the paint slot layer from a material's Principled BSDF.
+ *
+ * \param layer_type: The layer type of the paint slot
+ * \param ma: The material to attempt using as the default color source.
+ * If this fails or \p ma is null, a default Principled BSDF is used instead.
+ */
+static void default_paint_slot_color_get(int layer_type, Material *ma, float color[4])
+{
+ switch (layer_type) {
+ case LAYER_BASE_COLOR:
+ case LAYER_SPECULAR:
+ case LAYER_ROUGHNESS:
+ case LAYER_METALLIC: {
+ bNodeTree *ntree = NULL;
+ bNode *in_node = ma ? ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED) : NULL;
+ if (!in_node) {
+ /* An existing material or Principled BSDF node could not be found.
+ * Copy default color values from a default Principled BSDF instead. */
+ ntree = ntreeAddTree(NULL, "Temporary Shader Nodetree", ntreeType_Shader->idname);
+ in_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED);
}
- case SOCK_VECTOR:
- case SOCK_RGBA: {
- bNodeSocketValueRGBA *socket_data = in_sock->default_value;
- copy_v3_v3(color, socket_data->value);
- color[3] = 1.0f;
- break;
+ bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[layer_type].name);
+ switch (in_sock->type) {
+ case SOCK_FLOAT: {
+ bNodeSocketValueFloat *socket_data = in_sock->default_value;
+ copy_v3_fl(color, socket_data->value);
+ color[3] = 1.0f;
+ break;
+ }
+ case SOCK_VECTOR:
+ case SOCK_RGBA: {
+ bNodeSocketValueRGBA *socket_data = in_sock->default_value;
+ copy_v3_v3(color, socket_data->value);
+ color[3] = 1.0f;
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ rgba_float_args_set(color, 0.0f, 0.0f, 0.0f, 1.0f);
+ break;
}
- default: {
- return;
+ /* Cleanup */
+ if (ntree) {
+ ntreeFreeTree(ntree);
+ MEM_freeN(ntree);
}
+ return;
}
+ case LAYER_NORMAL:
+ /* Neutral tangent space normal map. */
+ rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f);
+ break;
+ case LAYER_BUMP:
+ case LAYER_DISPLACEMENT:
+ /* Neutral displacement and bump map. */
+ rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f);
+ break;
}
- else if (type == LAYER_NORMAL) {
- /* Neutral tangent space normal map. */
- rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f);
- }
- else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) {
- /* Neutral displacement and bump map. */
- rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f);
- }
- else {
- return;
- }
-
- RNA_float_set_array(op->ptr, "color", color);
}
static bool proj_paint_add_slot(bContext *C, wmOperator *op)
@@ -6516,19 +6584,20 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Material *ma;
Image *ima = NULL;
+ CustomDataLayer *layer = NULL;
if (!ob) {
return false;
}
- ma = BKE_object_material_get(ob, ob->actcol);
+ ma = get_or_create_current_material(C, ob);
if (ma) {
Main *bmain = CTX_data_main(C);
int type = RNA_enum_get(op->ptr, "type");
bool is_data = (type > LAYER_BASE_COLOR);
- bNode *imanode;
+ bNode *new_node;
bNodeTree *ntree = ma->nodetree;
if (!ntree) {
@@ -6538,17 +6607,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
ma->use_nodes = true;
- /* try to add an image node */
- imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE);
-
- ima = proj_paint_image_create(op, bmain, is_data);
- imanode->id = &ima->id;
-
- nodeSetActive(ntree, imanode);
+ const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ?
+ (ePaintCanvasSource)RNA_enum_get(op->ptr,
+ "slot_type") :
+ PAINT_CANVAS_SOURCE_IMAGE;
+
+ /* Create a new node. */
+ switch (slot_type) {
+ case PAINT_CANVAS_SOURCE_IMAGE: {
+ new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE);
+ ima = proj_paint_image_create(op, bmain, is_data);
+ new_node->id = &ima->id;
+ break;
+ }
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: {
+ new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE);
+ if ((layer = proj_paint_color_attribute_create(op, ob))) {
+ BLI_strncpy_utf8(
+ ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME);
+ }
+ break;
+ }
+ case PAINT_CANVAS_SOURCE_MATERIAL:
+ BLI_assert_unreachable();
+ return false;
+ }
+ nodeSetActive(ntree, new_node);
/* Connect to first available principled BSDF node. */
bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED);
- bNode *out_node = imanode;
+ bNode *out_node = new_node;
if (in_node != NULL) {
bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color");
@@ -6611,6 +6699,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE);
WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima);
}
+ if (layer) {
+ BKE_texpaint_slot_refresh_cache(scene, ma, ob);
+ DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data);
+ }
DEG_id_tag_update(&ntree->id, 0);
DEG_id_tag_update(&ma->id, ID_RECALC_SHADING);
@@ -6632,25 +6725,8 @@ static int get_texture_layer_type(wmOperator *op, const char *prop_name)
return type;
}
-static Material *get_or_create_current_material(bContext *C, Object *ob)
-{
- Material *ma = BKE_object_material_get(ob, ob->actcol);
- if (!ma) {
- Main *bmain = CTX_data_main(C);
- ma = BKE_material_add(bmain, "Material");
- BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF);
- }
- return ma;
-}
-
static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op)
{
- Object *ob = ED_object_active_context(C);
- Material *ma = get_or_create_current_material(C, ob);
-
- int type = get_texture_layer_type(op, "type");
- proj_paint_default_color(op, type, ma);
-
if (proj_paint_add_slot(C, op)) {
return OPERATOR_FINISHED;
}
@@ -6671,20 +6747,62 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- /* Get material and default color to display in the popup. */
Object *ob = ED_object_active_context(C);
- Material *ma = get_or_create_current_material(C, ob);
+ Material *ma = BKE_object_material_get(ob, ob->actcol);
int type = get_texture_layer_type(op, "type");
- proj_paint_default_color(op, type, ma);
+ /* Set default name. */
char imagename[MAX_ID_NAME - 2];
get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename));
RNA_string_set(op->ptr, "name", imagename);
+ /* Set default color. Copy the color from nodes, so it matches the existing material. */
+ float color[4];
+ default_paint_slot_color_get(type, ma, color);
+ RNA_float_set_array(op->ptr, "color", color);
+
return WM_operator_props_dialog_popup(C, op, 300);
}
+static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ Object *ob = ED_object_active_context(C);
+ ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE;
+
+ if (ob->mode == OB_MODE_SCULPT) {
+ slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type");
+ uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ }
+
+ uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE);
+
+ switch (slot_type) {
+ case PAINT_CANVAS_SOURCE_IMAGE: {
+ uiLayout *col = uiLayoutColumn(layout, true);
+ uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE);
+ break;
+ }
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ break;
+ case PAINT_CANVAS_SOURCE_MATERIAL:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE);
+}
+
#define IMA_DEF_NAME N_("Untitled")
void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot)
@@ -6692,40 +6810,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot)
PropertyRNA *prop;
static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ static const EnumPropertyItem slot_type_items[3] = {
+ {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""},
+ {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem domain_items[3] = {
+ {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""},
+ {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem attribute_type_items[3] = {
+ {CD_PROP_COLOR, "COLOR", 0, "Color", ""},
+ {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
- ot->name = "Add Texture Paint Slot";
- ot->description = "Add a texture paint slot";
+ ot->name = "Add Paint Slot";
+ ot->description = "Add a paint slot";
ot->idname = "PAINT_OT_add_texture_paint_slot";
/* api callbacks */
ot->invoke = texture_paint_add_texture_paint_slot_invoke;
ot->exec = texture_paint_add_texture_paint_slot_exec;
ot->poll = ED_operator_object_active_editable_mesh;
+ ot->ui = texture_paint_add_texture_paint_slot_ui;
/* flags */
ot->flag = OPTYPE_UNDO;
- /* properties */
- prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use");
+ /* Shared Properties */
+ prop = RNA_def_enum(ot->srna,
+ "type",
+ layer_type_items,
+ 0,
+ "Material Layer Type",
+ "Material layer type of new paint slot");
RNA_def_property_flag(prop, PROP_HIDDEN);
- RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name");
- prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
- RNA_def_property_subtype(prop, PROP_PIXEL);
- prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
- RNA_def_property_subtype(prop, PROP_PIXEL);
+
+ prop = RNA_def_enum(
+ ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot");
+
+ prop = RNA_def_string(
+ ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
prop = RNA_def_float_color(
ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
RNA_def_property_float_array_default(prop, default_color);
+
+ /* Image Properties */
+ prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+
+ prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+
RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel");
+
RNA_def_enum(ot->srna,
"generated_type",
rna_enum_image_generated_type_items,
IMA_GENTYPE_BLANK,
"Generated Type",
"Fill the image with a grid for UV map testing");
+
RNA_def_boolean(
ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth");
+
+ /* Color Attribute Properties */
+ RNA_def_enum(ot->srna,
+ "domain",
+ domain_items,
+ ATTR_DOMAIN_POINT,
+ "Domain",
+ "Type of element that attribute is stored on");
+
+ RNA_def_enum(ot->srna,
+ "data_type",
+ attribute_type_items,
+ CD_PROP_COLOR,
+ "Data Type",
+ "Type of data stored in attribute");
}
static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 187f793eefe..3f4e660b229 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -98,7 +98,6 @@ void *paint_stroke_mode_data(struct PaintStroke *stroke);
float paint_stroke_distance_get(struct PaintStroke *stroke);
void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
bool PAINT_brush_tool_poll(struct bContext *C);
-void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
/**
* Delete overlay cursor textures to preserve memory and invalidate all overlay flags.
*/
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index fb5e76b578f..89bbf2a3c92 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1584,9 +1584,9 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
- float mouse[2] = {event->mval[0], event->mval[1]};
+ const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_vertex_random_access_ensure(ss);
- ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
if (ss->gesture_initial_hit) {
copy_v3_v3(ss->gesture_initial_location, sgi.location);
copy_v3_v3(ss->gesture_initial_normal, sgi.normal);
@@ -1625,9 +1625,9 @@ static int sculpt_trim_gesture_lasso_invoke(bContext *C, wmOperator *op, const w
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
- float mouse[2] = {event->mval[0], event->mval[1]};
+ const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_vertex_random_access_ensure(ss);
- ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
if (ss->gesture_initial_hit) {
copy_v3_v3(ss->gesture_initial_location, sgi.location);
copy_v3_v3(ss->gesture_initial_normal, sgi.normal);
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index 926a564184a..0f2b02ed3ab 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -82,11 +82,86 @@ static void BRUSH_OT_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool,
+ enum eContextObjectMode mode)
+{
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL: {
+ if (STREQ(tool->runtime->data_block, "DRAW")) {
+ return GP_BRUSH_PRESET_PENCIL;
+ }
+ else if (STREQ(tool->runtime->data_block, "FILL")) {
+ return GP_BRUSH_PRESET_FILL_AREA;
+ }
+ else if (STREQ(tool->runtime->data_block, "ERASE")) {
+ return GP_BRUSH_PRESET_ERASER_SOFT;
+ }
+ else if (STREQ(tool->runtime->data_block, "TINT")) {
+ return GP_BRUSH_PRESET_TINT;
+ }
+ break;
+ }
+ case CTX_MODE_SCULPT_GPENCIL: {
+ if (STREQ(tool->runtime->data_block, "SMOOTH")) {
+ return GP_BRUSH_PRESET_SMOOTH_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "STRENGTH")) {
+ return GP_BRUSH_PRESET_STRENGTH_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "THICKNESS")) {
+ return GP_BRUSH_PRESET_THICKNESS_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "GRAB")) {
+ return GP_BRUSH_PRESET_GRAB_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "PUSH")) {
+ return GP_BRUSH_PRESET_PUSH_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "TWIST")) {
+ return GP_BRUSH_PRESET_TWIST_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "PINCH")) {
+ return GP_BRUSH_PRESET_PINCH_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "RANDOMIZE")) {
+ return GP_BRUSH_PRESET_RANDOMIZE_STROKE;
+ }
+ else if (STREQ(tool->runtime->data_block, "CLONE")) {
+ return GP_BRUSH_PRESET_CLONE_STROKE;
+ }
+ break;
+ }
+ case CTX_MODE_WEIGHT_GPENCIL: {
+ return GP_BRUSH_PRESET_DRAW_WEIGHT;
+ }
+ case CTX_MODE_VERTEX_GPENCIL: {
+ if (STREQ(tool->runtime->data_block, "DRAW")) {
+ return GP_BRUSH_PRESET_VERTEX_DRAW;
+ }
+ else if (STREQ(tool->runtime->data_block, "BLUR")) {
+ return GP_BRUSH_PRESET_VERTEX_BLUR;
+ }
+ else if (STREQ(tool->runtime->data_block, "AVERAGE")) {
+ return GP_BRUSH_PRESET_VERTEX_AVERAGE;
+ }
+ else if (STREQ(tool->runtime->data_block, "SMEAR")) {
+ return GP_BRUSH_PRESET_VERTEX_SMEAR;
+ }
+ else if (STREQ(tool->runtime->data_block, "REPLACE")) {
+ return GP_BRUSH_PRESET_VERTEX_REPLACE;
+ }
+ break;
+ }
+ default:
+ return GP_BRUSH_PRESET_UNKNOWN;
+ break;
+ }
+ return GP_BRUSH_PRESET_UNKNOWN;
+}
+
static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op))
{
- // int type = RNA_enum_get(op->ptr, "type");
- ToolSettings *ts = CTX_data_tool_settings(C);
- Paint *paint = &ts->gp_paint->paint;
+ Paint *paint = BKE_paint_get_active_from_context(C);
Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
@@ -94,15 +169,70 @@ static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op))
br = (Brush *)BKE_id_copy(bmain, &br->id);
}
else {
- br = BKE_brush_add(bmain, "Brush", OB_MODE_PAINT_GPENCIL);
+ /* Get the active tool to determine what type of brush is active. */
+ bScreen *screen = CTX_wm_screen(C);
+ if (screen == NULL) {
+ return OPERATOR_CANCELLED;
+ }
- /* Init grease pencil specific data. */
- BKE_brush_init_gpencil_settings(br);
- }
+ bToolRef *tool = NULL;
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ if (area->spacetype == SPACE_VIEW3D) {
+ /* Check the current tool is a brush. */
+ bToolRef *tref = area->runtime.tool;
+ if (tref && tref->runtime && tref->runtime->data_block[0]) {
+ tool = tref;
+ break;
+ }
+ }
+ }
- id_us_min(&br->id); /* fake user only */
+ if (tool == NULL) {
+ return OPERATOR_CANCELLED;
+ }
- BKE_paint_brush_set(paint, br);
+ /* Get Brush mode base on context mode. */
+ const enum eContextObjectMode mode = CTX_data_mode_enum(C);
+ eObjectMode obmode = OB_MODE_PAINT_GPENCIL;
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL:
+ obmode = OB_MODE_PAINT_GPENCIL;
+ break;
+ case CTX_MODE_SCULPT_GPENCIL:
+ obmode = OB_MODE_SCULPT_GPENCIL;
+ break;
+ case CTX_MODE_WEIGHT_GPENCIL:
+ obmode = OB_MODE_WEIGHT_GPENCIL;
+ break;
+ case CTX_MODE_VERTEX_GPENCIL:
+ obmode = OB_MODE_VERTEX_GPENCIL;
+ break;
+ default:
+ return OPERATOR_CANCELLED;
+ break;
+ }
+
+ /* Get brush preset using the actual tool. */
+ eGPBrush_Presets preset = gpencil_get_brush_preset_from_tool(tool, mode);
+
+ /* Capitalize Brush name first letter using the tool name. */
+ char name[64];
+ BLI_strncpy(name, tool->runtime->data_block, sizeof(name));
+ BLI_str_tolower_ascii(name, sizeof(name));
+ name[0] = BLI_toupper_ascii(name[0]);
+
+ /* Create the brush and assign default values. */
+ br = BKE_brush_add(bmain, name, obmode);
+ if (br) {
+ BKE_brush_init_gpencil_settings(br);
+ BKE_gpencil_brush_preset_set(bmain, br, preset);
+ }
+ }
+
+ if (br) {
+ id_us_min(&br->id); /* fake user only */
+ BKE_paint_brush_set(paint, br);
+ }
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index d2005473512..63e6dc7e965 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -506,7 +506,7 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert)
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
static void paint_brush_stroke_add_step(
- bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure)
+ bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure)
{
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
@@ -546,7 +546,7 @@ static void paint_brush_stroke_add_step(
/* copy last position -before- jittering, or space fill code
* will create too many dabs */
- copy_v2_v2(stroke->last_mouse_position, mouse_in);
+ copy_v2_v2(stroke->last_mouse_position, mval);
stroke->last_pressure = pressure;
if (paint_stroke_use_scene_spacing(brush, mode)) {
@@ -562,24 +562,24 @@ static void paint_brush_stroke_add_step(
factor *= pressure;
}
- BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out);
+ BKE_brush_jitter_pos(scene, brush, mval, mouse_out);
/* XXX: meh, this is round about because
* BKE_brush_jitter_pos isn't written in the best way to
* be reused here */
if (factor != 1.0f) {
- sub_v2_v2v2(delta, mouse_out, mouse_in);
+ sub_v2_v2v2(delta, mouse_out, mval);
mul_v2_fl(delta, factor);
- add_v2_v2v2(mouse_out, mouse_in, delta);
+ add_v2_v2v2(mouse_out, mval, delta);
}
}
else {
- copy_v2_v2(mouse_out, mouse_in);
+ copy_v2_v2(mouse_out, mval);
}
bool is_location_is_set;
ups->last_hit = paint_brush_update(
- C, brush, mode, stroke, mouse_in, mouse_out, pressure, location, &is_location_is_set);
+ C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set);
if (is_location_is_set) {
copy_v3_v3(ups->last_location, location);
}
@@ -605,7 +605,7 @@ static void paint_brush_stroke_add_step(
/* Mouse coordinates modified by the stroke type options. */
RNA_float_set_array(&itemptr, "mouse", mouse_out);
/* Original mouse coordinates. */
- RNA_float_set_array(&itemptr, "mouse_event", mouse_in);
+ RNA_float_set_array(&itemptr, "mouse_event", mval);
RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip);
RNA_float_set(&itemptr, "pressure", pressure);
RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt);
@@ -912,8 +912,13 @@ PaintStroke *paint_stroke_new(bContext *C,
rv3d->rflag |= RV3D_PAINTING;
}
- zero_v3(ups->average_stroke_accum);
- ups->average_stroke_counter = 0;
+ /* Preserve location from last stroke while applying and resetting
+ * ups->average_stroke_counter to 1.
+ */
+ if (ups->average_stroke_counter) {
+ mul_v3_fl(ups->average_stroke_accum, 1.0f / (float)ups->average_stroke_counter);
+ ups->average_stroke_counter = 1;
+ }
/* initialize here to avoid initialization conflict with threaded strokes */
BKE_curvemapping_init(br->curve);
@@ -923,6 +928,8 @@ PaintStroke *paint_stroke_new(bContext *C,
BKE_paint_set_overlay_override(br->overlay_flags);
+ ups->start_pixel_radius = BKE_brush_size_get(CTX_data_scene(C), br);
+
return stroke;
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc
index 16b22775b9e..67b60acc667 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.cc
+++ b/source/blender/editors/sculpt_paint/paint_vertex.cc
@@ -56,6 +56,7 @@
#include "WM_types.h"
#include "ED_armature.h"
+#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -139,8 +140,8 @@ struct NormalAnglePrecalc {
/* Returns number of elements. */
static int get_vcol_elements(Mesh *me, size_t *r_elem_size)
{
- CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
- AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
+ const eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer);
if (r_elem_size) {
*r_elem_size = layer->type == CD_PROP_COLOR ? sizeof(float) * 4ULL : 4ULL;
@@ -217,7 +218,7 @@ static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev,
MDeformVert *dvert_curr,
int index)
{
- MDeformVert *dv_curr = &dvert_curr[index];
+ const MDeformVert *dv_curr = &dvert_curr[index];
MDeformVert *dv_prev = &dvert_prev[index];
if (dv_prev->flag == 1) {
dv_prev->flag = 0;
@@ -233,7 +234,7 @@ static bool vertex_paint_use_fast_update_check(Object *ob)
const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
if (me_eval != nullptr) {
- Mesh *me = BKE_mesh_from_object(ob);
+ const Mesh *me = BKE_mesh_from_object(ob);
if (me && me->mloopcol) {
return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_PROP_BYTE_COLOR));
}
@@ -252,13 +253,14 @@ static void paint_last_stroke_update(Scene *scene, const float location[3])
bool vertex_paint_mode_poll(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
- if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly)) {
+ if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((const Mesh *)ob->data)->totpoly)) {
return false;
}
- CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(
+ static_cast<const ID *>(ob->data));
return layer != nullptr;
}
@@ -291,15 +293,15 @@ bool vertex_paint_poll_ignore_tool(bContext *C)
bool weight_paint_mode_poll(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
- return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly;
+ return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((const Mesh *)ob->data)->totpoly;
}
static bool weight_paint_poll_ex(bContext *C, bool check_tool)
{
- Object *ob = CTX_data_active_object(C);
- ScrArea *area;
+ const Object *ob = CTX_data_active_object(C);
+ const ScrArea *area;
if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) &&
(BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) &&
@@ -324,15 +326,14 @@ bool weight_paint_poll_ignore_tool(bContext *C)
return weight_paint_poll_ex(C, false);
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
{
- Brush *brush = BKE_paint_brush(&vp->paint);
+ const Brush *brush = BKE_paint_brush_for_read(&vp->paint);
float color[4];
const float *brush_color = secondary ? BKE_brush_secondary_color_get(scene, brush) :
BKE_brush_color_get(scene, brush);
- copy_v3_v3(color, brush_color);
- IMB_colormanagement_srgb_to_scene_linear_v3(color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(color, brush_color);
color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */
@@ -361,7 +362,7 @@ static Color vpaint_blend(const VPaint *vp,
const Brush *brush = vp->paint.brush;
const IMB_BlendMode blend = (IMB_BlendMode)brush->blend;
- Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha);
+ const Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha);
/* If no accumulate, clip color adding with `color_orig` & `color_test`. */
if (!brush_use_accumulate(vp)) {
@@ -1322,7 +1323,7 @@ static void ed_vwpaintmode_enter_generic(
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
- paint_cursor_start(paint, vertex_paint_poll);
+ ED_paint_cursor_start(paint, vertex_paint_poll);
BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT);
}
else if (mode_flag == OB_MODE_WEIGHT_PAINT) {
@@ -1330,7 +1331,7 @@ static void ed_vwpaintmode_enter_generic(
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
- paint_cursor_start(paint, weight_paint_poll);
+ ED_paint_cursor_start(paint, weight_paint_poll);
BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT);
/* weight paint specific */
@@ -1607,10 +1608,10 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
/* Initialize the stroke cache invariants from operator properties */
static void vwpaint_update_cache_invariants(
- bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2])
+ bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mval[2])
{
StrokeCache *cache;
- Scene *scene = CTX_data_scene(C);
+ const Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
ViewContext *vc = paint_stroke_view_context((PaintStroke *)op->customdata);
Object *ob = CTX_data_active_object(C);
@@ -1628,8 +1629,8 @@ static void vwpaint_update_cache_invariants(
}
/* Initial mouse location */
- if (mouse) {
- copy_v2_v2(cache->initial_mouse, mouse);
+ if (mval) {
+ copy_v2_v2(cache->initial_mouse, mval);
}
else {
zero_v2(cache->initial_mouse);
@@ -1652,7 +1653,7 @@ static void vwpaint_update_cache_invariants(
}
copy_v2_v2(cache->mouse, cache->initial_mouse);
- Brush *brush = vp->paint.brush;
+ const Brush *brush = vp->paint.brush;
/* Truly temporary data that isn't stored in properties */
cache->vc = vc;
cache->brush = brush;
@@ -2824,11 +2825,11 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
struct VPaintDataBase {
ViewContext vc;
- AttributeDomain domain;
- CustomDataType type;
+ eAttrDomain domain;
+ eCustomDataType type;
};
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
struct VPaintData : public VPaintDataBase {
NormalAnglePrecalc normal_angle_precalc;
@@ -2856,7 +2857,7 @@ struct VPaintData : public VPaintDataBase {
} smear;
};
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void *vpaint_init_vpaint(bContext *C,
wmOperator *op,
Scene *scene,
@@ -2950,7 +2951,7 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
return false;
}
- AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
+ eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer);
void *vpd = nullptr;
if (domain == ATTR_DOMAIN_POINT) {
@@ -3273,7 +3274,7 @@ static void do_vpaint_brush_blur_verts(bContext *C,
});
}
-template<typename Color = ColorPaint4b, typename Traits, AttributeDomain domain>
+template<typename Color = ColorPaint4b, typename Traits, eAttrDomain domain>
static void do_vpaint_brush_smear(bContext *C,
Sculpt *UNUSED(sd),
VPaint *vp,
@@ -3452,7 +3453,7 @@ static void do_vpaint_brush_smear(bContext *C,
});
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
Object *ob,
Mesh *me,
@@ -3542,7 +3543,7 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
}
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static float paint_and_tex_color_alpha(VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
const float v_co[3],
@@ -3560,7 +3561,7 @@ static float paint_and_tex_color_alpha(VPaint *vp,
return rgba[3];
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_draw(bContext *C,
Sculpt *UNUSED(sd),
VPaint *vp,
@@ -3700,7 +3701,7 @@ static void vpaint_do_draw(bContext *C,
});
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_blur(bContext *C,
Sculpt *sd,
VPaint *vp,
@@ -3719,7 +3720,7 @@ static void vpaint_do_blur(bContext *C,
}
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_paint_leaves(bContext *C,
Sculpt *sd,
VPaint *vp,
@@ -3755,7 +3756,7 @@ static void vpaint_paint_leaves(bContext *C,
}
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_paint(bContext *C,
Sculpt *sd,
VPaint *vp,
@@ -3786,7 +3787,7 @@ static void vpaint_do_paint(bContext *C,
}
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_radial_symmetry(bContext *C,
Sculpt *sd,
VPaint *vp,
@@ -3805,7 +3806,7 @@ static void vpaint_do_radial_symmetry(bContext *C,
/* near duplicate of: sculpt.c's,
* 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_symmetrical_brush_actions(
bContext *C, Sculpt *sd, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob)
{
@@ -3852,7 +3853,7 @@ static void vpaint_do_symmetrical_brush_actions(
cache->is_last_valid = true;
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, PointerRNA *itemptr)
{
Scene *scene = CTX_data_scene(C);
@@ -3940,7 +3941,7 @@ static void vpaint_stroke_update_step(bContext *C,
}
}
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_free_vpaintdata(Object *UNUSED(ob), void *_vpd)
{
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd);
@@ -4097,7 +4098,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
/** \name Set Vertex Colors Operator
* \{ */
-template<typename Color, typename Traits, AttributeDomain domain>
+template<typename Color, typename Traits, eAttrDomain domain>
static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_layer)
{
Mesh *me;
@@ -4165,7 +4166,7 @@ static bool paint_object_attributes_active_color_fill_ex(Object *ob,
me->editflag &= ~ME_EDIT_PAINT_FACE_SEL;
me->editflag &= ~ME_EDIT_PAINT_VERT_SEL;
}
- AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
+ eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer);
bool ok = false;
if (domain == ATTR_DOMAIN_POINT) {
if (layer->type == CD_PROP_COLOR) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
index a2e1adff50a..6a47aceb2b0 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
@@ -11,6 +11,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BLI_array.hh"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
@@ -30,6 +31,8 @@
#include "paint_intern.h" /* own include */
+using blender::Array;
+
/* -------------------------------------------------------------------- */
/** \name Internal Utility Functions
* \{ */
@@ -45,7 +48,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C)
static void tag_object_after_update(Object *object)
{
BLI_assert(object->type == OB_MESH);
- Mesh *mesh = object->data;
+ Mesh *mesh = static_cast<Mesh *>(object->data);
DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE);
/* NOTE: Original mesh is used for display, so tag it directly here. */
BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
@@ -63,7 +66,8 @@ static bool vertex_paint_from_weight(Object *ob)
const MPoly *mp;
int vgroup_active;
- if (((me = BKE_mesh_from_object(ob)) == NULL || (ED_mesh_color_ensure(me, NULL)) == false)) {
+ if (((me = BKE_mesh_from_object(ob)) == nullptr ||
+ (ED_mesh_color_ensure(me, nullptr)) == false)) {
return false;
}
@@ -128,14 +132,13 @@ static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag)
{
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const MPoly *mp;
- int(*scol)[4];
bool has_shared = false;
- if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) {
+ if (me->mloopcol == nullptr || me->totvert == 0 || me->totpoly == 0) {
return;
}
- scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol");
+ int(*scol)[4] = static_cast<int(*)[4]>(MEM_callocN(sizeof(int) * me->totvert * 5, "scol"));
int i;
for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
@@ -185,16 +188,15 @@ static bool vertex_color_smooth(Object *ob)
const MPoly *mp;
int i, j;
- bool *mlooptag;
-
- if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) {
+ if (((me = BKE_mesh_from_object(ob)) == nullptr) ||
+ (ED_mesh_color_ensure(me, nullptr) == false)) {
return false;
}
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
- mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
+ Array<bool> loop_tag(me->totloop, false);
/* simply tag loops of selected faces */
mp = me->mpoly;
@@ -208,7 +210,7 @@ static bool vertex_color_smooth(Object *ob)
j = 0;
do {
if (!(use_vert_sel && !(me->mvert[ml->v].flag & SELECT))) {
- mlooptag[mp->loopstart + j] = true;
+ loop_tag[mp->loopstart + j] = true;
}
ml++;
j++;
@@ -218,9 +220,7 @@ static bool vertex_color_smooth(Object *ob)
/* remove stale me->mcol, will be added later */
BKE_mesh_tessface_clear(me);
- vertex_color_smooth_looptag(me, mlooptag);
-
- MEM_freeN(mlooptag);
+ vertex_color_smooth_looptag(me, loop_tag.data());
tag_object_after_update(ob);
@@ -268,7 +268,8 @@ static void vpaint_tx_brightness_contrast(const float col[3],
const void *user_data,
float r_col[3])
{
- const struct VPaintTx_BrightContrastData *data = user_data;
+ const VPaintTx_BrightContrastData *data = static_cast<const VPaintTx_BrightContrastData *>(
+ user_data);
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * col[i] + data->offset;
@@ -302,10 +303,9 @@ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
}
}
- const struct VPaintTx_BrightContrastData user_data = {
- .gain = gain,
- .offset = offset,
- };
+ VPaintTx_BrightContrastData user_data{};
+ user_data.gain = gain;
+ user_data.offset = offset;
if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
@@ -345,7 +345,7 @@ struct VPaintTx_HueSatData {
static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
{
- const struct VPaintTx_HueSatData *data = user_data;
+ const VPaintTx_HueSatData *data = static_cast<const VPaintTx_HueSatData *>(user_data);
float hsv[3];
rgb_to_hsv_v(col, hsv);
@@ -366,11 +366,10 @@ static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
- const struct VPaintTx_HueSatData user_data = {
- .hue = RNA_float_get(op->ptr, "h"),
- .sat = RNA_float_get(op->ptr, "s"),
- .val = RNA_float_get(op->ptr, "v"),
- };
+ VPaintTx_HueSatData user_data{};
+ user_data.hue = RNA_float_get(op->ptr, "h");
+ user_data.sat = RNA_float_get(op->ptr, "s");
+ user_data.val = RNA_float_get(op->ptr, "v");
if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
@@ -410,7 +409,7 @@ static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
- if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
+ if (ED_vpaint_color_transform(obact, vpaint_tx_invert, nullptr)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
@@ -439,7 +438,7 @@ struct VPaintTx_LevelsData {
static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
{
- const struct VPaintTx_LevelsData *data = user_data;
+ const VPaintTx_LevelsData *data = static_cast<const VPaintTx_LevelsData *>(user_data);
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * (col[i] + data->offset);
}
@@ -449,10 +448,9 @@ static int vertex_color_levels_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
- const struct VPaintTx_LevelsData user_data = {
- .gain = RNA_float_get(op->ptr, "gain"),
- .offset = RNA_float_get(op->ptr, "offset"),
- };
+ VPaintTx_LevelsData user_data{};
+ user_data.gain = RNA_float_get(op->ptr, "gain");
+ user_data.offset = RNA_float_get(op->ptr, "offset");
if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 32b7047c2b0..9ce80e4a433 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -148,7 +148,7 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
bool SCULPT_has_loop_colors(const Object *ob)
{
Mesh *me = BKE_object_get_original_mesh(ob);
- CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
+ const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER;
}
@@ -1354,8 +1354,23 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
SculptUndoNode *unode;
- SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
- SCULPT_UNDO_COORDS);
+ SculptUndoType type;
+
+ switch (data->brush->sculpt_tool) {
+ case SCULPT_TOOL_MASK:
+ type = SCULPT_UNDO_MASK;
+ BKE_pbvh_node_mark_update_mask(data->nodes[n]);
+ break;
+ case SCULPT_TOOL_PAINT:
+ case SCULPT_TOOL_SMEAR:
+ type = SCULPT_UNDO_COLOR;
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+ break;
+ default:
+ type = SCULPT_UNDO_COORDS;
+ BKE_pbvh_node_mark_update(data->nodes[n]);
+ break;
+ }
if (ss->bm) {
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type);
@@ -1397,8 +1412,6 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
}
}
BKE_pbvh_vertex_iter_end;
-
- BKE_pbvh_node_mark_update(data->nodes[n]);
}
static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
@@ -3248,8 +3261,8 @@ static void do_brush_action(Sculpt *sd,
}
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
}
-
- if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) {
+ const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob);
+ if (use_pixels) {
sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob);
}
@@ -3302,16 +3315,18 @@ static void do_brush_action(Sculpt *sd,
}
float location[3];
- SculptThreadedTaskData task_data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ if (!use_pixels) {
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
+ }
if (sculpt_brush_needs_normal(ss, brush)) {
update_sculpt_normal(sd, ob, nodes, totnode);
@@ -3520,15 +3535,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
Object *ob = data->ob;
-
- /* These brushes start from original coordinates. */
- const bool use_orco = ELEM(data->brush->sculpt_tool,
- SCULPT_TOOL_GRAB,
- SCULPT_TOOL_ROTATE,
- SCULPT_TOOL_THUMB,
- SCULPT_TOOL_ELASTIC_DEFORM,
- SCULPT_TOOL_BOUNDARY,
- SCULPT_TOOL_POSE);
+ const bool use_orco = data->use_proxies_orco;
PBVHVertexIter vd;
PBVHProxyNode *proxies;
@@ -3583,12 +3590,23 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
return;
}
+ /* First line is tools that don't support proxies. */
+ const bool use_orco = ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_GRAB,
+ SCULPT_TOOL_ROTATE,
+ SCULPT_TOOL_THUMB,
+ SCULPT_TOOL_ELASTIC_DEFORM,
+ SCULPT_TOOL_BOUNDARY,
+ SCULPT_TOOL_POSE);
+
BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
+ .use_proxies_orco = use_orco,
};
TaskParallelSettings settings;
@@ -3597,6 +3615,27 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
MEM_SAFE_FREE(nodes);
}
+void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHNode **nodes;
+ int totnode;
+
+ BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .use_proxies_orco = false,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+}
+
/**
* Copy the modified vertices from the #PBVH to the active key.
*/
@@ -4135,7 +4174,7 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
/* Initialize the stroke cache invariants from operator properties. */
static void sculpt_update_cache_invariants(
- bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2])
+ bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2])
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
@@ -4165,8 +4204,8 @@ static void sculpt_update_cache_invariants(
sculpt_init_mirror_clipping(ob, ss);
/* Initial mouse location. */
- if (mouse) {
- copy_v2_v2(cache->initial_mouse, mouse);
+ if (mval) {
+ copy_v2_v2(cache->initial_mouse, mval);
}
else {
zero_v2(cache->initial_mouse);
@@ -4304,7 +4343,8 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
SCULPT_TOOL_POSE,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_THUMB,
- SCULPT_TOOL_ELASTIC_DEFORM)) {
+ SCULPT_TOOL_ELASTIC_DEFORM,
+ SCULPT_TOOL_SMEAR)) {
return true;
}
if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
@@ -4334,7 +4374,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
{
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
- const float mouse[2] = {
+ const float mval[2] = {
cache->mouse_event[0],
cache->mouse_event[1],
};
@@ -4353,6 +4393,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_POSE,
SCULPT_TOOL_BOUNDARY,
+ SCULPT_TOOL_SMEAR,
SCULPT_TOOL_THUMB) &&
!sculpt_brush_use_topology_rake(ss, brush)) {
return;
@@ -4374,9 +4415,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
add_v3_v3(cache->true_location, cache->grab_delta);
}
- /* Compute 3d coordinate at same z from original location + mouse. */
+ /* Compute 3d coordinate at same z from original location + mval. */
mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location);
- ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mouse, grab_location);
+ ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mval, grab_location);
/* Compute delta to move verts by. */
if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
@@ -4744,7 +4785,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t
}
float SCULPT_raycast_init(ViewContext *vc,
- const float mouse[2],
+ const float mval[2],
float ray_start[3],
float ray_end[3],
float ray_normal[3],
@@ -4758,7 +4799,7 @@ float SCULPT_raycast_init(ViewContext *vc,
/* TODO: what if the segment is totally clipped? (return == 0). */
ED_view3d_win_to_segment_clipped(
- vc->depsgraph, vc->region, vc->v3d, mouse, ray_start, ray_end, true);
+ vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true);
invert_m4_m4(obimat, ob->obmat);
mul_m4_v3(obimat, ray_start);
@@ -4782,7 +4823,7 @@ float SCULPT_raycast_init(ViewContext *vc,
bool SCULPT_cursor_geometry_info_update(bContext *C,
SculptCursorGeometryInfo *out,
- const float mouse[2],
+ const float mval[2],
bool use_sampled_normal)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
@@ -4811,7 +4852,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
}
/* PBVH raycast to get active vertex and face normal. */
- depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
SCULPT_stroke_modifiers_check(C, ob, brush);
SculptRaycastData srd = {
@@ -4909,7 +4950,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
return true;
}
-bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
+bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2])
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob;
@@ -4931,7 +4972,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
SCULPT_stroke_modifiers_check(C, ob, brush);
- depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
@@ -5236,14 +5277,10 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
/* Returns whether the mouse/stylus is over the mesh (1)
* or over the background (0). */
-static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y)
+static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2])
{
- float mouse[2], co[3];
-
- mouse[0] = x;
- mouse[1] = y;
-
- return SCULPT_stroke_get_location(C, co, mouse);
+ float co_dummy[3];
+ return SCULPT_stroke_get_location(C, co_dummy, mval);
}
bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports)
@@ -5264,36 +5301,44 @@ bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports)
return false;
}
-static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
+static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mval[2])
{
- /* Don't start the stroke until mouse goes over the mesh.
- * NOTE: mouse will only be null when re-executing the saved stroke.
- * We have exception for 'exec' strokes since they may not set 'mouse',
+ /* Don't start the stroke until `mval` goes over the mesh.
+ * NOTE: `mval` will only be null when re-executing the saved stroke.
+ * We have exception for 'exec' strokes since they may not set `mval`,
* only 'location', see: T52195. */
- if (((op->flag & OP_IS_INVOKE) == 0) || (mouse == NULL) ||
- over_mesh(C, op, mouse[0], mouse[1])) {
+ if (((op->flag & OP_IS_INVOKE) == 0) || (mval == NULL) || over_mesh(C, op, mval)) {
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ ToolSettings *tool_settings = CTX_data_tool_settings(C);
/* NOTE: This should be removed when paint mode is available. Paint mode can force based on the
* canvas it is painting on. (ref. use_sculpt_texture_paint). */
if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) {
View3D *v3d = CTX_wm_view3d(C);
- if (v3d) {
+ if (v3d->shading.type == OB_SOLID) {
v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR;
}
}
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
- sculpt_update_cache_invariants(C, sd, ss, op, mouse);
+ sculpt_update_cache_invariants(C, sd, ss, op, mval);
SculptCursorGeometryInfo sgi;
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval, false);
- SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
+ /* Setup the correct undo system. Image painting and sculpting are mutual exclusive.
+ * Color attributes are part of the sculpting undo system. */
+ if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT &&
+ SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+ ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT);
+ }
+ else {
+ SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
+ }
return true;
}
@@ -5420,7 +5465,13 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
SCULPT_cache_free(ss->cache);
ss->cache = NULL;
- SCULPT_undo_push_end(ob);
+ if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT &&
+ SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+ ED_image_undo_push_end();
+ }
+ else {
+ SCULPT_undo_push_end(ob);
+ }
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
@@ -5469,7 +5520,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
/* For tablet rotation. */
ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click");
- if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) {
+ if (ignore_background_click && !over_mesh(C, op, (const float[2]){UNPACK2(event->mval)})) {
paint_stroke_free(C, op, op->customdata);
return OPERATOR_PASS_THROUGH;
}
@@ -5729,7 +5780,8 @@ void SCULPT_connected_components_ensure(Object *ob)
{
SculptSession *ss = ob->sculpt;
- /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
+ /* Topology IDs already initialized. They only need to be recalculated when the PBVH is
+ * rebuild.
*/
if (ss->vertex_info.connected_component) {
return;
@@ -5793,7 +5845,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
- /* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
+ /* Fake neighbors were already initialized with the same distance, so no need to be
+ * recalculated.
*/
if (ss->fake_neighbors.fake_neighbor_index &&
ss->fake_neighbors.current_max_distance == max_dist) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
index d7a2a18504c..bb101717c9b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
@@ -9,6 +9,7 @@
#include "BLI_blenlib.h"
#include "BLI_hash.h"
+#include "BLI_index_range.hh"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -43,8 +44,10 @@
#include "bmesh.h"
-#include <math.h>
-#include <stdlib.h>
+#include <cmath>
+#include <cstdlib>
+
+using blender::IndexRange;
AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
{
@@ -54,7 +57,7 @@ AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
if (ss->filter_cache) {
return ss->filter_cache->automasking;
}
- return NULL;
+ return nullptr;
}
bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
@@ -167,18 +170,18 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
return false;
}
-typedef struct AutomaskFloodFillData {
+struct AutomaskFloodFillData {
float *automask_factor;
float radius;
bool use_radius;
float location[3];
char symm;
-} AutomaskFloodFillData;
+};
static bool automask_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
{
- AutomaskFloodFillData *data = userdata;
+ AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata;
data->automask_factor[to_v] = 1.0f;
data->automask_factor[from_v] = 1.0f;
@@ -194,11 +197,11 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Topology masking: pmap missing");
- return NULL;
+ return nullptr;
}
const int totvert = SCULPT_vertex_count_get(ss);
- for (int i = 0; i < totvert; i++) {
+ for (int i : IndexRange(totvert)) {
automask_factor[i] = 0.0f;
}
@@ -209,12 +212,13 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
- AutomaskFloodFillData fdata = {
- .automask_factor = automask_factor,
- .radius = radius,
- .use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
- .symm = SCULPT_mesh_symmetry_xyz_get(ob),
- };
+ AutomaskFloodFillData fdata = {nullptr};
+
+ fdata.automask_factor = automask_factor;
+ fdata.radius = radius;
+ fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush);
+ fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob);
+
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
@@ -228,17 +232,17 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
- return NULL;
+ return nullptr;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Face Sets automasking: pmap missing");
- return NULL;
+ return nullptr;
}
int tot_vert = SCULPT_vertex_count_get(ss);
int active_face_set = SCULPT_active_face_set_get(ss);
- for (int i = 0; i < tot_vert; i++) {
+ for (int i : IndexRange(tot_vert)) {
if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
automask_factor[i] *= 0.0f;
}
@@ -258,13 +262,13 @@ float *SCULPT_boundary_automasking_init(Object *ob,
if (!ss->pmap) {
BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
- return NULL;
+ return nullptr;
}
const int totvert = SCULPT_vertex_count_get(ss);
- int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor");
+ int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor");
- for (int i = 0; i < totvert; i++) {
+ for (int i : IndexRange(totvert)) {
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
@@ -280,8 +284,8 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
}
- for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) {
- for (int i = 0; i < totvert; i++) {
+ for (int propagation_it : IndexRange(propagation_steps)) {
+ for (int i : IndexRange(totvert)) {
if (edge_distance[i] != EDGE_DISTANCE_INF) {
continue;
}
@@ -295,7 +299,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
}
- for (int i = 0; i < totvert; i++) {
+ for (int i : IndexRange(totvert)) {
if (edge_distance[i] == EDGE_DISTANCE_INF) {
continue;
}
@@ -323,10 +327,11 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
const int totvert = SCULPT_vertex_count_get(ss);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
- return NULL;
+ return nullptr;
}
- AutomaskingCache *automasking = MEM_callocN(sizeof(AutomaskingCache), "automasking cache");
+ AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache),
+ "automasking cache");
SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
SCULPT_boundary_info_ensure(ob);
@@ -334,8 +339,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
return automasking;
}
- automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
- for (int i = 0; i < totvert; i++) {
+ automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
+ for (int i : IndexRange(totvert)) {
automasking->factor[i] = 1.0f;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index dcf90f9e819..9d231f2ccd2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -1553,11 +1553,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const eSculptClothFilterType filter_type = RNA_enum_get(op->ptr, "type");
/* Update the active vertex */
- float mouse[2];
+ float mval_fl[2] = {UNPACK2(event->mval)};
SculptCursorGeometryInfo sgi;
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
SCULPT_vertex_random_access_ensure(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index fe69cf6b84f..00503087e39 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -158,7 +158,7 @@ static EnumPropertyItem prop_sculpt_sample_detail_mode_types[] = {
{0, NULL, 0, NULL, NULL},
};
-static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
+static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = vc->obact;
@@ -169,8 +169,8 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
SCULPT_vertex_random_access_ensure(ss);
/* Update the active vertex. */
- const float mouse[2] = {mx, my};
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ const float mval_fl[2] = {UNPACK2(mval)};
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
@@ -201,7 +201,7 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin)
}
}
-static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, int mx, int my)
+static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2])
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = vc->obact;
@@ -209,9 +209,9 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region,
SCULPT_stroke_modifiers_check(C, ob, brush);
- const float mouse[2] = {mx - region->winrct.xmin, my - region->winrct.ymin};
+ const float mval_fl[2] = {UNPACK2(mval)};
float ray_start[3], ray_end[3], ray_normal[3];
- float depth = SCULPT_raycast_init(vc, mouse, ray_start, ray_end, ray_normal, false);
+ float depth = SCULPT_raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false);
SculptDetailRaycastData srd;
srd.hit = 0;
@@ -228,14 +228,12 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region,
}
}
-static int sample_detail(bContext *C, int mx, int my, int mode)
+static int sample_detail(bContext *C, const int event_xy[2], int mode)
{
/* Find 3D view to pick from. */
bScreen *screen = CTX_wm_screen(C);
- ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, (const int[2]){mx, my});
- ARegion *region = (area) ?
- BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}) :
- NULL;
+ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, event_xy);
+ ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, event_xy) : NULL;
if (region == NULL) {
return OPERATOR_CANCELLED;
}
@@ -260,6 +258,11 @@ static int sample_detail(bContext *C, int mx, int my, int mode)
return OPERATOR_CANCELLED;
}
+ const int mval[2] = {
+ event_xy[0] - region->winrct.xmin,
+ event_xy[1] - region->winrct.ymin,
+ };
+
/* Pick sample detail. */
switch (mode) {
case SAMPLE_DETAIL_DYNTOPO:
@@ -268,7 +271,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode)
CTX_wm_region_set(C, prev_region);
return OPERATOR_CANCELLED;
}
- sample_detail_dyntopo(C, &vc, region, mx, my);
+ sample_detail_dyntopo(C, &vc, mval);
break;
case SAMPLE_DETAIL_VOXEL:
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
@@ -276,7 +279,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode)
CTX_wm_region_set(C, prev_region);
return OPERATOR_CANCELLED;
}
- sample_detail_voxel(C, &vc, mx, my);
+ sample_detail_voxel(C, &vc, mval);
break;
}
@@ -292,7 +295,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op)
int ss_co[2];
RNA_int_get_array(op->ptr, "location", ss_co);
int mode = RNA_enum_get(op->ptr, "mode");
- return sample_detail(C, ss_co[0], ss_co[1], mode);
+ return sample_detail(C, ss_co, mode);
}
static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e))
@@ -308,12 +311,10 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm
switch (event->type) {
case LEFTMOUSE:
if (event->val == KM_PRESS) {
- const int ss_co[2] = {event->xy[0], event->xy[1]};
-
int mode = RNA_enum_get(op->ptr, "mode");
- sample_detail(C, ss_co[0], ss_co[1], mode);
+ sample_detail(C, event->xy, mode);
- RNA_int_set_array(op->ptr, "location", ss_co);
+ RNA_int_set_array(op->ptr, "location", event->xy);
WM_cursor_modal_restore(CTX_wm_window(C));
ED_workspace_status_text(C, NULL);
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -657,11 +658,13 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE
ED_region_tag_redraw(region);
- if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) {
- cd->sample_mode = true;
- }
- if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) {
- cd->sample_mode = false;
+ if (ELEM(event->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
+ if (event->val == KM_PRESS) {
+ cd->sample_mode = true;
+ }
+ else if (event->val == KM_RELEASE) {
+ cd->sample_mode = false;
+ }
}
/* Sample mode sets the detail size sampling the average edge length under the surface. */
diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 46940b619e6..fbf988d63e8 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -1450,13 +1450,11 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
* Updates the #SculptSession cursor data and gets the active vertex
* if the cursor is over the mesh.
*/
-static int sculpt_expand_target_vertex_update_and_get(bContext *C,
- Object *ob,
- const float mouse[2])
+static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2])
{
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
- if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
+ if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) {
return SCULPT_active_vertex_get(ss);
}
return SCULPT_EXPAND_VERTEX_NONE;
@@ -1588,16 +1586,16 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
Object *ob,
ExpandCache *expand_cache,
- const float mouse[2])
+ const float mval[2])
{
SculptSession *ss = ob->sculpt;
- int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+ int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval);
if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) {
/* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active
* vertex in the sculpt session. */
initial_vertex = SCULPT_active_vertex_get(ss);
}
- copy_v2_v2(ss->expand_cache->initial_mouse, mouse);
+ copy_v2_v2(ss->expand_cache->initial_mouse, mval);
expand_cache->initial_active_vertex = initial_vertex;
expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss);
@@ -1629,14 +1627,14 @@ static void sculpt_expand_move_propagation_origin(bContext *C,
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- const float mouse[2] = {event->mval[0], event->mval[1]};
+ const float mval_fl[2] = {UNPACK2(event->mval)};
float move_disp[2];
- sub_v2_v2v2(move_disp, mouse, expand_cache->initial_mouse_move);
+ sub_v2_v2v2(move_disp, mval_fl, expand_cache->initial_mouse_move);
- float new_mouse[2];
- add_v2_v2v2(new_mouse, move_disp, expand_cache->original_mouse_move);
+ float new_mval[2];
+ add_v2_v2v2(new_mval, move_disp, expand_cache->original_mouse_move);
- sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse);
+ sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mval);
sculpt_expand_falloff_factors_from_vertex_and_symm_create(
expand_cache,
sd,
@@ -1697,8 +1695,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
sculpt_expand_ensure_sculptsession_data(ob);
/* Update and get the active vertex (and face) from the cursor. */
- const float mouse[2] = {event->mval[0], event->mval[1]};
- const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl);
/* Handle the modal keymap state changes. */
ExpandCache *expand_cache = ss->expand_cache;
@@ -1756,7 +1754,7 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
}
expand_cache->move = true;
expand_cache->move_original_falloff_type = expand_cache->falloff_type;
- copy_v2_v2(expand_cache->initial_mouse_move, mouse);
+ copy_v2_v2(expand_cache->initial_mouse_move, mval_fl);
copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse);
if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC &&
SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) {
@@ -2001,7 +1999,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C,
BKE_curvemapping_init(expand_cache->brush->curve);
copy_v4_fl(expand_cache->fill_color, 1.0f);
copy_v3_v3(expand_cache->fill_color, BKE_brush_color_get(ss->scene, expand_cache->brush));
- IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color, expand_cache->fill_color);
expand_cache->scene = CTX_data_scene(C);
expand_cache->mtex = &expand_cache->brush->mtex;
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 7171c241534..ce704e619ea 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -60,7 +60,7 @@
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
{
- int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
if (!face_sets) {
return SCULPT_FACE_SET_NONE;
}
@@ -150,25 +150,22 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
}
}
}
-
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
- {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- if (fade > 0.05f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
- }
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ if (fade > 0.05f) {
+ SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
}
}
}
@@ -952,11 +949,9 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C,
/* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
* cursor updates. */
SculptCursorGeometryInfo sgi;
- float mouse[2];
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
+ const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
return sculpt_face_sets_change_visibility_exec(C, op);
}
@@ -1404,8 +1399,8 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
/* Update the current active Face Set and Vertex as the operator can be used directly from the
* tool without brush cursor. */
SculptCursorGeometryInfo sgi;
- const float mouse[2] = {event->mval[0], event->mval[1]};
- if (!SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index 09f13ff110d..95c01d24c6d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -124,8 +124,8 @@ static void color_filter_task_cb(void *__restrict userdata,
}
case COLOR_FILTER_HUE:
rgb_to_hsv_v(orig_color, hsv_color);
- hue = hsv_color[0] + fade;
- hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
+ hue = hsv_color[0];
+ hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1);
hsv_to_rgb_v(hsv_color, final_color);
break;
case COLOR_FILTER_SATURATION:
@@ -298,7 +298,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent
float fill_color[3];
RNA_float_get_array(op->ptr, "fill_color", fill_color);
- IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(fill_color, fill_color);
if (filter_strength < 0.0 && !ss->filter_cache->pre_smoothed_color) {
sculpt_color_presmooth_init(ss);
@@ -328,18 +328,20 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
{
Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ View3D *v3d = CTX_wm_view3d(C);
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
+ if (v3d->shading.type == OB_SOLID) {
+ v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR;
+ }
const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL);
if (use_automasking) {
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
- float mouse[2];
+ float mval_fl[2] = {UNPACK2(event->mval)};
SculptCursorGeometryInfo sgi;
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
}
/* Disable for multires and dyntopo for now */
@@ -374,7 +376,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
/* identifiers */
ot->name = "Filter Color";
ot->idname = "SCULPT_OT_color_filter";
- ot->description = "Applies a filter to modify the current sculpt vertex colors";
+ ot->description = "Applies a filter to modify the active color attribute";
/* api callbacks */
ot->invoke = sculpt_color_filter_invoke;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index a32b1c3015b..dbed5624adf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -675,11 +675,9 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
if (use_automasking) {
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
- float mouse[2];
+ float mval_fl[2] = {UNPACK2(event->mval)};
SculptCursorGeometryInfo sgi;
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
}
SCULPT_vertex_random_access_ensure(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index f9633c91087..0693b445fe5 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -247,6 +247,10 @@ typedef struct SculptThreadedTaskData {
float (*mat)[4];
float (*vertCos)[3];
+ /* When true, the displacement stored in the proxies will be aplied to the original coordinates
+ * instead of to the current coordinates. */
+ bool use_proxies_orco;
+
/* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to
* the stroke direction are needed. */
float (*stroke_xz)[3];
@@ -290,6 +294,10 @@ typedef struct SculptThreadedTaskData {
bool mask_expand_create_face_set;
float transform_mats[8][4][4];
+ float elastic_transform_mat[4][4];
+ float elastic_transform_pivot[3];
+ float elastic_transform_pivot_init[3];
+ float elastic_transform_radius;
/* Boundary brush */
float boundary_deform_strength;
@@ -372,6 +380,14 @@ typedef enum SculptFilterOrientation {
SCULPT_FILTER_ORIENTATION_VIEW = 2,
} SculptFilterOrientation;
+/* Defines how transform tools are going to apply its displacement. */
+typedef enum SculptTransformDisplacementMode {
+ /* Displaces the elements from their original coordinates. */
+ SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL = 0,
+ /* Displaces the elements incrementally from their previous position. */
+ SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL = 1,
+} SculptTransformDisplacementMode;
+
#define SCULPT_CLAY_STABILIZER_LEN 10
typedef struct AutomaskingSettings {
@@ -440,6 +456,8 @@ typedef struct FilterCache {
int active_face_set;
+ SculptTransformDisplacementMode transform_displacement_mode;
+
/* Auto-masking. */
AutomaskingCache *automasking;
@@ -1137,12 +1155,15 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v);
*/
bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v);
+void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob);
+
/**
* Initialize a point-in-brush test with a given falloff shape.
*
* \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE.
* \return The brush falloff function.
*/
+
SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss,
SculptBrushTest *test,
char falloff_shape);
@@ -1652,11 +1673,11 @@ void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings,
int totnode) ATTR_NONNULL();
/**
- * @brief Get the image canvas for painting on the given object.
+ * \brief Get the image canvas for painting on the given object.
*
- * @return #true if an image is found. The #r_image and #r_image_user fields are filled with the
+ * \return #true if an image is found. The #r_image and #r_image_user fields are filled with the
* image and image user. Returns false when the image isn't found. In the later case the r_image
- * and r_image_user are set to nullptr/NULL.
+ * and r_image_user are set to NULL.
*/
bool SCULPT_paint_image_canvas_get(struct PaintModeSettings *paint_mode_settings,
struct Object *ob,
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index 201e02b8235..4593c6a8b60 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -167,10 +167,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
if (RNA_boolean_get(op->ptr, "use_cursor")) {
SculptCursorGeometryInfo sgi;
- float mouse[2];
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
}
@@ -340,16 +338,14 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
SculptCursorGeometryInfo sgi;
- float mouse[2];
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
+ const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_vertex_random_access_ensure(ss);
op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position");
- copy_v2_v2(op->customdata, mouse);
+ copy_v2_v2(op->customdata, mval_fl);
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
index 2b5a20205bd..f16763be735 100644
--- a/source/blender/editors/sculpt_paint/sculpt_ops.c
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -84,6 +84,7 @@
#include "WM_toolsystem.h"
#include "WM_types.h"
+#include "ED_image.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
@@ -349,7 +350,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT);
BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT);
- paint_cursor_start(paint, SCULPT_mode_poll_view3d);
+ ED_paint_cursor_start(paint, SCULPT_mode_poll_view3d);
/* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes,
* As long as no data was added that is not supported. */
@@ -637,16 +638,16 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
if (MPropCol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
- MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+ const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
- MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+ const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
for (int i = 0; i < mesh->totpoly; i++) {
- MPoly *c_poly = &polys[i];
+ const MPoly *c_poly = &polys[i];
for (int j = 0; j < c_poly->totloop; j++) {
int loop_index = c_poly->loopstart + j;
- MLoop *c_loop = &loops[c_poly->loopstart + j];
+ const MLoop *c_loop = &loops[c_poly->loopstart + j];
float srgb_color[4];
linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color);
loopcols[loop_index].r = (char)(srgb_color[0] * 255);
@@ -711,7 +712,8 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
if (mloopcol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
- MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
+ const MLoopCol *loopcols = CustomData_get_layer_n(
+ &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
if (MPropCol_layer_n == -1) {
@@ -719,14 +721,14 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
}
MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
- MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+ const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
for (int i = 0; i < mesh->totpoly; i++) {
- MPoly *c_poly = &polys[i];
+ const MPoly *c_poly = &polys[i];
for (int j = 0; j < c_poly->totloop; j++) {
int loop_index = c_poly->loopstart + j;
- MLoop *c_loop = &loops[c_poly->loopstart + j];
+ const MLoop *c_loop = &loops[c_poly->loopstart + j];
vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
@@ -780,8 +782,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent
}
float color_srgb[3];
- copy_v3_v3(color_srgb, active_vertex_color);
- IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb, active_vertex_color);
BKE_brush_color_set(scene, brush, color_srgb);
WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
@@ -1043,6 +1044,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d->shading.type == OB_SOLID) {
+ v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR;
+ }
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
@@ -1056,10 +1061,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
* so it needs to be updated here. */
SculptCursorGeometryInfo sgi;
- float mouse[2];
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
SCULPT_undo_push_begin(ob, "Mask by color");
BKE_sculpt_color_layer_create_if_needed(ob);
@@ -1094,7 +1097,7 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
/* identifiers */
ot->name = "Mask by Color";
ot->idname = "SCULPT_OT_mask_by_color";
- ot->description = "Creates a mask based on the sculpt vertex colors";
+ ot->description = "Creates a mask based on the active color attribute";
/* api callbacks */
ot->invoke = sculpt_mask_by_color_invoke;
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index 7a8a6e8e484..fa9f24377da 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -124,7 +124,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
copy_v3_v3(brush_color,
ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) :
BKE_brush_color_get(ss->scene, brush));
- IMB_colormanagement_srgb_to_scene_linear_v3(brush_color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@@ -380,6 +380,15 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ float brush_delta[3];
+
+ if (brush->flag & BRUSH_ANCHORED) {
+ copy_v3_v3(brush_delta, ss->cache->grab_delta_symmetry);
+ }
+ else {
+ sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location);
+ }
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@@ -404,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
switch (brush->smear_deform_type) {
case BRUSH_SMEAR_DEFORM_DRAG:
- sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ copy_v3_v3(current_disp, brush_delta);
break;
case BRUSH_SMEAR_DEFORM_PINCH:
sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
@@ -529,12 +538,10 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
const int totvert = SCULPT_vertex_count_get(ss);
- if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
- if (!ss->cache->prev_colors) {
- ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors");
- for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]);
- }
+ if (!ss->cache->prev_colors) {
+ ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors");
+ for (int i = 0; i < totvert; i++) {
+ SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
index df1ccc0fbe9..975a8f21aaf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -360,6 +360,86 @@ static void do_paint_pixels(void *__restrict userdata,
node_data.flags.dirty |= pixels_updated;
}
+static void undo_region_tiles(
+ ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th)
+{
+ int srcx = 0, srcy = 0;
+ IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
+ *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS);
+ *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS);
+ *tx = (x >> ED_IMAGE_UNDO_TILE_BITS);
+ *ty = (y >> ED_IMAGE_UNDO_TILE_BITS);
+}
+
+static void push_undo(const NodeData &node_data,
+ Image &image,
+ ImageUser &image_user,
+ const image::ImageTileWrapper &image_tile,
+ ImBuf &image_buffer,
+ ImBuf **tmpibuf)
+{
+ for (const UDIMTileUndo &tile_undo : node_data.undo_regions) {
+ if (tile_undo.tile_number != image_tile.get_tile_number()) {
+ continue;
+ }
+ int tilex, tiley, tilew, tileh;
+ ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ undo_region_tiles(&image_buffer,
+ tile_undo.region.xmin,
+ tile_undo.region.ymin,
+ BLI_rcti_size_x(&tile_undo.region),
+ BLI_rcti_size_y(&tile_undo.region),
+ &tilex,
+ &tiley,
+ &tilew,
+ &tileh);
+ for (int ty = tiley; ty <= tileh; ty++) {
+ for (int tx = tilex; tx <= tilew; tx++) {
+ ED_image_paint_tile_push(undo_tiles,
+ &image,
+ &image_buffer,
+ tmpibuf,
+ &image_user,
+ tx,
+ ty,
+ nullptr,
+ nullptr,
+ true,
+ true);
+ }
+ }
+ }
+}
+
+static void do_push_undo_tile(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata);
+ PBVHNode *node = data->nodes[n];
+
+ NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
+ Image *image = data->image_data.image;
+ ImageUser *image_user = data->image_data.image_user;
+
+ ImBuf *tmpibuf = nullptr;
+ ImageUser local_image_user = *image_user;
+ LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
+ image::ImageTileWrapper image_tile(tile);
+ local_image_user.tile = image_tile.get_tile_number();
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &local_image_user, nullptr);
+ if (image_buffer == nullptr) {
+ continue;
+ }
+
+ push_undo(node_data, *image, *image_user, image_tile, *image_buffer, &tmpibuf);
+ BKE_image_release_ibuf(image, image_buffer, nullptr);
+ }
+ if (tmpibuf) {
+ IMB_freeImBuf(tmpibuf);
+ }
+}
+
static void do_mark_dirty_regions(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
@@ -380,8 +460,9 @@ bool SCULPT_paint_image_canvas_get(PaintModeSettings *paint_mode_settings,
Image **r_image,
ImageUser **r_image_user)
{
- BLI_assert(r_image);
- BLI_assert(r_image_user);
+ *r_image = nullptr;
+ *r_image_user = nullptr;
+
ImageData image_data;
if (!ImageData::init_active_image(ob, &image_data, paint_mode_settings)) {
return false;
@@ -421,6 +502,7 @@ void SCULPT_do_paint_brush_image(
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings);
BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings);
TaskParallelSettings settings_flush;
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index b3616254b26..48033f3407e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -16,6 +16,7 @@
#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_kelvinlet.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
@@ -33,6 +34,7 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
+#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -54,6 +56,10 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale);
+ copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
+ copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
+ copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
+
SCULPT_undo_push_begin(ob, "Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
@@ -61,10 +67,18 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
+
+ if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) {
+ ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL;
+ }
+ else {
+ ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL;
+ }
}
static void sculpt_transform_matrices_init(SculptSession *ss,
const char symm,
+ const SculptTransformDisplacementMode t_mode,
float r_transform_mats[8][4][4])
{
@@ -73,9 +87,18 @@ static void sculpt_transform_matrices_init(SculptSession *ss,
transform_mat[4][4];
float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
- copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
- copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
- copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
+ switch (t_mode) {
+ case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
+ copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
+ copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
+ copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
+ break;
+ case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
+ copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos);
+ copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot);
+ copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale);
+ break;
+ }
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
ePaintSymmetryAreas v_symm = i;
@@ -134,16 +157,26 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float *start_co;
float transformed_co[3], orig_co[3], disp[3];
float fade = vd.mask ? *vd.mask : 0.0f;
copy_v3_v3(orig_co, orig_data.co);
char symm_area = SCULPT_get_vertex_symm_area(orig_co);
- copy_v3_v3(transformed_co, orig_co);
+ switch (ss->filter_cache->transform_displacement_mode) {
+ case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
+ start_co = orig_co;
+ break;
+ case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
+ start_co = vd.co;
+ break;
+ }
+
+ copy_v3_v3(transformed_co, start_co);
mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co);
- sub_v3_v3v3(disp, transformed_co, orig_co);
+ sub_v3_v3v3(disp, transformed_co, start_co);
mul_v3_fl(disp, 1.0f - fade);
- add_v3_v3v3(vd.co, orig_co, disp);
+ add_v3_v3v3(vd.co, start_co, disp);
if (vd.mvert) {
BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
@@ -165,7 +198,8 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
.nodes = ss->filter_cache->nodes,
};
- sculpt_transform_matrices_init(ss, symm, data.transform_mats);
+ sculpt_transform_matrices_init(
+ ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */
@@ -175,6 +209,98 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
}
+static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHNode *node = data->nodes[i];
+
+ float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+
+ KelvinletParams params;
+ /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
+ * preservation like in the elastic deform brushes. Setting them to the same default as elastic
+ * deform triscale grab because they work well in most cases. */
+ const float force = 1.0f;
+ const float shear_modulus = 1.0f;
+ const float poisson_ratio = 0.4f;
+ BKE_kelvinlet_init_params(
+ &params, data->elastic_transform_radius, force, shear_modulus, poisson_ratio);
+
+ SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float transformed_co[3], orig_co[3], disp[3];
+ const float fade = vd.mask ? *vd.mask : 0.0f;
+ copy_v3_v3(orig_co, orig_data.co);
+
+ copy_v3_v3(transformed_co, vd.co);
+ mul_m4_v3(data->elastic_transform_mat, transformed_co);
+ sub_v3_v3v3(disp, transformed_co, vd.co);
+
+ float final_disp[3];
+ BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, data->elastic_transform_pivot, disp);
+ mul_v3_fl(final_disp, 20.0f * (1.0f - fade));
+
+ copy_v3_v3(proxy[vd.i], final_disp);
+
+ if (vd.mvert) {
+ BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BKE_pbvh_node_mark_update(node);
+}
+
+static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius)
+{
+ SculptSession *ss = ob->sculpt;
+ BLI_assert(ss->filter_cache->transform_displacement_mode ==
+ SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL);
+
+ const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .elastic_transform_radius = transform_radius,
+ };
+
+ sculpt_transform_matrices_init(
+ ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
+
+ /* Elastic transform needs to apply all transform matrices to all vertices and then combine the
+ * displacement proxies as all vertices are modified by all symmetry passes. */
+ for (ePaintSymmetryFlags symmpass = 0; symmpass <= symm; symmpass++) {
+ if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) {
+ flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass);
+ flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
+
+ printf(
+ "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]);
+
+ const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot);
+ copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]);
+ BLI_task_parallel_range(
+ 0, ss->filter_cache->totnode, &data, sculpt_elastic_transform_task_cb, &settings);
+ }
+ }
+ SCULPT_combine_transform_proxies(sd, ob);
+}
+
void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -184,7 +310,36 @@ void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- sculpt_transform_all_vertices(sd, ob);
+ switch (sd->transform_mode) {
+ case SCULPT_TRANSFORM_MODE_ALL_VERTICES: {
+ sculpt_transform_all_vertices(sd, ob);
+ break;
+ }
+ case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: {
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ Scene *scene = CTX_data_scene(C);
+ float transform_radius;
+
+ if (BKE_brush_use_locked_size(scene, brush)) {
+ transform_radius = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+ else {
+ ViewContext vc;
+
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+
+ transform_radius = paint_calc_object_space_radius(
+ &vc, ss->init_pivot_pos, BKE_brush_size_get(scene, brush));
+ }
+
+ sculpt_transform_radius_elastic(sd, ob, transform_radius);
+ break;
+ }
+ }
+
+ copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
+ copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
+ copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
if (ss->deform_modifiers_active || ss->shapekey_active) {
SCULPT_flush_stroke_deform(sd, ob, true);
@@ -267,10 +422,11 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
/* Pivot to ray-cast surface. */
else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) {
float stroke_location[3];
- float mouse[2];
- mouse[0] = RNA_float_get(op->ptr, "mouse_x");
- mouse[1] = RNA_float_get(op->ptr, "mouse_y");
- if (SCULPT_stroke_get_location(C, stroke_location, mouse)) {
+ const float mval[2] = {
+ RNA_float_get(op->ptr, "mouse_x"),
+ RNA_float_get(op->ptr, "mouse_y"),
+ };
+ if (SCULPT_stroke_get_location(C, stroke_location, mval)) {
copy_v3_v3(ss->pivot_pos, stroke_location);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 5867dc558de..e82f14b1ca7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -50,6 +50,7 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "ED_geometry.h"
#include "ED_object.h"
#include "ED_sculpt.h"
#include "ED_undo.h"
@@ -105,7 +106,7 @@ typedef struct UndoSculpt {
} UndoSculpt;
typedef struct SculptAttrRef {
- AttributeDomain domain;
+ eAttrDomain domain;
int type;
char name[MAX_CUSTOMDATA_LAYER_NAME];
bool was_set;
@@ -1265,7 +1266,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets");
- int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
+ const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
unode->face_sets[i] = face_sets[i];
}
@@ -1464,7 +1465,7 @@ static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b)
static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr)
{
Mesh *me = BKE_object_get_original_mesh(ob);
- CustomDataLayer *layer;
+ const CustomDataLayer *layer;
if (ob && me && (layer = BKE_id_attributes_active_color_get((ID *)me))) {
attr->domain = BKE_id_attribute_domain((ID *)me, layer);
@@ -1565,6 +1566,24 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr
CustomDataLayer *layer;
layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain);
+ /* Temporary fix for T97408. This is a fundamental
+ * bug in the undo stack; the operator code needs to push
+ * an extra undo step before running an operator if a
+ * non-memfile undo system is active.
+ *
+ * For now, detect if the layer does exist but with a different
+ * domain and just unconvert it.
+ */
+ if (!layer) {
+ layer = BKE_id_attribute_search(&me->id, attr->name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
+ eAttrDomain domain = layer ? BKE_id_attribute_domain(&me->id, layer) : ATTR_DOMAIN_NUM;
+
+ if (layer && ED_geometry_attribute_convert(
+ me, attr->name, layer->type, domain, attr->type, attr->domain)) {
+ layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain);
+ }
+ }
+
if (!layer) {
/* Memfile undo killed the layer; re-create it. */
CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata;
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index a63ed142ed8..2a8a2be8b65 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -761,7 +761,8 @@ static int sound_pack_exec(bContext *C, wmOperator *op)
sound->packedfile = BKE_packedfile_new(
op->reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id));
- BKE_sound_load(bmain, sound);
+
+ DEG_id_tag_update_ex(bmain, &sound->id, ID_RECALC_AUDIO);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index b972ccbaf89..aff888818e0 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -887,6 +887,7 @@ void ACTION_OT_select_circle(wmOperatorType *ot)
ot->exec = action_circle_select_exec;
ot->poll = ED_operator_action_active;
ot->cancel = WM_gesture_circle_cancel;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 1d0061ab7d8..052af39319c 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -724,6 +724,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
/* Needed to refresh context path when changing active particle system index. */
buttons_area_redraw(area, BCONTEXT_PARTICLE);
break;
+ case ND_DRAW_ANIMVIZ:
+ buttons_area_redraw(area, BCONTEXT_OBJECT);
+ break;
default:
/* Not all object RNA props have a ND_ notifier (yet) */
ED_area_tag_redraw(area);
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index e24d461019b..72df2b74b11 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -303,7 +303,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event)
cb->marker->pattern_corners[a][1] *= scale_y;
}
- BKE_tracking_marker_clamp(cb->marker, CLAMP_PAT_DIM);
+ BKE_tracking_marker_clamp_search_size(cb->marker);
ok = true;
}
@@ -319,7 +319,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event)
sub_v2_v2v2(cb->marker->search_min, delta, side);
add_v2_v2v2(cb->marker->search_max, delta, side);
- BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_POS);
+ BKE_tracking_marker_clamp_search_position(cb->marker);
ok = true;
}
@@ -340,7 +340,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event)
cb->marker->search_max[0] += dim[0];
cb->marker->search_max[1] += dim[1];
- BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_DIM);
+ BKE_tracking_marker_clamp_search_size(cb->marker);
ok = true;
}
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 99524184323..7800ce797aa 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -676,6 +676,40 @@ static void track_colors(MovieTrackingTrack *track, int act, float col[3], float
}
}
+static void set_draw_marker_area_color(const MovieTrackingTrack *track,
+ const MovieTrackingMarker *marker,
+ const bool is_track_active,
+ const bool is_area_selected,
+ const float color[3],
+ const float selected_color[3])
+{
+ if (track->flag & TRACK_LOCKED) {
+ if (is_track_active) {
+ immUniformThemeColor(TH_ACT_MARKER);
+ }
+ else if (is_area_selected) {
+ immUniformThemeColorShade(TH_LOCK_MARKER, 64);
+ }
+ else {
+ immUniformThemeColor(TH_LOCK_MARKER);
+ }
+ }
+ else if (marker->flag & MARKER_DISABLED) {
+ if (is_track_active) {
+ immUniformThemeColor(TH_ACT_MARKER);
+ }
+ else if (is_area_selected) {
+ immUniformThemeColorShade(TH_DIS_MARKER, 128);
+ }
+ else {
+ immUniformThemeColor(TH_DIS_MARKER);
+ }
+ }
+ else {
+ immUniformColor3fv(is_area_selected ? selected_color : color);
+ }
+}
+
static void draw_marker_areas(SpaceClip *sc,
MovieTrackingTrack *track,
MovieTrackingMarker *marker,
@@ -785,31 +819,7 @@ static void draw_marker_areas(SpaceClip *sc,
GPU_matrix_push();
GPU_matrix_translate_2fv(marker_pos);
- if (track->flag & TRACK_LOCKED) {
- if (act) {
- immUniformThemeColor(TH_ACT_MARKER);
- }
- else if (track->pat_flag & SELECT) {
- immUniformThemeColorShade(TH_LOCK_MARKER, 64);
- }
- else {
- immUniformThemeColor(TH_LOCK_MARKER);
- }
- }
- else if (marker->flag & MARKER_DISABLED) {
- if (act) {
- immUniformThemeColor(TH_ACT_MARKER);
- }
- else if (track->pat_flag & SELECT) {
- immUniformThemeColorShade(TH_DIS_MARKER, 128);
- }
- else {
- immUniformThemeColor(TH_DIS_MARKER);
- }
- }
- else {
- immUniformColor3fv((track->pat_flag & SELECT) ? scol : col);
- }
+ set_draw_marker_area_color(track, marker, act, track->pat_flag & SELECT, col, scol);
if (tiny) {
immUniform1f("dash_width", 6.0f);
@@ -834,6 +844,8 @@ static void draw_marker_areas(SpaceClip *sc,
0;
if ((track->search_flag & SELECT) == sel && (sc->flag & SC_SHOW_MARKER_SEARCH) && show_search) {
+ set_draw_marker_area_color(track, marker, act, track->search_flag & SELECT, col, scol);
+
imm_draw_box_wire_2d(shdr_pos,
marker->search_min[0],
marker->search_min[1],
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 87e88d094d7..cf7c3b51ae3 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -102,6 +102,16 @@ bool ED_space_clip_maskedit_poll(bContext *C)
return false;
}
+bool ED_space_clip_maskedit_visible_splines_poll(bContext *C)
+{
+ if (!ED_space_clip_maskedit_poll(C)) {
+ return false;
+ }
+
+ const SpaceClip *space_clip = CTX_wm_space_clip(C);
+ return space_clip->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE;
+}
+
bool ED_space_clip_maskedit_mask_poll(bContext *C)
{
if (ED_space_clip_maskedit_poll(C)) {
@@ -117,6 +127,16 @@ bool ED_space_clip_maskedit_mask_poll(bContext *C)
return false;
}
+bool ED_space_clip_maskedit_mask_visible_splines_poll(bContext *C)
+{
+ if (!ED_space_clip_maskedit_mask_poll(C)) {
+ return false;
+ }
+
+ const SpaceClip *space_clip = CTX_wm_space_clip(C);
+ return space_clip->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -272,7 +292,7 @@ bool ED_space_clip_get_position(struct SpaceClip *sc,
return true;
}
-bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, int mval[2], float r_col[3])
+bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, const int mval[2], float r_col[3])
{
ImBuf *ibuf;
float fx, fy, co[2];
diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h
index dd01c095479..7f9cf61b748 100644
--- a/source/blender/editors/space_clip/clip_intern.h
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -187,8 +187,10 @@ void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene);
/* tracking_ops.c */
-struct MovieTrackingTrack *tracking_marker_check_slide(
- struct bContext *C, const struct wmEvent *event, int *r_area, int *r_action, int *r_corner);
+/* Find track which can be slid in a proximity of the given event.
+ * Uses the same distance tolerance rule as the "Slide Marker" operator. */
+struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C,
+ const float co[2]);
void CLIP_OT_add_marker(struct wmOperatorType *ot);
void CLIP_OT_add_marker_at_click(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index e61c264ca06..f5bf850791a 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -777,7 +777,7 @@ void CLIP_OT_view_zoom_in(wmOperatorType *ot)
ot->poll = ED_space_clip_view_clip_poll;
/* flags */
- ot->flag |= OPTYPE_LOCK_BYPASS;
+ ot->flag = OPTYPE_LOCK_BYPASS;
/* properties */
prop = RNA_def_float_vector(ot->srna,
@@ -834,7 +834,7 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot)
ot->poll = ED_space_clip_view_clip_poll;
/* flags */
- ot->flag |= OPTYPE_LOCK_BYPASS;
+ ot->flag = OPTYPE_LOCK_BYPASS;
/* properties */
prop = RNA_def_float_vector(ot->srna,
@@ -883,7 +883,7 @@ void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
ot->poll = ED_space_clip_view_clip_poll;
/* flags */
- ot->flag |= OPTYPE_LOCK_BYPASS;
+ ot->flag = OPTYPE_LOCK_BYPASS;
/* properties */
RNA_def_float(ot->srna,
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 91fef23019c..a73883e7624 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -619,6 +619,44 @@ static void clip_dropboxes(void)
WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL);
}
+static bool clip_set_region_visible(const bContext *C,
+ ARegion *region,
+ const bool is_visible,
+ const short alignment,
+ const bool view_all_on_show)
+{
+ bool view_changed = false;
+
+ if (is_visible) {
+ if (region && (region->flag & RGN_FLAG_HIDDEN)) {
+ region->flag &= ~RGN_FLAG_HIDDEN;
+ region->v2d.flag &= ~V2D_IS_INIT;
+ if (view_all_on_show) {
+ region->v2d.cur = region->v2d.tot;
+ }
+ view_changed = true;
+ }
+ if (region && region->alignment != alignment) {
+ region->alignment = alignment;
+ view_changed = true;
+ }
+ }
+ else {
+ if (region && !(region->flag & RGN_FLAG_HIDDEN)) {
+ region->flag |= RGN_FLAG_HIDDEN;
+ region->v2d.flag &= ~V2D_IS_INIT;
+ WM_event_remove_handlers((bContext *)C, &region->handlers);
+ view_changed = true;
+ }
+ if (region && region->alignment != RGN_ALIGN_NONE) {
+ region->alignment = RGN_ALIGN_NONE;
+ view_changed = true;
+ }
+ }
+
+ return view_changed;
+}
+
static void clip_refresh(const bContext *C, ScrArea *area)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -662,127 +700,14 @@ static void clip_refresh(const bContext *C, ScrArea *area)
break;
}
- if (main_visible) {
- if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) {
- region_main->flag &= ~RGN_FLAG_HIDDEN;
- region_main->v2d.flag &= ~V2D_IS_INIT;
- view_changed = true;
- }
-
- if (region_main && region_main->alignment != RGN_ALIGN_NONE) {
- region_main->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
- else {
- if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) {
- region_main->flag |= RGN_FLAG_HIDDEN;
- region_main->v2d.flag &= ~V2D_IS_INIT;
- WM_event_remove_handlers((bContext *)C, &region_main->handlers);
- view_changed = true;
- }
- if (region_main && region_main->alignment != RGN_ALIGN_NONE) {
- region_main->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
-
- if (properties_visible) {
- if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) {
- region_properties->flag &= ~RGN_FLAG_HIDDEN;
- region_properties->v2d.flag &= ~V2D_IS_INIT;
- view_changed = true;
- }
- if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) {
- region_properties->alignment = RGN_ALIGN_RIGHT;
- view_changed = true;
- }
- }
- else {
- if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) {
- region_properties->flag |= RGN_FLAG_HIDDEN;
- region_properties->v2d.flag &= ~V2D_IS_INIT;
- WM_event_remove_handlers((bContext *)C, &region_properties->handlers);
- view_changed = true;
- }
- if (region_properties && region_properties->alignment != RGN_ALIGN_NONE) {
- region_properties->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
-
- if (tools_visible) {
- if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) {
- region_tools->flag &= ~RGN_FLAG_HIDDEN;
- region_tools->v2d.flag &= ~V2D_IS_INIT;
- view_changed = true;
- }
- if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) {
- region_tools->alignment = RGN_ALIGN_LEFT;
- view_changed = true;
- }
- }
- else {
- if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) {
- region_tools->flag |= RGN_FLAG_HIDDEN;
- region_tools->v2d.flag &= ~V2D_IS_INIT;
- WM_event_remove_handlers((bContext *)C, &region_tools->handlers);
- view_changed = true;
- }
- if (region_tools && region_tools->alignment != RGN_ALIGN_NONE) {
- region_tools->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
-
- if (preview_visible) {
- if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) {
- region_preview->flag &= ~RGN_FLAG_HIDDEN;
- region_preview->v2d.flag &= ~V2D_IS_INIT;
- region_preview->v2d.cur = region_preview->v2d.tot;
- view_changed = true;
- }
- if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) {
- region_preview->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
- else {
- if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) {
- region_preview->flag |= RGN_FLAG_HIDDEN;
- region_preview->v2d.flag &= ~V2D_IS_INIT;
- WM_event_remove_handlers((bContext *)C, &region_preview->handlers);
- view_changed = true;
- }
- if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) {
- region_preview->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
-
- if (channels_visible) {
- if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) {
- region_channels->flag &= ~RGN_FLAG_HIDDEN;
- region_channels->v2d.flag &= ~V2D_IS_INIT;
- view_changed = true;
- }
- if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) {
- region_channels->alignment = RGN_ALIGN_LEFT;
- view_changed = true;
- }
- }
- else {
- if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) {
- region_channels->flag |= RGN_FLAG_HIDDEN;
- region_channels->v2d.flag &= ~V2D_IS_INIT;
- WM_event_remove_handlers((bContext *)C, &region_channels->handlers);
- view_changed = true;
- }
- if (region_channels && region_channels->alignment != RGN_ALIGN_NONE) {
- region_channels->alignment = RGN_ALIGN_NONE;
- view_changed = true;
- }
- }
+ view_changed |= clip_set_region_visible(C, region_main, main_visible, RGN_ALIGN_NONE, false);
+ view_changed |= clip_set_region_visible(
+ C, region_properties, properties_visible, RGN_ALIGN_RIGHT, false);
+ view_changed |= clip_set_region_visible(C, region_tools, tools_visible, RGN_ALIGN_LEFT, false);
+ view_changed |= clip_set_region_visible(
+ C, region_preview, preview_visible, RGN_ALIGN_NONE, true);
+ view_changed |= clip_set_region_visible(
+ C, region_channels, channels_visible, RGN_ALIGN_LEFT, false);
if (view_changed) {
ED_area_init(wm, window, area);
@@ -935,6 +860,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *region)
sc->mask_info.draw_flag,
sc->mask_info.draw_type,
sc->mask_info.overlay_mode,
+ sc->mask_info.blend_factor,
mask_width,
mask_height,
aspx,
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index e7bdbfe7c68..ca224b04da5 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -336,35 +336,44 @@ void CLIP_OT_delete_marker(wmOperatorType *ot)
/** \name Slide Marker Operator
* \{ */
-enum {
- SLIDE_ACTION_POS = 0,
+typedef enum eSlideAction {
+ SLIDE_ACTION_NONE,
+
+ SLIDE_ACTION_POS,
SLIDE_ACTION_SIZE,
SLIDE_ACTION_OFFSET,
SLIDE_ACTION_TILT_SIZE,
-};
+} eSlideAction;
typedef struct {
- short area, action;
+ short area;
+ eSlideAction action;
MovieTrackingTrack *track;
MovieTrackingMarker *marker;
int mval[2];
int width, height;
- float *min, *max, *pos, *offset, (*corners)[2];
- float spos[2];
+ float *min, *max, *pos, (*corners)[2];
bool lock, accurate;
/* Data to restore on cancel. */
- float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2];
+ float old_search_min[2], old_search_max[2], old_pos[2];
float old_corners[4][2];
float (*old_markers)[2];
} SlideMarkerData;
-static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2])
+static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2])
+{
+ add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]);
+}
+
+static void slide_marker_tilt_slider(const float marker_pos[2],
+ const float pattern_corners[4][2],
+ float r_slider[2])
{
- add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]);
- add_v2_v2(r_slider, marker->pos);
+ slide_marker_tilt_slider_relative(pattern_corners, r_slider);
+ add_v2_v2(r_slider, marker_pos);
}
static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
@@ -373,7 +382,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
const wmEvent *event,
int area,
int corner,
- int action,
+ eSlideAction action,
int width,
int height)
{
@@ -389,29 +398,14 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
if (area == TRACK_AREA_POINT) {
data->pos = marker->pos;
- data->offset = track->offset;
}
else if (area == TRACK_AREA_PAT) {
- if (action == SLIDE_ACTION_SIZE) {
- data->corners = marker->pattern_corners;
- }
- else if (action == SLIDE_ACTION_OFFSET) {
- data->pos = marker->pos;
- data->offset = track->offset;
- data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr,
- "slide markers");
- for (int a = 0; a < track->markersnr; a++) {
- copy_v2_v2(data->old_markers[a], track->markers[a].pos);
- }
- }
- else if (action == SLIDE_ACTION_POS) {
+ if (action == SLIDE_ACTION_POS) {
data->corners = marker->pattern_corners;
data->pos = marker->pattern_corners[corner];
- copy_v2_v2(data->spos, data->pos);
}
else if (action == SLIDE_ACTION_TILT_SIZE) {
data->corners = marker->pattern_corners;
- slide_marker_tilt_slider(marker, data->spos);
}
}
else if (area == TRACK_AREA_SEARCH) {
@@ -434,7 +428,6 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
copy_v2_v2(data->old_search_min, marker->search_min);
copy_v2_v2(data->old_search_max, marker->search_max);
copy_v2_v2(data->old_pos, marker->pos);
- copy_v2_v2(data->old_offset, track->offset);
return data;
}
@@ -497,7 +490,7 @@ static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker,
int height)
{
float slider[2];
- slide_marker_tilt_slider(marker, slider);
+ slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider);
return mouse_to_slide_zone_distance_squared(co, slider, width, height);
}
@@ -534,117 +527,96 @@ static bool slide_check_corners(float (*corners)[2])
return true;
}
-MovieTrackingTrack *tracking_marker_check_slide(
- bContext *C, const wmEvent *event, int *r_area, int *r_action, int *r_corner)
+static MovieTrackingTrack *tracking_marker_check_slide(
+ bContext *C, const float co[2], int *r_area, eSlideAction *r_action, int *r_corner)
{
const float distance_clip_squared = 12.0f * 12.0f;
SpaceClip *sc = CTX_wm_space_clip(C);
- ARegion *region = CTX_wm_region(C);
-
MovieClip *clip = ED_space_clip_get_clip(sc);
- MovieTrackingTrack *track;
- int width, height;
- float co[2];
ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
- int framenr = ED_space_clip_get_clip_frame_number(sc);
+ const int framenr = ED_space_clip_get_clip_frame_number(sc);
float global_min_distance_squared = FLT_MAX;
- /* Sliding zone designator which is the closest to the mouse
- * across all the tracks.
- */
- int min_action = -1, min_area = 0, min_corner = -1;
+ /* Sliding zone designator which is the closest to the mouse across all the tracks. */
+ eSlideAction min_action;
+ int min_area = 0, min_corner = -1;
MovieTrackingTrack *min_track = NULL;
+ int width, height;
ED_space_clip_get_size(sc, &width, &height);
-
if (width == 0 || height == 0) {
return NULL;
}
- ED_clip_mouse_pos(sc, region, event->mval, co);
+ LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) {
+ if (!TRACK_VIEW_SELECTED(sc, track) || (track->flag & TRACK_LOCKED)) {
+ continue;
+ }
- track = tracksbase->first;
- while (track) {
- if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
- const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
- /* Sliding zone designator which is the closest to the mouse for
- * the current tracks.
- */
- float min_distance_squared = FLT_MAX;
- int action = -1, area = 0, corner = -1;
-
- if ((marker->flag & MARKER_DISABLED) == 0) {
- float distance_squared;
-
- /* We start checking with whether the mouse is close enough
- * to the pattern offset area.
- */
- distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height);
- area = TRACK_AREA_POINT;
- action = SLIDE_ACTION_POS;
+ const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
+ if (marker->flag & MARKER_DISABLED) {
+ continue;
+ }
- /* NOTE: All checks here are assuming there's no maximum distance
- * limit, so checks are quite simple here.
- * Actual distance clipping happens later once all the sliding
- * zones are checked.
- */
+ /* We start checking with whether the mouse is close enough to the pattern offset area. */
+ float distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height);
+
+ /* Sliding zone designator which is the closest to the mouse for the current tracks.
+ *
+ * NOTE: All checks here are assuming there's no maximum distance limit, so checks are quite
+ * simple here. Actual distance clipping happens later once all the sliding zones are checked.
+ */
+ float min_distance_squared = distance_squared;
+ int area = TRACK_AREA_POINT;
+ int action = SLIDE_ACTION_POS;
+ int corner = -1;
+
+ /* If search area is visible, check how close to its sliding zones mouse is. */
+ if (sc->flag & SC_SHOW_MARKER_SEARCH) {
+ distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
+ if (distance_squared < min_distance_squared) {
+ area = TRACK_AREA_SEARCH;
+ action = SLIDE_ACTION_OFFSET;
min_distance_squared = distance_squared;
+ }
- /* If search area is visible, check how close to its sliding
- * zones mouse is.
- */
- if (sc->flag & SC_SHOW_MARKER_SEARCH) {
- distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
- if (distance_squared < min_distance_squared) {
- area = TRACK_AREA_SEARCH;
- action = SLIDE_ACTION_OFFSET;
- min_distance_squared = distance_squared;
- }
-
- distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
- if (distance_squared < min_distance_squared) {
- area = TRACK_AREA_SEARCH;
- action = SLIDE_ACTION_SIZE;
- min_distance_squared = distance_squared;
- }
- }
-
- /* If pattern area is visible, check which corner is closest to
- * the mouse.
- */
- if (sc->flag & SC_SHOW_MARKER_PATTERN) {
- int current_corner = -1;
- distance_squared = mouse_to_closest_pattern_corner_distance_squared(
- marker, co, width, height, &current_corner);
- if (distance_squared < min_distance_squared) {
- area = TRACK_AREA_PAT;
- action = SLIDE_ACTION_POS;
- corner = current_corner;
- min_distance_squared = distance_squared;
- }
+ distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
+ if (distance_squared < min_distance_squared) {
+ area = TRACK_AREA_SEARCH;
+ action = SLIDE_ACTION_SIZE;
+ min_distance_squared = distance_squared;
+ }
+ }
- /* Here we also check whether the mouse is actually closer to
- * the widget which controls scale and tilt.
- */
- distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
- if (distance_squared < min_distance_squared) {
- area = TRACK_AREA_PAT;
- action = SLIDE_ACTION_TILT_SIZE;
- min_distance_squared = distance_squared;
- }
- }
+ /* If pattern area is visible, check which corner is closest to the mouse. */
+ if (sc->flag & SC_SHOW_MARKER_PATTERN) {
+ int current_corner = -1;
+ distance_squared = mouse_to_closest_pattern_corner_distance_squared(
+ marker, co, width, height, &current_corner);
+ if (distance_squared < min_distance_squared) {
+ area = TRACK_AREA_PAT;
+ action = SLIDE_ACTION_POS;
+ corner = current_corner;
+ min_distance_squared = distance_squared;
+ }
- if (min_distance_squared < global_min_distance_squared) {
- min_area = area;
- min_action = action;
- min_corner = corner;
- min_track = track;
- global_min_distance_squared = min_distance_squared;
- }
+ /* Here we also check whether the mouse is actually closer to the widget which controls scale
+ * and tilt. */
+ distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
+ if (distance_squared < min_distance_squared) {
+ area = TRACK_AREA_PAT;
+ action = SLIDE_ACTION_TILT_SIZE;
+ min_distance_squared = distance_squared;
}
}
- track = track->next;
+ if (min_distance_squared < global_min_distance_squared) {
+ min_area = area;
+ min_action = action;
+ min_corner = corner;
+ min_track = track;
+ global_min_distance_squared = min_distance_squared;
+ }
}
if (global_min_distance_squared < distance_clip_squared / sc->zoom) {
@@ -659,9 +631,16 @@ MovieTrackingTrack *tracking_marker_check_slide(
}
return min_track;
}
+
return NULL;
}
+struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C,
+ const float co[2])
+{
+ return tracking_marker_check_slide(C, co, NULL, NULL, NULL);
+}
+
static void *slide_marker_customdata(bContext *C, const wmEvent *event)
{
SpaceClip *sc = CTX_wm_space_clip(C);
@@ -672,7 +651,8 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event)
float co[2];
void *customdata = NULL;
int framenr = ED_space_clip_get_clip_frame_number(sc);
- int area, action, corner;
+ eSlideAction action;
+ int area, corner;
ED_space_clip_get_size(sc, &width, &height);
@@ -682,7 +662,7 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event)
ED_clip_mouse_pos(sc, region, event->mval, co);
- track = tracking_marker_check_slide(C, event, &area, &action, &corner);
+ track = tracking_marker_check_slide(C, co, &area, &action, &corner);
if (track != NULL) {
MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
customdata = create_slide_marker_data(
@@ -718,14 +698,12 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event
static void cancel_mouse_slide(SlideMarkerData *data)
{
- MovieTrackingTrack *track = data->track;
MovieTrackingMarker *marker = data->marker;
memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners));
copy_v2_v2(marker->search_min, data->old_search_min);
copy_v2_v2(marker->search_max, data->old_search_max);
copy_v2_v2(marker->pos, data->old_pos);
- copy_v2_v2(track->offset, data->old_offset);
if (data->old_markers != NULL) {
for (int a = 0; a < data->track->markersnr; a++) {
@@ -765,7 +743,6 @@ static void free_slide_data(SlideMarkerData *data)
static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceClip *sc = CTX_wm_space_clip(C);
- ARegion *region = CTX_wm_region(C);
SlideMarkerData *data = (SlideMarkerData *)op->customdata;
float dx, dy, mdelta[2];
@@ -804,110 +781,66 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (data->area == TRACK_AREA_POINT) {
- if (data->action == SLIDE_ACTION_OFFSET) {
- data->offset[0] = data->old_offset[0] + dx;
- data->offset[1] = data->old_offset[1] + dy;
- }
- else {
- data->pos[0] = data->old_pos[0] + dx;
- data->pos[1] = data->old_pos[1] + dy;
- }
+ data->pos[0] += dx;
+ data->pos[1] += dy;
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
DEG_id_tag_update(&sc->clip->id, 0);
}
else if (data->area == TRACK_AREA_PAT) {
- if (data->action == SLIDE_ACTION_SIZE) {
- float start[2], end[2];
- float scale;
-
- ED_clip_point_stable_pos(sc, region, data->mval[0], data->mval[1], &start[0], &start[1]);
-
- sub_v2_v2(start, data->old_pos);
-
- if (len_squared_v2(start) != 0.0f) {
- float mval[2];
-
- if (data->accurate) {
- mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
- mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
- }
- else {
- mval[0] = event->mval[0];
- mval[1] = event->mval[1];
- }
+ if (data->action == SLIDE_ACTION_POS) {
+ float prev_pos[2];
+ copy_v2_v2(prev_pos, data->pos);
- ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]);
-
- sub_v2_v2(end, data->old_pos);
- scale = len_v2(end) / len_v2(start);
-
- if (scale > 0.0f) {
- for (int a = 0; a < 4; a++) {
- mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
- }
- }
- }
-
- BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
- }
- else if (data->action == SLIDE_ACTION_OFFSET) {
- const float d[2] = {dx, dy};
- for (int a = 0; a < data->track->markersnr; a++) {
- add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d);
- }
- sub_v2_v2v2(data->offset, data->old_offset, d);
- }
- else if (data->action == SLIDE_ACTION_POS) {
- float spos[2];
-
- copy_v2_v2(spos, data->pos);
-
- data->pos[0] = data->spos[0] + dx;
- data->pos[1] = data->spos[1] + dy;
+ data->pos[0] += dx;
+ data->pos[1] += dy;
if (!slide_check_corners(data->corners)) {
- copy_v2_v2(data->pos, spos);
+ copy_v2_v2(data->pos, prev_pos);
}
- /* Currently only patterns are allowed to have such
- * combination of event and data.
- */
- BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
+ /* Allow pattern to be arbitrary size and resize search area if needed. */
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
else if (data->action == SLIDE_ACTION_TILT_SIZE) {
- const float mouse_delta[2] = {dx, dy};
-
- /* Vector which connects marker position with tilt/scale sliding area before sliding
- * began. */
- float start[2];
- sub_v2_v2v2(start, data->spos, data->old_pos);
- start[0] *= data->width;
- start[1] *= data->height;
-
- /* Vector which connects marker position with tilt/scale sliding area with the sliding
- * delta applied. */
- float end[2];
- add_v2_v2v2(end, data->spos, mouse_delta);
- sub_v2_v2(end, data->old_pos);
- end[0] *= data->width;
- end[1] *= data->height;
+ const float delta[2] = {dx, dy};
+
+ /* Slider position relative to the marker position using current state of pattern
+ * corners. */
+ float slider[2];
+ slide_marker_tilt_slider_relative(data->corners, slider);
+
+ /* Vector which connects marker position with the slider state at the current corners
+ * state.
+ * The coordinate is in the pixel space. */
+ float start_px[2];
+ copy_v2_v2(start_px, slider);
+ start_px[0] *= data->width;
+ start_px[1] *= data->height;
+
+ /* Vector which connects marker position with the slider state with the new mouse delta
+ * taken into account.
+ * The coordinate is in the pixel space. */
+ float end_px[2];
+ add_v2_v2v2(end_px, slider, delta);
+ end_px[0] *= data->width;
+ end_px[1] *= data->height;
float scale = 1.0f;
- if (len_squared_v2(start) != 0.0f) {
- scale = len_v2(end) / len_v2(start);
+ if (len_squared_v2(start_px) != 0.0f) {
+ scale = len_v2(end_px) / len_v2(start_px);
if (scale < 0.0f) {
scale = 0.0;
}
}
- const float angle = -angle_signed_v2v2(start, end);
+ const float angle = -angle_signed_v2v2(start_px, end_px);
for (int a = 0; a < 4; a++) {
float vec[2];
- mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
+ mul_v2_fl(data->corners[a], scale);
copy_v2_v2(vec, data->corners[a]);
vec[0] *= data->width;
@@ -917,30 +850,32 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height;
}
- BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
}
else if (data->area == TRACK_AREA_SEARCH) {
if (data->action == SLIDE_ACTION_SIZE) {
- data->min[0] = data->old_search_min[0] - dx;
- data->max[0] = data->old_search_max[0] + dx;
+ data->min[0] -= dx;
+ data->min[1] += dy;
- data->min[1] = data->old_search_min[1] + dy;
- data->max[1] = data->old_search_max[1] - dy;
+ data->max[0] += dx;
+ data->max[1] -= dy;
- BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
- }
- else if (data->area == TRACK_AREA_SEARCH) {
- const float d[2] = {dx, dy};
- add_v2_v2v2(data->min, data->old_search_min, d);
- add_v2_v2v2(data->max, data->old_search_max, d);
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
+ else if (data->action == SLIDE_ACTION_OFFSET) {
+ const float delta[2] = {dx, dy};
+ add_v2_v2(data->min, delta);
+ add_v2_v2(data->max, delta);
- BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
+ BKE_tracking_marker_clamp_search_position(data->marker);
+ }
}
data->marker->flag &= ~MARKER_TRACKED;
+ copy_v2_v2_int(data->mval, event->mval);
+
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
break;
@@ -1012,9 +947,9 @@ static int clear_track_path_exec(bContext *C, wmOperator *op)
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
- int action = RNA_enum_get(op->ptr, "action");
+ const eTrackClearAction action = RNA_enum_get(op->ptr, "action");
const bool clear_active = RNA_boolean_get(op->ptr, "clear_active");
- int framenr = ED_space_clip_get_clip_frame_number(sc);
+ const int framenr = ED_space_clip_get_clip_frame_number(sc);
if (clear_active) {
MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c
index 5f940b817a9..28e304bfa74 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -273,7 +273,18 @@ void ed_tracking_deselect_all_plane_tracks(ListBase *plane_tracks_base)
}
}
-static int mouse_select(bContext *C, const float co[2], const bool extend, const bool deselect_all)
+static bool select_poll(bContext *C)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ return sc->clip && sc->view == SC_VIEW_CLIP;
+ }
+
+ return false;
+}
+
+static int select_exec(bContext *C, wmOperator *op)
{
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
@@ -281,12 +292,35 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
- MovieTrackingTrack *track;
- MovieTrackingPlaneTrack *plane_track;
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
+
+ float co[2];
+ RNA_float_get_array(op->ptr, "location", co);
+
+ /* Special code which allows to slide a marker which belongs to currently selected but not yet
+ * active track. If such track is found activate it and return pass-though so that marker slide
+ * operator can be used immediately after.
+ * This logic makes it convenient to slide markers when left mouse selection is used. */
+ if (!extend) {
+ MovieTrackingTrack *track = tracking_find_slidable_track_in_proximity(C, co);
+ if (track != NULL) {
+ MovieClip *clip_iter = ED_space_clip_get_clip(sc);
+
+ clip_iter->tracking.act_track = track;
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ DEG_id_tag_update(&clip_iter->id, ID_RECALC_SELECT);
+
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
float distance_to_track, distance_to_plane_track;
- track = find_nearest_track(sc, tracksbase, co, &distance_to_track);
- plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track);
+ MovieTrackingTrack *track = find_nearest_track(sc, tracksbase, co, &distance_to_track);
+ MovieTrackingPlaneTrack *plane_track = find_nearest_plane_track(
+ sc, plane_tracks_base, co, &distance_to_plane_track);
ClipViewLockState lock_state;
ED_clip_view_lock_state_store(C, &lock_state);
@@ -360,8 +394,6 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const
else if (deselect_all) {
ed_tracking_deselect_all_tracks(tracksbase);
ed_tracking_deselect_all_plane_tracks(plane_tracks_base);
- /* Mask as well if we are in combined mask / track view. */
- ED_mask_deselect_all(C);
}
ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
@@ -375,51 +407,12 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
-static bool select_poll(bContext *C)
-{
- SpaceClip *sc = CTX_wm_space_clip(C);
-
- if (sc) {
- return sc->clip && sc->view == SC_VIEW_CLIP;
- }
-
- return false;
-}
-
-static int select_exec(bContext *C, wmOperator *op)
-{
- float co[2];
-
- RNA_float_get_array(op->ptr, "location", co);
- const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
-
- return mouse_select(C, co, extend, deselect_all);
-}
-
static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceClip *sc = CTX_wm_space_clip(C);
ARegion *region = CTX_wm_region(C);
float co[2];
- const bool extend = RNA_boolean_get(op->ptr, "extend");
-
- if (!extend) {
- MovieTrackingTrack *track = tracking_marker_check_slide(C, event, NULL, NULL, NULL);
-
- if (track) {
- MovieClip *clip = ED_space_clip_get_clip(sc);
-
- clip->tracking.act_track = track;
-
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
- DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
-
- return OPERATOR_PASS_THROUGH;
- }
- }
-
ED_clip_mouse_pos(sc, region, event->mval, co);
RNA_float_set_array(op->ptr, "location", co);
@@ -835,6 +828,7 @@ void CLIP_OT_select_circle(wmOperatorType *ot)
ot->modal = WM_gesture_circle_modal;
ot->exec = circle_select_exec;
ot->poll = ED_space_clip_tracking_poll;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
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 e5a4919f548..87595ecdb88 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -407,8 +407,15 @@ std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &dr
std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") :
TIP_("Move asset to catalog");
- return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() +
- ")";
+ basic_tip += ": " + catalog_item_.get_name();
+
+ /* Display the full catalog path, but only if it's not exactly the same as the already shown name
+ * (i.e. not a root level catalog with no parent). */
+ if (catalog_item_.get_name() != catalog_item_.catalog_path().str()) {
+ basic_tip += " (" + catalog_item_.catalog_path().str() + ")";
+ }
+
+ return basic_tip;
}
bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag)
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 1ee6445f4ba..655a7983e2b 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -83,6 +83,7 @@ void FILE_OT_rename(struct wmOperatorType *ot);
void FILE_OT_smoothscroll(struct wmOperatorType *ot);
void FILE_OT_filepath_drop(struct wmOperatorType *ot);
void FILE_OT_start_filter(struct wmOperatorType *ot);
+void FILE_OT_edit_directory_path(struct wmOperatorType *ot);
void FILE_OT_view_selected(struct wmOperatorType *ot);
void file_directory_enter_handle(bContext *C, void *arg_unused, void *arg_but);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 578288ca289..62bdd583bc1 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -2914,9 +2914,9 @@ void FILE_OT_delete(struct wmOperatorType *ot)
static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op))
{
- ScrArea *area = CTX_wm_area(C);
- SpaceFile *sfile = CTX_wm_space_file(C);
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ const ScrArea *area = CTX_wm_area(C);
+ const SpaceFile *sfile = CTX_wm_space_file(C);
+ const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
ARegion *region_ctx = CTX_wm_region(C);
@@ -2950,6 +2950,46 @@ void FILE_OT_start_filter(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Edit Directory Path Operator
+ * \{ */
+
+static int file_edit_directory_path_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ const ScrArea *area = CTX_wm_area(C);
+ const SpaceFile *sfile = CTX_wm_space_file(C);
+ const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ ARegion *region_ctx = CTX_wm_region(C);
+
+ if (area) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ CTX_wm_region_set(C, region);
+ if (UI_textbutton_activate_rna(C, region, params, "directory")) {
+ break;
+ }
+ }
+ }
+
+ CTX_wm_region_set(C, region_ctx);
+
+ return OPERATOR_FINISHED;
+}
+
+void FILE_OT_edit_directory_path(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Edit Directory Path";
+ ot->description = "Start editing directory field";
+ ot->idname = "FILE_OT_edit_directory_path";
+
+ /* api callbacks */
+ ot->exec = file_edit_directory_path_exec;
+ ot->poll = ED_operator_file_active;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Macro Operators
* \{ */
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 9f71d6f77c7..183af0c14f5 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -336,7 +336,7 @@ enum {
};
typedef struct FileListEntryPreview {
- char path[FILE_MAX];
+ char filepath[FILE_MAX];
uint flags;
int index;
int attributes; /* from FileDirEntry. */
@@ -1636,13 +1636,13 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
source = THB_SOURCE_FONT;
}
- IMB_thumb_path_lock(preview->path);
+ IMB_thumb_path_lock(preview->filepath);
/* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
* in case user switch to a bigger preview size. Do not create preview when file is offline. */
ImBuf *imbuf = (preview->attributes & FILE_ATTR_OFFLINE) ?
- IMB_thumb_read(preview->path, THB_LARGE) :
- IMB_thumb_manage(preview->path, THB_LARGE, source);
- IMB_thumb_path_unlock(preview->path);
+ IMB_thumb_read(preview->filepath, THB_LARGE) :
+ IMB_thumb_manage(preview->filepath, THB_LARGE, source);
+ IMB_thumb_path_unlock(preview->filepath);
if (imbuf) {
preview->icon_id = BKE_icon_imbuf_create(imbuf);
}
@@ -1753,7 +1753,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
if (preview_in_memory) {
/* TODO(mano-wii): No need to use the thread API here. */
BLI_assert(BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW));
- preview->path[0] = '\0';
+ preview->filepath[0] = '\0';
ImBuf *imbuf = BKE_previewimg_to_imbuf(preview_in_memory, ICON_SIZE_PREVIEW);
if (imbuf) {
preview->icon_id = BKE_icon_imbuf_create(imbuf);
@@ -1762,13 +1762,13 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
}
else {
if (entry->redirection_path) {
- BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
+ BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR);
}
else {
BLI_join_dirfile(
- preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
+ preview->filepath, sizeof(preview->filepath), filelist->filelist.root, entry->relpath);
}
- // printf("%s: %d - %s\n", __func__, preview->index, preview->path);
+ // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath);
FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
__func__);
@@ -2667,7 +2667,7 @@ bool filelist_cache_previews_update(FileList *filelist)
* we do not want to cache it again here. */
entry = filelist_file_ex(filelist, preview->index, false);
- // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->filepath, preview->img);
if (entry) {
if (preview->icon_id) {
@@ -2802,7 +2802,8 @@ int ED_path_extension_type(const char *path)
if (BLI_path_extension_check(path, ".zip")) {
return FILE_TYPE_ARCHIVE;
}
- if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) {
+ if (BLI_path_extension_check_n(
+ path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", ".stl", NULL)) {
return FILE_TYPE_OBJECT_IO;
}
if (BLI_path_extension_check_array(path, imb_ext_image)) {
@@ -3074,7 +3075,7 @@ static int filelist_readjob_list_dir(const char *root,
}
target = entry->redirection_path;
#ifdef WIN32
- /* On Windows don't show ".lnk" extension for valid shortcuts. */
+ /* On Windows don't show `.lnk` extension for valid shortcuts. */
BLI_path_extension_replace(entry->relpath, FILE_MAXDIR, "");
#endif
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 011506368ee..e42e1e98660 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -983,6 +983,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
if (params->display == FILE_IMGDISPLAY) {
const float pad_fac = compact ? 0.15f : 0.3f;
+ /* Matches UI_preview_tile_size_x()/_y() by default. */
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 = pad_fac * UI_UNIT_X;
@@ -1009,6 +1010,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
else if (params->display == FILE_VERTICALDISPLAY) {
int rowcount;
+ /* Matches UI_preview_tile_size_x()/_y() by default. */
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.4f * UI_UNIT_X;
@@ -1030,6 +1032,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
layout->flag = FILE_LAYOUT_VER;
}
else if (params->display == FILE_HORIZONTALDISPLAY) {
+ /* Matches UI_preview_tile_size_x()/_y() by default. */
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.4f * UI_UNIT_X;
@@ -1364,3 +1367,21 @@ ScrArea *ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator *
return NULL;
}
+
+ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win)
+{
+ const bScreen *screen = WM_window_get_active_screen(win);
+
+ ED_screen_areas_iter (win, screen, area) {
+ if (area->spacetype != SPACE_FILE) {
+ continue;
+ }
+
+ const SpaceFile *sfile = area->spacedata.first;
+ if (sfile->op) {
+ return area;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 65354591034..310c688383b 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -519,13 +519,13 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx
}
}
-void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
+void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath)
{
FSMenuEntry *fsm_iter = NULL;
char fsm_name[FILE_MAX];
int nwritten = 0;
- FILE *fp = BLI_fopen(filename, "w");
+ FILE *fp = BLI_fopen(filepath, "w");
if (!fp) {
return;
}
@@ -556,14 +556,14 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
fclose(fp);
}
-void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename)
+void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath)
{
char line[FILE_MAXDIR];
char name[FILE_MAXFILE];
FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
FILE *fp;
- fp = BLI_fopen(filename, "r");
+ fp = BLI_fopen(filepath, "r");
if (!fp) {
return;
}
@@ -890,7 +890,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
continue;
}
- /* Exclude "all my files" as it makes no sense in blender fileselector */
+ /* Exclude "all my files" as it makes no sense in blender file-selector. */
/* Exclude "airdrop" if wlan not active as it would show "" ) */
if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) {
fsmenu_insert_entry(
diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h
index 861c37ecaa1..6e980a326fc 100644
--- a/source/blender/editors/space_file/fsmenu.h
+++ b/source/blender/editors/space_file/fsmenu.h
@@ -38,10 +38,10 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i
void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx);
/** saves the 'bookmarks' to the specified file */
-void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename);
+void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath);
/** reads the 'bookmarks' from the specified file */
-void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename);
+void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath);
/** adds system specific directories */
void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 0170361f244..a462476aae0 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -695,6 +695,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_smoothscroll);
WM_operatortype_append(FILE_OT_filepath_drop);
WM_operatortype_append(FILE_OT_start_filter);
+ WM_operatortype_append(FILE_OT_edit_directory_path);
WM_operatortype_append(FILE_OT_view_selected);
}
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 39b980ac4c3..e71c5114b0a 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -1080,6 +1080,7 @@ void GRAPH_OT_select_circle(wmOperatorType *ot)
ot->exec = graph_circle_select_exec;
ot->poll = graphop_visible_keyframes_poll;
ot->cancel = WM_gesture_circle_cancel;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index c385420b18e..39fb41245bf 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -61,7 +61,7 @@ if(WITH_IMAGE_CINEON)
endif()
if(WITH_IMAGE_WEBP)
- add_definitions(-DWITH_WEBP)
+ add_definitions(-DWITH_WEBP)
endif()
blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 208928afc1f..d0c21f85472 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -845,7 +845,10 @@ void uiTemplateImage(uiLayout *layout,
row = uiLayoutRow(row, true);
uiLayoutSetEnabled(row, is_packed == false);
- uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE);
+
+ prop = RNA_struct_find_property(&imaptr, "filepath");
+ uiDefAutoButR(block, &imaptr, prop, -1, "", ICON_NONE, 0, 0, 200, UI_UNIT_Y);
+ uiItemO(row, "", ICON_FILEBROWSER, "image.file_browse");
uiItemO(row, "", ICON_FILE_REFRESH, "image.reload");
}
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index e851b99d3ba..950acd77f6a 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -483,6 +483,16 @@ bool ED_space_image_maskedit_poll(bContext *C)
return false;
}
+bool ED_space_image_maskedit_visible_splines_poll(bContext *C)
+{
+ if (!ED_space_image_maskedit_poll(C)) {
+ return false;
+ }
+
+ const SpaceImage *space_image = CTX_wm_space_image(C);
+ return space_image->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE;
+}
+
bool ED_space_image_paint_curve(const bContext *C)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -508,6 +518,16 @@ bool ED_space_image_maskedit_mask_poll(bContext *C)
return false;
}
+bool ED_space_image_maskedit_mask_visible_splines_poll(bContext *C)
+{
+ if (!ED_space_image_maskedit_mask_poll(C)) {
+ return false;
+ }
+
+ const SpaceImage *space_image = CTX_wm_space_image(C);
+ return space_image->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE;
+}
+
bool ED_space_image_cursor_poll(bContext *C)
{
return ED_operator_uvedit_space_image(C) || ED_space_image_maskedit_poll(C) ||
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index 2322420069e..364bec1377d 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -49,6 +49,7 @@ void IMAGE_OT_new(struct wmOperatorType *ot);
* Called by other space types too.
*/
void IMAGE_OT_open(struct wmOperatorType *ot);
+void IMAGE_OT_file_browse(struct wmOperatorType *ot);
/**
* Called by other space types too.
*/
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index aa77aab2283..537132ac428 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -197,6 +197,20 @@ static ImageUser *image_user_from_context(const bContext *C)
return (sima) ? &sima->iuser : NULL;
}
+static ImageUser image_user_from_active_tile(Image *ima)
+{
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
+
+ /* Use the file associated with the active tile. Otherwise use the first tile. */
+ if (ima && ima->source == IMA_SRC_TILED) {
+ const ImageTile *active = (ImageTile *)BLI_findlink(&ima->tiles, ima->active_tile_index);
+ iuser.tile = active ? active->tile_number : ((ImageTile *)ima->tiles.first)->tile_number;
+ }
+
+ return iuser;
+}
+
static bool image_from_context_has_data_poll(bContext *C)
{
Image *ima = image_from_context(C);
@@ -214,13 +228,14 @@ static bool image_from_context_has_data_poll(bContext *C)
}
/**
- * Use this when the image buffer is accessed without the image user.
+ * Use this when the image buffer is accessing the active tile without the image user.
*/
-static bool image_from_context_has_data_poll_no_image_user(bContext *C)
+static bool image_from_context_has_data_poll_active_tile(bContext *C)
{
Image *ima = image_from_context(C);
+ ImageUser iuser = image_user_from_active_tile(ima);
- return BKE_image_has_ibuf(ima, NULL);
+ return BKE_image_has_ibuf(ima, &iuser);
}
static bool image_not_packed_poll(bContext *C)
@@ -949,7 +964,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
static bool image_view_selected_poll(bContext *C)
{
- return (space_image_main_region_poll(C) && (ED_operator_uvedit(C) || ED_operator_mask(C)));
+ return (space_image_main_region_poll(C) && (ED_operator_uvedit(C) || ED_maskedit_poll(C)));
}
void IMAGE_OT_view_selected(wmOperatorType *ot)
@@ -1274,8 +1289,8 @@ static Image *image_open_single(Main *bmain,
BKE_image_free_views(ima);
}
- if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) {
- if (range->udim_tiles.first) {
+ if (ima->source == IMA_SRC_FILE) {
+ if (range->udims_detected && range->udim_tiles.first) {
ima->source = IMA_SRC_TILED;
ImageTile *first_tile = ima->tiles.first;
first_tile->tile_number = range->offset;
@@ -1283,7 +1298,7 @@ static Image *image_open_single(Main *bmain,
BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL);
}
}
- else {
+ else if (range->length > 1) {
ima->source = IMA_SRC_SEQUENCE;
}
}
@@ -1446,7 +1461,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
image_open_init(C, op);
- /* show multiview save options only if scene has multiviews */
+ /* Show multi-view save options only if scene has multi-views. */
PropertyRNA *prop;
prop = RNA_struct_find_property(op->ptr, "show_multiview");
RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0);
@@ -1535,6 +1550,115 @@ void IMAGE_OT_open(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Browse Image Operator
+ * \{ */
+
+static int image_file_browse_exec(bContext *C, wmOperator *op)
+{
+ Image *ima = op->customdata;
+ if (ima == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ char filepath[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filepath);
+
+ /* If loading into a tiled texture, ensure that the filename is tokenized. */
+ if (ima->source == IMA_SRC_TILED) {
+ char *filename = (char *)BLI_path_basename(filepath);
+ BKE_image_ensure_tile_token(filename);
+ }
+
+ PointerRNA imaptr;
+ PropertyRNA *imaprop;
+ RNA_id_pointer_create(&ima->id, &imaptr);
+ imaprop = RNA_struct_find_property(&imaptr, "filepath");
+
+ RNA_property_string_set(&imaptr, imaprop, filepath);
+ RNA_property_update(C, &imaptr, imaprop);
+
+ return OPERATOR_FINISHED;
+}
+
+static int image_file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Image *ima = image_from_context(C);
+ if (!ima) {
+ return OPERATOR_CANCELLED;
+ }
+
+ char filepath[FILE_MAX];
+ BLI_strncpy(filepath, ima->filepath, sizeof(filepath));
+
+ /* Shift+Click to open the file, Alt+Click to browse a folder in the OS's browser. */
+ if (event->modifier & (KM_SHIFT | KM_ALT)) {
+ wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true);
+ PointerRNA props_ptr;
+
+ if (event->modifier & KM_ALT) {
+ char *lslash = (char *)BLI_path_slash_rfind(filepath);
+ if (lslash) {
+ *lslash = '\0';
+ }
+ }
+ else if (ima->source == IMA_SRC_TILED) {
+ ImageUser iuser = image_user_from_active_tile(ima);
+ BKE_image_user_file_path(&iuser, ima, filepath);
+ }
+
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_string_set(&props_ptr, "filepath", filepath);
+ WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr, NULL);
+ WM_operator_properties_free(&props_ptr);
+
+ return OPERATOR_CANCELLED;
+ }
+
+ /* The image is typically passed to the operator via layout/button context (e.g.
+ * #uiLayoutSetContextPointer()). The File Browser doesn't support restoring this context
+ * when calling `exec()` though, so we have to pass it the image via custom data. */
+ op->customdata = ima;
+
+ image_filesel(C, op, filepath);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static bool image_file_browse_poll(bContext *C)
+{
+ return image_from_context(C) != NULL;
+}
+
+void IMAGE_OT_file_browse(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Browse Image";
+ ot->description =
+ "Open an image file browser, hold Shift to open the file, Alt to browse containing "
+ "directory";
+ ot->idname = "IMAGE_OT_file_browse";
+
+ /* api callbacks */
+ ot->exec = image_file_browse_exec;
+ ot->invoke = image_file_browse_invoke;
+ ot->poll = image_file_browse_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
+ FILE_SPECIAL,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_DEFAULT);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Match Movie Length Operator
* \{ */
@@ -1680,184 +1804,27 @@ void IMAGE_OT_replace(wmOperatorType *ot)
typedef struct ImageSaveData {
ImageUser *iuser;
Image *image;
- ImageFormatData im_format;
+ ImageSaveOptions opts;
} ImageSaveData;
-static char imtype_best_depth(ImBuf *ibuf, const char imtype)
-{
- const char depth_ok = BKE_imtype_valid_depths(imtype);
-
- if (ibuf->rect_float) {
- if (depth_ok & R_IMF_CHAN_DEPTH_32) {
- return R_IMF_CHAN_DEPTH_32;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_24) {
- return R_IMF_CHAN_DEPTH_24;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_16) {
- return R_IMF_CHAN_DEPTH_16;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_12) {
- return R_IMF_CHAN_DEPTH_12;
- }
- return R_IMF_CHAN_DEPTH_8;
- }
-
- if (depth_ok & R_IMF_CHAN_DEPTH_8) {
- return R_IMF_CHAN_DEPTH_8;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_12) {
- return R_IMF_CHAN_DEPTH_12;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_16) {
- return R_IMF_CHAN_DEPTH_16;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_24) {
- return R_IMF_CHAN_DEPTH_24;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_32) {
- return R_IMF_CHAN_DEPTH_32;
- }
- return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
-}
-
-static int image_save_options_init(Main *bmain,
- ImageSaveOptions *opts,
- Image *ima,
- ImageUser *iuser,
- const bool guess_path,
- const bool save_as_render)
+static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOperator *op)
{
- void *lock;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
-
- if (ibuf) {
- Scene *scene = opts->scene;
- bool is_depth_set = false;
-
- if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- /* imtype */
- BKE_image_format_init_for_write(&opts->im_format, scene, NULL);
- is_depth_set = true;
- if (!BKE_image_is_multiview(ima)) {
- /* In case multiview is disabled,
- * render settings would be invalid for render result in this area. */
- opts->im_format.stereo3d_format = *ima->stereo3d_format;
- opts->im_format.views_format = ima->views_format;
- }
- }
- else {
- if (ima->source == IMA_SRC_GENERATED) {
- opts->im_format.imtype = R_IMF_IMTYPE_PNG;
- opts->im_format.compress = ibuf->foptions.quality;
- opts->im_format.planes = ibuf->planes;
- }
- else {
- BKE_image_format_from_imbuf(&opts->im_format, ibuf);
- }
-
- /* use the multiview image settings as the default */
- opts->im_format.stereo3d_format = *ima->stereo3d_format;
- opts->im_format.views_format = ima->views_format;
-
- BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene);
- }
-
- opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
-
- if (ima->source == IMA_SRC_TILED) {
- BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath));
- BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
- }
- else {
- BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath));
- }
-
- /* sanitize all settings */
-
- /* unlikely but just in case */
- if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) {
- opts->im_format.planes = R_IMF_PLANES_RGBA;
- }
-
- /* depth, account for float buffer and format support */
- if (is_depth_set == false) {
- opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype);
- }
-
- /* some formats don't use quality so fallback to scenes quality */
- if (opts->im_format.quality == 0) {
- opts->im_format.quality = scene->r.im_format.quality;
- }
-
- /* check for empty path */
- if (guess_path && opts->filepath[0] == 0) {
- const bool is_prev_save = !STREQ(G.ima, "//");
- if (save_as_render) {
- if (is_prev_save) {
- BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath));
- }
- else {
- BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath));
- BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain));
- }
- }
- else {
- BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2);
- BLI_path_make_safe(opts->filepath);
- BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
- }
-
- /* append UDIM marker if not present */
- if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == NULL) {
- int len = strlen(opts->filepath);
- STR_CONCAT(opts->filepath, len, ".<UDIM>");
- }
- }
- }
-
- BKE_image_release_ibuf(ima, ibuf, lock);
-
- return (ibuf != NULL);
-}
-
-static void image_save_options_from_op(Main *bmain,
- ImageSaveOptions *opts,
- wmOperator *op,
- ImageFormatData *imf)
-{
- if (imf) {
- BKE_image_format_free(&opts->im_format);
- BKE_image_format_copy(&opts->im_format, imf);
- }
-
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
RNA_string_get(op->ptr, "filepath", opts->filepath);
BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain));
}
-}
-
-static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op)
-{
- if (op->customdata) {
- ImageSaveData *isd = op->customdata;
- BKE_image_format_free(&isd->im_format);
- BKE_image_format_copy(&isd->im_format, &opts->im_format);
- }
- RNA_string_set(op->ptr, "filepath", opts->filepath);
-}
-
-static bool save_image_op(
- Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts)
-{
opts->relative = (RNA_struct_find_property(op->ptr, "relative_path") &&
RNA_boolean_get(op->ptr, "relative_path"));
opts->save_copy = (RNA_struct_find_property(op->ptr, "copy") &&
RNA_boolean_get(op->ptr, "copy"));
opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") &&
RNA_boolean_get(op->ptr, "save_as_render"));
+}
+static bool save_image_op(
+ Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts)
+{
WM_cursor_wait(true);
bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts);
@@ -1872,11 +1839,56 @@ static bool save_image_op(
return ok;
}
+static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Image *image = image_from_context(C);
+ ImageUser *iuser = image_user_from_context(C);
+ Scene *scene = CTX_data_scene(C);
+
+ ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__);
+ isd->image = image;
+ isd->iuser = iuser;
+
+ if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true, false)) {
+ BKE_image_save_options_free(&isd->opts);
+ MEM_freeN(isd);
+ return NULL;
+ }
+
+ isd->opts.do_newpath = true;
+
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ RNA_string_set(op->ptr, "filepath", isd->opts.filepath);
+ }
+
+ /* Enable save_copy by default for render results. */
+ if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) &&
+ !RNA_struct_property_is_set(op->ptr, "copy")) {
+ RNA_boolean_set(op->ptr, "copy", true);
+ }
+
+ if (!RNA_struct_property_is_set(op->ptr, "save_as_render")) {
+ RNA_boolean_set(op->ptr, "save_as_render", isd->opts.save_as_render);
+ }
+
+ /* Show multiview save options only if image has multiviews. */
+ PropertyRNA *prop;
+ prop = RNA_struct_find_property(op->ptr, "show_multiview");
+ RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image));
+ prop = RNA_struct_find_property(op->ptr, "use_multiview");
+ RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image));
+
+ op->customdata = isd;
+
+ return isd;
+}
+
static void image_save_as_free(wmOperator *op)
{
if (op->customdata) {
ImageSaveData *isd = op->customdata;
- BKE_image_format_free(&isd->im_format);
+ BKE_image_save_options_free(&isd->opts);
MEM_freeN(op->customdata);
op->customdata = NULL;
@@ -1886,96 +1898,55 @@ static void image_save_as_free(wmOperator *op)
static int image_save_as_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- ImageSaveOptions opts;
+ ImageSaveData *isd;
- Image *image = NULL;
- ImageUser *iuser = NULL;
- ImageFormatData *imf = NULL;
if (op->customdata) {
- ImageSaveData *isd = op->customdata;
- image = isd->image;
- iuser = isd->iuser;
- imf = &isd->im_format;
+ isd = op->customdata;
}
else {
- image = image_from_context(C);
- iuser = image_user_from_context(C);
+ isd = image_save_as_init(C, op);
+ if (isd == NULL) {
+ return OPERATOR_CANCELLED;
+ }
}
- BKE_image_save_options_init(&opts, bmain, scene);
-
- /* just in case to initialize values,
- * these should be set on invoke or by the caller. */
- image_save_options_init(bmain, &opts, image, iuser, false, false);
-
- image_save_options_from_op(bmain, &opts, op, imf);
- opts.do_newpath = true;
+ image_save_options_from_op(bmain, &isd->opts, op);
+ BKE_image_save_options_update(&isd->opts, isd->image);
- save_image_op(bmain, image, iuser, op, &opts);
+ save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts);
- if (opts.save_copy == false) {
- BKE_image_free_packedfiles(image);
+ if (isd->opts.save_copy == false) {
+ BKE_image_free_packedfiles(isd->image);
}
- BKE_image_save_options_free(&opts);
-
image_save_as_free(op);
return OPERATOR_FINISHED;
}
-static bool image_save_as_check(bContext *UNUSED(C), wmOperator *op)
+static bool image_save_as_check(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
ImageSaveData *isd = op->customdata;
- return WM_operator_filesel_ensure_ext_imtype(op, &isd->im_format);
+
+ image_save_options_from_op(bmain, &isd->opts, op);
+ BKE_image_save_options_update(&isd->opts, isd->image);
+
+ return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format);
}
static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- Main *bmain = CTX_data_main(C);
- Image *ima = image_from_context(C);
- ImageUser *iuser = image_user_from_context(C);
- Scene *scene = CTX_data_scene(C);
- ImageSaveOptions opts;
- PropertyRNA *prop;
- const bool save_as_render = (ima->source == IMA_SRC_VIEWER);
-
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return image_save_as_exec(C, op);
}
- BKE_image_save_options_init(&opts, bmain, scene);
-
- if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) {
- BKE_image_save_options_free(&opts);
+ ImageSaveData *isd = image_save_as_init(C, op);
+ if (isd == NULL) {
return OPERATOR_CANCELLED;
}
- image_save_options_to_op(&opts, op);
- /* enable save_copy by default for render results */
- if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) &&
- !RNA_struct_property_is_set(op->ptr, "copy")) {
- RNA_boolean_set(op->ptr, "copy", true);
- }
-
- RNA_boolean_set(op->ptr, "save_as_render", save_as_render);
-
- ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__);
- isd->image = ima;
- isd->iuser = iuser;
-
- BKE_image_format_copy(&isd->im_format, &opts.im_format);
- op->customdata = isd;
-
- /* show multiview save options only if image has multiviews */
- prop = RNA_struct_find_property(op->ptr, "show_multiview");
- RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima));
- prop = RNA_struct_find_property(op->ptr, "use_multiview");
- RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima));
-
- image_filesel(C, op, opts.filepath);
- BKE_image_save_options_free(&opts);
+ image_filesel(C, op, isd->opts.filepath);
return OPERATOR_RUNNING_MODAL;
}
@@ -2003,7 +1974,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
ImageSaveData *isd = op->customdata;
PointerRNA imf_ptr;
const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
- const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render");
+ const bool save_as_render = RNA_boolean_get(op->ptr, "save_as_render");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -2015,8 +1986,15 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
uiItemS(layout);
/* image template */
- RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr);
- uiTemplateImageSettings(layout, &imf_ptr, use_color_management);
+ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->opts.im_format, &imf_ptr);
+ uiTemplateImageSettings(layout, &imf_ptr, save_as_render);
+
+ if (!save_as_render) {
+ PointerRNA linear_settings_ptr = RNA_pointer_get(&imf_ptr, "linear_colorspace_settings");
+ uiLayout *col = uiLayoutColumn(layout, true);
+ uiItemS(col);
+ uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE);
+ }
/* multiview template */
if (is_multiview) {
@@ -2062,16 +2040,19 @@ void IMAGE_OT_save_as(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna,
- "save_as_render",
- 0,
- "Save As Render",
- "Apply render part of display transform when saving byte image");
- RNA_def_boolean(ot->srna,
- "copy",
- 0,
- "Copy",
- "Create a new image file without modifying the current image in blender");
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna,
+ "save_as_render",
+ 0,
+ "Save As Render",
+ "Apply render part of display transform when saving byte image");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "copy",
+ 0,
+ "Copy",
+ "Create a new image file without modifying the current image in blender");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
image_operator_prop_allow_tokens(ot);
WM_operator_properties_filesel(ot,
@@ -2138,12 +2119,11 @@ static int image_save_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- BKE_image_save_options_init(&opts, bmain, scene);
- if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) {
+ if (!BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false, false)) {
BKE_image_save_options_free(&opts);
return OPERATOR_CANCELLED;
}
- image_save_options_from_op(bmain, &opts, op, NULL);
+ image_save_options_from_op(bmain, &opts, op);
/* Check if file write permission is ok. */
if (BLI_exists(opts.filepath) && !BLI_file_is_writable(opts.filepath)) {
@@ -2316,6 +2296,14 @@ static bool image_has_valid_path(Image *ima)
return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/');
}
+static bool image_should_pack_during_save_all(const Image *ima)
+{
+ /* Images without a filepath (implied with IMA_SRC_GENERATED) should
+ * be packed during a save_all operation. */
+ return (ima->source == IMA_SRC_GENERATED) ||
+ (ima->source == IMA_SRC_TILED && !BKE_image_has_filepath(ima));
+}
+
bool ED_image_should_save_modified(const Main *bmain)
{
ReportList reports;
@@ -2339,7 +2327,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports)
bool is_format_writable;
if (image_should_be_saved(ima, &is_format_writable)) {
- if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) {
+ if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) {
if (!ID_IS_LINKED(ima)) {
num_saveable_images++;
}
@@ -2396,15 +2384,14 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports)
bool is_format_writable;
if (image_should_be_saved(ima, &is_format_writable)) {
- if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) {
+ if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) {
BKE_image_memorypack(ima);
}
else if (is_format_writable) {
if (image_has_valid_path(ima)) {
ImageSaveOptions opts;
Scene *scene = CTX_data_scene(C);
- BKE_image_save_options_init(&opts, bmain, scene);
- if (image_save_options_init(bmain, &opts, ima, NULL, false, false)) {
+ if (!BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false, false)) {
bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts);
ok = ok && saved_successfully;
}
@@ -2711,7 +2698,8 @@ void IMAGE_OT_new(wmOperatorType *ot)
static int image_flip_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ImageUser iuser = image_user_from_active_tile(ima);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
SpaceImage *sima = CTX_wm_space_image(C);
const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
@@ -2808,7 +2796,7 @@ void IMAGE_OT_flip(wmOperatorType *ot)
/* api callbacks */
ot->exec = image_flip_exec;
- ot->poll = image_from_context_has_data_poll_no_image_user;
+ ot->poll = image_from_context_has_data_poll_active_tile;
/* properties */
PropertyRNA *prop;
@@ -2831,7 +2819,8 @@ void IMAGE_OT_flip(wmOperatorType *ot)
static int image_invert_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ImageUser iuser = image_user_from_active_tile(ima);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
SpaceImage *sima = CTX_wm_space_image(C);
const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
@@ -2929,7 +2918,7 @@ void IMAGE_OT_invert(wmOperatorType *ot)
/* api callbacks */
ot->exec = image_invert_exec;
- ot->poll = image_from_context_has_data_poll_no_image_user;
+ ot->poll = image_from_context_has_data_poll_active_tile;
/* properties */
prop = RNA_def_boolean(ot->srna, "invert_r", 0, "Red", "Invert red channel");
@@ -2954,9 +2943,10 @@ void IMAGE_OT_invert(wmOperatorType *ot)
static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Image *ima = image_from_context(C);
+ ImageUser iuser = image_user_from_active_tile(ima);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size");
if (!RNA_property_is_set(op->ptr, prop)) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
const int size[2] = {ibuf->x, ibuf->y};
RNA_property_int_set_array(op->ptr, prop, size);
BKE_image_release_ibuf(ima, ibuf, NULL);
@@ -2967,7 +2957,8 @@ static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED
static int image_scale_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ImageUser iuser = image_user_from_active_tile(ima);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
SpaceImage *sima = CTX_wm_space_image(C);
const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
@@ -3017,7 +3008,7 @@ void IMAGE_OT_resize(wmOperatorType *ot)
/* api callbacks */
ot->invoke = image_scale_invoke;
ot->exec = image_scale_exec;
- ot->poll = image_from_context_has_data_poll_no_image_user;
+ ot->poll = image_from_context_has_data_poll_active_tile;
/* properties */
RNA_def_int_vector(ot->srna, "size", 2, NULL, 1, INT_MAX, "Size", "", 1, SHRT_MAX);
@@ -3040,9 +3031,8 @@ static bool image_pack_test(bContext *C, wmOperator *op)
return false;
}
- if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
- BKE_report(
- op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported");
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
+ BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported");
return false;
}
@@ -3110,9 +3100,8 @@ static int image_unpack_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
- BKE_report(
- op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported");
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
+ BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -3144,9 +3133,8 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
return OPERATOR_CANCELLED;
}
- if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
- BKE_report(
- op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported");
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
+ BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -3216,7 +3204,7 @@ bool ED_space_image_get_position(SpaceImage *sima,
}
bool ED_space_image_color_sample(
- SpaceImage *sima, ARegion *region, int mval[2], float r_col[3], bool *r_is_data)
+ SpaceImage *sima, ARegion *region, const int mval[2], float r_col[3], bool *r_is_data)
{
if (r_is_data) {
*r_is_data = false;
diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c
index 365cf2542b2..fbeef47e278 100644
--- a/source/blender/editors/space_image/image_sequence.c
+++ b/source/blender/editors/space_image/image_sequence.c
@@ -107,10 +107,10 @@ static void image_detect_frame_range(ImageFrameRange *range, const bool detect_u
/* UDIM */
if (detect_udim) {
int udim_start, udim_range;
- bool result = BKE_image_get_tile_info(
+ range->udims_detected = BKE_image_get_tile_info(
range->filepath, &range->udim_tiles, &udim_start, &udim_range);
- if (result) {
+ if (range->udims_detected) {
range->offset = udim_start;
range->length = udim_range;
return;
diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c
index c3a48abcae1..a7a8bde1115 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.c
@@ -287,7 +287,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
}
if (ibuf->mipmap[0]) {
- ibuf->userflags |= IB_MIPMAP_INVALID; /* force mip-map recreation. */
+ ibuf->userflags |= IB_MIPMAP_INVALID; /* Force MIP-MAP recreation. */
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
@@ -1040,7 +1040,7 @@ static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode)
bContext *C = NULL; /* special case, we never read from this. */
UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
ImageUndoStep *us = (ImageUndoStep *)us_p;
- BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D));
+ BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT));
us->paint_mode = paint_mode;
return us;
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 568bd064e3e..785a5419e04 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -103,7 +103,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(
simage->overlay.flag = SI_OVERLAY_SHOW_OVERLAYS | SI_OVERLAY_SHOW_GRID_BACKGROUND;
BKE_imageuser_default(&simage->iuser);
- simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS | IMA_SHOW_MAX_RESOLUTION;
+ simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS;
BKE_scopes_new(&simage->scopes);
simage->sample_line_hist.height = 100;
@@ -199,6 +199,7 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_new);
WM_operatortype_append(IMAGE_OT_open);
+ WM_operatortype_append(IMAGE_OT_file_browse);
WM_operatortype_append(IMAGE_OT_match_movie_length);
WM_operatortype_append(IMAGE_OT_replace);
WM_operatortype_append(IMAGE_OT_reload);
@@ -315,6 +316,9 @@ static void image_listener(const wmSpaceTypeListenerParams *params)
ED_area_tag_redraw(area);
break;
case ND_MODE:
+ ED_paint_cursor_start(&params->scene->toolsettings->imapaint.paint,
+ ED_image_tools_paint_poll);
+
if (wmn->subtype == NS_EDITMODE_MESH) {
ED_area_tag_refresh(area);
}
@@ -690,6 +694,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
sima->mask_info.draw_flag & ~MASK_DRAWFLAG_OVERLAY,
sima->mask_info.draw_type,
sima->mask_info.overlay_mode,
+ sima->mask_info.blend_factor,
width,
height,
aspx,
diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc
index b817ff887ce..29a7eb150a1 100644
--- a/source/blender/editors/space_info/info_stats.cc
+++ b/source/blender/editors/space_info/info_stats.cc
@@ -46,6 +46,7 @@
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "BKE_subdiv_ccg.h"
+#include "BKE_subdiv_modifier.h"
#include "DEG_depsgraph_query.h"
@@ -92,15 +93,18 @@ static bool stats_mesheval(const Mesh *me_eval, bool is_selected, SceneStats *st
}
int totvert, totedge, totface, totloop;
- if (me_eval->runtime.subdiv_ccg != nullptr) {
- const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg;
+
+ const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg;
+ const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime.subsurf_runtime_data;
+
+ if (subdiv_ccg != nullptr) {
BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop);
}
- else if (me_eval->runtime.subsurf_resolution != 0) {
- totvert = me_eval->runtime.subsurf_totvert;
- totedge = me_eval->runtime.subsurf_totedge;
- totface = me_eval->runtime.subsurf_totpoly;
- totloop = me_eval->runtime.subsurf_totloop;
+ else if (subsurf_runtime_data && subsurf_runtime_data->resolution != 0) {
+ totvert = subsurf_runtime_data->stats_totvert;
+ totedge = subsurf_runtime_data->stats_totedge;
+ totface = subsurf_runtime_data->stats_totpoly;
+ totloop = subsurf_runtime_data->stats_totloop;
}
else {
totvert = me_eval->totvert;
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 8b059b33a9a..40082b08806 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -58,14 +58,12 @@
* --> Most channels are now selection only.
*/
-static int mouse_nla_channels(
- bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode)
+static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
- View2D *v2d = &ac->region->v2d;
int notifierFlags = 0;
/* get the channel that was clicked on */
@@ -203,47 +201,8 @@ static int mouse_nla_channels(
}
case ANIMTYPE_NLATRACK: {
NlaTrack *nlt = (NlaTrack *)ale->data;
- AnimData *adt = ale->adt;
- short offset;
-
- /* offset for start of channel (on LHS of channel-list) */
- if (ale->id) {
- /* special exception for materials and particles */
- if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) {
- offset = 21 + NLACHANNEL_BUTTON_WIDTH;
- }
- else {
- offset = 14;
- }
- }
- else {
- offset = 0;
- }
- if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
- /* toggle protection (only if there's a toggle there) */
- nlt->flag ^= NLATRACK_PROTECTED;
-
- /* notifier flags - channel was edited */
- notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
- }
- else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) {
- /* toggle mute */
- nlt->flag ^= NLATRACK_MUTED;
-
- /* notifier flags - channel was edited */
- notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
- ale->update |= ANIM_UPDATE_DEPS;
- }
- else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) {
- /* toggle 'solo' */
- BKE_nlatrack_solo_toggle(adt, nlt);
-
- /* notifier flags - channel was edited */
- notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
- ale->update |= ANIM_UPDATE_DEPS;
- }
- else if (nlaedit_is_tweakmode_on(ac) == 0) {
+ if (nlaedit_is_tweakmode_on(ac) == 0) {
/* set selection */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this F-Curve only */
@@ -269,61 +228,40 @@ static int mouse_nla_channels(
case ANIMTYPE_NLAACTION: {
AnimData *adt = BKE_animdata_from_id(ale->id);
- /* button region... */
- if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
- if (nlaedit_is_tweakmode_on(ac) == 0) {
- /* 'push-down' action - only usable when not in tweak-mode */
- /* TODO: make this use the operator instead of calling the function directly
- * however, calling the operator requires that we supply the args,
- * and that works with proper buttons only */
- BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id));
- }
- else {
- /* When in tweak-mode, this button becomes the toggle for mapped editing. */
- adt->flag ^= ADT_NLA_EDIT_NOMAP;
- }
+ /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block
+ * - this is useful when there's no clear divider, and makes more sense in
+ * the case of users trying to use this to change actions
+ * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection
+ * while in tweak-mode is really evil!
+ * - we disable "solo" flags too, to make it easier to work with stashed actions
+ * with less trouble
+ */
+ if (nlaedit_is_tweakmode_on(ac)) {
+ /* Exit tweak-mode immediately. */
+ nlaedit_disable_tweakmode(ac, true);
/* changes to NLA-Action occurred */
notifierFlags |= ND_NLA_ACTCHANGE;
ale->update |= ANIM_UPDATE_DEPS;
}
- /* OR rest of name... */
else {
- /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block
- * - this is useful when there's no clear divider, and makes more sense in
- * the case of users trying to use this to change actions
- * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection
- * while in tweak-mode is really evil!
- * - we disable "solo" flags too, to make it easier to work with stashed actions
- * with less trouble
- */
- if (nlaedit_is_tweakmode_on(ac)) {
- /* Exit tweak-mode immediately. */
- nlaedit_disable_tweakmode(ac, true);
-
- /* changes to NLA-Action occurred */
- notifierFlags |= ND_NLA_ACTCHANGE;
- ale->update |= ANIM_UPDATE_DEPS;
+ /* select/deselect */
+ if (selectmode == SELECT_INVERT) {
+ /* inverse selection status of this AnimData block only */
+ adt->flag ^= ADT_UI_SELECTED;
}
else {
- /* select/deselect */
- if (selectmode == SELECT_INVERT) {
- /* inverse selection status of this AnimData block only */
- adt->flag ^= ADT_UI_SELECTED;
- }
- else {
- /* select AnimData block by itself */
- ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
- adt->flag |= ADT_UI_SELECTED;
- }
-
- /* set active? */
- if (adt->flag & ADT_UI_SELECTED) {
- adt->flag |= ADT_UI_ACTIVE;
- }
+ /* select AnimData block by itself */
+ ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
+ adt->flag |= ADT_UI_SELECTED;
+ }
- notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
+ /* set active? */
+ if (adt->flag & ADT_UI_SELECTED) {
+ adt->flag |= ADT_UI_ACTIVE;
}
+
+ notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
}
break;
}
@@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv
&channel_index);
/* handle mouse-click in the relevant channel then */
- notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode);
+ notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode);
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 6c631f46069..bb9e201d94a 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -621,7 +621,6 @@ static void nla_draw_strip(SpaceNla *snla,
static void nla_draw_strip_text(AnimData *adt,
NlaTrack *nlt,
NlaStrip *strip,
- int index,
View2D *v2d,
float xminc,
float xmaxc,
@@ -636,7 +635,7 @@ static void nla_draw_strip_text(AnimData *adt,
/* just print the name and the range */
if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
- str_len = BLI_snprintf_rlen(str, sizeof(str), "%d) Temp-Meta", index);
+ str_len = BLI_snprintf_rlen(str, sizeof(str), "Temp-Meta");
}
else {
str_len = BLI_strncpy_rlen(str, strip->name, sizeof(str));
@@ -702,6 +701,89 @@ static void nla_draw_strip_frames_text(
/* ---------------------- */
+/**
+ * Gets the first and last visible NLA strips on a track.
+ * Note that this also includes tracks that might only be
+ * visible because of their extendmode.
+ */
+static ListBase get_visible_nla_strips(NlaTrack *nlt, View2D *v2d)
+{
+ if (BLI_listbase_is_empty(&nlt->strips)) {
+ ListBase empty = {NULL, NULL};
+ return empty;
+ }
+
+ NlaStrip *first = NULL;
+ NlaStrip *last = NULL;
+
+ /* Find the first strip that is within the bounds of the view. */
+ LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
+ if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
+ first = last = strip;
+ break;
+ }
+ }
+
+ const bool has_strips_within_bounds = first != NULL;
+
+ if (has_strips_within_bounds) {
+ /* Find the last visible strip. */
+ for (NlaStrip *strip = first->next; strip; strip = strip->next) {
+ if (!BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
+ break;
+ }
+ last = strip;
+ }
+ /* Check if the first strip is adjacent to a strip outside the view to the left
+ * that has an extendmode region that should be drawn.
+ * If so, adjust the first strip to include drawing that strip as well.
+ */
+ NlaStrip *prev = first->prev;
+ if (prev && prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
+ first = prev;
+ }
+ }
+ else {
+ /* No immediately visible strips.
+ * Figure out where our view is relative to the strips, then determine
+ * if the view is adjacent to a strip that should have its extendmode
+ * rendered.
+ */
+ NlaStrip *first_strip = nlt->strips.first;
+ NlaStrip *last_strip = nlt->strips.last;
+ if (first_strip && v2d->cur.xmax < first_strip->start &&
+ first_strip->extendmode == NLASTRIP_EXTEND_HOLD) {
+ /* The view is to the left of all strips and the first strip has an
+ * extendmode that should be drawn.
+ */
+ first = last = first_strip;
+ }
+ else if (last_strip && v2d->cur.xmin > last_strip->end &&
+ last_strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
+ /* The view is to the right of all strips and the last strip has an
+ * extendmode that should be drawn.
+ */
+ first = last = last_strip;
+ }
+ else {
+ /* The view is in the middle of two strips. */
+ LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
+ /* Find the strip to the left by finding the strip to the right and getting its prev. */
+ if (v2d->cur.xmax < strip->start) {
+ /* If the strip to the left has an extendmode, set that as the only visible strip. */
+ if (strip->prev && strip->prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
+ first = last = strip->prev;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ ListBase visible_strips = {first, last};
+ return visible_strips;
+}
+
void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
{
View2D *v2d = &region->v2d;
@@ -737,29 +819,26 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
case ANIMTYPE_NLATRACK: {
AnimData *adt = ale->adt;
NlaTrack *nlt = (NlaTrack *)ale->data;
- NlaStrip *strip;
- int index;
-
- /* draw each strip in the track (if visible) */
- for (strip = nlt->strips.first, index = 1; strip; strip = strip->next, index++) {
- if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
- const float xminc = strip->start + text_margin_x;
- const float xmaxc = strip->end - text_margin_x;
-
- /* draw the visualization of the strip */
- nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax);
-
- /* add the text for this strip to the cache */
- if (xminc < xmaxc) {
- nla_draw_strip_text(adt, nlt, strip, index, v2d, xminc, xmaxc, ymin, ymax);
- }
-
- /* if transforming strips (only real reason for temp-metas currently),
- * add to the cache the frame numbers of the strip's extents
- */
- if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
- nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax);
- }
+ ListBase visible_nla_strips = get_visible_nla_strips(nlt, v2d);
+
+ /* Draw each visible strip in the track. */
+ LISTBASE_FOREACH (NlaStrip *, strip, &visible_nla_strips) {
+ const float xminc = strip->start + text_margin_x;
+ const float xmaxc = strip->end - text_margin_x;
+
+ /* draw the visualization of the strip */
+ nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax);
+
+ /* add the text for this strip to the cache */
+ if (xminc < xmaxc) {
+ nla_draw_strip_text(adt, nlt, strip, v2d, xminc, xmaxc, ymin, ymax);
+ }
+
+ /* if transforming strips (only real reason for temp-metas currently),
+ * add to the cache the frame numbers of the strip's extents
+ */
+ if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
+ nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax);
}
}
break;
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 2aa9b347ed7..81520445000 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -1004,7 +1004,7 @@ static int nlaedit_add_meta_exec(bContext *C, wmOperator *UNUSED(op))
NlaStrip *strip;
if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) {
- /* No making metastrips in non-local tracks of override data. */
+ /* No making meta-strips in non-local tracks of override data. */
continue;
}
@@ -1078,7 +1078,7 @@ static int nlaedit_remove_meta_exec(bContext *C, wmOperator *UNUSED(op))
NlaTrack *nlt = (NlaTrack *)ale->data;
if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) {
- /* No removing metastrips from non-local tracks of override data. */
+ /* No removing meta-strips from non-local tracks of override data. */
continue;
}
@@ -1714,7 +1714,7 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op)
BKE_nlatrack_add_strip(nlt, sb, is_liboverride);
}
- /* clear (temp) metastrips */
+ /* Clear (temp) meta-strips. */
BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
}
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index d5507619e0d..6806d715004 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -20,6 +20,7 @@
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_scene.h"
#include "BKE_tracking.h"
@@ -1271,14 +1272,14 @@ static void node_file_output_socket_draw(bContext *C,
static bool socket_needs_attribute_search(bNode &node, bNodeSocket &socket)
{
- if (node.declaration == nullptr) {
+ if (node.runtime->declaration == nullptr) {
return false;
}
if (socket.in_out == SOCK_OUT) {
return false;
}
const int socket_index = BLI_findindex(&node.inputs, &socket);
- return node.declaration->inputs()[socket_index]->is_attribute_name();
+ return node.runtime->declaration->inputs()[socket_index]->is_attribute_name();
}
static void std_node_socket_draw(
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 7fb15d69ab5..975d4eda7e3 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -303,10 +303,8 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
wmOperator *op,
bNodeTree *ntree)
{
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
-
- bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name);
+ bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT));
if (!node_group) {
return nullptr;
}
@@ -348,8 +346,14 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+ const char *node_idname = node_group_idname(C);
+ if (node_idname[0] == '\0') {
+ BKE_report(op->reports, RPT_WARNING, "Could not determine type of group node");
+ return OPERATOR_CANCELLED;
+ }
+
bNode *group_node = node_add_node(*C,
- node_group_idname(C),
+ node_idname,
(node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP :
NODE_GROUP,
snode->runtime->cursor[0],
@@ -365,9 +369,25 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
nodeSetActive(ntree, group_node);
ED_node_tree_propagate_change(C, bmain, nullptr);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
+static bool node_add_group_poll(bContext *C)
+{
+ if (!ED_operator_node_editable(C)) {
+ return false;
+ }
+ const SpaceNode *snode = CTX_wm_space_node(C);
+ if (snode->edittree->type == NTREE_CUSTOM) {
+ CTX_wm_operator_poll_msg_set(C,
+ "This node editor displays a custom (Python defined) node tree. "
+ "Dropping node groups isn't supported for this.");
+ return false;
+ }
+ return true;
+}
+
static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
@@ -396,12 +416,12 @@ void NODE_OT_add_group(wmOperatorType *ot)
/* callbacks */
ot->exec = node_add_group_exec;
ot->invoke = node_add_group_invoke;
- ot->poll = ED_operator_node_editable;
+ ot->poll = node_add_group_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
@@ -410,26 +430,16 @@ void NODE_OT_add_group(wmOperatorType *ot)
/** \name Add Node Object Operator
* \{ */
-static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op)
-{
- if (RNA_struct_property_is_set(op->ptr, "session_uuid")) {
- const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid");
- return (Object *)BKE_libblock_find_session_uuid(bmain, ID_OB, session_uuid);
- }
-
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
- return (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
-}
-
static int node_add_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- Object *object;
- if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) {
+ Object *object = reinterpret_cast<Object *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
+
+ if (!object) {
return OPERATOR_CANCELLED;
}
@@ -486,8 +496,6 @@ static bool node_add_object_poll(bContext *C)
void NODE_OT_add_object(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Add Node Object";
ot->description = "Add an object info node to the current node editor";
@@ -501,17 +509,7 @@ void NODE_OT_add_object(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the data-block to assign",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
@@ -520,27 +518,16 @@ void NODE_OT_add_object(wmOperatorType *ot)
/** \name Add Node Collection Operator
* \{ */
-static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain,
- wmOperator *op)
-{
- if (RNA_struct_property_is_set(op->ptr, "session_uuid")) {
- const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid");
- return (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid);
- }
-
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
- return (Collection *)BKE_libblock_find_name(bmain, ID_GR, name);
-}
-
static int node_add_collection_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree *ntree = snode.edittree;
- Collection *collection;
- if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) {
+ Collection *collection = reinterpret_cast<Collection *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR));
+
+ if (!collection) {
return OPERATOR_CANCELLED;
}
@@ -597,8 +584,6 @@ static bool node_add_collection_poll(bContext *C)
void NODE_OT_add_collection(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Add Node Collection";
ot->description = "Add an collection info node to the current node editor";
@@ -612,18 +597,7 @@ void NODE_OT_add_collection(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_string(
- ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the data-block to assign",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
@@ -745,7 +719,7 @@ void NODE_OT_add_file(wmOperatorType *ot)
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
@@ -754,18 +728,6 @@ void NODE_OT_add_file(wmOperatorType *ot)
/** \name Add Mask Node Operator
* \{ */
-static ID *node_add_mask_get_and_poll_mask(Main *bmain, wmOperator *op)
-{
- if (RNA_struct_property_is_set(op->ptr, "session_uuid")) {
- const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid");
- return BKE_libblock_find_session_uuid(bmain, ID_MSK, session_uuid);
- }
-
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
- return BKE_libblock_find_name(bmain, ID_MSK, name);
-}
-
static bool node_add_mask_poll(bContext *C)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -779,7 +741,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
SpaceNode &snode = *CTX_wm_space_node(C);
bNode *node;
- ID *mask = node_add_mask_get_and_poll_mask(bmain, op);
+ ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK);
if (!mask) {
return OPERATOR_CANCELLED;
}
@@ -805,8 +767,6 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
void NODE_OT_add_mask(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Add Mask Node";
ot->description = "Add a mask node to the current node editor";
@@ -819,17 +779,7 @@ void NODE_OT_add_mask(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the data-block to assign",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc
index 4247d5a1fbc..b9bee3ed15e 100644
--- a/source/blender/editors/space_node/node_context_path.cc
+++ b/source/blender/editors/space_node/node_context_path.cc
@@ -26,8 +26,6 @@
#include "UI_interface.hh"
#include "UI_resources.h"
-#include "UI_interface.hh"
-
#include "node_intern.hh"
struct Curve;
@@ -139,7 +137,7 @@ static void get_context_path_node_geometry(const bContext &C,
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);
+ ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES);
context_path_add_node_tree_and_node_groups(snode, path);
}
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 9076b17a926..7003d51b2b6 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -66,6 +66,7 @@
#include "NOD_geometry_nodes_eval_log.hh"
#include "NOD_node_declaration.hh"
+#include "NOD_socket_declarations_geometry.hh"
#include "FN_field.hh"
#include "FN_field_cpp_type.hh"
@@ -75,7 +76,7 @@
using blender::GPointer;
using blender::fn::GField;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
-using geo_log::NamedAttributeUsage;
+using geo_log::eNamedAttrUsage;
extern "C" {
/* XXX interface.h */
@@ -871,7 +872,8 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v
}
static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log,
- std::stringstream &ss)
+ std::stringstream &ss,
+ const nodes::decl::Geometry *geometry)
{
Span<GeometryComponentType> component_types = value_log.component_types();
if (component_types.is_empty()) {
@@ -895,9 +897,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
BLI_snprintf(line,
sizeof(line),
TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"),
- to_string(mesh_info.tot_verts).c_str(),
- to_string(mesh_info.tot_edges).c_str(),
- to_string(mesh_info.tot_faces).c_str());
+ to_string(mesh_info.verts_num).c_str(),
+ to_string(mesh_info.edges_num).c_str(),
+ to_string(mesh_info.faces_num).c_str());
ss << line << line_end;
break;
}
@@ -908,7 +910,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
BLI_snprintf(line,
sizeof(line),
TIP_("\u2022 Point Cloud: %s points"),
- to_string(pointcloud_info.tot_points).c_str());
+ to_string(pointcloud_info.points_num).c_str());
ss << line << line_end;
break;
}
@@ -918,7 +920,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
BLI_snprintf(line,
sizeof(line),
TIP_("\u2022 Curve: %s splines"),
- to_string(curve_info.tot_splines).c_str());
+ to_string(curve_info.splines_num).c_str());
ss << line << line_end;
break;
}
@@ -928,7 +930,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
BLI_snprintf(line,
sizeof(line),
TIP_("\u2022 Instances: %s"),
- to_string(instances_info.tot_instances).c_str());
+ to_string(instances_info.instances_num).c_str());
ss << line << line_end;
break;
}
@@ -938,6 +940,45 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
}
}
}
+
+ /* If the geometry declaration is null, as is the case for input to group output,
+ * or it is an output socket don't show supported types. */
+ if (geometry == nullptr || geometry->in_out() == SOCK_OUT) {
+ return;
+ }
+
+ Span<GeometryComponentType> supported_types = geometry->supported_types();
+ if (supported_types.is_empty()) {
+ ss << ".\n\n" << TIP_("Supported: All Types");
+ return;
+ }
+
+ ss << ".\n\n" << TIP_("Supported: ");
+ for (GeometryComponentType type : supported_types) {
+ switch (type) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ ss << TIP_("Mesh");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ ss << TIP_("Point Cloud");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ ss << TIP_("Curve");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ ss << TIP_("Instances");
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ ss << TIP_("Volume");
+ break;
+ }
+ }
+ ss << ((type == supported_types.last()) ? "" : ", ");
+ }
}
static std::optional<std::string> create_socket_inspection_string(bContext *C,
@@ -970,7 +1011,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C,
}
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);
+ create_inspection_string_for_geometry(
+ *geo_value_log,
+ ss,
+ dynamic_cast<const nodes::decl::Geometry *>(socket.runtime->declaration));
}
return ss.str();
@@ -982,8 +1026,8 @@ static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket)
return true;
}
- if (socket->declaration != nullptr) {
- const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration;
+ if (socket->runtime->declaration != nullptr) {
+ const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration;
return !socket_decl.description().is_empty();
}
@@ -996,8 +1040,8 @@ static char *node_socket_get_tooltip(bContext *C,
bNodeSocket *socket)
{
std::stringstream output;
- if (socket->declaration != nullptr) {
- const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration;
+ if (socket->runtime->declaration != nullptr) {
+ const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration;
blender::StringRef description = socket_decl.description();
if (!description.is_empty()) {
output << TIP_(description.data());
@@ -1695,7 +1739,7 @@ struct NodeExtraInfoRow {
};
struct NamedAttributeTooltipArg {
- Map<std::string, NamedAttributeUsage> usage_by_attribute;
+ Map<std::string, eNamedAttrUsage> usage_by_attribute;
};
static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
@@ -1707,7 +1751,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
struct NameWithUsage {
StringRefNull name;
- NamedAttributeUsage usage;
+ eNamedAttrUsage usage;
};
Vector<NameWithUsage> sorted_used_attribute;
@@ -1722,16 +1766,16 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
for (const NameWithUsage &attribute : sorted_used_attribute) {
const StringRefNull name = attribute.name;
- const NamedAttributeUsage usage = attribute.usage;
+ const eNamedAttrUsage usage = attribute.usage;
ss << " \u2022 \"" << name << "\": ";
Vector<std::string> usages;
- if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) {
+ if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) {
usages.append(TIP_("read"));
}
- if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) {
+ if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) {
usages.append(TIP_("write"));
}
- if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) {
+ if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) {
usages.append(TIP_("remove"));
}
for (const int i : usages.index_range()) {
@@ -1749,7 +1793,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
}
static NodeExtraInfoRow row_from_used_named_attribute(
- const Map<std::string, NamedAttributeUsage> &usage_by_attribute_name)
+ const Map<std::string, eNamedAttrUsage> &usage_by_attribute_name)
{
const int attributes_num = usage_by_attribute_name.size();
@@ -1777,7 +1821,7 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp
return std::nullopt;
}
- Map<std::string, NamedAttributeUsage> usage_by_attribute;
+ Map<std::string, eNamedAttrUsage> usage_by_attribute;
tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) {
usage_by_attribute.lookup_or_add_as(used_attribute.name,
@@ -1807,7 +1851,7 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp
if (node_log == nullptr) {
return std::nullopt;
}
- Map<std::string, NamedAttributeUsage> usage_by_attribute;
+ Map<std::string, eNamedAttrUsage> usage_by_attribute;
for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) {
usage_by_attribute.lookup_or_add_as(used_attribute.name,
used_attribute.usage) |= used_attribute.usage;
@@ -2654,7 +2698,7 @@ static void frame_node_draw_label(const bNodeTree &ntree,
BLF_enable(fontid, BLF_ASPECT);
BLF_aspect(fontid, aspect, aspect, 1.0f);
/* clamp otherwise it can suck up a LOT of memory */
- BLF_size(fontid, MIN2(24.0f, font_size), U.dpi);
+ BLF_size(fontid, MIN2(24.0f, font_size) * U.pixelsize, U.dpi);
/* title color */
int color_id = node_get_colorid(node);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 2d7972e2291..ffc9befc81c 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -15,6 +15,7 @@
#include "DNA_text_types.h"
#include "DNA_world_types.h"
+#include "BKE_callbacks.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
@@ -66,7 +67,9 @@ namespace blender::ed::space_node {
#define USE_ESC_COMPO
-/* ***************** composite job manager ********************** */
+/* -------------------------------------------------------------------- */
+/** \name Composite Job Manager
+ * \{ */
enum {
COM_RECALC_COMPOSITE = 1,
@@ -273,6 +276,7 @@ static void compo_startjob(void *cjv,
// XXX BIF_store_spare();
/* 1 is do_previews */
+ BKE_callback_exec_id(cj->bmain, &scene->id, BKE_CB_EVT_COMPOSITE_PRE);
if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) {
ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, "");
@@ -291,8 +295,30 @@ static void compo_startjob(void *cjv,
ntree->progress = nullptr;
}
+static void compo_canceljob(void *cjv)
+{
+ CompoJob *cj = (CompoJob *)cjv;
+ Main *bmain = cj->bmain;
+ Scene *scene = cj->scene;
+ BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_CANCEL);
+}
+
+static void compo_completejob(void *cjv)
+{
+ CompoJob *cj = (CompoJob *)cjv;
+ Main *bmain = cj->bmain;
+ Scene *scene = cj->scene;
+ BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_POST);
+}
+
+/** \} */
+
} // namespace blender::ed::space_node
+/* -------------------------------------------------------------------- */
+/** \name Composite Job C API
+ * \{ */
+
void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
{
using namespace blender::ed::space_node;
@@ -331,14 +357,24 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
/* setup job */
WM_jobs_customdata_set(wm_job, cj, compo_freejob);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT);
- WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr);
+ WM_jobs_callbacks_ex(wm_job,
+ compo_startjob,
+ compo_initjob,
+ compo_updatejob,
+ nullptr,
+ compo_completejob,
+ compo_canceljob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
+/** \} */
+
namespace blender::ed::space_node {
-/* ***************************************** */
+/* -------------------------------------------------------------------- */
+/** \name Composite Poll & Utility Functions
+ * \{ */
bool composite_node_active(bContext *C)
{
@@ -388,8 +424,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
}
}
+/** \} */
+
} // namespace blender::ed::space_node
+/* -------------------------------------------------------------------- */
+/** \name Node Editor Public API Functions
+ * \{ */
+
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree)
{
if (C != nullptr) {
@@ -783,9 +825,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
// node_update_nodetree(C, ntree, 0.0f, 0.0f);
}
+/** \} */
+
namespace blender::ed::space_node {
-/* ***************** generic operator functions for nodes ***************** */
+/* -------------------------------------------------------------------- */
+/** \name Generic Operator Functions for Nodes
+ * \{ */
#if 0 /* UNUSED */
@@ -861,7 +907,11 @@ static void edit_node_properties_get(
}
#endif
-/* ************************** Node generic ************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Generic
+ * \{ */
/* is rct in visible part of node? */
static bNode *visible_node(SpaceNode &snode, const rctf &rct)
@@ -874,7 +924,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct)
return nullptr;
}
-/* ********************** size widget operator ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Size Widget Operator
+ * \{ */
struct NodeSizeWidget {
float mxstart, mystart;
@@ -1077,7 +1131,11 @@ void NODE_OT_resize(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING;
}
-/* ********************** hidden sockets ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Hidden Sockets
+ * \{ */
bool node_has_hidden_sockets(bNode *node)
{
@@ -1211,7 +1269,11 @@ bool node_find_indicated_socket(SpaceNode &snode,
return false;
}
-/* ****************** Link Dimming *********************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Link Dimming
+ * \{ */
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link)
{
@@ -1237,7 +1299,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f;
}
-/* ****************** Duplicate *********************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Duplicate Operator
+ * \{ */
static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map,
bNode *node)
@@ -1422,8 +1488,7 @@ void node_select_all(ListBase *lb, int action)
}
}
-/* ******************************** */
-/* XXX some code needing updating to operators. */
+/* XXX: some code needing updating to operators. */
/* goes over all scenes, reads render layers */
static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1526,7 +1591,11 @@ void NODE_OT_render_changed(wmOperatorType *ot)
ot->flag = 0;
}
-/* ****************** Hide operator *********************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Hide Operator
+ * \{ */
static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
{
@@ -1722,7 +1791,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Mute operator *********************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Mute Operator
+ * \{ */
static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1758,7 +1831,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Delete operator ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Delete Operator
+ * \{ */
static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1793,7 +1870,11 @@ void NODE_OT_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Switch View ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Switch View
+ * \{ */
static bool node_switch_view_poll(bContext *C)
{
@@ -1837,7 +1918,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Delete with reconnect ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Delete with Reconnect Operator
+ * \{ */
+
static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
@@ -1872,7 +1958,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** File Output Add Socket ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node File Output Add Socket Operator
+ * \{ */
static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
{
@@ -1922,7 +2012,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot)
ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file");
}
-/* ****************** Multi File Output Remove Socket ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Multi File Output Remove Socket Operator
+ * \{ */
static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1968,7 +2062,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Multi File Output Move Socket ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Multi File Output Move Socket Node
+ * \{ */
static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
{
@@ -2040,7 +2138,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", "");
}
-/* ****************** Copy Node Color ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Copy Node Color Operator
+ * \{ */
static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -2085,7 +2187,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Copy to clipboard ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Copy to Clipboard Operator
+ * \{ */
static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -2163,7 +2269,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Paste from clipboard ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Paste from Clipboard
+ * \{ */
static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
@@ -2287,7 +2397,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************** Add interface socket operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node-Tree Add Interface Socket Operator
+ * \{ */
static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
{
@@ -2357,7 +2471,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot)
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
-/********************** Remove interface socket operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node-Tree Remove Interface Socket Operator
+ * \{ */
static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
{
@@ -2403,7 +2521,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot)
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
-/********************** Change interface socket type operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node-Tree Change Interface Socket Type Operator
+ * \{ */
static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
{
@@ -2503,7 +2625,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot)
ot->prop = prop;
}
-/********************** Move interface socket operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node-Tree Move Interface Socket Operator
+ * \{ */
static const EnumPropertyItem move_direction_items[] = {
{1, "UP", 0, "Up", ""},
@@ -2577,7 +2703,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot)
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
-/* ********************** Shader Script Update ******************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Shader Script Update
+ * \{ */
static bool node_shader_script_update_poll(bContext *C)
{
@@ -2722,7 +2852,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ********************** Viewer border ******************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Viewer Border
+ * \{ */
static void viewer_border_corner_to_backdrop(SpaceNode *snode,
ARegion *region,
@@ -2844,7 +2978,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Cryptomatte Add Socket ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cryptomatte Add Socket
+ * \{ */
static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -2888,7 +3026,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Cryptomatte Remove Socket ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cryptomatte Remove Socket
+ * \{ */
static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -2933,4 +3075,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+/** \} */
+
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index fee64da0459..f08e23c8371 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -82,8 +82,10 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context(
if (const geo_log::GeometryValueLog *geo_value_log =
dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) {
for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
- if (names.add(attribute.name)) {
- attributes.append(&attribute);
+ if (bke::allow_procedural_attribute_access(attribute.name)) {
+ if (names.add(attribute.name)) {
+ attributes.append(&attribute);
+ }
}
}
}
@@ -126,7 +128,7 @@ static void attribute_search_update_fn(
* Some custom data types don't correspond to node types and therefore can't be
* used by the named attribute input node. Find the best option or fallback to float.
*/
-static CustomDataType data_type_in_attribute_input_node(const CustomDataType type)
+static eCustomDataType data_type_in_attribute_input_node(const eCustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT:
@@ -183,9 +185,9 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
BLI_assert(socket->type == SOCK_STRING);
/* For the attribute input node, also adjust the type and links connected to the output. */
- if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) {
+ if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE && item->data_type.has_value()) {
NodeGeometryInputNamedAttribute &storage = *(NodeGeometryInputNamedAttribute *)node->storage;
- const CustomDataType new_type = data_type_in_attribute_input_node(item->data_type);
+ const eCustomDataType new_type = data_type_in_attribute_input_node(*item->data_type);
if (new_type != storage.data_type) {
storage.data_type = new_type;
/* Make the output socket with the new type on the attribute input node active. */
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 10d4ad36d95..924537d0e8a 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -31,7 +31,9 @@ struct wmKeyConfig;
struct wmWindow;
/* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */
+extern "C" {
extern const char *node_context_dir[];
+};
namespace blender::ed::space_node {
@@ -74,8 +76,18 @@ struct SpaceNode_Runtime {
/** Mouse position for drawing socket-less links and adding nodes. */
float2 cursor;
- /** For auto compositing. */
- bool recalc;
+ /**
+ * Indicates that the compositing tree in the space needs to be re-evaluated using the
+ * auto-compositing pipeline.
+ * Takes priority over the regular compositing.
+ */
+ bool recalc_auto_compositing;
+
+ /**
+ * Indicates that the compositing int the space tree needs to be re-evaluated using
+ * regular compositing pipeline.
+ */
+ bool recalc_regular_compositing;
/** Temporary data for modal linking operator. */
std::unique_ptr<bNodeLinkDrag> linkdrag;
@@ -94,7 +106,7 @@ enum NodeResizeDirection {
};
ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
-/* Nodes draw without dpi - the view zoom is flexible. */
+/* Nodes draw without DPI - the view zoom is flexible. */
#define HIDDEN_RAD (0.75f * U.widget_unit)
#define BASIS_RAD (0.2f * U.widget_unit)
#define NODE_DYS (U.widget_unit / 2)
@@ -117,8 +129,6 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
*/
float2 space_node_group_offset(const SpaceNode &snode);
-rctf node_frame_rect_inside(const bNode &node);
-
int node_get_resize_cursor(NodeResizeDirection directions);
/**
* Usual convention here would be #node_socket_get_color(),
@@ -155,6 +165,9 @@ void node_keymap(wmKeyConfig *keyconf);
/* node_select.cc */
+rctf node_frame_rect_inside(const bNode &node);
+bool node_or_socket_isect_event(bContext *C, const wmEvent *event);
+
void node_deselect_all(SpaceNode &snode);
void node_socket_select(bNode *node, bNodeSocket &sock);
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node);
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index c757fb46407..e10bedb18f4 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -454,7 +454,7 @@ static bool socket_can_be_viewed(const OutputSocketRef &socket)
SOCK_RGBA);
}
-static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
+static eCustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
{
switch (socket_type) {
case SOCK_FLOAT:
@@ -491,7 +491,7 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
return viewer_socket;
}
NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node.storage;
- const CustomDataType data_type = socket_type_to_custom_data_type(
+ const eCustomDataType 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;
@@ -2048,7 +2048,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn
/* Try to get the main socket based on the socket declaration. */
nodeDeclarationEnsure(&ntree, &node);
- const nodes::NodeDeclaration *node_decl = node.declaration;
+ const nodes::NodeDeclaration *node_decl = node.runtime->declaration;
if (node_decl != nullptr) {
Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() :
node_decl->outputs();
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index 1d0097068f1..9d73156edab 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -48,6 +48,8 @@
namespace blender::ed::space_node {
+static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event);
+
/**
* Function to detect if there is a visible view3d that uses workbench in texture mode.
* This function is for fixing T76970 for Blender 2.83. The actual fix should add a mechanism in
@@ -98,6 +100,11 @@ rctf node_frame_rect_inside(const bNode &node)
return frame_inside;
}
+bool node_or_socket_isect_event(bContext *C, const wmEvent *event)
+{
+ return is_event_over_node_or_socket(C, event);
+}
+
static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse)
{
/* Frame nodes are selectable by their borders (including their whole rect - as for other nodes -
@@ -193,11 +200,6 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event)
return is_position_over_node_or_socket(*snode, mouse);
}
-static void node_toggle(bNode *node)
-{
- nodeSetSelected(node, !(node->flag & SELECT));
-}
-
void node_socket_select(bNode *node, bNodeSocket &sock)
{
sock.flag |= SELECT;
@@ -510,10 +512,10 @@ void node_select_single(bContext &C, bNode &node)
WM_event_add_notifier(&C, NC_NODE | NA_SELECTED, nullptr);
}
-static int node_mouse_select(bContext *C,
- wmOperator *op,
- const int mval[2],
- bool wait_to_deselect_others)
+static bool node_mouse_select(bContext *C,
+ wmOperator *op,
+ const int mval[2],
+ struct SelectPick_Params *params)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
@@ -525,36 +527,38 @@ static int node_mouse_select(bContext *C,
bNodeSocket *sock = nullptr;
bNodeSocket *tsock;
float cursor[2];
- int ret_value = OPERATOR_CANCELLED;
- const bool extend = RNA_boolean_get(op->ptr, "extend");
/* always do socket_select when extending selection. */
- const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select");
- const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
-
- /* These cases are never modal. */
- if (extend || socket_select) {
- wait_to_deselect_others = false;
- }
+ const bool socket_select = (params->sel_op == SEL_OP_XOR) ||
+ RNA_boolean_get(op->ptr, "socket_select");
+ bool changed = false;
+ bool found = false;
+ bool node_was_selected = false;
/* get mouse coordinates in view2d space */
UI_view2d_region_to_view(&region.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
/* first do socket selection, these generally overlap with nodes. */
if (socket_select) {
+ /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */
+ const bool extend = (params->sel_op == SEL_OP_XOR);
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
+ found = true;
+ node_was_selected = node->flag & SELECT;
+
/* NOTE: SOCK_IN does not take into account the extend case...
* This feature is not really used anyway currently? */
node_socket_toggle(node, *sock, true);
- ret_value = OPERATOR_FINISHED;
+ changed = true;
}
else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
+ found = true;
+ node_was_selected = node->flag & SELECT;
+
if (sock->flag & SELECT) {
if (extend) {
node_socket_deselect(node, *sock, true);
- }
- else {
- ret_value = OPERATOR_FINISHED;
+ changed = true;
}
}
else {
@@ -566,6 +570,7 @@ static int node_mouse_select(bContext *C,
continue;
}
node_socket_deselect(node, *tsock, true);
+ changed = true;
}
}
if (!extend) {
@@ -575,69 +580,71 @@ static int node_mouse_select(bContext *C,
}
for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) {
node_socket_deselect(tnode, *tsock, true);
+ changed = true;
}
}
}
node_socket_select(node, *sock);
- ret_value = OPERATOR_FINISHED;
+ changed = true;
}
}
}
if (!sock) {
+
/* find the closest visible node */
node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]);
+ found = (node != nullptr);
+ node_was_selected = node && (node->flag & SELECT);
- if (extend) {
- if (node != nullptr) {
- /* If node is selected but not active, we want to make it active,
- * but not toggle (deselect) it. */
- if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) {
- node_toggle(node);
- }
- ret_value = OPERATOR_FINISHED;
- }
- }
- else if (deselect_all && node == nullptr) {
- /* Rather than deselecting others, users may want to drag to box-select (drag from empty
- * space) or tweak-translate an already selected item. If these cases may apply, delay
- * deselection. */
- if (wait_to_deselect_others) {
- ret_value = OPERATOR_RUNNING_MODAL;
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (node->flag & SELECT)) {
+ found = false;
}
- else {
- /* Deselect in empty space. */
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) {
nodeSetSelected(tnode, false);
}
- ret_value = OPERATOR_FINISHED;
+ changed = true;
}
}
- else if (node != nullptr) {
- /* When clicking on an already selected node, we want to wait to deselect
- * others and allow the user to start moving the node without that. */
- if (wait_to_deselect_others && (node->flag & SELECT)) {
- ret_value = OPERATOR_RUNNING_MODAL;
- }
- else {
- nodeSetSelected(node, true);
- for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode != node) {
- nodeSetSelected(tnode, false);
- }
+ if (found) {
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ nodeSetSelected(node, true);
+ break;
+ }
+ case SEL_OP_SUB: {
+ nodeSetSelected(node, false);
+ break;
+ }
+ case SEL_OP_XOR: {
+ /* Check active so clicking on an inactive node activates it. */
+ bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE);
+ nodeSetSelected(node, !is_selected);
+ break;
+ }
+ case SEL_OP_SET: {
+ nodeSetSelected(node, true);
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
-
- ret_value = OPERATOR_FINISHED;
}
+
+ changed = true;
}
}
/* update node order */
- if (ret_value != OPERATOR_CANCELLED) {
+ if (changed || found) {
bool active_texture_changed = false;
bool viewer_node_changed = false;
- if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) {
+ if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) {
viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER;
ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
}
@@ -654,23 +661,35 @@ static int node_mouse_select(bContext *C,
WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
- return ret_value;
+ return changed || found;
}
static int node_select_exec(bContext *C, wmOperator *op)
{
- const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
-
/* get settings from RNA properties for operator */
int mval[2];
- mval[0] = RNA_int_get(op->ptr, "mouse_x");
- mval[1] = RNA_int_get(op->ptr, "mouse_y");
+ RNA_int_get_array(op->ptr, "location", mval);
+
+ struct SelectPick_Params params = {};
+ ED_select_pick_params_from_operator(op->ptr, &params);
/* perform the select */
- const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others);
+ const bool changed = node_mouse_select(C, op, mval, &params);
+
+ if (changed) {
+ return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
+ }
+ /* Nothing selected, just passthrough. */
+ return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED;
+}
+
+static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ RNA_int_set_array(op->ptr, "location", event->mval);
- /* allow tweak event to work too */
- return ret_value | OPERATOR_PASS_THROUGH;
+ const int retval = node_select_exec(C, op);
+
+ return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void NODE_OT_select(wmOperatorType *ot)
@@ -684,24 +703,29 @@ void NODE_OT_select(wmOperatorType *ot)
/* api callbacks */
ot->exec = node_select_exec;
- ot->invoke = WM_generic_select_invoke;
- ot->modal = WM_generic_select_modal;
+ ot->invoke = node_select_invoke;
ot->poll = ED_operator_node_active;
+ ot->get_name = ED_select_pick_get_name;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- WM_operator_properties_generic_select(ot);
- prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ WM_operator_properties_mouse_select(ot);
+
+ prop = RNA_def_int_vector(ot->srna,
+ "location",
+ 2,
+ nullptr,
+ INT_MIN,
+ INT_MAX,
+ "Location",
+ "Mouse location",
+ INT_MIN,
+ INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
- prop = RNA_def_boolean(ot->srna,
- "deselect_all",
- false,
- "Deselect On Nothing",
- "Deselect all when nothing under the cursor");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -871,8 +895,8 @@ void NODE_OT_select_circle(wmOperatorType *ot)
ot->invoke = WM_gesture_circle_invoke;
ot->exec = node_circleselect_exec;
ot->modal = WM_gesture_circle_modal;
-
ot->poll = ED_operator_node_active;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index 91a21527ac9..6f30632244b 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -643,6 +643,12 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ARegion *region = CTX_wm_region(C);
ImageSampleInfo *info;
+ /* Don't handle events intended for nodes (which rely on click/drag distinction).
+ * which this operator would use since sampling is normally activated on press, see: T98191. */
+ if (node_or_socket_isect_event(C, event)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
if (!ED_node_is_compositor(snode) || !(snode->flag & SNODE_BACKDRAW)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index 34f357ae5c3..15afd024766 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -324,6 +324,41 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
return false;
}
+/**
+ * Tag the space to recalculate the compositing tree using auto-compositing pipeline.
+ *
+ * Will check the space to be using a compositing tree, and check whether auto-compositing
+ * is enabled. If the checks do not pass then the function has no affect.
+ */
+static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area)
+{
+ if (!ED_node_is_compositor(snode)) {
+ return;
+ }
+
+ if (snode->flag & SNODE_AUTO_RENDER) {
+ snode->runtime->recalc_auto_compositing = true;
+ ED_area_tag_refresh(area);
+ }
+}
+
+/**
+ * Tag the space to recalculate the current tree.
+ *
+ * For all node trees this will do `snode_set_context()` which takes care of setting an active
+ * tree. This will be done in the area refresh callback.
+ *
+ * For compositor tree this will additionally start of the compositor job.
+ */
+static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area)
+{
+ if (ED_node_is_compositor(snode)) {
+ snode->runtime->recalc_regular_compositing = true;
+ }
+
+ ED_area_tag_refresh(area);
+}
+
static void node_area_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
@@ -346,25 +381,20 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
UI_view2d_center_set(&region->v2d, path->view_center[0], path->view_center[1]);
}
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
break;
}
case ND_FRAME:
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
break;
case ND_COMPO_RESULT:
ED_area_tag_redraw(area);
break;
case ND_TRANSFORM_DONE:
- if (ED_node_is_compositor(snode)) {
- if (snode->flag & SNODE_AUTO_RENDER) {
- snode->runtime->recalc = true;
- ED_area_tag_refresh(area);
- }
- }
+ node_area_tag_recalc_auto_compositing(snode, area);
break;
case ND_LAYER_CONTENT:
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
break;
}
break;
@@ -373,46 +403,46 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
case NC_MATERIAL:
if (ED_node_is_shader(snode)) {
if (wmn->data == ND_SHADING) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
else if (wmn->data == ND_SHADING_DRAW) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
else if (wmn->data == ND_SHADING_LINKS) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
break;
case NC_TEXTURE:
if (ED_node_is_shader(snode) || ED_node_is_texture(snode)) {
if (wmn->data == ND_NODES) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
break;
case NC_WORLD:
if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_WORLD) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
break;
case NC_OBJECT:
if (ED_node_is_shader(snode)) {
if (wmn->data == ND_OB_SHADING) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
else if (ED_node_is_geometry(snode)) {
/* Rather strict check: only redraw when the reference matches the current editor's ID. */
if (wmn->data == ND_MODIFIER) {
if (wmn->reference == snode->id || snode->id == nullptr) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
}
break;
case NC_SPACE:
if (wmn->data == ND_SPACE_NODE) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
else if (wmn->data == ND_SPACE_NODE_VIEW) {
ED_area_tag_redraw(area);
@@ -420,7 +450,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
break;
case NC_NODE:
if (wmn->action == NA_EDITED) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
else if (wmn->action == NA_SELECTED) {
ED_area_tag_redraw(area);
@@ -429,14 +459,14 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
case NC_SCREEN:
switch (wmn->data) {
case ND_ANIMPLAY:
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
break;
}
break;
case NC_MASK:
if (wmn->action == NA_EDITED) {
if (snode->nodetree && snode->nodetree->type == NTREE_COMPOSIT) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
break;
@@ -447,7 +477,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
/* Without this check drawing on an image could become very slow when the compositor is
* open. */
if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
}
@@ -457,7 +487,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
if (wmn->action == NA_EDITED) {
if (ED_node_is_compositor(snode)) {
if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
}
}
@@ -465,12 +495,12 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
case NC_LINESTYLE:
if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_LINESTYLE) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
break;
case NC_WM:
if (wmn->data == ND_UNDO) {
- ED_area_tag_refresh(area);
+ node_area_tag_tree_recalc(snode, area);
}
break;
case NC_GPENCIL:
@@ -493,11 +523,13 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area)
Scene *scene = (Scene *)snode->id;
if (scene->use_nodes) {
/* recalc is set on 3d view changes for auto compo */
- if (snode->runtime->recalc) {
- snode->runtime->recalc = false;
+ if (snode->runtime->recalc_auto_compositing) {
+ snode->runtime->recalc_auto_compositing = false;
+ snode->runtime->recalc_regular_compositing = false;
node_render_changed_exec((struct bContext *)C, nullptr);
}
- else {
+ else if (snode->runtime->recalc_regular_compositing) {
+ snode->runtime->recalc_regular_compositing = false;
ED_node_composite_job(C, snode->nodetree, scene);
}
}
@@ -640,7 +672,7 @@ static void node_group_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *d
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
- RNA_string_set(drop->ptr, "name", id->name + 2);
+ RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid);
}
static void node_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
@@ -655,7 +687,7 @@ static void node_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
if (id) {
- RNA_string_set(drop->ptr, "name", id->name + 2);
+ RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid);
RNA_struct_property_unset(drop->ptr, "filepath");
}
else if (drag->path[0]) {
@@ -794,8 +826,10 @@ static void node_region_listener(const wmRegionListenerParams *params)
} // namespace blender::ed::space_node
/* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */
+extern "C" {
const char *node_context_dir[] = {
"selected_nodes", "active_node", "light", "material", "world", nullptr};
+};
namespace blender::ed::space_node {
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 59f6bd85d59..97d2957eed2 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SRC
outliner_draw.cc
outliner_edit.cc
outliner_ops.cc
+ outliner_query.cc
outliner_select.cc
outliner_sync.cc
outliner_tools.cc
@@ -57,6 +58,7 @@ set(SRC
tree/tree_element_scene_objects.cc
tree/tree_element_seq.cc
tree/tree_element_view_layer.cc
+ tree/tree_iterator.cc
outliner_intern.hh
tree/common.hh
@@ -75,6 +77,7 @@ set(SRC
tree/tree_element_scene_objects.hh
tree/tree_element_seq.hh
tree/tree_element_view_layer.hh
+ tree/tree_iterator.hh
)
set(LIB
diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc
index d07b6641836..1a804cb58b8 100644
--- a/source/blender/editors/space_outliner/outliner_context.cc
+++ b/source/blender/editors/space_outliner/outliner_context.cc
@@ -12,23 +12,25 @@
#include "DNA_space_types.h"
#include "outliner_intern.hh"
+#include "tree/tree_iterator.hh"
-static void outliner_context_selected_ids_recursive(const ListBase *subtree,
+using namespace blender::ed::outliner;
+
+static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner,
bContextDataResult *result)
{
- LISTBASE_FOREACH (const TreeElement *, te, subtree) {
+ tree_iterator::all(space_outliner, [&](const TreeElement *te) {
const TreeStoreElem *tse = TREESTORE(te);
if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))) {
CTX_data_id_list_add(result, tse->id);
}
- outliner_context_selected_ids_recursive(&te->subtree, result);
- }
+ });
}
static void outliner_context_selected_ids(const SpaceOutliner *space_outliner,
bContextDataResult *result)
{
- outliner_context_selected_ids_recursive(&space_outliner->tree, result);
+ outliner_context_selected_ids_recursive(*space_outliner, result);
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc
index 88640210ea3..e20958c1b1e 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.cc
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc
@@ -170,7 +170,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C,
*r_insert_type = TE_INSERT_BEFORE;
return first;
}
- BLI_assert(0);
+
+ BLI_assert_unreachable();
return nullptr;
}
@@ -315,7 +316,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
- bool changed = outliner_flag_set(&space_outliner->tree, TSE_DRAG_ANY, false);
+ bool changed = outliner_flag_set(*space_outliner, TSE_DRAG_ANY, false);
if (changed) {
ED_region_tag_redraw_no_rebuild(CTX_wm_region(C));
}
@@ -846,8 +847,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
ARegion *region = CTX_wm_region(C);
- bool changed = outliner_flag_set(
- &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
+ bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin);
if (!drop_data) {
@@ -1194,8 +1194,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
ARegion *region = CTX_wm_region(C);
- bool changed = outliner_flag_set(
- &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
+ bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
CollectionDrop data;
if (((event->modifier & KM_SHIFT) == 0) &&
@@ -1460,7 +1459,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
/* Only drag element under mouse if it was not selected before. */
if ((tselem->flag & TSE_SELECTED) == 0) {
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0);
+ outliner_flag_set(*space_outliner, TSE_SELECTED, 0);
tselem->flag |= TSE_SELECTED;
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index d898be4eb2c..753de83a10d 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -38,6 +38,7 @@
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_report.h"
@@ -70,7 +71,9 @@
#include "tree/tree_element_id.hh"
#include "tree/tree_element_overrides.hh"
#include "tree/tree_element_rna.hh"
+#include "tree/tree_iterator.hh"
+using namespace blender;
using namespace blender::ed::outliner;
/* -------------------------------------------------------------------- */
@@ -1713,72 +1716,70 @@ static void outliner_draw_restrictbuts(uiBlock *block,
}
static void outliner_draw_userbuts(uiBlock *block,
- ARegion *region,
- SpaceOutliner *space_outliner,
- ListBase *lb)
+ const ARegion *region,
+ const SpaceOutliner *space_outliner)
{
+ tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) {
+ if (!outliner_is_element_in_view(te, &region->v2d)) {
+ return;
+ }
- LISTBASE_FOREACH (TreeElement *, te, lb) {
- TreeStoreElem *tselem = TREESTORE(te);
- if (outliner_is_element_in_view(te, &region->v2d)) {
- if (tselem->type == TSE_SOME_ID) {
- uiBut *bt;
- ID *id = tselem->id;
- const char *tip = nullptr;
- char buf[16] = "";
- int but_flag = UI_BUT_DRAG_LOCK;
+ const TreeStoreElem *tselem = TREESTORE(te);
+ if (tselem->type != TSE_SOME_ID) {
+ return;
+ }
- if (ID_IS_LINKED(id)) {
- but_flag |= UI_BUT_DISABLED;
- }
+ uiBut *bt;
+ ID *id = tselem->id;
+ const char *tip = nullptr;
+ char buf[16] = "";
+ int but_flag = UI_BUT_DRAG_LOCK;
- BLI_str_format_int_grouped(buf, id->us);
- bt = uiDefBut(block,
- UI_BTYPE_BUT,
- 1,
- buf,
- (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS),
- te->ys,
- UI_UNIT_X,
- UI_UNIT_Y,
- nullptr,
- 0.0,
- 0.0,
- 0,
- 0,
- TIP_("Number of users of this data-block"));
- UI_but_flag_enable(bt, but_flag);
-
- if (id->flag & LIB_FAKEUSER) {
- tip = TIP_("Data-block will be retained using a fake user");
- }
- else {
- tip = TIP_("Data-block has no users and will be deleted");
- }
- bt = uiDefIconButBitS(block,
- UI_BTYPE_ICON_TOGGLE,
- LIB_FAKEUSER,
- 1,
- ICON_FAKE_USER_OFF,
- (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
- te->ys,
- UI_UNIT_X,
- UI_UNIT_Y,
- &id->flag,
- 0,
- 0,
- 0,
- 0,
- tip);
- UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr);
- UI_but_flag_enable(bt, but_flag);
- }
+ if (ID_IS_LINKED(id)) {
+ but_flag |= UI_BUT_DISABLED;
}
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_draw_userbuts(block, region, space_outliner, &te->subtree);
+ BLI_str_format_int_grouped(buf, id->us);
+ bt = uiDefBut(block,
+ UI_BTYPE_BUT,
+ 1,
+ buf,
+ (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS),
+ te->ys,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ nullptr,
+ 0.0,
+ 0.0,
+ 0,
+ 0,
+ TIP_("Number of users of this data-block"));
+ UI_but_flag_enable(bt, but_flag);
+
+ if (id->flag & LIB_FAKEUSER) {
+ tip = TIP_("Data-block will be retained using a fake user");
}
- }
+ else {
+ tip = TIP_("Data-block has no users and will be deleted");
+ }
+ bt = uiDefIconButBitS(block,
+ UI_BTYPE_ICON_TOGGLE,
+ LIB_FAKEUSER,
+ 1,
+ ICON_FAKE_USER_OFF,
+ (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
+ te->ys,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ &id->flag,
+ 0,
+ 0,
+ 0,
+ 0,
+ tip);
+ UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr);
+ UI_but_flag_enable(bt, but_flag);
+ });
}
static void outliner_draw_overrides_rna_buts(uiBlock *block,
@@ -1808,6 +1809,25 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block,
TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>(
te);
+ if (!override_elem.is_rna_path_valid) {
+ uiBut *but = uiDefBut(block,
+ UI_BTYPE_LABEL,
+ 0,
+ override_elem.rna_path.c_str(),
+ x + pad_x,
+ te->ys + pad_y,
+ item_max_width,
+ item_height,
+ nullptr,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ "");
+ UI_but_flag_enable(but, UI_BUT_REDALERT);
+ continue;
+ }
+
PointerRNA *ptr = &override_elem.override_rna_ptr;
PropertyRNA *prop = &override_elem.override_rna_prop;
const PropertyType prop_type = RNA_property_type(prop);
@@ -1900,90 +1920,6 @@ static void outliner_draw_overrides_restrictbuts(Main *bmain,
}
}
-static bool outliner_draw_overrides_warning_buts(uiBlock *block,
- ARegion *region,
- SpaceOutliner *space_outliner,
- ListBase *lb,
- const bool is_open)
-{
- bool any_item_has_warnings = false;
-
- LISTBASE_FOREACH (TreeElement *, te, lb) {
- bool item_has_warnings = false;
- const bool do_draw = outliner_is_element_in_view(te, &region->v2d);
- int but_flag = UI_BUT_DRAG_LOCK;
- const char *tip = nullptr;
-
- TreeStoreElem *tselem = TREESTORE(te);
- switch (tselem->type) {
- case TSE_LIBRARY_OVERRIDE_BASE: {
- ID *id = tselem->id;
-
- if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) {
- item_has_warnings = true;
- if (do_draw) {
- tip = TIP_(
- "This override data-block is not needed anymore, but was detected as user-edited");
- }
- }
- else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) {
- item_has_warnings = true;
- if (do_draw) {
- tip = TIP_("This override data-block is unused");
- }
- }
- break;
- }
- case TSE_LIBRARY_OVERRIDE: {
- const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata));
- if (!is_rna_path_valid) {
- item_has_warnings = true;
- if (do_draw) {
- tip = TIP_(
- "This override property does not exist in current data, it will be removed on "
- "next .blend file save");
- }
- }
- break;
- }
- default:
- break;
- }
-
- const bool any_child_has_warnings = outliner_draw_overrides_warning_buts(
- block,
- region,
- space_outliner,
- &te->subtree,
- is_open && TSELEM_OPEN(tselem, space_outliner));
-
- if (do_draw &&
- (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) {
- if (tip == nullptr) {
- tip = TIP_("Some sub-items require attention");
- }
- uiBut *bt = uiDefIconBut(block,
- UI_BTYPE_BUT,
- 1,
- ICON_ERROR,
- (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
- te->ys,
- UI_UNIT_X,
- UI_UNIT_Y,
- nullptr,
- 0.0,
- 0.0,
- 0.0,
- 0.0,
- tip);
- UI_but_flag_enable(bt, but_flag);
- }
- any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings;
- }
-
- return any_item_has_warnings;
-}
-
static void outliner_draw_separator(ARegion *region, const int x)
{
View2D *v2d = &region->v2d;
@@ -2004,81 +1940,82 @@ static void outliner_draw_separator(ARegion *region, const int x)
immUnbindProgram();
}
-static void outliner_draw_rnabuts(
- uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb)
+static void outliner_draw_rnabuts(uiBlock *block,
+ ARegion *region,
+ SpaceOutliner *space_outliner,
+ int sizex)
{
PointerRNA ptr;
PropertyRNA *prop;
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
- if (outliner_is_element_in_view(te, &region->v2d)) {
- if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) {
- ptr = te_rna_prop->getPointerRNA();
- prop = te_rna_prop->getPropertyRNA();
-
- if (!TSELEM_OPEN(tselem, space_outliner)) {
- if (RNA_property_type(prop) == PROP_POINTER) {
- uiBut *but = uiDefAutoButR(block,
- &ptr,
- prop,
- -1,
- "",
- ICON_NONE,
- sizex,
- te->ys,
- OL_RNA_COL_SIZEX,
- UI_UNIT_Y - 1);
- UI_but_flag_enable(but, UI_BUT_DISABLED);
- }
- else if (RNA_property_type(prop) == PROP_ENUM) {
- uiDefAutoButR(block,
- &ptr,
- prop,
- -1,
- nullptr,
- ICON_NONE,
- sizex,
- te->ys,
- OL_RNA_COL_SIZEX,
- UI_UNIT_Y - 1);
- }
- else {
- uiDefAutoButR(block,
- &ptr,
- prop,
- -1,
- "",
- ICON_NONE,
- sizex,
- te->ys,
- OL_RNA_COL_SIZEX,
- UI_UNIT_Y - 1);
- }
- }
- }
- else if (TreeElementRNAArrayElement *te_rna_array_elem =
- tree_element_cast<TreeElementRNAArrayElement>(te)) {
- ptr = te_rna_array_elem->getPointerRNA();
- prop = te_rna_array_elem->getPropertyRNA();
-
- uiDefAutoButR(block,
- &ptr,
- prop,
- te->index,
- "",
- ICON_NONE,
- sizex,
- te->ys,
- OL_RNA_COL_SIZEX,
- UI_UNIT_Y - 1);
- }
- }
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_draw_rnabuts(block, region, space_outliner, sizex, &te->subtree);
- }
- }
+ if (!outliner_is_element_in_view(te, &region->v2d)) {
+ return;
+ }
+
+ if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) {
+ ptr = te_rna_prop->getPointerRNA();
+ prop = te_rna_prop->getPropertyRNA();
+
+ if (!TSELEM_OPEN(tselem, space_outliner)) {
+ if (RNA_property_type(prop) == PROP_POINTER) {
+ uiBut *but = uiDefAutoButR(block,
+ &ptr,
+ prop,
+ -1,
+ "",
+ ICON_NONE,
+ sizex,
+ te->ys,
+ OL_RNA_COL_SIZEX,
+ UI_UNIT_Y - 1);
+ UI_but_flag_enable(but, UI_BUT_DISABLED);
+ }
+ else if (RNA_property_type(prop) == PROP_ENUM) {
+ uiDefAutoButR(block,
+ &ptr,
+ prop,
+ -1,
+ nullptr,
+ ICON_NONE,
+ sizex,
+ te->ys,
+ OL_RNA_COL_SIZEX,
+ UI_UNIT_Y - 1);
+ }
+ else {
+ uiDefAutoButR(block,
+ &ptr,
+ prop,
+ -1,
+ "",
+ ICON_NONE,
+ sizex,
+ te->ys,
+ OL_RNA_COL_SIZEX,
+ UI_UNIT_Y - 1);
+ }
+ }
+ }
+ else if (TreeElementRNAArrayElement *te_rna_array_elem =
+ tree_element_cast<TreeElementRNAArrayElement>(te)) {
+ ptr = te_rna_array_elem->getPointerRNA();
+ prop = te_rna_array_elem->getPropertyRNA();
+
+ uiDefAutoButR(block,
+ &ptr,
+ prop,
+ te->index,
+ "",
+ ICON_NONE,
+ sizex,
+ te->ys,
+ OL_RNA_COL_SIZEX,
+ UI_UNIT_Y - 1);
+ }
+ });
}
static void outliner_buttons(const bContext *C,
@@ -2164,9 +2101,9 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED
static void outliner_draw_mode_column_toggle(uiBlock *block,
TreeViewContext *tvc,
TreeElement *te,
- TreeStoreElem *tselem,
const bool lock_object_modes)
{
+ TreeStoreElem *tselem = TREESTORE(te);
if ((tselem->type != TSE_SOME_ID) || (te->idcode != ID_OB)) {
return;
}
@@ -2237,59 +2174,63 @@ static void outliner_draw_mode_column_toggle(uiBlock *block,
}
}
-static void outliner_draw_mode_column(const bContext *C,
- uiBlock *block,
+static void outliner_draw_mode_column(uiBlock *block,
TreeViewContext *tvc,
- SpaceOutliner *space_outliner,
- ListBase *tree)
+ SpaceOutliner *space_outliner)
{
- TreeStoreElem *tselem;
const bool lock_object_modes = tvc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK;
- LISTBASE_FOREACH (TreeElement *, te, tree) {
- tselem = TREESTORE(te);
-
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
if (tvc->obact && tvc->obact->mode != OB_MODE_OBJECT) {
- outliner_draw_mode_column_toggle(block, tvc, te, tselem, lock_object_modes);
+ outliner_draw_mode_column_toggle(block, tvc, te, lock_object_modes);
}
+ });
+}
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_draw_mode_column(C, block, tvc, space_outliner, &te->subtree);
+static StringRefNull outliner_draw_get_warning_tree_element_subtree(const TreeElement *parent_te)
+{
+ LISTBASE_FOREACH (const TreeElement *, sub_te, &parent_te->subtree) {
+ const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(sub_te);
+ StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : "";
+
+ if (!warning_msg.is_empty()) {
+ return warning_msg;
+ }
+
+ warning_msg = outliner_draw_get_warning_tree_element_subtree(sub_te);
+ if (!warning_msg.is_empty()) {
+ return warning_msg;
}
}
+
+ return "";
}
-/* Returns `true` if some warning was drawn for that element or one of its sub-elements (if it is
- * not open). */
-static bool outliner_draw_warning_tree_element(uiBlock *block,
- SpaceOutliner *space_outliner,
- TreeElement *te,
- TreeStoreElem *tselem,
- const bool use_mode_column,
- const int te_ys)
+static StringRefNull outliner_draw_get_warning_tree_element(const SpaceOutliner &space_outliner,
+ const TreeElement *te)
{
- if ((te->flag & TE_HAS_WARNING) == 0) {
- /* If given element has no warning, recursively try to display the first sub-elements' warning.
- */
- if (!TSELEM_OPEN(tselem, space_outliner)) {
- LISTBASE_FOREACH (TreeElement *, sub_te, &te->subtree) {
- TreeStoreElem *sub_tselem = TREESTORE(sub_te);
+ const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(te);
+ const StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : "";
- if (outliner_draw_warning_tree_element(
- block, space_outliner, sub_te, sub_tselem, use_mode_column, te_ys)) {
- return true;
- }
- }
- }
- return false;
+ if (!warning_msg.is_empty()) {
+ return warning_msg;
}
- int icon = ICON_NONE;
- const char *tip = "";
- const bool has_warning = tree_element_warnings_get(te, &icon, &tip);
- BLI_assert(has_warning);
- UNUSED_VARS_NDEBUG(has_warning);
+ /* If given element has no warning, recursively try to display the first sub-element's warning.
+ */
+ if (!TSELEM_OPEN(te->store_elem, &space_outliner)) {
+ return outliner_draw_get_warning_tree_element_subtree(te);
+ }
+ return "";
+}
+
+static void outliner_draw_warning_tree_element(uiBlock *block,
+ const SpaceOutliner *space_outliner,
+ StringRefNull warning_msg,
+ const bool use_mode_column,
+ const int te_ys)
+{
/* Move the warnings a unit left in view layer mode. */
const short mode_column_offset = (use_mode_column && (space_outliner->outlinevis == SO_SCENES)) ?
UI_UNIT_X :
@@ -2299,7 +2240,7 @@ static bool outliner_draw_warning_tree_element(uiBlock *block,
uiBut *but = uiDefIconBut(block,
UI_BTYPE_ICON_TOGGLE,
0,
- icon,
+ ICON_ERROR,
mode_column_offset,
te_ys,
UI_UNIT_X,
@@ -2309,28 +2250,25 @@ static bool outliner_draw_warning_tree_element(uiBlock *block,
0.0,
0.0,
0.0,
- tip);
+ warning_msg.c_str());
/* No need for undo here, this is a pure info widget. */
UI_but_flag_disable(but, UI_BUT_UNDO);
-
- return true;
}
-static void outliner_draw_warning_column(const bContext *C,
- uiBlock *block,
- SpaceOutliner *space_outliner,
- const bool use_mode_column,
- ListBase *tree)
+static void outliner_draw_warning_column(uiBlock *block,
+ const SpaceOutliner *space_outliner,
+ const bool use_mode_column)
{
- LISTBASE_FOREACH (TreeElement *, te, tree) {
- TreeStoreElem *tselem = TREESTORE(te);
+ tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) {
+ /* Get warning for this element, or if there is none and the element is collapsed, the first
+ * warning in the collapsed sub-tree. */
+ StringRefNull warning_msg = outliner_draw_get_warning_tree_element(*space_outliner, te);
- outliner_draw_warning_tree_element(block, space_outliner, te, tselem, use_mode_column, te->ys);
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &te->subtree);
+ if (!warning_msg.is_empty()) {
+ outliner_draw_warning_tree_element(
+ block, space_outliner, warning_msg, use_mode_column, te->ys);
}
- }
+ });
}
/** \} */
@@ -2504,6 +2442,11 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id)
return ICON_WORKSPACE;
case ID_MSK:
return ICON_MOD_MASK;
+ case ID_NT: {
+ const bNodeTree *ntree = (bNodeTree *)id;
+ const bNodeTreeType *ntreetype = ntree->typeinfo;
+ return (BIFIconID)ntreetype->ui_icon;
+ }
case ID_MC:
return ICON_SEQUENCE;
case ID_PC:
@@ -3206,18 +3149,16 @@ static void outliner_draw_iconrow(bContext *C,
}
/* closed tree element */
-static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty)
+static void outliner_set_subtree_coords(const TreeElement *te)
{
- /* closed items may be displayed in row of parent, don't change their coordinate! */
- if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) {
- te->xs = 0;
- te->ys = 0;
- te->xend = 0;
- }
-
- LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) {
- outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty);
- }
+ tree_iterator::all(te->subtree, [&](TreeElement *te) {
+ /* closed items may be displayed in row of parent, don't change their coordinate! */
+ if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) {
+ te->xs = 0;
+ te->ys = 0;
+ te->xend = 0;
+ }
+ });
}
static bool element_should_draw_faded(const TreeViewContext *tvc,
@@ -3469,10 +3410,7 @@ static void outliner_draw_tree_element(bContext *C,
}
}
else {
- LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) {
- outliner_set_coord_tree_element(ten, startx, *starty);
- }
-
+ outliner_set_subtree_coords(te);
*starty -= UI_UNIT_Y;
}
}
@@ -3628,22 +3566,21 @@ static void outliner_draw_struct_marks(ARegion *region,
}
}
-static void outliner_draw_highlights_recursive(uint pos,
- const ARegion *region,
- const SpaceOutliner *space_outliner,
- const ListBase *lb,
- const float col_selection[4],
- const float col_active[4],
- const float col_highlight[4],
- const float col_searchmatch[4],
- int start_x,
- int *io_start_y)
+static void outliner_draw_highlights(uint pos,
+ const ARegion *region,
+ const SpaceOutliner *space_outliner,
+ const float col_selection[4],
+ const float col_active[4],
+ const float col_highlight[4],
+ const float col_searchmatch[4],
+ int start_x,
+ int *io_start_y)
{
const bool is_searching = (SEARCHING_OUTLINER(space_outliner) ||
(space_outliner->outlinevis == SO_DATA_API &&
space_outliner->search_string[0] != 0));
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) {
const TreeStoreElem *tselem = TREESTORE(te);
const int start_y = *io_start_y;
@@ -3699,19 +3636,7 @@ static void outliner_draw_highlights_recursive(uint pos,
}
*io_start_y -= UI_UNIT_Y;
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_draw_highlights_recursive(pos,
- region,
- space_outliner,
- &te->subtree,
- col_selection,
- col_active,
- col_highlight,
- col_searchmatch,
- start_x + UI_UNIT_X,
- io_start_y);
- }
- }
+ });
}
static void outliner_draw_highlights(ARegion *region,
@@ -3733,16 +3658,15 @@ static void outliner_draw_highlights(ARegion *region,
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- outliner_draw_highlights_recursive(pos,
- region,
- space_outliner,
- &space_outliner->tree,
- col_selection,
- col_active,
- col_highlight,
- col_searchmatch,
- startx,
- starty);
+ outliner_draw_highlights(pos,
+ region,
+ space_outliner,
+ col_selection,
+ col_active,
+ col_highlight,
+ col_searchmatch,
+ startx,
+ starty);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
@@ -3935,13 +3859,8 @@ void draw_outliner(const bContext *C)
UI_view2d_view_ortho(v2d);
/* Only show mode column in View Layers and Scenes view. */
- const bool use_mode_column = (space_outliner->flag & SO_MODE_COLUMN) &&
- (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES));
-
- const bool use_warning_column = ELEM(space_outliner->outlinevis,
- SO_LIBRARIES,
- SO_OVERRIDES_LIBRARY) &&
- space_outliner->runtime->tree_display->hasWarnings();
+ const bool use_mode_column = outliner_shows_mode_column(*space_outliner);
+ const bool use_warning_column = outliner_has_element_warnings(*space_outliner);
/* Draw outliner stuff (background, hierarchy lines and names). */
const float right_column_width = outliner_right_columns_width(space_outliner);
@@ -3971,18 +3890,14 @@ void draw_outliner(const bContext *C)
outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX);
UI_block_emboss_set(block, UI_EMBOSS);
- outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree);
+ outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x);
UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS);
}
else if (space_outliner->outlinevis == SO_ID_ORPHANS) {
/* draw user toggle columns */
- outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree);
+ outliner_draw_userbuts(block, region, space_outliner);
}
else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) {
- /* Draw overrides status columns. */
- outliner_draw_overrides_warning_buts(
- block, region, space_outliner, &space_outliner->tree, true);
-
const int x = region->v2d.cur.xmax - right_column_width;
outliner_draw_separator(region, x);
if (space_outliner->lib_override_view_mode == SO_LIB_OVERRIDE_VIEW_PROPERTIES) {
@@ -4011,12 +3926,12 @@ void draw_outliner(const bContext *C)
/* Draw mode icons */
if (use_mode_column) {
- outliner_draw_mode_column(C, block, &tvc, space_outliner, &space_outliner->tree);
+ outliner_draw_mode_column(block, &tvc, space_outliner);
}
/* Draw warning icons */
if (use_warning_column) {
- outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &space_outliner->tree);
+ outliner_draw_warning_column(block, space_outliner, use_mode_column);
}
UI_block_emboss_set(block, UI_EMBOSS);
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index d6c5901b546..c4a9398a5f7 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -60,6 +60,7 @@
#include "outliner_intern.hh"
#include "tree/tree_element_rna.hh"
+#include "tree/tree_iterator.hh"
using namespace blender::ed::outliner;
@@ -107,7 +108,7 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const
if (!hovered_te || !is_over_icon || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED) ||
!(icon_te->store_elem->flag & TSE_HIGHLIGHTED_ICON)) {
/* Clear highlights when nothing is hovered or when a new item is hovered. */
- changed = outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
+ changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
if (hovered_te) {
hovered_te->store_elem->flag |= TSE_HIGHLIGHTED;
changed = true;
@@ -167,7 +168,7 @@ void outliner_item_openclose(SpaceOutliner *space_outliner,
}
if (toggle_all) {
- outliner_flag_set(&te->subtree, TSE_CLOSED, !open);
+ outliner_flag_set(te->subtree, TSE_CLOSED, !open);
}
}
@@ -334,8 +335,16 @@ static void do_item_rename(ARegion *region,
add_textbut = true;
}
}
- else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) {
- BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
+ else if (te->idcode == ID_LI) {
+ if (reinterpret_cast<Library *>(tselem->id)->parent) {
+ BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
+ }
+ else {
+ BKE_report(
+ reports,
+ RPT_WARNING,
+ "Library path is not editable from here anymore, please use Relocate operation instead");
+ }
}
else {
add_textbut = true;
@@ -1069,11 +1078,16 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel)
return 0;
}
-bool outliner_flag_set(ListBase *lb, short flag, short set)
+bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set)
+{
+ return outliner_flag_set(space_outliner.tree, flag, set);
+}
+
+bool outliner_flag_set(const ListBase &lb, const short flag, const short set)
{
bool changed = false;
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all(lb, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
bool has_flag = (tselem->flag & flag);
if (set == 0) {
@@ -1086,21 +1100,24 @@ bool outliner_flag_set(ListBase *lb, short flag, short set)
tselem->flag |= flag;
changed = true;
}
- changed |= outliner_flag_set(&te->subtree, flag, set);
- }
+ });
return changed;
}
-bool outliner_flag_flip(ListBase *lb, short flag)
+bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag)
+{
+ return outliner_flag_flip(space_outliner.tree, flag);
+}
+
+bool outliner_flag_flip(const ListBase &lb, const short flag)
{
bool changed = false;
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all(lb, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
tselem->flag ^= flag;
- changed |= outliner_flag_flip(&te->subtree, flag);
- }
+ });
return changed;
}
@@ -1117,10 +1134,10 @@ static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
ARegion *region = CTX_wm_region(C);
if (outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1)) {
- outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 0);
+ outliner_flag_set(*space_outliner, TSE_CLOSED, 0);
}
else {
- outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 1);
+ outliner_flag_set(*space_outliner, TSE_CLOSED, 1);
}
ED_region_tag_redraw(region);
@@ -1161,13 +1178,13 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op)
switch (action) {
case SEL_SELECT:
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 1);
+ outliner_flag_set(*space_outliner, TSE_SELECTED, 1);
break;
case SEL_DESELECT:
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0);
+ outliner_flag_set(*space_outliner, TSE_SELECTED, 0);
break;
case SEL_INVERT:
- outliner_flag_flip(&space_outliner->tree, TSE_SELECTED);
+ outliner_flag_flip(*space_outliner, TSE_SELECTED);
break;
}
@@ -1203,32 +1220,16 @@ void OUTLINER_OT_select_all(wmOperatorType *ot)
/** \name View Show Active (Outliner) Operator
* \{ */
-static void outliner_set_coordinates_element_recursive(SpaceOutliner *space_outliner,
- TreeElement *te,
- int startx,
- int *starty)
-{
- TreeStoreElem *tselem = TREESTORE(te);
-
- /* store coord and continue, we need coordinates for elements outside view too */
- te->xs = (float)startx;
- te->ys = (float)(*starty);
- *starty -= UI_UNIT_Y;
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) {
- outliner_set_coordinates_element_recursive(space_outliner, ten, startx + UI_UNIT_X, starty);
- }
- }
-}
-
-void outliner_set_coordinates(ARegion *region, SpaceOutliner *space_outliner)
+void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner)
{
int starty = (int)(region->v2d.tot.ymax) - UI_UNIT_Y;
- LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
- outliner_set_coordinates_element_recursive(space_outliner, te, 0, &starty);
- }
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
+ /* store coord and continue, we need coordinates for elements outside view too */
+ te->xs = 0;
+ te->ys = (float)starty;
+ starty -= UI_UNIT_Y;
+ });
}
/* return 1 when levels were opened */
@@ -1616,11 +1617,11 @@ static int subtree_has_objects(ListBase *lb)
return 0;
}
-/* recursive helper function for Show Hierarchy operator */
-static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner, ListBase *lb)
+/* Helper function for Show Hierarchy operator */
+static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner)
{
/* open all object elems, close others */
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
if (ELEM(tselem->type,
@@ -1648,11 +1649,7 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outli
else {
tselem->flag |= TSE_CLOSED;
}
-
- if (TSELEM_OPEN(tselem, space_outliner)) {
- tree_element_show_hierarchy(scene, space_outliner, &te->subtree);
- }
- }
+ });
}
/* show entire object level hierarchy */
@@ -1663,7 +1660,7 @@ static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
Scene *scene = CTX_data_scene(C);
/* recursively open/close levels */
- tree_element_show_hierarchy(scene, space_outliner, &space_outliner->tree);
+ tree_element_show_hierarchy(scene, space_outliner);
ED_region_tag_redraw(region);
@@ -1865,79 +1862,75 @@ enum {
DRIVERS_EDITMODE_REMOVE,
} /*eDrivers_EditModes*/;
-/* Recursively iterate over tree, finding and working on selected items */
+/* Iterate over tree, finding and working on selected items */
static void do_outliner_drivers_editop(SpaceOutliner *space_outliner,
- ListBase *tree,
ReportList *reports,
short mode)
{
- LISTBASE_FOREACH (TreeElement *, te, tree) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
/* if item is selected, perform operation */
- if (tselem->flag & TSE_SELECTED) {
- ID *id = nullptr;
- char *path = nullptr;
- int array_index = 0;
- short flag = 0;
- short groupmode = KSP_GROUP_KSNAME;
-
- TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
- PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL;
- PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr;
-
- /* check if RNA-property described by this selected element is an animatable prop */
- if (prop && RNA_property_animateable(&ptr, prop)) {
- /* get id + path + index info from the selected element */
- tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
- }
+ if (!(tselem->flag & TSE_SELECTED)) {
+ return;
+ }
- /* only if ID and path were set, should we perform any actions */
- if (id && path) {
- short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
- int arraylen = 1;
+ ID *id = nullptr;
+ char *path = nullptr;
+ int array_index = 0;
+ short flag = 0;
+ short groupmode = KSP_GROUP_KSNAME;
- /* array checks */
- if (flag & KSP_FLAG_WHOLE_ARRAY) {
- /* entire array was selected, so add drivers for all */
- arraylen = RNA_property_array_length(&ptr, prop);
- }
- else {
- arraylen = array_index;
- }
+ TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
+ PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL;
+ PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr;
- /* we should do at least one step */
- if (arraylen == array_index) {
- arraylen++;
- }
+ /* check if RNA-property described by this selected element is an animatable prop */
+ if (prop && RNA_property_animateable(&ptr, prop)) {
+ /* get id + path + index info from the selected element */
+ tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
+ }
- /* for each array element we should affect, add driver */
- for (; array_index < arraylen; array_index++) {
- /* action depends on mode */
- switch (mode) {
- case DRIVERS_EDITMODE_ADD: {
- /* add a new driver with the information obtained (only if valid) */
- ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
- break;
- }
- case DRIVERS_EDITMODE_REMOVE: {
- /* remove driver matching the information obtained (only if valid) */
- ANIM_remove_driver(reports, id, path, array_index, dflags);
- break;
- }
+ /* only if ID and path were set, should we perform any actions */
+ if (id && path) {
+ short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
+ int arraylen = 1;
+
+ /* array checks */
+ if (flag & KSP_FLAG_WHOLE_ARRAY) {
+ /* entire array was selected, so add drivers for all */
+ arraylen = RNA_property_array_length(&ptr, prop);
+ }
+ else {
+ arraylen = array_index;
+ }
+
+ /* we should do at least one step */
+ if (arraylen == array_index) {
+ arraylen++;
+ }
+
+ /* for each array element we should affect, add driver */
+ for (; array_index < arraylen; array_index++) {
+ /* action depends on mode */
+ switch (mode) {
+ case DRIVERS_EDITMODE_ADD: {
+ /* add a new driver with the information obtained (only if valid) */
+ ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
+ break;
+ }
+ case DRIVERS_EDITMODE_REMOVE: {
+ /* remove driver matching the information obtained (only if valid) */
+ ANIM_remove_driver(reports, id, path, array_index, dflags);
+ break;
}
}
-
- /* free path, since it had to be generated */
- MEM_freeN(path);
}
- }
- /* go over sub-tree */
- if (TSELEM_OPEN(tselem, space_outliner)) {
- do_outliner_drivers_editop(space_outliner, &te->subtree, reports, mode);
+ /* free path, since it had to be generated */
+ MEM_freeN(path);
}
- }
+ });
}
/** \} */
@@ -1956,8 +1949,7 @@ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
}
/* recursively go into tree, adding selected items */
- do_outliner_drivers_editop(
- space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
+ do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_ADD);
/* send notifiers */
WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, nullptr); /* XXX */
@@ -1996,8 +1988,7 @@ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
}
/* recursively go into tree, adding selected items */
- do_outliner_drivers_editop(
- space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
+ do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_REMOVE);
/* send notifiers */
WM_event_add_notifier(C, ND_KEYS, nullptr); /* XXX */
@@ -2064,68 +2055,64 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add)
return ks;
}
-/* Recursively iterate over tree, finding and working on selected items */
+/* Iterate over tree, finding and working on selected items */
static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner,
KeyingSet *ks,
- ListBase *tree,
- short mode)
+ const short mode)
{
- LISTBASE_FOREACH (TreeElement *, te, tree) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
/* if item is selected, perform operation */
- if (tselem->flag & TSE_SELECTED) {
- ID *id = nullptr;
- char *path = nullptr;
- int array_index = 0;
- short flag = 0;
- short groupmode = KSP_GROUP_KSNAME;
-
- /* check if RNA-property described by this selected element is an animatable prop */
- const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
- PointerRNA ptr = te_rna->getPointerRNA();
- if (te_rna && te_rna->getPropertyRNA() &&
- RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) {
- /* get id + path + index info from the selected element */
- tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
- }
+ if (!(tselem->flag & TSE_SELECTED)) {
+ return;
+ }
- /* only if ID and path were set, should we perform any actions */
- if (id && path) {
- /* action depends on mode */
- switch (mode) {
- case KEYINGSET_EDITMODE_ADD: {
- /* add a new path with the information obtained (only if valid) */
- /* TODO: what do we do with group name?
- * for now, we don't supply one, and just let this use the KeyingSet name */
- BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode);
- ks->active_path = BLI_listbase_count(&ks->paths);
- break;
- }
- case KEYINGSET_EDITMODE_REMOVE: {
- /* find the relevant path, then remove it from the KeyingSet */
- KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode);
+ ID *id = nullptr;
+ char *path = nullptr;
+ int array_index = 0;
+ short flag = 0;
+ short groupmode = KSP_GROUP_KSNAME;
+
+ /* check if RNA-property described by this selected element is an animatable prop */
+ const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te);
+ PointerRNA ptr = te_rna->getPointerRNA();
+ if (te_rna && te_rna->getPropertyRNA() &&
+ RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) {
+ /* get id + path + index info from the selected element */
+ tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
+ }
- if (ksp) {
- /* free path's data */
- BKE_keyingset_free_path(ks, ksp);
+ /* only if ID and path were set, should we perform any actions */
+ if (id && path) {
+ /* action depends on mode */
+ switch (mode) {
+ case KEYINGSET_EDITMODE_ADD: {
+ /* add a new path with the information obtained (only if valid) */
+ /* TODO: what do we do with group name?
+ * for now, we don't supply one, and just let this use the KeyingSet name */
+ BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode);
+ ks->active_path = BLI_listbase_count(&ks->paths);
+ break;
+ }
+ case KEYINGSET_EDITMODE_REMOVE: {
+ /* find the relevant path, then remove it from the KeyingSet */
+ KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode);
- ks->active_path = 0;
- }
- break;
+ if (ksp) {
+ /* free path's data */
+ BKE_keyingset_free_path(ks, ksp);
+
+ ks->active_path = 0;
}
+ break;
}
-
- /* free path, since it had to be generated */
- MEM_freeN(path);
}
- }
- /* go over sub-tree */
- if (TSELEM_OPEN(tselem, space_outliner)) {
- do_outliner_keyingset_editop(space_outliner, ks, &te->subtree, mode);
+ /* free path, since it had to be generated */
+ MEM_freeN(path);
}
- }
+ });
}
/** \} */
@@ -2150,7 +2137,7 @@ static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
}
/* recursively go into tree, adding selected items */
- do_outliner_keyingset_editop(space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_ADD);
+ do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_ADD);
/* send notifiers */
WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr);
@@ -2191,8 +2178,7 @@ static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(o
}
/* recursively go into tree, adding selected items */
- do_outliner_keyingset_editop(
- space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_REMOVE);
+ do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_REMOVE);
/* send notifiers */
WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr);
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index f3bcb7b0f1e..a0dcb49aa43 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -160,8 +160,6 @@ enum {
/* Child elements of the same type in the icon-row are drawn merged as one icon.
* This flag is set for an element that is part of these merged child icons. */
TE_ICONROW_MERGED = (1 << 7),
- /* This element has some warning to be displayed. */
- TE_HAS_WARNING = (1 << 8),
};
/* button events */
@@ -410,8 +408,12 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel);
* Set or unset \a flag for all outliner elements in \a lb and sub-trees.
* \return if any flag was modified.
*/
-bool outliner_flag_set(ListBase *lb, short flag, short set);
-bool outliner_flag_flip(ListBase *lb, short flag);
+extern "C++" {
+bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set);
+bool outliner_flag_set(const ListBase &lb, short flag, short set);
+bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag);
+bool outliner_flag_flip(const ListBase &lb, short flag);
+}
void item_rename_fn(struct bContext *C,
struct ReportList *reports,
@@ -453,7 +455,8 @@ void id_remap_fn(struct bContext *C,
/**
* To retrieve coordinates with redrawing the entire tree.
*/
-void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner);
+void outliner_set_coordinates(const struct ARegion *region,
+ const struct SpaceOutliner *space_outliner);
/**
* Open or close a tree element, optionally toggling all children recursively.
@@ -510,6 +513,11 @@ void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot);
void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot);
+/* outliner_query.cc ---------------------------------------------- */
+
+bool outliner_shows_mode_column(const SpaceOutliner &space_outliner);
+bool outliner_has_element_warnings(const SpaceOutliner &space_outliner);
+
/* outliner_tools.c ---------------------------------------------- */
void merged_element_search_menu_invoke(struct bContext *C,
diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc
new file mode 100644
index 00000000000..d6483c44fce
--- /dev/null
+++ b/source/blender/editors/space_outliner/outliner_query.cc
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include <functional>
+
+#include "BLI_listbase.h"
+
+#include "DNA_space_types.h"
+
+#include "outliner_intern.hh"
+#include "tree/tree_display.hh"
+
+using namespace blender::ed::outliner;
+
+bool outliner_shows_mode_column(const SpaceOutliner &space_outliner)
+{
+ const AbstractTreeDisplay &tree_display = *space_outliner.runtime->tree_display;
+
+ return tree_display.supportsModeColumn() && (space_outliner.flag & SO_MODE_COLUMN);
+}
+
+/**
+ * Iterate over the entire tree (including collapsed sub-elements), probing if any of the elements
+ * has a warning to be displayed.
+ */
+bool outliner_has_element_warnings(const SpaceOutliner &space_outliner)
+{
+ std::function<bool(const ListBase &)> recursive_fn;
+
+ recursive_fn = [&](const ListBase &lb) {
+ LISTBASE_FOREACH (const TreeElement *, te, &lb) {
+ if (te->abstract_element && !te->abstract_element->getWarning().is_empty()) {
+ return true;
+ }
+
+ if (recursive_fn(te->subtree)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ return recursive_fn(space_outliner.tree);
+}
diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc
index fd0ee422df0..877e0fc325c 100644
--- a/source/blender/editors/space_outliner/outliner_select.cc
+++ b/source/blender/editors/space_outliner/outliner_select.cc
@@ -66,7 +66,9 @@
#include "RNA_prototypes.h"
#include "outliner_intern.hh"
+#include "tree/tree_display.hh"
#include "tree/tree_element_seq.hh"
+#include "tree/tree_iterator.hh"
using namespace blender::ed::outliner;
@@ -1456,7 +1458,7 @@ void outliner_item_select(bContext *C,
/* Clear previous active when activating and clear selection when not extending selection */
const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED);
if (clear_flag) {
- outliner_flag_set(&space_outliner->tree, clear_flag, false);
+ outliner_flag_set(*space_outliner, clear_flag, false);
}
if (select_flag & OL_ITEM_SELECT) {
@@ -1530,7 +1532,7 @@ static void do_outliner_range_select(bContext *C,
const bool active_selected = (tselem->flag & TSE_SELECTED);
if (!extend) {
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
+ outliner_flag_set(*space_outliner, TSE_SELECTED, false);
}
/* Select active if under cursor */
@@ -1557,12 +1559,11 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou
bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2])
{
- /* Mode toggles only show in View Layer and Scenes modes. */
- if (!ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) {
+ if (!outliner_shows_mode_column(*space_outliner)) {
return false;
}
- return space_outliner->flag & SO_MODE_COLUMN && view_mval[0] < UI_UNIT_X;
+ return view_mval[0] < UI_UNIT_X;
}
static bool outliner_is_co_within_active_mode_column(bContext *C,
@@ -1604,7 +1605,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) {
if (deselect_all) {
- changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
+ changed |= outliner_flag_set(*space_outliner, TSE_SELECTED, false);
}
}
/* Don't allow toggle on scene collection */
@@ -1692,7 +1693,7 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot)
ot->poll = ED_operator_outliner_active;
- ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection for activation");
@@ -1715,26 +1716,17 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot)
/** \name Box Select Operator
* \{ */
-static void outliner_item_box_select(bContext *C,
- SpaceOutliner *space_outliner,
- Scene *scene,
- rctf *rectf,
- TreeElement *te,
- bool select)
+static void outliner_box_select(bContext *C,
+ SpaceOutliner *space_outliner,
+ const rctf *rectf,
+ const bool select)
{
- TreeStoreElem *tselem = TREESTORE(te);
-
- if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
- outliner_item_select(
- C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND);
- }
-
- /* Look at its children. */
- if (TSELEM_OPEN(tselem, space_outliner)) {
- LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
- outliner_item_box_select(C, space_outliner, scene, rectf, te_sub, select);
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
+ if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
+ outliner_item_select(
+ C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND);
}
- }
+ });
}
static int outliner_box_select_exec(bContext *C, wmOperator *op)
@@ -1747,15 +1739,13 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op)
const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0);
+ outliner_flag_set(*space_outliner, TSE_SELECTED, 0);
}
WM_operator_properties_border_to_rctf(op, &rectf);
UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
- LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
- outliner_item_box_select(C, space_outliner, scene, &rectf, te, select);
- }
+ outliner_box_select(C, space_outliner, &rectf, select);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
@@ -2038,7 +2028,7 @@ void OUTLINER_OT_select_walk(wmOperatorType *ot)
ot->invoke = outliner_walk_select_invoke;
ot->poll = ED_operator_outliner_active;
- ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop;
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 5da64177e51..ec19e8d5e5b 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -84,6 +84,7 @@
#include "outliner_intern.hh"
#include "tree/tree_element_rna.hh"
#include "tree/tree_element_seq.hh"
+#include "tree/tree_iterator.hh"
static CLG_LogRef LOG = {"ed.outliner.tools"};
@@ -221,53 +222,81 @@ static void unlink_action_fn(bContext *C,
}
static void unlink_material_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *UNUSED(scene),
TreeElement *te,
TreeStoreElem *tsep,
- TreeStoreElem *UNUSED(tselem),
+ TreeStoreElem *tselem,
void *UNUSED(user_data))
{
+ const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA);
+
+ if (!te_is_material) {
+ /* Just fail silently. Another element may be selected that is a material, we don't want to
+ * confuse users with an error in that case. */
+ return;
+ }
+
+ if (!tsep || !TSE_IS_REAL_ID(tsep)) {
+ /* Valid case, no parent element of the material or it is not an ID (could be a #TSE_ID_BASE
+ * for example) so there's no data to unlink from. */
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Cannot unlink material '%s'. It's not clear which object or object-data it "
+ "should be unlinked from, there's no object or object-data as parent in the "
+ "Outliner tree",
+ tselem->id->name + 2);
+ return;
+ }
+
Material **matar = nullptr;
int a, totcol = 0;
- if (GS(tsep->id->name) == ID_OB) {
- Object *ob = (Object *)tsep->id;
- totcol = ob->totcol;
- matar = ob->mat;
- }
- else if (GS(tsep->id->name) == ID_ME) {
- Mesh *me = (Mesh *)tsep->id;
- totcol = me->totcol;
- matar = me->mat;
- }
- else if (GS(tsep->id->name) == ID_CU_LEGACY) {
- Curve *cu = (Curve *)tsep->id;
- totcol = cu->totcol;
- matar = cu->mat;
- }
- else if (GS(tsep->id->name) == ID_MB) {
- MetaBall *mb = (MetaBall *)tsep->id;
- totcol = mb->totcol;
- matar = mb->mat;
- }
- else if (GS(tsep->id->name) == ID_CV) {
- Curves *curves = (Curves *)tsep->id;
- totcol = curves->totcol;
- matar = curves->mat;
- }
- else if (GS(tsep->id->name) == ID_PT) {
- PointCloud *pointcloud = (PointCloud *)tsep->id;
- totcol = pointcloud->totcol;
- matar = pointcloud->mat;
- }
- else if (GS(tsep->id->name) == ID_VO) {
- Volume *volume = (Volume *)tsep->id;
- totcol = volume->totcol;
- matar = volume->mat;
- }
- else {
- BLI_assert(0);
+ switch (GS(tsep->id->name)) {
+ case ID_OB: {
+ Object *ob = (Object *)tsep->id;
+ totcol = ob->totcol;
+ matar = ob->mat;
+ break;
+ }
+ case ID_ME: {
+ Mesh *me = (Mesh *)tsep->id;
+ totcol = me->totcol;
+ matar = me->mat;
+ break;
+ }
+ case ID_CU_LEGACY: {
+ Curve *cu = (Curve *)tsep->id;
+ totcol = cu->totcol;
+ matar = cu->mat;
+ break;
+ }
+ case ID_MB: {
+ MetaBall *mb = (MetaBall *)tsep->id;
+ totcol = mb->totcol;
+ matar = mb->mat;
+ break;
+ }
+ case ID_CV: {
+ Curves *curves = (Curves *)tsep->id;
+ totcol = curves->totcol;
+ matar = curves->mat;
+ break;
+ }
+ case ID_PT: {
+ PointCloud *pointcloud = (PointCloud *)tsep->id;
+ totcol = pointcloud->totcol;
+ matar = pointcloud->mat;
+ break;
+ }
+ case ID_VO: {
+ Volume *volume = (Volume *)tsep->id;
+ totcol = volume->totcol;
+ matar = volume->mat;
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
}
if (LIKELY(matar != nullptr)) {
@@ -404,11 +433,10 @@ static void outliner_do_libdata_operation(bContext *C,
ReportList *reports,
Scene *scene,
SpaceOutliner *space_outliner,
- ListBase *lb,
outliner_operation_fn operation_fn,
void *user_data)
{
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
if (tselem->flag & TSE_SELECTED) {
if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) ||
@@ -417,11 +445,7 @@ static void outliner_do_libdata_operation(bContext *C,
operation_fn(C, reports, scene, te, tsep, tselem, user_data);
}
}
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_do_libdata_operation(
- C, reports, scene, space_outliner, &te->subtree, operation_fn, user_data);
- }
- }
+ });
}
/** \} */
@@ -492,7 +516,7 @@ static int outliner_scene_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Delete Scene(s)");
}
else {
- BLI_assert(0);
+ BLI_assert_unreachable();
return OPERATOR_CANCELLED;
}
@@ -755,6 +779,10 @@ static void id_local_fn(bContext *C,
struct OutlinerLibOverrideData {
bool do_hierarchy;
+
+ /** When creating new overrides, make them all user-editable. */
+ bool do_fully_editable;
+
/**
* For resync operation, force keeping newly created override IDs (or original linked IDs)
* instead of re-applying relevant existing ID pointer property override operations. Helps
@@ -949,7 +977,8 @@ static void id_override_library_create_fn(bContext *C,
id_root_reference,
id_hierarchy_root_reference,
id_instance_hint,
- &id_root_override);
+ &id_root_override,
+ data->do_fully_editable);
BLI_assert(id_root_override != nullptr);
BLI_assert(!ID_IS_LINKED(id_root_override));
@@ -1589,21 +1618,17 @@ static void outliner_do_data_operation(
SpaceOutliner *space_outliner,
int type,
int event,
- ListBase *lb,
void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *),
void *arg)
{
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
if (tselem->flag & TSE_SELECTED) {
if (tselem->type == type) {
operation_fn(event, te, tselem, arg);
}
}
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_do_data_operation(space_outliner, type, event, &te->subtree, operation_fn, arg);
- }
- }
+ });
}
static Base *outliner_batch_delete_hierarchy(
@@ -1726,53 +1751,59 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
event = RNA_enum_get(op->ptr, "type");
- if (event == OL_OP_SELECT) {
- Scene *sce = scene; /* To be able to delete, scenes are set... */
- outliner_do_object_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn);
- if (scene != sce) {
- WM_window_set_active_scene(bmain, C, win, sce);
- }
+ switch (event) {
+ case OL_OP_SELECT: {
+ Scene *sce = scene; /* To be able to delete, scenes are set... */
+ outliner_do_object_operation(
+ C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn);
+ /* FIXME: This is most certainly broken, maybe check should rather be
+ * `if (CTX_data_scene(C) != scene)` ? */
+ if (scene != sce) {
+ WM_window_set_active_scene(bmain, C, win, sce);
+ }
- str = "Select Objects";
- selection_changed = true;
- }
- else if (event == OL_OP_SELECT_HIERARCHY) {
- Scene *sce = scene; /* To be able to delete, scenes are set... */
- outliner_do_object_operation_ex(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- object_select_hierarchy_fn,
- nullptr,
- false);
- if (scene != sce) {
- WM_window_set_active_scene(bmain, C, win, sce);
- }
- str = "Select Object Hierarchy";
- selection_changed = true;
- }
- else if (event == OL_OP_DESELECT) {
- outliner_do_object_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn);
- str = "Deselect Objects";
- selection_changed = true;
- }
- else if (event == OL_OP_REMAP) {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr);
- /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
- * trick does not work here). */
- }
- else if (event == OL_OP_RENAME) {
- outliner_do_object_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn);
- str = "Rename Object";
- }
- else {
- BLI_assert(0);
- return OPERATOR_CANCELLED;
+ str = "Select Objects";
+ selection_changed = true;
+ break;
+ }
+ case OL_OP_SELECT_HIERARCHY: {
+ Scene *sce = scene; /* To be able to delete, scenes are set... */
+ outliner_do_object_operation_ex(C,
+ op->reports,
+ scene,
+ space_outliner,
+ &space_outliner->tree,
+ object_select_hierarchy_fn,
+ nullptr,
+ false);
+ /* FIXME: This is most certainly broken, maybe check should rather be
+ * `if (CTX_data_scene(C) != scene)` ? */
+ if (scene != sce) {
+ WM_window_set_active_scene(bmain, C, win, sce);
+ }
+ str = "Select Object Hierarchy";
+ selection_changed = true;
+ break;
+ }
+ case OL_OP_DESELECT:
+ outliner_do_object_operation(
+ C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn);
+ str = "Deselect Objects";
+ selection_changed = true;
+ break;
+ case OL_OP_REMAP:
+ outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr);
+ /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
+ * trick does not work here). */
+ break;
+ case OL_OP_RENAME:
+ outliner_do_object_operation(
+ C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn);
+ str = "Rename Object";
+ break;
+ default:
+ BLI_assert_unreachable();
+ return OPERATOR_CANCELLED;
}
if (selection_changed) {
@@ -1943,7 +1974,7 @@ void OUTLINER_OT_delete(wmOperatorType *ot)
ot->poll = ED_operator_outliner_active;
/* flags */
- ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop = RNA_def_boolean(
@@ -1964,6 +1995,7 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
@@ -1996,7 +2028,7 @@ static const EnumPropertyItem prop_id_op_types[] = {
0,
"Remap Users",
"Make all users of selected data-blocks to use instead current (clicked) one"},
- {0, "", 0, nullptr, nullptr},
+ RNA_ENUM_ITEM_SEPR,
{OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
"OVERRIDE_LIBRARY_CREATE",
0,
@@ -2009,6 +2041,12 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Make Library Override Hierarchy",
"Make a local override of this linked data-block, and its hierarchy of dependencies - only "
"applies to active Outliner item"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE,
+ "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE",
+ 0,
+ "Make Library Override Hierarchy Fully Editable",
+ "Make a local override of this linked data-block, and its hierarchy of dependencies, making "
+ "them all fully user-editable - only applies to active Outliner item"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
"OVERRIDE_LIBRARY_MAKE_EDITABLE",
0,
@@ -2049,10 +2087,10 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Clear Library Override Hierarchy",
"Delete this local override (including its hierarchy of override dependencies) and relink "
"its usages to the linked data-blocks"},
- {0, "", 0, nullptr, nullptr},
+ RNA_ENUM_ITEM_SEPR,
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
- {0, "", 0, nullptr, nullptr},
+ RNA_ENUM_ITEM_SEPR,
{OUTLINER_IDOP_FAKE_ADD,
"ADD_FAKE",
0,
@@ -2088,6 +2126,7 @@ static bool outliner_id_operation_item_poll(bContext *C,
}
return false;
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY:
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE:
if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) {
return true;
}
@@ -2164,13 +2203,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_IDOP_UNLINK: {
/* unlink datablock from its parent */
if (objectlevel) {
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_object_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_object_fn, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr);
ED_undo_push(C, "Unlink Object");
@@ -2179,61 +2213,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
switch (idlevel) {
case ID_AC:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_action_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_action_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Unlink action");
break;
case ID_MA:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_material_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_material_fn, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr);
ED_undo_push(C, "Unlink material");
break;
case ID_TE:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_texture_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_texture_fn, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr);
ED_undo_push(C, "Unlink texture");
break;
case ID_WO:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_world_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_world_fn, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr);
ED_undo_push(C, "Unlink world");
break;
case ID_GR:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- unlink_collection_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, unlink_collection_fn, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr);
ED_undo_push(C, "Unlink Collection");
@@ -2246,20 +2255,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_LOCAL: {
/* make local */
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, id_local_fn, nullptr);
+ outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr);
ED_undo_push(C, "Localized Data");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_create_fn,
- &override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
ED_undo_push(C, "Overridden Data");
break;
}
@@ -2270,19 +2273,30 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
op->reports,
scene,
space_outliner,
- &space_outliner->tree,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
+ id_override_library_create_hierarchy_post_process(C, &override_data);
+
+ ED_undo_push(C, "Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ override_data.do_fully_editable = true;
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
- &space_outliner->tree,
- id_override_library_create_fn,
+ id_override_library_create_hierarchy_pre_process_fn,
&override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
id_override_library_create_hierarchy_post_process(C, &override_data);
- ED_undo_push(C, "Overridden Data Hierarchy");
+ ED_undo_push(C, "Overridden Data Hierarchy Fully Editable");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: {
@@ -2290,7 +2304,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
op->reports,
scene,
space_outliner,
- &space_outliner->tree,
id_override_library_toggle_flag_fn,
POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED));
@@ -2299,39 +2312,24 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_reset_fn,
- &override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
ED_undo_push(C, "Reset Overridden Data");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_reset_fn,
- &override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
ED_undo_push(C, "Reset Overridden Data Hierarchy");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_resync_fn,
- &override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
ED_undo_push(C, "Resync Overridden Data Hierarchy");
break;
}
@@ -2339,35 +2337,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_resync_hierarchy_enforce = true;
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_resync_fn,
- &override_data);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
ED_undo_push(C, "Resync Overridden Data Hierarchy");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: {
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_clear_hierarchy_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr);
ED_undo_push(C, "Clear Overridden Data Hierarchy");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: {
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_override_library_clear_single_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr);
ED_undo_push(C, "Clear Overridden Data Hierarchy");
break;
}
@@ -2375,26 +2358,16 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
/* make single user */
switch (idlevel) {
case ID_AC:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- singleuser_action_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, singleuser_action_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Single-User Action");
break;
case ID_WO:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- singleuser_world_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, singleuser_world_fn, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr);
ED_undo_push(C, "Single-User World");
@@ -2409,15 +2382,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_IDOP_DELETE: {
if (idlevel > 0) {
outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr);
+ C, op->reports, scene, space_outliner, id_delete_fn, nullptr);
ED_undo_push(C, "Delete");
}
break;
}
case OUTLINER_IDOP_REMAP: {
if (idlevel > 0) {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr);
+ outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
}
@@ -2440,13 +2412,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_FAKE_ADD: {
/* set fake user */
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_fake_user_set_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_fake_user_set_fn, nullptr);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Add Fake User");
@@ -2454,13 +2421,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_FAKE_CLEAR: {
/* clear fake user */
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_fake_user_clear_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_fake_user_clear_fn, nullptr);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Clear Fake User");
@@ -2469,20 +2431,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_IDOP_RENAME: {
/* rename */
outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr);
+ C, op->reports, scene, space_outliner, item_rename_fn, nullptr);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Rename");
break;
}
case OUTLINER_IDOP_SELECT_LINKED:
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- &space_outliner->tree,
- id_select_linked_fn,
- nullptr);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_select_linked_fn, nullptr);
ED_outliner_select_sync_from_all_tag(C);
ED_undo_push(C, "Select");
break;
@@ -2527,14 +2484,12 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot)
enum eOutlinerLibOpTypes {
OL_LIB_INVALID = 0,
- OL_LIB_RENAME,
OL_LIB_DELETE,
OL_LIB_RELOCATE,
OL_LIB_RELOAD,
};
static const EnumPropertyItem outliner_lib_op_type_items[] = {
- {OL_LIB_RENAME, "RENAME", 0, "Rename", ""},
{OL_LIB_DELETE,
"DELETE",
ICON_X,
@@ -2566,30 +2521,20 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type");
switch (event) {
- case OL_LIB_RENAME: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr);
-
- WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
- ED_undo_push(C, "Rename Library");
- break;
- }
case OL_LIB_DELETE: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr);
+ outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr);
ED_undo_push(C, "Delete Library");
break;
}
case OL_LIB_RELOCATE: {
outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, lib_relocate_fn, nullptr);
+ C, op->reports, scene, space_outliner, lib_relocate_fn, nullptr);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
break;
}
case OL_LIB_RELOAD: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, &space_outliner->tree, lib_reload_fn, nullptr);
+ outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn, nullptr);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
break;
@@ -2632,11 +2577,10 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot)
static void outliner_do_id_set_operation(
SpaceOutliner *space_outliner,
int type,
- ListBase *lb,
ID *newid,
void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
{
- LISTBASE_FOREACH (TreeElement *, te, lb) {
+ tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
if (tselem->flag & TSE_SELECTED) {
if (tselem->type == type) {
@@ -2644,10 +2588,7 @@ static void outliner_do_id_set_operation(
operation_fn(te, tselem, tsep, newid);
}
}
- if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_do_id_set_operation(space_outliner, type, &te->subtree, newid, operation_fn);
- }
- }
+ });
}
static void actionset_id_fn(TreeElement *UNUSED(te),
@@ -2702,12 +2643,10 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op)
/* perform action if valid channel */
if (datalevel == TSE_ANIM_DATA) {
- outliner_do_id_set_operation(
- space_outliner, datalevel, &space_outliner->tree, (ID *)act, actionset_id_fn);
+ outliner_do_id_set_operation(space_outliner, datalevel, (ID *)act, actionset_id_fn);
}
else if (idlevel == ID_AC) {
- outliner_do_id_set_operation(
- space_outliner, idlevel, &space_outliner->tree, (ID *)act, actionset_id_fn);
+ outliner_do_id_set_operation(space_outliner, idlevel, (ID *)act, actionset_id_fn);
}
else {
return OPERATOR_CANCELLED;
@@ -2794,8 +2733,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
switch (event) {
case OUTLINER_ANIMOP_CLEAR_ADT:
/* Remove Animation Data - this may remove the active action, in some cases... */
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, clear_animdata_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, clear_animdata_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Clear Animation Data");
@@ -2812,32 +2750,23 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_ANIMOP_CLEAR_ACT:
/* clear active action - using standard rules */
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, unlinkact_animdata_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, unlinkact_animdata_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Unlink action");
break;
case OUTLINER_ANIMOP_REFRESH_DRV:
- outliner_do_data_operation(space_outliner,
- datalevel,
- event,
- &space_outliner->tree,
- refreshdrivers_animdata_fn,
- nullptr);
+ outliner_do_data_operation(
+ space_outliner, datalevel, event, refreshdrivers_animdata_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
// ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */
break;
case OUTLINER_ANIMOP_CLEAR_DRV:
- outliner_do_data_operation(space_outliner,
- datalevel,
- event,
- &space_outliner->tree,
- cleardrivers_animdata_fn,
- nullptr);
+ outliner_do_data_operation(
+ space_outliner, datalevel, event, cleardrivers_animdata_fn, nullptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr);
ED_undo_push(C, "Clear Drivers");
@@ -2887,8 +2816,7 @@ static int outliner_constraint_operation_exec(bContext *C, wmOperator *op)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
eOutliner_PropConstraintOps event = (eOutliner_PropConstraintOps)RNA_enum_get(op->ptr, "type");
- outliner_do_data_operation(
- space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C);
+ outliner_do_data_operation(space_outliner, TSE_CONSTRAINT, event, constraint_fn, C);
if (event == OL_CONSTRAINTOP_DELETE) {
outliner_cleanup_tree(space_outliner);
@@ -2934,8 +2862,7 @@ static int outliner_modifier_operation_exec(bContext *C, wmOperator *op)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
eOutliner_PropModifierOps event = (eOutliner_PropModifierOps)RNA_enum_get(op->ptr, "type");
- outliner_do_data_operation(
- space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C);
+ outliner_do_data_operation(space_outliner, TSE_MODIFIER, event, modifier_fn, C);
if (event == OL_MODIFIER_OP_DELETE) {
outliner_cleanup_tree(space_outliner);
@@ -2978,24 +2905,21 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
eOutliner_PropDataOps event = (eOutliner_PropDataOps)RNA_enum_get(op->ptr, "type");
switch (datalevel) {
case TSE_POSE_CHANNEL: {
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, pchan_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, pchan_fn, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
ED_undo_push(C, "PoseChannel operation");
break;
}
case TSE_BONE: {
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, bone_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, bone_fn, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
ED_undo_push(C, "Bone operation");
break;
}
case TSE_EBONE: {
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, ebone_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, ebone_fn, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
ED_undo_push(C, "EditBone operation");
@@ -3003,16 +2927,14 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
}
case TSE_SEQUENCE: {
Scene *scene = CTX_data_scene(C);
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene);
+ outliner_do_data_operation(space_outliner, datalevel, event, sequence_fn, scene);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
ED_undo_push(C, "Sequencer operation");
break;
}
case TSE_GP_LAYER: {
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, gpencil_layer_fn, nullptr);
+ outliner_do_data_operation(space_outliner, datalevel, event, gpencil_layer_fn, nullptr);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, nullptr);
ED_undo_push(C, "Grease Pencil Layer operation");
@@ -3020,8 +2942,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
}
case TSE_RNA_STRUCT:
if (event == OL_DOP_SELECT_LINKED) {
- outliner_do_data_operation(
- space_outliner, datalevel, event, &space_outliner->tree, data_select_linked_fn, C);
+ outliner_do_data_operation(space_outliner, datalevel, event, data_select_linked_fn, C);
}
break;
diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc
index bbd9b48c260..aa739758ecb 100644
--- a/source/blender/editors/space_outliner/outliner_tree.cc
+++ b/source/blender/editors/space_outliner/outliner_tree.cc
@@ -802,7 +802,8 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
void *idv,
TreeElement *parent,
short type,
- short index)
+ short index,
+ const bool expand)
{
ID *id = reinterpret_cast<ID *>(idv);
@@ -894,10 +895,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
te->idcode = GS(id->name);
}
- if (te->abstract_element && te->abstract_element->isExpandValid()) {
+ if (expand && te->abstract_element && te->abstract_element->isExpandValid()) {
tree_element_expand(*te->abstract_element, *space_outliner);
}
- else if (type == TSE_SOME_ID) {
+ else if (expand && (type == TSE_SOME_ID)) {
/* ID types not (fully) ported to new design yet. */
if (te->abstract_element->expandPoll(*space_outliner)) {
outliner_add_id_contents(space_outliner, te, tselem, id);
@@ -919,10 +920,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design");
}
- if (tree_element_warnings_get(te, nullptr, nullptr)) {
- te->flag |= TE_HAS_WARNING;
- }
-
return te;
}
@@ -1686,6 +1683,9 @@ void outliner_build_tree(Main *mainvar,
space_outliner->storeflag &= ~SO_TREESTORE_REBUILD;
if (region->do_draw & RGN_DRAW_NO_REBUILD) {
+ BLI_assert_msg(space_outliner->runtime->tree_display != nullptr,
+ "Skipping rebuild before tree was built properly, a full redraw should be "
+ "triggered instead");
return;
}
diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc
index 4b947154864..0db612ce6db 100644
--- a/source/blender/editors/space_outliner/outliner_utils.cc
+++ b/source/blender/editors/space_outliner/outliner_utils.cc
@@ -27,6 +27,9 @@
#include "UI_view2d.h"
#include "outliner_intern.hh"
+#include "tree/tree_iterator.hh"
+
+using namespace blender::ed::outliner;
/* -------------------------------------------------------------------- */
/** \name Tree View Context
diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc
index 97dc659155f..5bcd1edebc0 100644
--- a/source/blender/editors/space_outliner/space_outliner.cc
+++ b/source/blender/editors/space_outliner/space_outliner.cc
@@ -438,7 +438,7 @@ static void outliner_deactivate(struct ScrArea *area)
{
/* Remove hover highlights */
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first);
- outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY, false);
+ outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false);
ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
}
diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc
index 141c68594e8..6ab497b3fbb 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display.cc
@@ -45,9 +45,9 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode(
return nullptr;
}
-bool AbstractTreeDisplay::hasWarnings() const
+bool AbstractTreeDisplay::supportsModeColumn() const
{
- return has_warnings;
+ return false;
}
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index 327f29aa15e..190e35c81d6 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -75,12 +75,16 @@ class AbstractTreeDisplay {
*/
virtual ListBase buildTree(const TreeSourceData &source_data) = 0;
- /** Accessor to whether given tree has some warnings to display. */
- bool hasWarnings() const;
+ /**
+ * Define if the display mode should be allowed to show a mode column on the left. This column
+ * adds an icon to indicate which objects are in the current mode (edit mode, pose mode, etc.)
+ * and allows adding other objects to the mode by clicking the icon.
+ *
+ * Returns false by default.
+ */
+ virtual bool supportsModeColumn() const;
protected:
- bool has_warnings = false;
-
/** All derived classes will need a handle to this, so storing it in the base for convenience. */
SpaceOutliner &space_outliner_;
};
@@ -100,6 +104,8 @@ class TreeDisplayViewLayer final : public AbstractTreeDisplay {
ListBase buildTree(const TreeSourceData &source_data) override;
+ bool supportsModeColumn() const override;
+
private:
void add_view_layer(Scene &, ListBase &, TreeElement *);
void add_layer_collections_recursive(ListBase &, ListBase &, TreeElement &);
@@ -212,6 +218,8 @@ class TreeDisplayScenes final : public AbstractTreeDisplay {
TreeDisplayScenes(SpaceOutliner &space_outliner);
ListBase buildTree(const TreeSourceData &source_data) override;
+
+ bool supportsModeColumn() const override;
};
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
index 476bbdb63ae..405f1dd73f4 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
@@ -116,6 +116,11 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase
ID *id = static_cast<ID *>(lbarray[a]->first);
const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr);
+ /* Don't show deprecated types. */
+ if (ID_TYPE_IS_DEPRECATED(GS(id->name))) {
+ continue;
+ }
+
/* check if there's data in current lib */
for (ID *id_iter : List<ID>(lbarray[a])) {
if (id_iter->lib == lib) {
@@ -136,9 +141,6 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase
tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
tenlib->name = IFACE_("Current File");
}
- if (tenlib->flag & TE_HAS_WARNING) {
- has_warnings = true;
- }
}
/* Create data-block list parent element on demand. */
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
index 38025b58fd2..67798e978ab 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
@@ -34,13 +34,6 @@ TreeDisplayOverrideLibraryHierarchies::TreeDisplayOverrideLibraryHierarchies(
{
}
-/* XXX Remove expanded subtree, we add our own items here. Expanding should probably be
- * optional. */
-static void remove_expanded_children(TreeElement &te)
-{
- outliner_free_tree(&te.subtree);
-}
-
ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &source_data)
{
ListBase tree = {nullptr};
@@ -114,8 +107,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
});
TreeElement *new_id_te = outliner_add_element(
- &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0);
- remove_expanded_children(*new_id_te);
+ &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false);
build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te));
}
@@ -193,8 +185,8 @@ static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data)
&id,
&build_data.parent_te->getLegacyElement(),
TSE_SOME_ID,
- 0);
- remove_expanded_children(*new_te);
+ 0,
+ false);
build_data.sibling_ids.add(&id);
BuildHierarchyForeachIDCbData child_build_data{build_data.bmain,
diff --git a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc
index 9e00a425a5a..6b1de7f8b95 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc
@@ -26,6 +26,11 @@ TreeDisplayScenes::TreeDisplayScenes(SpaceOutliner &space_outliner)
{
}
+bool TreeDisplayScenes::supportsModeColumn() const
+{
+ return true;
+}
+
ListBase TreeDisplayScenes::buildTree(const TreeSourceData &source_data)
{
/* On first view we open scenes. */
diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
index 19811e45b90..c8869d90eca 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
@@ -55,6 +55,11 @@ TreeDisplayViewLayer::TreeDisplayViewLayer(SpaceOutliner &space_outliner)
{
}
+bool TreeDisplayViewLayer::supportsModeColumn() const
+{
+ return true;
+}
+
ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data)
{
ListBase tree = {nullptr};
@@ -266,14 +271,14 @@ void ObjectsChildrenBuilder::make_object_parent_hierarchy_collections()
if (!found) {
/* We add the child in the tree even if it is not in the collection.
- * We deliberately clear its sub-tree though, to make it less prominent. */
+ * We don't expand its sub-tree though, to make it less prominent. */
TreeElement *child_ob_tree_element = outliner_add_element(&outliner_,
&parent_ob_tree_element->subtree,
child,
parent_ob_tree_element,
TSE_SOME_ID,
- 0);
- outliner_free_tree(&child_ob_tree_element->subtree);
+ 0,
+ false);
child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION;
child_ob_tree_elements.append(child_ob_tree_element);
}
diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc
index 1e3fd2df7c2..94d55b70e3c 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element.cc
@@ -100,6 +100,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i
return nullptr;
}
+StringRefNull AbstractTreeElement::getWarning() const
+{
+ return "";
+}
+
void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te)
{
if (!TREESTORE(legacy_te)->used) {
@@ -118,39 +123,4 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner
tree_element.expand(space_outliner);
}
-bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message)
-{
- TreeStoreElem *tselem = te->store_elem;
-
- if (tselem->type != TSE_SOME_ID) {
- return false;
- }
- if (te->idcode != ID_LI) {
- return false;
- }
-
- Library *library = (Library *)tselem->id;
- if (library->tag & LIBRARY_TAG_RESYNC_REQUIRED) {
- if (r_icon) {
- *r_icon = ICON_ERROR;
- }
- if (r_message) {
- *r_message = TIP_(
- "Contains linked library overrides that need to be resynced, updating the library is "
- "recommended");
- }
- return true;
- }
- if (library->id.tag & LIB_TAG_MISSING) {
- if (r_icon) {
- *r_icon = ICON_ERROR;
- }
- if (r_message) {
- *r_message = TIP_("Missing library");
- }
- return true;
- }
- return false;
-}
-
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh
index c6593a517dd..1098068d628 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element.hh
@@ -8,6 +8,8 @@
#include <memory>
+#include "BLI_string_ref.hh"
+
struct ListBase;
struct SpaceOutliner;
struct TreeElement;
@@ -56,6 +58,12 @@ class AbstractTreeElement {
}
/**
+ * By letting this return a warning message, the tree element will display a warning icon with
+ * the message in the tooltip.
+ */
+ virtual StringRefNull getWarning() const;
+
+ /**
* Expand this tree element if it is displayed for the first time (as identified by its
* tree-store element).
*
@@ -86,23 +94,20 @@ class AbstractTreeElement {
* \note "ID" is not always a real ID.
* \note If child items are only added to the tree if the item is open,
* the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change().
+ *
+ * \param expand: If true, the element may add its own sub-tree. E.g. objects will list their
+ * animation data, object data, constraints, modifiers, ... This often adds visual
+ * noise, and can be expensive to add in big scenes. So prefer setting this to
+ * false.
*/
struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
ListBase *lb,
void *idv,
struct TreeElement *parent,
short type,
- short index);
+ short index,
+ const bool expand = true);
void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner);
-/**
- * Get actual warning data of a tree element, if any.
- *
- * \param r_icon: The icon to display as warning.
- * \param r_message: The message to display as warning.
- * \return true if there is a warning, false otherwise.
- */
-bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message);
-
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc
index ef5e056f229..86f5fd4eff5 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc
@@ -27,6 +27,11 @@ namespace blender::ed::outliner {
std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_te, ID &id)
{
+ if (ID_TYPE_IS_DEPRECATED(GS(id.name))) {
+ BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type");
+ return nullptr;
+ }
+
switch (ID_Type type = GS(id.name); type) {
case ID_LI:
return std::make_unique<TreeElementIDLibrary>(legacy_te, (Library &)id);
@@ -70,10 +75,9 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
case ID_PC:
case ID_CF:
return std::make_unique<TreeElementID>(legacy_te, id);
- /* Deprecated */
case ID_IP:
- BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type");
- return nullptr;
+ BLI_assert_unreachable();
+ break;
}
return nullptr;
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc
index 0dcaec0385a..4f1b951ccaf 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc
@@ -4,6 +4,8 @@
* \ingroup spoutliner
*/
+#include "BLT_translation.h"
+
#include "DNA_ID.h"
#include "DNA_listBase.h"
@@ -24,4 +26,21 @@ bool TreeElementIDLibrary::isExpandValid() const
return true;
}
+StringRefNull TreeElementIDLibrary::getWarning() const
+{
+ Library &library = reinterpret_cast<Library &>(id_);
+
+ if (library.tag & LIBRARY_TAG_RESYNC_REQUIRED) {
+ return TIP_(
+ "Contains linked library overrides that need to be resynced, updating the library is "
+ "recommended");
+ }
+
+ if (library.id.tag & LIB_TAG_MISSING) {
+ return TIP_("Missing library");
+ }
+
+ return {};
+}
+
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh
index ed599cf04da..2d89b55813f 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh
@@ -17,6 +17,8 @@ class TreeElementIDLibrary final : public TreeElementID {
TreeElementIDLibrary(TreeElement &legacy_te, Library &library);
bool isExpandValid() const override;
+
+ blender::StringRefNull getWarning() const override;
};
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
index 857f5577e59..871de39b1dd 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -38,6 +38,19 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i
}
}
+StringRefNull TreeElementOverridesBase::getWarning() const
+{
+ if (id.flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) {
+ return TIP_("This override data-block is not needed anymore, but was detected as user-edited");
+ }
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) {
+ return TIP_("This override data-block is unused");
+ }
+
+ return {};
+}
+
void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
{
BLI_assert(id.override_library != nullptr);
@@ -54,21 +67,38 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
for (auto *override_prop :
ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) {
+ int rnaprop_index = 0;
const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
- &idpoin, override_prop, &override_rna_ptr, &override_rna_prop);
- if (is_rna_path_valid && !show_system_overrides &&
- ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
- RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
- bool do_continue = true;
- for (auto *override_prop_op :
- ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
- if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
- do_continue = false;
- break;
+ &idpoin, override_prop, &override_rna_ptr, &override_rna_prop, &rnaprop_index);
+
+ /* Check for conditions where the liboverride property should be considered as a system
+ * override, if needed. */
+ if (is_rna_path_valid && !show_system_overrides) {
+ bool do_skip = true;
+ bool is_system_override = false;
+
+ /* Matching ID pointers are considered as system overrides. */
+ if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
+ RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
+ for (auto *override_prop_op :
+ ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
+ if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ do_skip = false;
+ break;
+ }
+ else {
+ is_system_override = true;
+ }
}
}
- if (do_continue) {
+ /* Animated/driven properties are considered as system overrides. */
+ if (!is_system_override && !BKE_lib_override_library_property_is_animated(
+ &id, override_prop, override_rna_prop, rnaprop_index)) {
+ do_skip = false;
+ }
+
+ if (do_skip) {
continue;
}
}
@@ -84,14 +114,24 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t
TreeElementOverridesData &override_data)
: AbstractTreeElement(legacy_te),
override_rna_ptr(override_data.override_rna_ptr),
- override_rna_prop(override_data.override_rna_prop)
+ override_rna_prop(override_data.override_rna_prop),
+ rna_path(override_data.override_property.rna_path),
+ is_rna_path_valid(override_data.is_rna_path_valid)
{
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE);
legacy_te.name = override_data.override_property.rna_path;
- /* Abusing this for now, better way to do it is also pending current refactor of the whole tree
- * code to use C++. */
- legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid);
+}
+
+StringRefNull TreeElementOverridesProperty::getWarning() const
+{
+ if (!is_rna_path_valid) {
+ return TIP_(
+ "This override property does not exist in current data, it will be removed on "
+ "next .blend file save");
+ }
+
+ return {};
}
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
index a2d1409f193..1db46d9af1d 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
@@ -8,6 +8,8 @@
#include "RNA_types.h"
+#include "BLI_string_ref.hh"
+
#include "tree_element.hh"
struct ID;
@@ -32,6 +34,8 @@ class TreeElementOverridesBase final : public AbstractTreeElement {
TreeElementOverridesBase(TreeElement &legacy_te, ID &id);
void expand(SpaceOutliner &) const override;
+
+ StringRefNull getWarning() const override;
};
class TreeElementOverridesProperty final : public AbstractTreeElement {
@@ -39,8 +43,13 @@ class TreeElementOverridesProperty final : public AbstractTreeElement {
PointerRNA override_rna_ptr;
PropertyRNA &override_rna_prop;
+ StringRefNull rna_path;
+ bool is_rna_path_valid;
+
public:
TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data);
+
+ StringRefNull getWarning() const override;
};
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.cc b/source/blender/editors/space_outliner/tree/tree_iterator.cc
new file mode 100644
index 00000000000..8d2b0b21433
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_iterator.cc
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "DNA_space_types.h"
+
+#include "BLI_listbase.h"
+
+#include "../outliner_intern.hh"
+
+#include "tree_iterator.hh"
+
+namespace blender::ed::outliner::tree_iterator {
+
+void all(const SpaceOutliner &space_outliner, const VisitorFn visitor)
+{
+ all_open(space_outliner, space_outliner.tree, visitor);
+}
+
+void all(const ListBase &subtree, const VisitorFn visitor)
+{
+ LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) {
+ /* Get needed data out in case element gets freed. */
+ const ListBase subtree = element->subtree;
+
+ visitor(element);
+ /* Don't access element from now on, it may be freed. */
+
+ all(subtree, visitor);
+ }
+}
+
+void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor)
+{
+ all_open(space_outliner, space_outliner.tree, visitor);
+}
+
+void all_open(const SpaceOutliner &space_outliner,
+ const ListBase &subtree,
+ const VisitorFn visitor)
+{
+ LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) {
+ /* Get needed data out in case element gets freed. */
+ const TreeStoreElem *tselem = TREESTORE(element);
+ const ListBase subtree = element->subtree;
+
+ visitor(element);
+ /* Don't access element from now on, it may be freed. Note that the open/collapsed state may
+ * also have been changed in the visitor callback. */
+
+ if (TSELEM_OPEN(tselem, &space_outliner)) {
+ all_open(space_outliner, subtree, visitor);
+ }
+ }
+}
+
+} // namespace blender::ed::outliner::tree_iterator
diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh
new file mode 100644
index 00000000000..de5bcd2c462
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+
+struct ListBase;
+struct SpaceOutliner;
+struct TreeElement;
+
+namespace blender::ed::outliner {
+namespace tree_iterator {
+
+using VisitorFn = FunctionRef<void(TreeElement *)>;
+
+/**
+ * Preorder (meaning depth-first) traversal of all elements (regardless of collapsed state).
+ * Freeing the currently visited element in \a visitor is fine.
+ */
+void all(const SpaceOutliner &space_outliner, VisitorFn visitor);
+void all(const ListBase &subtree, VisitorFn visitor);
+
+/**
+ * Preorder (meaning depth-first) traversal of all elements not part of a collapsed sub-tree.
+ * Freeing the currently visited element in \a visitor is fine (but not its tree-store element).
+ */
+void all_open(const SpaceOutliner &, VisitorFn visitor);
+void all_open(const SpaceOutliner &, const ListBase &subtree, VisitorFn visitor);
+
+} // namespace tree_iterator
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 9298eb83b46..647d13a4d56 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -31,6 +31,7 @@
#include "BKE_mask.h"
#include "BKE_movieclip.h"
#include "BKE_report.h"
+#include "BKE_scene.h"
#include "BKE_sound.h"
#include "IMB_imbuf.h"
@@ -54,6 +55,7 @@
#include "SEQ_transform.h"
#include "SEQ_utils.h"
+#include "ED_scene.h"
/* For menu, popup, icons, etc. */
#include "ED_screen.h"
#include "ED_sequencer.h"
@@ -136,7 +138,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
ot->srna,
"overlap_shuffle_override",
false,
- "Override Overlap Shuffle Behaviour",
+ "Override Overlap Shuffle Behavior",
"Use the overlap_mode tool settings to determine how to shuffle overlapping strips");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
@@ -189,10 +191,11 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type)
}
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if ((ELEM(type, -1, seq->type)) && (seq->enddisp < timeline_frame) &&
- (timeline_frame - seq->enddisp < proximity)) {
+ const int strip_end = SEQ_time_right_handle_frame_get(seq);
+ if ((ELEM(type, -1, seq->type)) && (strip_end < timeline_frame) &&
+ (timeline_frame - strip_end < proximity)) {
tgt = seq;
- proximity = timeline_frame - seq->enddisp;
+ proximity = timeline_frame - strip_end;
}
}
@@ -337,7 +340,7 @@ static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence
ScrArea *area = CTX_wm_area(C);
const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) !=
0;
- SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers);
+ SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers);
SEQ_collection_free(strip_col);
}
@@ -469,6 +472,125 @@ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot)
ot->prop = prop;
}
+static EnumPropertyItem strip_new_scene_items[] = {
+ {SCE_COPY_NEW, "NEW", 0, "New", "Add new Strip with a new empty Scene with default settings"},
+ {SCE_COPY_EMPTY,
+ "EMPTY",
+ 0,
+ "Copy Settings",
+ "Add a new Strip, with an empty scene, and copy settings from the current scene"},
+ {SCE_COPY_LINK_COLLECTION,
+ "LINK_COPY",
+ 0,
+ "Linked Copy",
+ "Add a Strip and link in the collections from the current scene (shallow copy)"},
+ {SCE_COPY_FULL,
+ "FULL_COPY",
+ 0,
+ "Full Copy",
+ "Add a Strip and make a full copy of the current scene"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sequencer_add_scene_strip_new_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = SEQ_editing_ensure(scene);
+
+ if (RNA_boolean_get(op->ptr, "replace_sel")) {
+ ED_sequencer_deselect_all(scene);
+ }
+
+ SeqLoadData load_data;
+ load_data_init_from_operator(&load_data, C, op);
+
+ int type = RNA_enum_get(op->ptr, "type");
+ Scene *scene_new = ED_scene_sequencer_add(bmain, C, type, false);
+ if (scene_new == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+ load_data.scene = scene_new;
+
+ Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data);
+ seq_load_apply_generic_options(C, op, seq);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+static int sequencer_add_scene_strip_new_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ sequencer_disable_one_time_properties(C, op);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE);
+ return sequencer_add_scene_strip_new_exec(C, op);
+}
+
+static const EnumPropertyItem *strip_new_sequencer_enum_itemf(bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+ uint item_index;
+
+ item_index = RNA_enum_from_value(strip_new_scene_items, SCE_COPY_NEW);
+ RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]);
+
+ bool has_scene_or_no_context = false;
+ if (C == NULL) {
+ /* For documentation generation. */
+ has_scene_or_no_context = true;
+ }
+ else {
+ Scene *scene = CTX_data_scene(C);
+ Sequence *seq = SEQ_select_active_get(scene);
+ if ((seq && (seq->type == SEQ_TYPE_SCENE) && (seq->scene != NULL))) {
+ has_scene_or_no_context = true;
+ }
+ }
+
+ if (has_scene_or_no_context) {
+ int values[] = {SCE_COPY_EMPTY, SCE_COPY_LINK_COLLECTION, SCE_COPY_FULL};
+ for (int i = 0; i < ARRAY_SIZE(values); i++) {
+ item_index = RNA_enum_from_value(strip_new_scene_items, values[i]);
+ RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]);
+ }
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+ return item;
+}
+
+void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Add Strip with a new Scene";
+ ot->idname = "SEQUENCER_OT_scene_strip_add_new";
+ ot->description = "Create a new Strip and add a assign a new Scene as source";
+
+ /* Api callbacks. */
+ ot->invoke = sequencer_add_scene_strip_new_invoke;
+ ot->exec = sequencer_add_scene_strip_new_exec;
+ ot->poll = ED_operator_sequencer_active_editable;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME);
+
+ ot->prop = RNA_def_enum(ot->srna, "type", strip_new_scene_items, SCE_COPY_NEW, "Type", "");
+ RNA_def_enum_funcs(ot->prop, strip_new_sequencer_enum_itemf);
+ RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE);
+}
+
static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -664,7 +786,6 @@ static void seq_build_proxy(bContext *C, SeqCollection *movie_strips)
}
static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene,
- ListBase *seqbase,
Sequence *seq_movie,
Sequence *seq_sound)
{
@@ -672,9 +793,8 @@ static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene,
return;
}
- SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie));
- SEQ_transform_set_left_handle_frame(seq_sound, SEQ_transform_get_left_handle_frame(seq_movie));
- SEQ_time_update_sequence(scene, seqbase, seq_sound);
+ SEQ_time_right_handle_frame_set(scene, seq_sound, SEQ_time_right_handle_frame_get(seq_movie));
+ SEQ_time_left_handle_frame_set(scene, seq_sound, SEQ_time_left_handle_frame_get(seq_movie));
}
static void sequencer_add_movie_multiple_strips(bContext *C,
@@ -712,7 +832,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
else {
if (RNA_boolean_get(op->ptr, "sound")) {
seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
- sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
+ sequencer_add_movie_clamp_sound_strip_length(scene, seq_movie, seq_sound);
if (seq_sound) {
/* The video has sound, shift the video strip up a channel to make room for the sound
@@ -721,7 +841,8 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
}
}
- load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
+ load_data->start_frame += SEQ_time_right_handle_frame_get(seq_movie) -
+ SEQ_time_left_handle_frame_get(seq_movie);
if (overlap_shuffle_override) {
has_seq_overlap |= seq_load_apply_generic_options_only_test_overlap(
C, op, seq_sound, strip_col);
@@ -742,7 +863,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
ScrArea *area = CTX_wm_area(C);
const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag &
SEQ_MARKER_TRANS) != 0;
- SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers);
+ SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers);
}
SEQ_collection_free(strip_col);
@@ -769,7 +890,7 @@ static bool sequencer_add_movie_single_strip(bContext *C,
}
if (RNA_boolean_get(op->ptr, "sound")) {
seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
- sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
+ sequencer_add_movie_clamp_sound_strip_length(scene, seq_movie, seq_sound);
if (seq_sound) {
/* The video has sound, shift the video strip up a channel to make room for the sound
* strip. */
@@ -792,7 +913,7 @@ static bool sequencer_add_movie_single_strip(bContext *C,
ScrArea *area = CTX_wm_area(C);
const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag &
SEQ_MARKER_TRANS) != 0;
- SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers);
+ SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers);
}
SEQ_collection_free(strip_col);
@@ -952,7 +1073,8 @@ static void sequencer_add_sound_multiple_strips(bContext *C,
}
else {
seq_load_apply_generic_options(C, op, seq);
- load_data->start_frame += seq->enddisp - seq->startdisp;
+ load_data->start_frame += SEQ_time_right_handle_frame_get(seq) -
+ SEQ_time_left_handle_frame_get(seq);
}
}
RNA_END;
@@ -1179,8 +1301,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
/* Adjust length. */
if (load_data.image.len == 1) {
- SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame);
- SEQ_time_update_sequence(scene, SEQ_active_seqbase_get(ed), seq);
+ SEQ_time_right_handle_frame_set(scene, seq, load_data.image.end_frame);
}
seq_load_apply_generic_options(C, op, seq);
diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.c b/source/blender/editors/space_sequencer/sequencer_channels_draw.c
index a777132e9fc..c11388e8555 100644
--- a/source/blender/editors/space_sequencer/sequencer_channels_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.c
@@ -200,7 +200,7 @@ static float text_size_get(const SeqChannelDrawContext *context)
return UI_fontstyle_height_max(&style->widget) * 1.5f * context->scale;
}
-/* Todo: decide what gets priority - label or buttons */
+/* TODO: decide what gets priority - label or buttons. */
static rctf label_rect_init(const SeqChannelDrawContext *context,
const int channel_index,
const float used_width)
@@ -286,7 +286,7 @@ static void draw_channel_labels(const SeqChannelDrawContext *context,
}
}
-/* Todo: different text/buttons alignment */
+/* TODO: different text/buttons alignment. */
static void draw_channel_header(const SeqChannelDrawContext *context,
uiBlock *block,
const int channel_index)
diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
index 645c0dc9a1d..8dadb9360e3 100644
--- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c
+++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
@@ -231,9 +231,8 @@ static void update_overlay_strip_poistion_data(bContext *C, const int mval[2])
else {
/* Check if there is a strip that would intersect with the new strip(s). */
coords->is_intersecting = false;
- Sequence dummy_seq = {.machine = coords->channel,
- .startdisp = coords->start_frame,
- .enddisp = coords->start_frame + coords->strip_len};
+ Sequence dummy_seq = {
+ .machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len};
Editing *ed = SEQ_editing_get(scene);
for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) {
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 4cb41c702da..25701c323b9 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -572,8 +572,6 @@ static void drawmeta_contents(Scene *scene,
float y2,
const bool show_strip_color_tag)
{
- Editing *ed = SEQ_editing_get(scene);
- ListBase *channels = SEQ_channels_displayed_get(ed);
Sequence *seq;
uchar col[4];
@@ -582,11 +580,16 @@ static void drawmeta_contents(Scene *scene,
int chan_range = 0;
float draw_range = y2 - y1;
float draw_height;
- ListBase *seqbase;
+
+ Editing *ed = SEQ_editing_get(scene);
+ ListBase *channels = SEQ_channels_displayed_get(ed);
+ ListBase *meta_seqbase;
+ ListBase *meta_channels;
int offset;
- seqbase = SEQ_get_seqbase_from_sequence(seqm, &offset);
- if (!seqbase || BLI_listbase_is_empty(seqbase)) {
+ meta_seqbase = SEQ_get_seqbase_from_sequence(seqm, &meta_channels, &offset);
+
+ if (!meta_seqbase || BLI_listbase_is_empty(meta_seqbase)) {
return;
}
@@ -599,7 +602,7 @@ static void drawmeta_contents(Scene *scene,
GPU_blend(GPU_BLEND_ALPHA);
- for (seq = seqbase->first; seq; seq = seq->next) {
+ for (seq = meta_seqbase->first; seq; seq = seq->next) {
chan_min = min_ii(chan_min, seq->machine);
chan_max = max_ii(chan_max, seq->machine);
}
@@ -613,9 +616,9 @@ static void drawmeta_contents(Scene *scene,
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
/* Draw only immediate children (1 level depth). */
- for (seq = seqbase->first; seq; seq = seq->next) {
- const int startdisp = seq->startdisp + offset;
- const int enddisp = seq->enddisp + offset;
+ for (seq = meta_seqbase->first; seq; seq = seq->next) {
+ const int startdisp = SEQ_time_left_handle_frame_get(seq) + offset;
+ const int enddisp = SEQ_time_right_handle_frame_get(seq) + offset;
if ((startdisp > x2 || enddisp < x1) == 0) {
float y_chan = (seq->machine - chan_min) / (float)(chan_range)*draw_range;
@@ -631,7 +634,7 @@ static void drawmeta_contents(Scene *scene,
color3ubv_from_seq(scene, seq, show_strip_color_tag, col);
}
- if (SEQ_render_is_muted(channels, seqm) || SEQ_render_is_muted(&seqm->channels, seq)) {
+ if (SEQ_render_is_muted(channels, seqm) || SEQ_render_is_muted(meta_channels, seq)) {
col[3] = 64;
}
else {
@@ -665,7 +668,10 @@ float sequence_handle_size_get_clamped(Sequence *seq, const float pixelx)
const float maxhandle = (pixelx * SEQ_HANDLE_SIZE) * U.pixelsize;
/* Ensure that handle is not wider, than quarter of strip. */
- return min_ff(maxhandle, ((float)(seq->enddisp - seq->startdisp) / 4.0f));
+ return min_ff(
+ maxhandle,
+ ((float)(SEQ_time_right_handle_frame_get(seq) - SEQ_time_left_handle_frame_get(seq)) /
+ 4.0f));
}
/* Draw a handle, on left or right side of strip. */
@@ -683,8 +689,8 @@ static void draw_seq_handle(View2D *v2d,
uint whichsel = 0;
uchar col[4];
- x1 = seq->startdisp;
- x2 = seq->enddisp;
+ x1 = SEQ_time_left_handle_frame_get(seq);
+ x2 = SEQ_time_right_handle_frame_get(seq);
y1 = seq->machine + SEQ_STRIP_OFSBOTTOM;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
@@ -736,7 +742,11 @@ static void draw_seq_handle(View2D *v2d,
BLF_set_default();
/* Calculate if strip is wide enough for showing the labels. */
- numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d%d", seq->startdisp, seq->enddisp);
+ numstr_len = BLI_snprintf_rlen(numstr,
+ sizeof(numstr),
+ "%d%d",
+ SEQ_time_left_handle_frame_get(seq),
+ SEQ_time_right_handle_frame_get(seq));
float tot_width = BLF_width(fontid, numstr, numstr_len);
if ((x2 - x1) / pixelx > 20 + tot_width) {
@@ -744,12 +754,14 @@ static void draw_seq_handle(View2D *v2d,
float text_margin = 1.2f * handsize_clamped;
if (direction == SEQ_LEFTHANDLE) {
- numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->startdisp);
+ numstr_len = BLI_snprintf_rlen(
+ numstr, sizeof(numstr), "%d", SEQ_time_left_handle_frame_get(seq));
x1 += text_margin;
y1 += 0.09f;
}
else {
- numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->enddisp - 1);
+ numstr_len = BLI_snprintf_rlen(
+ numstr, sizeof(numstr), "%d", SEQ_time_right_handle_frame_get(seq) - 1);
x1 = x2 - (text_margin + pixelx * BLF_width(fontid, numstr, numstr_len));
y1 += 0.09f;
}
@@ -910,7 +922,8 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq,
char strip_duration_text[16];
if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) {
- const int strip_duration = seq->enddisp - seq->startdisp;
+ const int strip_duration = SEQ_time_right_handle_frame_get(seq) -
+ SEQ_time_left_handle_frame_get(seq);
SNPRINTF(strip_duration_text, "%d", strip_duration);
if (i != 0) {
text_array[i++] = text_sep;
@@ -977,8 +990,8 @@ static void draw_sequence_extensions_overlay(
float x1, x2, y1, y2;
uchar col[4], blend_col[3];
- x1 = seq->startdisp;
- x2 = seq->enddisp;
+ x1 = SEQ_time_left_handle_frame_get(seq);
+ x2 = SEQ_time_right_handle_frame_get(seq);
y1 = seq->machine + SEQ_STRIP_OFSBOTTOM;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
@@ -1035,15 +1048,19 @@ static void draw_color_strip_band(
immUniformColor4ubv(col);
- immRectf(pos, seq->startdisp, y1, seq->enddisp, text_margin_y);
+ immRectf(pos,
+ SEQ_time_left_handle_frame_get(seq),
+ y1,
+ SEQ_time_right_handle_frame_get(seq),
+ text_margin_y);
/* 1px line to better separate the color band. */
UI_GetColorPtrShade3ubv(col, col, -20);
immUniformColor4ubv(col);
immBegin(GPU_PRIM_LINES, 2);
- immVertex2f(pos, seq->startdisp, text_margin_y);
- immVertex2f(pos, seq->enddisp, text_margin_y);
+ immVertex2f(pos, SEQ_time_left_handle_frame_get(seq), text_margin_y);
+ immVertex2f(pos, SEQ_time_right_handle_frame_get(seq), text_margin_y);
immEnd();
GPU_blend(GPU_BLEND_NONE);
@@ -1095,28 +1112,25 @@ static void draw_seq_background(Scene *scene,
/* Draw the main strip body. */
if (is_single_image) {
- immRectf(pos,
- SEQ_transform_get_left_handle_frame(seq),
- y1,
- SEQ_transform_get_right_handle_frame(seq),
- y2);
+ immRectf(
+ pos, SEQ_time_left_handle_frame_get(seq), y1, SEQ_time_right_handle_frame_get(seq), y2);
}
else {
immRectf(pos, x1, y1, x2, y2);
}
/* Draw background for hold still regions. */
- if (!is_single_image && (seq->startstill || seq->endstill)) {
+ if (!is_single_image && SEQ_time_has_still_frames(seq)) {
UI_GetColorPtrShade3ubv(col, col, -35);
immUniformColor4ubv(col);
- if (seq->startstill) {
- const float content_start = min_ff(seq->enddisp, seq->start);
- immRectf(pos, seq->startdisp, y1, content_start, y2);
+ if (SEQ_time_has_left_still_frames(seq)) {
+ const float content_start = min_ff(SEQ_time_right_handle_frame_get(seq), seq->start);
+ immRectf(pos, SEQ_time_left_handle_frame_get(seq), y1, content_start, y2);
}
- if (seq->endstill) {
- const float content_end = max_ff(seq->startdisp, seq->start + seq->len);
- immRectf(pos, content_end, y1, seq->enddisp, y2);
+ if (SEQ_time_has_right_still_frames(seq)) {
+ const float content_end = max_ff(SEQ_time_left_handle_frame_get(seq), seq->start + seq->len);
+ immRectf(pos, content_end, y1, SEQ_time_right_handle_frame_get(seq), y2);
}
}
@@ -1333,14 +1347,15 @@ static void draw_seq_strip(const bContext *C,
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG);
/* Draw strip body. */
- x1 = (seq->startstill) ? seq->start : seq->startdisp;
+ x1 = SEQ_time_has_left_still_frames(seq) ? seq->start : SEQ_time_left_handle_frame_get(seq);
y1 = seq->machine + SEQ_STRIP_OFSBOTTOM;
- x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
+ x2 = SEQ_time_has_right_still_frames(seq) ? (seq->start + seq->len) :
+ SEQ_time_right_handle_frame_get(seq);
y2 = seq->machine + SEQ_STRIP_OFSTOP;
/* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */
- x1 = min_ff(x1, seq->enddisp);
- x2 = max_ff(x2, seq->startdisp);
+ x1 = min_ff(x1, SEQ_time_right_handle_frame_get(seq));
+ x2 = max_ff(x2, SEQ_time_left_handle_frame_get(seq));
float text_margin_y;
bool y_threshold;
@@ -1380,8 +1395,8 @@ static void draw_seq_strip(const bContext *C,
}
immUnbindProgram();
- x1 = seq->startdisp;
- x2 = seq->enddisp;
+ x1 = SEQ_time_left_handle_frame_get(seq);
+ x2 = SEQ_time_right_handle_frame_get(seq);
if ((seq->type == SEQ_TYPE_META) ||
((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) {
@@ -1471,23 +1486,23 @@ static void draw_effect_inputs_highlight(Sequence *seq)
immUniformColor4ub(255, 255, 255, 48);
immRectf(pos,
- seq1->startdisp,
+ SEQ_time_left_handle_frame_get(seq1),
seq1->machine + SEQ_STRIP_OFSBOTTOM,
- seq1->enddisp,
+ SEQ_time_right_handle_frame_get(seq1),
seq1->machine + SEQ_STRIP_OFSTOP);
if (seq2 && seq2 != seq1) {
immRectf(pos,
- seq2->startdisp,
+ SEQ_time_left_handle_frame_get(seq2),
seq2->machine + SEQ_STRIP_OFSBOTTOM,
- seq2->enddisp,
+ SEQ_time_right_handle_frame_get(seq2),
seq2->machine + SEQ_STRIP_OFSTOP);
}
if (seq3 && !ELEM(seq3, seq1, seq2)) {
immRectf(pos,
- seq3->startdisp,
+ SEQ_time_left_handle_frame_get(seq3),
seq3->machine + SEQ_STRIP_OFSBOTTOM,
- seq3->enddisp,
+ SEQ_time_right_handle_frame_get(seq3),
seq3->machine + SEQ_STRIP_OFSTOP);
}
immUnbindProgram();
@@ -2081,10 +2096,10 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene)
int preview_frame;
if (last_seq->flag & SEQ_RIGHTSEL) {
- preview_frame = last_seq->enddisp - 1;
+ preview_frame = SEQ_time_right_handle_frame_get(last_seq) - 1;
}
else {
- preview_frame = last_seq->startdisp;
+ preview_frame = SEQ_time_left_handle_frame_get(last_seq);
}
return preview_frame;
@@ -2310,10 +2325,10 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
if (seq == last_seq && (last_seq->flag & SELECT)) {
continue;
}
- if (min_ii(seq->startdisp, seq->start) > v2d->cur.xmax) {
+ if (min_ii(SEQ_time_left_handle_frame_get(seq), seq->start) > v2d->cur.xmax) {
continue;
}
- if (max_ii(seq->enddisp, seq->start + seq->len) < v2d->cur.xmin) {
+ if (max_ii(SEQ_time_right_handle_frame_get(seq), seq->start + seq->len) < v2d->cur.xmin) {
continue;
}
if (seq->machine + 1.0f < v2d->cur.ymin) {
@@ -2368,9 +2383,9 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
immUniformColor4ub(255, 255, 255, 48);
immRectf(pos,
- seq->startdisp,
+ SEQ_time_left_handle_frame_get(seq),
seq->machine + SEQ_STRIP_OFSBOTTOM,
- seq->enddisp,
+ SEQ_time_right_handle_frame_get(seq),
seq->machine + SEQ_STRIP_OFSTOP);
immUnbindProgram();
@@ -2597,7 +2612,8 @@ static void draw_cache_view(const bContext *C)
continue;
}
- if (seq->startdisp > v2d->cur.xmax || seq->enddisp < v2d->cur.xmin) {
+ if (SEQ_time_left_handle_frame_get(seq) > v2d->cur.xmax ||
+ SEQ_time_right_handle_frame_get(seq) < v2d->cur.xmin) {
continue;
}
@@ -2607,7 +2623,11 @@ static void draw_cache_view(const bContext *C)
if (scene->ed->cache_flag & SEQ_CACHE_VIEW_RAW) {
const float bg_color[4] = {1.0f, 0.1f, 0.02f, 0.1f};
immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]);
- immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top);
+ immRectf(pos,
+ SEQ_time_left_handle_frame_get(seq),
+ stripe_bot,
+ SEQ_time_right_handle_frame_get(seq),
+ stripe_top);
}
stripe_bot += stripe_ht + stripe_ofs_y;
@@ -2616,7 +2636,11 @@ static void draw_cache_view(const bContext *C)
if (scene->ed->cache_flag & SEQ_CACHE_VIEW_PREPROCESSED) {
const float bg_color[4] = {0.1f, 0.1f, 0.75f, 0.1f};
immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]);
- immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top);
+ immRectf(pos,
+ SEQ_time_left_handle_frame_get(seq),
+ stripe_bot,
+ SEQ_time_right_handle_frame_get(seq),
+ stripe_top);
}
stripe_top = seq->machine + SEQ_STRIP_OFSTOP - stripe_ofs_y;
@@ -2625,7 +2649,11 @@ static void draw_cache_view(const bContext *C)
if (scene->ed->cache_flag & SEQ_CACHE_VIEW_COMPOSITE) {
const float bg_color[4] = {1.0f, 0.6f, 0.0f, 0.1f};
immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]);
- immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top);
+ immRectf(pos,
+ SEQ_time_left_handle_frame_get(seq),
+ stripe_bot,
+ SEQ_time_right_handle_frame_get(seq),
+ stripe_top);
}
}
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 0305ad279a0..86c438c616e 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -57,6 +57,7 @@
#include "ED_keyframing.h"
#include "ED_numinput.h"
#include "ED_outliner.h"
+#include "ED_scene.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
@@ -76,8 +77,6 @@
typedef struct TransSeq {
int start, machine;
- int startstill, endstill;
- int startdisp, enddisp;
int startofs, endofs;
int anim_startofs, anim_endofs;
/* int final_left, final_right; */ /* UNUSED */
@@ -345,7 +344,6 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
- ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
Sequence *seq;
int snap_frame;
@@ -357,20 +355,19 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
if (seq->flag & SELECT && !SEQ_transform_is_locked(channels, seq) &&
SEQ_transform_sequence_can_be_translated(seq)) {
if ((seq->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) == 0) {
- SEQ_transform_translate_sequence(
- scene, seq, (snap_frame - seq->startofs + seq->startstill) - seq->start);
+ SEQ_transform_translate_sequence(scene, seq, (snap_frame - seq->startofs) - seq->start);
}
else {
if (seq->flag & SEQ_LEFTSEL) {
- SEQ_transform_set_left_handle_frame(seq, snap_frame);
+ SEQ_time_left_handle_frame_set(scene, seq, snap_frame);
}
else { /* SEQ_RIGHTSEL */
- SEQ_transform_set_right_handle_frame(seq, snap_frame);
+ SEQ_time_right_handle_frame_set(scene, seq, snap_frame);
}
- SEQ_transform_handle_xlimits(seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL);
- SEQ_transform_fix_single_image_seq_offsets(seq);
+ SEQ_transform_handle_xlimits(
+ scene, seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL);
+ SEQ_transform_fix_single_image_seq_offsets(scene, seq);
}
- SEQ_time_update_sequence(scene, seqbase, seq);
}
}
@@ -391,27 +388,22 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
if (seq->seq1 && (seq->seq1->flag & SELECT)) {
if (!either_handle_selected) {
- SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
+ SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq)));
}
- SEQ_time_update_sequence(scene, seqbase, seq);
}
else if (seq->seq2 && (seq->seq2->flag & SELECT)) {
if (!either_handle_selected) {
- SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
+ SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq)));
}
- SEQ_time_update_sequence(scene, seqbase, seq);
}
else if (seq->seq3 && (seq->seq3->flag & SELECT)) {
if (!either_handle_selected) {
- SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp));
+ SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq)));
}
- SEQ_time_update_sequence(scene, seqbase, seq);
}
}
}
- SEQ_sort(SEQ_active_seqbase_get(ed));
-
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -478,10 +470,6 @@ static void transseq_backup(TransSeq *ts, Sequence *seq)
{
ts->start = seq->start;
ts->machine = seq->machine;
- ts->startstill = seq->startstill;
- ts->endstill = seq->endstill;
- ts->startdisp = seq->startdisp;
- ts->enddisp = seq->enddisp;
ts->startofs = seq->startofs;
ts->endofs = seq->endofs;
ts->anim_startofs = seq->anim_startofs;
@@ -493,10 +481,6 @@ static void transseq_restore(TransSeq *ts, Sequence *seq)
{
seq->start = ts->start;
seq->machine = ts->machine;
- seq->startstill = ts->startstill;
- seq->endstill = ts->endstill;
- seq->startdisp = ts->startdisp;
- seq->enddisp = ts->enddisp;
seq->startofs = ts->startofs;
seq->endofs = ts->endofs;
seq->anim_startofs = ts->anim_startofs;
@@ -595,69 +579,22 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve
return OPERATOR_RUNNING_MODAL;
}
-static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
+static void sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
{
- /* Only data types supported for now. */
- bool changed = false;
-
- /* Iterate in reverse so meta-strips are iterated after their children. */
for (int i = data->num_seq - 1; i >= 0; i--) {
Sequence *seq = data->seq_array[i];
- int endframe;
- /* Offset seq start. */
seq->start = data->ts[i].start + offset;
-
if (data->trim[i]) {
- /* Find the end-frame. */
- endframe = seq->start + seq->len;
-
- /* Compute the sequence offsets. */
- if (endframe > seq->enddisp) {
- seq->endstill = 0;
- seq->endofs = endframe - seq->enddisp;
- changed = true;
- }
- else {
- seq->endstill = seq->enddisp - endframe;
- seq->endofs = 0;
- changed = true;
- }
-
- if (seq->start > seq->startdisp) {
- seq->startstill = seq->start - seq->startdisp;
- seq->startofs = 0;
- changed = true;
- }
- else {
- seq->startstill = 0;
- seq->startofs = seq->startdisp - seq->start;
- changed = true;
- }
- }
- else {
- /* No transform data (likely effect strip). Only move start and end. */
- seq->startdisp = data->ts[i].startdisp + offset;
- seq->enddisp = data->ts[i].enddisp + offset;
- changed = true;
- }
-
- /* Effects are only added if we they are in a meta-strip.
- * In this case, dependent strips will just be transformed and
- * we can skip calculating for effects.
- * This way we can avoid an extra loop just for effects. */
- if (!(seq->type & SEQ_TYPE_EFFECT)) {
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
- SEQ_time_update_sequence(scene, seqbase, seq);
+ seq->startofs = data->ts[i].startofs - offset;
+ seq->endofs = data->ts[i].endofs + offset;
}
}
- if (changed) {
- for (int i = data->num_seq - 1; i >= 0; i--) {
- Sequence *seq = data->seq_array[i];
- SEQ_relations_invalidate_cache_preprocessed(scene, seq);
- }
+
+ for (int i = data->num_seq - 1; i >= 0; i--) {
+ Sequence *seq = data->seq_array[i];
+ SEQ_relations_invalidate_cache_preprocessed(scene, seq);
}
- return changed;
}
/* Make sure, that each strip contains at least 1 frame of content. */
@@ -670,12 +607,12 @@ static void sequencer_slip_apply_limits(SlipData *data, int *offset)
int seq_content_end = seq_content_start + seq->len + seq->anim_startofs + seq->anim_endofs;
int diff = 0;
- if (seq_content_start >= seq->enddisp) {
- diff = seq->enddisp - seq_content_start - 1;
+ if (seq_content_start >= SEQ_time_right_handle_frame_get(seq)) {
+ diff = SEQ_time_right_handle_frame_get(seq) - seq_content_start - 1;
}
- if (seq_content_end <= seq->startdisp) {
- diff = seq->startdisp - seq_content_end + 1;
+ if (seq_content_end <= SEQ_time_left_handle_frame_get(seq)) {
+ diff = SEQ_time_left_handle_frame_get(seq) - seq_content_end + 1;
}
*offset += diff;
}
@@ -687,7 +624,6 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
int offset = RNA_int_get(op->ptr, "offset");
- bool success = false;
/* Recursively count the trimmed elements. */
int num_seq = slip_count_sequences_recursive(ed->seqbasep, true);
@@ -709,19 +645,16 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op)
}
sequencer_slip_apply_limits(data, &offset);
- success = sequencer_slip_recursively(scene, data, offset);
+ sequencer_slip_recursively(scene, data, offset);
MEM_freeN(data->seq_array);
MEM_freeN(data->trim);
MEM_freeN(data->ts);
MEM_freeN(data);
- if (success) {
- WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
- DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
- return OPERATOR_FINISHED;
- }
- return OPERATOR_CANCELLED;
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ return OPERATOR_FINISHED;
}
static void sequencer_slip_update_header(Scene *scene, ScrArea *area, SlipData *data, int offset)
@@ -762,9 +695,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even
RNA_int_set(op->ptr, "offset", offset);
- if (sequencer_slip_recursively(scene, data, offset)) {
- WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
- }
+ sequencer_slip_recursively(scene, data, offset);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_RUNNING_MODAL;
}
@@ -795,9 +727,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even
RNA_int_set(op->ptr, "offset", offset);
- if (sequencer_slip_recursively(scene, data, offset)) {
- WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
- }
+ sequencer_slip_recursively(scene, data, offset);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
}
break;
}
@@ -827,8 +758,6 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even
for (int i = 0; i < data->num_seq; i++) {
Sequence *seq = data->seq_array[i];
SEQ_add_reload_new_file(bmain, scene, seq, false);
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
- SEQ_time_update_sequence(scene, seqbase, seq);
}
MEM_freeN(data->seq_array);
@@ -875,9 +804,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even
RNA_int_set(op->ptr, "offset", offset);
- if (sequencer_slip_recursively(scene, data, offset)) {
- WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
- }
+ sequencer_slip_recursively(scene, data, offset);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
}
return OPERATOR_RUNNING_MODAL;
@@ -1325,7 +1253,6 @@ static int sequencer_reassign_inputs_exec(bContext *C, wmOperator *op)
last_seq->seq3 = seq3;
int old_start = last_seq->start;
- SEQ_time_update_recursive(scene, last_seq);
SEQ_relations_invalidate_cache_preprocessed(scene, last_seq);
SEQ_offset_animdata(scene, last_seq, (last_seq->start - old_start));
@@ -1483,13 +1410,15 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
if (ignore_selection) {
if (use_cursor_position) {
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if (seq->enddisp == split_frame && seq->machine == split_channel) {
+ if (SEQ_time_right_handle_frame_get(seq) == split_frame &&
+ seq->machine == split_channel) {
seq_selected = seq->flag & SEQ_ALLSEL;
}
}
if (!seq_selected) {
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if (seq->startdisp == split_frame && seq->machine == split_channel) {
+ if (SEQ_time_left_handle_frame_get(seq) == split_frame &&
+ seq->machine == split_channel) {
seq->flag &= ~SEQ_ALLSEL;
}
}
@@ -1500,20 +1429,18 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
if (split_side != SEQ_SIDE_BOTH) {
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (split_side == SEQ_SIDE_LEFT) {
- if (seq->startdisp >= split_frame) {
+ if (SEQ_time_left_handle_frame_get(seq) >= split_frame) {
seq->flag &= ~SEQ_ALLSEL;
}
}
else {
- if (seq->enddisp <= split_frame) {
+ if (SEQ_time_right_handle_frame_get(seq) <= split_frame) {
seq->flag &= ~SEQ_ALLSEL;
}
}
}
}
}
-
- SEQ_sort(SEQ_active_seqbase_get(ed));
}
if (changed) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1719,11 +1646,26 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot)
/** \name Erase Strips Operator
* \{ */
-static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
+static void sequencer_delete_strip_data(bContext *C, Sequence *seq)
+{
+ if (seq->type != SEQ_TYPE_SCENE) {
+ return;
+ }
+
+ Main *bmain = CTX_data_main(C);
+ if (seq->scene) {
+ if (ED_scene_delete(C, bmain, seq->scene)) {
+ WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, seq->scene);
+ }
+ }
+}
+
+static int sequencer_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene));
+ const bool delete_data = RNA_boolean_get(op->ptr, "delete_data");
if (sequencer_view_has_preview_poll(C) && !sequencer_view_preview_only_poll(C)) {
return OPERATOR_CANCELLED;
@@ -1736,6 +1678,9 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
SEQ_ITERATOR_FOREACH (seq, selected_strips) {
SEQ_edit_flag_for_removal(scene, seqbasep, seq);
+ if (delete_data) {
+ sequencer_delete_strip_data(C, seq);
+ }
}
SEQ_edit_remove_flagged_sequences(scene, seqbasep);
@@ -1778,6 +1723,14 @@ void SEQUENCER_OT_delete(wmOperatorType *ot)
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties. */
+ ot->prop = RNA_def_boolean(ot->srna,
+ "delete_data",
+ false,
+ "Delete Data",
+ "After removing the Strip, delete the associated data also");
+ RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -1795,15 +1748,13 @@ static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op))
/* For effects, try to find a replacement input. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if ((seq->type & SEQ_TYPE_EFFECT) == 0 && (seq->flag & SELECT)) {
- seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0;
+ seq->startofs = seq->endofs = 0;
}
}
/* Update lengths, etc. */
seq = ed->seqbasep->first;
while (seq) {
- ListBase *seqbase = SEQ_active_seqbase_get(ed);
- SEQ_time_update_sequence(scene, seqbase, seq);
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
seq = seq->next;
}
@@ -1869,8 +1820,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
/* TODO: remove f-curve and assign to split image strips.
* The old animation system would remove the user of `seq->ipo`. */
- start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq);
- frame_end = SEQ_transform_get_right_handle_frame(seq);
+ start_ofs = timeline_frame = SEQ_time_left_handle_frame_get(seq);
+ frame_end = SEQ_time_right_handle_frame_get(seq);
while (timeline_frame < frame_end) {
/* New seq. */
@@ -1881,7 +1832,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
seq_new->start = start_ofs;
seq_new->type = SEQ_TYPE_IMAGE;
seq_new->len = 1;
- seq_new->endstill = step - 1;
+ seq_new->endofs = 1 - step;
/* New strip. */
strip_new = seq_new->strip;
@@ -1894,8 +1845,6 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
BLI_strncpy(se_new->name, se->name, sizeof(se_new->name));
strip_new->stripdata = se_new;
- SEQ_time_update_sequence(scene, seqbase, seq_new);
-
if (step > 1) {
seq_new->flag &= ~SEQ_OVERLAP;
if (SEQ_transform_test_overlap(seqbase, seq_new)) {
@@ -1919,9 +1868,6 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
}
SEQ_edit_remove_flagged_sequences(scene, seqbase);
-
- SEQ_sort(seqbase);
-
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
@@ -2031,8 +1977,8 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op)
BLI_addtail(&seqm->seqbase, seq);
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
channel_max = max_ii(seq->machine, channel_max);
- meta_start_frame = min_ii(seq->startdisp, meta_start_frame);
- meta_end_frame = max_ii(seq->enddisp, meta_end_frame);
+ meta_start_frame = min_ii(SEQ_time_left_handle_frame_get(seq), meta_start_frame);
+ meta_end_frame = max_ii(SEQ_time_right_handle_frame_get(seq), meta_end_frame);
}
}
@@ -2041,7 +1987,6 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op)
SEQ_sequence_base_unique_name_recursive(scene, &ed->seqbase, seqm);
seqm->start = meta_start_frame;
seqm->len = meta_end_frame - meta_start_frame;
- SEQ_time_update_sequence(scene, active_seqbase, seqm);
SEQ_select_active_set(scene, seqm);
if (SEQ_transform_test_overlap(active_seqbase, seqm)) {
SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene);
@@ -2109,7 +2054,6 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- SEQ_sort(active_seqbase);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2215,19 +2159,18 @@ static const EnumPropertyItem prop_side_lr_types[] = {
static void swap_sequence(Scene *scene, Sequence *seqa, Sequence *seqb)
{
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
- int gap = seqb->startdisp - seqa->enddisp;
+ int gap = SEQ_time_left_handle_frame_get(seqb) - SEQ_time_right_handle_frame_get(seqa);
int seq_a_start;
int seq_b_start;
- seq_b_start = (seqb->start - seqb->startdisp) + seqa->startdisp;
+ seq_b_start = (seqb->start - SEQ_time_left_handle_frame_get(seqb)) +
+ SEQ_time_left_handle_frame_get(seqa);
SEQ_transform_translate_sequence(scene, seqb, seq_b_start - seqb->start);
- SEQ_time_update_sequence(scene, seqbase, seqb);
SEQ_relations_invalidate_cache_preprocessed(scene, seqb);
- seq_a_start = (seqa->start - seqa->startdisp) + seqb->enddisp + gap;
+ seq_a_start = (seqa->start - SEQ_time_left_handle_frame_get(seqa)) +
+ SEQ_time_right_handle_frame_get(seqb) + gap;
SEQ_transform_translate_sequence(scene, seqa, seq_a_start - seqa->start);
- SEQ_time_update_sequence(scene, seqbase, seqa);
SEQ_relations_invalidate_cache_preprocessed(scene, seqa);
}
@@ -2252,13 +2195,13 @@ static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, i
switch (lr) {
case SEQ_SIDE_LEFT:
- if (seq->enddisp <= test->startdisp) {
- dist = test->enddisp - seq->startdisp;
+ if (SEQ_time_right_handle_frame_get(seq) <= SEQ_time_left_handle_frame_get(test)) {
+ dist = SEQ_time_right_handle_frame_get(test) - SEQ_time_left_handle_frame_get(seq);
}
break;
case SEQ_SIDE_RIGHT:
- if (seq->startdisp >= test->enddisp) {
- dist = seq->startdisp - test->enddisp;
+ if (SEQ_time_left_handle_frame_get(seq) >= SEQ_time_right_handle_frame_get(test)) {
+ dist = SEQ_time_left_handle_frame_get(seq) - SEQ_time_right_handle_frame_get(test);
}
break;
}
@@ -2318,14 +2261,6 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op)
break;
}
- /* XXX: Should be a generic function. */
- for (iseq = seqbase->first; iseq; iseq = iseq->next) {
- if ((iseq->type & SEQ_TYPE_EFFECT) &&
- (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) {
- SEQ_time_update_sequence(scene, seqbase, iseq);
- }
- }
-
/* Do this in a new loop since both effects need to be calculated first. */
for (iseq = seqbase->first; iseq; iseq = iseq->next) {
if ((iseq->type & SEQ_TYPE_EFFECT) &&
@@ -2337,10 +2272,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(SEQ_active_seqbase_get(ed));
-
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
-
return OPERATOR_FINISHED;
}
@@ -2457,6 +2389,13 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq)
return;
}
+ /* Add curves for strips inside meta strip. */
+ if (seq->type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) {
+ sequencer_copy_animation(scene, meta_child);
+ }
+ }
+
GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves);
if (fcurves == NULL) {
return;
@@ -2466,6 +2405,7 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq)
BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu));
}
GSET_FOREACH_END();
+
BLI_gset_free(fcurves, NULL);
}
@@ -2587,8 +2527,8 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
else {
int min_seq_startdisp = INT_MAX;
LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) {
- if (seq->startdisp < min_seq_startdisp) {
- min_seq_startdisp = seq->startdisp;
+ if (SEQ_time_left_handle_frame_get(seq) < min_seq_startdisp) {
+ min_seq_startdisp = SEQ_time_left_handle_frame_get(seq);
}
}
/* Paste strips relative to the current-frame. */
@@ -2596,7 +2536,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
}
/* Paste animation.
- * Note: Only fcurves are copied. Drivers and NLA action strips are not copied.
+ * NOTE: Only fcurves are copied. Drivers and NLA action strips are not copied.
* First backup original curves from scene and move curves from clipboard into scene. This way,
* when pasted strips are renamed, pasted fcurves are renamed with them. Finally restore original
* curves from backup.
@@ -2619,13 +2559,17 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
* in the new list. */
BLI_movelisttolist(ed->seqbasep, &nseqbase);
+ /* Make sure, that pasted strips have unique names. This has to be done immediately after adding
+ * strips to seqbase, for lookup cache to work correctly. */
+ for (iseq = iseq_first; iseq; iseq = iseq->next) {
+ SEQ_ensure_unique_name(iseq, scene);
+ }
+
for (iseq = iseq_first; iseq; iseq = iseq->next) {
if (SEQ_clipboard_pasted_seq_was_active(iseq)) {
SEQ_select_active_set(scene, iseq);
}
- /* Make sure, that pasted strips have unique names. */
- SEQ_ensure_unique_name(iseq, scene);
/* Translate after name has been changed, otherwise this will affect animdata of original
* strip. */
SEQ_transform_translate_sequence(scene, iseq, ofs);
@@ -2703,10 +2647,6 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op)
seq_act->scene_sound = NULL;
seq_other->scene_sound = NULL;
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
- SEQ_time_update_sequence(scene, seqbase, seq_act);
- SEQ_time_update_sequence(scene, seqbase, seq_other);
-
if (seq_act->sound) {
BKE_sound_add_scene_sound_defaults(scene, seq_act);
}
@@ -2949,9 +2889,6 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
/* Correct start/end frames so we don't move.
* Important not to set seq->len = len; allow the function to handle it. */
SEQ_add_reload_new_file(bmain, scene, seq, true);
-
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
- SEQ_time_update_sequence(scene, seqbase, seq);
}
else if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) {
bSound *sound = seq->sound;
@@ -3040,6 +2977,81 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Change Strip Scene Operator
+ * \{ */
+
+static bool sequencer_strip_change_scene_poll(bContext *C)
+{
+ Editing *ed = SEQ_editing_get(CTX_data_scene(C));
+ if (ed == NULL) {
+ return false;
+ }
+ Sequence *seq = ed->act_seq;
+ return ((seq != NULL) && (seq->type == SEQ_TYPE_SCENE));
+}
+static int sequencer_change_scene_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Scene *scene_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"));
+
+ if (scene_seq == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Scene not found");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Assign new scene. */
+ Sequence *seq = SEQ_select_active_get(scene);
+ if (seq) {
+ seq->scene = scene_seq;
+ /* Do a refresh of the sequencer data. */
+ SEQ_relations_invalidate_cache_raw(scene, seq);
+ DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS);
+ DEG_relations_tag_update(bmain);
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+static int sequencer_change_scene_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "scene")) {
+ return WM_enum_search_invoke(C, op, event);
+ }
+
+ return sequencer_change_scene_exec(C, op);
+}
+
+void SEQUENCER_OT_change_scene(struct wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* Identifiers. */
+ ot->name = "Change Scene";
+ ot->idname = "SEQUENCER_OT_change_scene";
+ ot->description = "Change Scene assigned to Strip";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_change_scene_exec;
+ ot->invoke = sequencer_change_scene_invoke;
+ ot->poll = sequencer_strip_change_scene_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties. */
+ prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", "");
+ RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf);
+ RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
+ ot->prop = prop;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Export Subtitles Operator
* \{ */
@@ -3049,8 +3061,8 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b)
Sequence *seq_a = (Sequence *)a;
Sequence *seq_b = (Sequence *)b;
- int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a);
- int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b);
+ int seq_a_start = SEQ_time_left_handle_frame_get(seq_a);
+ int seq_b_start = SEQ_time_left_handle_frame_get(seq_b);
/* If strips have the same start frame favor the one with a higher channel. */
if (seq_a_start == seq_b_start) {
@@ -3096,7 +3108,7 @@ static bool seq_get_text_strip_cb(Sequence *seq, void *user_data)
ListBase *channels = SEQ_channels_displayed_get(ed);
/* Only text strips that are not muted and don't end with negative frame. */
if ((seq->type == SEQ_TYPE_TEXT) && !SEQ_render_is_muted(channels, seq) &&
- (seq->enddisp > cd->scene->r.sfra)) {
+ (SEQ_time_right_handle_frame_get(seq) > cd->scene->r.sfra)) {
BLI_addtail(cd->text_seq, MEM_dupallocN(seq));
}
return true;
@@ -3154,16 +3166,17 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
char timecode_str_end[32];
/* Write time-code relative to start frame of scene. Don't allow negative time-codes. */
- BLI_timecode_string_from_time(timecode_str_start,
- sizeof(timecode_str_start),
- -2,
- FRA2TIME(max_ii(seq->startdisp - scene->r.sfra, 0)),
- FPS,
- USER_TIMECODE_SUBRIP);
+ BLI_timecode_string_from_time(
+ timecode_str_start,
+ sizeof(timecode_str_start),
+ -2,
+ FRA2TIME(max_ii(SEQ_time_left_handle_frame_get(seq) - scene->r.sfra, 0)),
+ FPS,
+ USER_TIMECODE_SUBRIP);
BLI_timecode_string_from_time(timecode_str_end,
sizeof(timecode_str_end),
-2,
- FRA2TIME(seq->enddisp - scene->r.sfra),
+ FRA2TIME(SEQ_time_right_handle_frame_get(seq) - scene->r.sfra),
FPS,
USER_TIMECODE_SUBRIP);
@@ -3231,8 +3244,8 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op)
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT) {
selected = true;
- sfra = min_ii(sfra, seq->startdisp);
- efra = max_ii(efra, seq->enddisp - 1);
+ sfra = min_ii(sfra, SEQ_time_left_handle_frame_get(seq));
+ efra = max_ii(efra, SEQ_time_right_handle_frame_get(seq) - 1);
}
}
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 67df065ef35..3307c3fde2f 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -197,6 +197,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot);
void SEQUENCER_OT_change_effect_input(struct wmOperatorType *ot);
void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot);
void SEQUENCER_OT_change_path(struct wmOperatorType *ot);
+void SEQUENCER_OT_change_scene(struct wmOperatorType *ot);
void SEQUENCER_OT_copy(struct wmOperatorType *ot);
void SEQUENCER_OT_paste(struct wmOperatorType *ot);
@@ -231,6 +232,7 @@ void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot);
/* sequencer_add.c */
void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot);
+void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot);
void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index 1aa2991f07a..f7a9bcf41e6 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -58,6 +58,7 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_change_effect_input);
WM_operatortype_append(SEQUENCER_OT_change_effect_type);
WM_operatortype_append(SEQUENCER_OT_change_path);
+ WM_operatortype_append(SEQUENCER_OT_change_scene);
WM_operatortype_append(SEQUENCER_OT_set_range_to_strips);
WM_operatortype_append(SEQUENCER_OT_strip_transform_clear);
@@ -81,6 +82,7 @@ void sequencer_operatortypes(void)
/* sequencer_add.c */
WM_operatortype_append(SEQUENCER_OT_scene_strip_add);
+ WM_operatortype_append(SEQUENCER_OT_scene_strip_add_new);
WM_operatortype_append(SEQUENCER_OT_movieclip_strip_add);
WM_operatortype_append(SEQUENCER_OT_mask_strip_add);
WM_operatortype_append(SEQUENCER_OT_movie_strip_add);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 95707f83d85..f237fbc0a12 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -116,13 +116,13 @@ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int
if (channel == seq->machine) {
switch (sel_side) {
case SEQ_SIDE_LEFT:
- if (frame > (seq->startdisp)) {
+ if (frame > (SEQ_time_left_handle_frame_get(seq))) {
seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
seq->flag |= SELECT;
}
break;
case SEQ_SIDE_RIGHT:
- if (frame < (seq->startdisp)) {
+ if (frame < (SEQ_time_left_handle_frame_get(seq))) {
seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
seq->flag |= SELECT;
}
@@ -152,13 +152,13 @@ static void select_active_side_range(ListBase *seqbase,
}
switch (sel_side) {
case SEQ_SIDE_LEFT:
- if (frame > (seq->startdisp)) {
+ if (frame > (SEQ_time_left_handle_frame_get(seq))) {
seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
seq->flag |= SELECT;
}
break;
case SEQ_SIDE_RIGHT:
- if (frame < (seq->startdisp)) {
+ if (frame < (SEQ_time_left_handle_frame_get(seq))) {
seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
seq->flag |= SELECT;
}
@@ -179,8 +179,8 @@ static void select_linked_time(ListBase *seqbase, Sequence *seq_link)
for (seq = seqbase->first; seq; seq = seq->next) {
if (seq_link->machine != seq->machine) {
- int left_match = (seq->startdisp == seq_link->startdisp) ? 1 : 0;
- int right_match = (seq->enddisp == seq_link->enddisp) ? 1 : 0;
+ int left_match = (SEQ_time_left_handle_frame_get(seq) == seq_link->startdisp) ? 1 : 0;
+ int right_match = (SEQ_time_right_handle_frame_get(seq) == seq_link->enddisp) ? 1 : 0;
if (left_match && right_match) {
/* Direct match, copy the selection settings. */
@@ -247,8 +247,8 @@ void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool desel
void seq_rectf(Sequence *seq, rctf *rect)
{
- rect->xmin = seq->startdisp;
- rect->xmax = seq->enddisp;
+ rect->xmin = SEQ_time_left_handle_frame_get(seq);
+ rect->xmax = SEQ_time_right_handle_frame_get(seq);
rect->ymin = seq->machine + SEQ_STRIP_OFSBOTTOM;
rect->ymax = seq->machine + SEQ_STRIP_OFSTOP;
}
@@ -273,12 +273,12 @@ Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int se
(sel == 0 && (seq->flag & SELECT) == 0))) {
switch (lr) {
case SEQ_SIDE_LEFT:
- if (test->startdisp == (seq->enddisp)) {
+ if (SEQ_time_left_handle_frame_get(test) == (SEQ_time_right_handle_frame_get(seq))) {
return seq;
}
break;
case SEQ_SIDE_RIGHT:
- if (test->enddisp == (seq->startdisp)) {
+ if (SEQ_time_right_handle_frame_get(test) == (SEQ_time_left_handle_frame_get(seq))) {
return seq;
}
break;
@@ -311,13 +311,18 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[
while (seq) {
if (seq->machine == (int)y) {
/* Check for both normal strips, and strips that have been flipped horizontally. */
- if (((seq->startdisp < seq->enddisp) && (seq->startdisp <= x && seq->enddisp >= x)) ||
- ((seq->startdisp > seq->enddisp) && (seq->startdisp >= x && seq->enddisp <= x))) {
+ if (((SEQ_time_left_handle_frame_get(seq) < SEQ_time_right_handle_frame_get(seq)) &&
+ (SEQ_time_left_handle_frame_get(seq) <= x &&
+ SEQ_time_right_handle_frame_get(seq) >= x)) ||
+ ((SEQ_time_left_handle_frame_get(seq) > SEQ_time_right_handle_frame_get(seq)) &&
+ (SEQ_time_left_handle_frame_get(seq) >= x &&
+ SEQ_time_right_handle_frame_get(seq) <= x))) {
if (SEQ_transform_sequence_can_be_translated(seq)) {
/* Clamp handles to defined size in pixel space. */
handsize = 2.0f * sequence_handle_size_get_clamped(seq, pixelx);
- displen = (float)abs(seq->startdisp - seq->enddisp);
+ displen = (float)abs(SEQ_time_left_handle_frame_get(seq) -
+ SEQ_time_right_handle_frame_get(seq));
/* Don't even try to grab the handles of small strips. */
if (displen / pixelx > 16) {
@@ -332,10 +337,10 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[
CLAMP(handsize, 7 * pixelx, 30 * pixelx);
}
- if (handsize + seq->startdisp >= x) {
+ if (handsize + SEQ_time_left_handle_frame_get(seq) >= x) {
*hand = SEQ_SIDE_LEFT;
}
- else if (-handsize + seq->enddisp <= x) {
+ else if (-handsize + SEQ_time_right_handle_frame_get(seq) <= x) {
*hand = SEQ_SIDE_RIGHT;
}
}
@@ -578,8 +583,8 @@ static void sequencer_select_side_of_frame(const bContext *C,
const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) {
- if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) ||
- ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) {
+ if (((x < CFRA) && (SEQ_time_right_handle_frame_get(seq_iter) <= CFRA)) ||
+ ((x >= CFRA) && (SEQ_time_left_handle_frame_get(seq_iter) >= CFRA))) {
/* Select left or right. */
seq_iter->flag |= SELECT;
recurs_sel_seq(seq_iter);
@@ -634,7 +639,8 @@ static void sequencer_select_linked_handle(const bContext *C,
case SEQ_SIDE_LEFT:
if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) {
seq->flag |= SELECT;
- select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp);
+ select_active_side(
+ ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, SEQ_time_left_handle_frame_get(seq));
}
else {
seq->flag |= SELECT;
@@ -647,7 +653,8 @@ static void sequencer_select_linked_handle(const bContext *C,
case SEQ_SIDE_RIGHT:
if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) {
seq->flag |= SELECT;
- select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp);
+ select_active_side(
+ ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, SEQ_time_left_handle_frame_get(seq));
}
else {
seq->flag |= SELECT;
@@ -661,7 +668,8 @@ static void sequencer_select_linked_handle(const bContext *C,
}
else {
- select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp);
+ select_active_side(
+ ed->seqbasep, sel_side, seq->machine, SEQ_time_left_handle_frame_get(seq));
}
}
}
@@ -996,6 +1004,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot)
ot->invoke = sequencer_select_invoke;
ot->modal = WM_generic_select_modal;
ot->poll = ED_operator_sequencer_active;
+ ot->get_name = ED_select_pick_get_name;
/* Flags. */
ot->flag = OPTYPE_UNDO;
@@ -1427,10 +1436,10 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
bool test = false;
switch (side) {
case -1:
- test = (timeline_frame >= seq->enddisp);
+ test = (timeline_frame >= SEQ_time_right_handle_frame_get(seq));
break;
case 1:
- test = (timeline_frame <= seq->startdisp);
+ test = (timeline_frame <= SEQ_time_left_handle_frame_get(seq));
break;
case 2:
test = SEQ_time_strip_intersects_frame(seq, timeline_frame);
@@ -1504,10 +1513,10 @@ static int sequencer_select_side_exec(bContext *C, wmOperator *op)
if (seq->flag & SELECT) {
selected = true;
if (sel_side == SEQ_SIDE_LEFT) {
- *frame_limit_p = max_ii(*frame_limit_p, seq->startdisp);
+ *frame_limit_p = max_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(seq));
}
else {
- *frame_limit_p = min_ii(*frame_limit_p, seq->startdisp);
+ *frame_limit_p = min_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(seq));
}
}
}
@@ -1647,7 +1656,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
float handsize = sequence_handle_size_get_clamped(seq, pixelx);
/* Right handle. */
- if (rectf.xmax > (seq->enddisp - handsize)) {
+ if (rectf.xmax > (SEQ_time_right_handle_frame_get(seq) - handsize)) {
if (select) {
seq->flag |= SELECT | SEQ_RIGHTSEL;
}
@@ -1660,7 +1669,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
}
}
/* Left handle. */
- if (rectf.xmin < (seq->startdisp + handsize)) {
+ if (rectf.xmin < (SEQ_time_left_handle_frame_get(seq) + handsize)) {
if (select) {
seq->flag |= SELECT | SEQ_LEFTSEL;
}
@@ -1952,7 +1961,8 @@ static bool select_grouped_time_overlap(SeqCollection *strips,
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
- if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) {
+ if (SEQ_time_left_handle_frame_get(seq) < SEQ_time_right_handle_frame_get(actseq) &&
+ SEQ_time_right_handle_frame_get(seq) > SEQ_time_left_handle_frame_get(actseq)) {
seq->flag |= SELECT;
changed = true;
}
@@ -1970,8 +1980,10 @@ static void query_lower_channel_strips(Sequence *seq_reference,
if (seq_test->machine > seq_reference->machine) {
continue; /* Not lower channel. */
}
- if (seq_test->enddisp <= seq_reference->startdisp ||
- seq_test->startdisp >= seq_reference->enddisp) {
+ if (SEQ_time_right_handle_frame_get(seq_test) <=
+ SEQ_time_left_handle_frame_get(seq_reference) ||
+ SEQ_time_left_handle_frame_get(seq_test) >=
+ SEQ_time_right_handle_frame_get(seq_reference)) {
continue; /* Not intersecting in time. */
}
SEQ_collection_append_strip(seq_test, collection);
diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c
index 43c5e004040..984d3b1f374 100644
--- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c
+++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c
@@ -23,6 +23,7 @@
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -73,10 +74,10 @@ static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area)
if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
return false;
}
- if (min_ii(seq->startdisp, seq->start) > view_area->xmax) {
+ if (min_ii(SEQ_time_left_handle_frame_get(seq), seq->start) > view_area->xmax) {
return false;
}
- if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) {
+ if (max_ii(SEQ_time_right_handle_frame_get(seq), seq->start + seq->len) < view_area->xmin) {
return false;
}
if (seq->machine + 1.0f < view_area->ymin) {
@@ -205,7 +206,7 @@ static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Edi
else {
if (val_need_update != NULL) {
val_need_update->seq_dupli->start = seq->start;
- val_need_update->seq_dupli->startdisp = seq->startdisp;
+ val_need_update->seq_dupli->startdisp = SEQ_time_left_handle_frame_get(seq);
}
}
}
@@ -362,15 +363,16 @@ static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame,
static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame)
{
- if (timeline_frame <= seq->startdisp) {
- return seq->startdisp;
+ if (timeline_frame <= SEQ_time_left_handle_frame_get(seq)) {
+ return SEQ_time_left_handle_frame_get(seq);
}
/* Set of "guaranteed" thumbnails. */
- const int frame_index = timeline_frame - seq->startdisp;
+ const int frame_index = timeline_frame - SEQ_time_left_handle_frame_get(seq);
const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq);
const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step;
- const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp;
+ const int nearest_guaranted_absolute_frame = relative_base_frame +
+ SEQ_time_left_handle_frame_get(seq);
return nearest_guaranted_absolute_frame;
}
@@ -443,9 +445,11 @@ void draw_seq_strip_thumbnail(View2D *v2d,
float thumb_y_end = y1 + thumb_height;
float cut_off = 0;
- float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
+ float upper_thumb_bound = SEQ_time_has_right_still_frames(seq) ?
+ (seq->start + seq->len) :
+ SEQ_time_right_handle_frame_get(seq);
if (seq->type == SEQ_TYPE_IMAGE) {
- upper_thumb_bound = seq->enddisp;
+ upper_thumb_bound = SEQ_time_right_handle_frame_get(seq);
}
float timeline_frame = SEQ_render_thumbnail_first_frame_get(seq, thumb_width, &v2d->cur);
@@ -471,8 +475,8 @@ void draw_seq_strip_thumbnail(View2D *v2d,
}
/* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */
- if (IN_RANGE_INCL(seq->startdisp, timeline_frame, thumb_x_end)) {
- cut_off = seq->startdisp - timeline_frame;
+ if (IN_RANGE_INCL(SEQ_time_left_handle_frame_get(seq), timeline_frame, thumb_x_end)) {
+ cut_off = SEQ_time_left_handle_frame_get(seq) - timeline_frame;
clipped = true;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index b3dbc3c72d1..857ca6d989b 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -20,8 +20,6 @@
#include "UI_view2d.h"
-#include "RNA_define.h"
-
#include "SEQ_iterator.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
@@ -308,8 +306,8 @@ static void seq_view_collection_rect_timeline(Scene *scene, SeqCollection *strip
int xmargin = FPS;
SEQ_ITERATOR_FOREACH (seq, strips) {
- xmin = min_ii(xmin, seq->startdisp);
- xmax = max_ii(xmax, seq->enddisp);
+ xmin = min_ii(xmin, SEQ_time_left_handle_frame_get(seq));
+ xmax = max_ii(xmax, SEQ_time_right_handle_frame_get(seq));
ymin = min_ii(ymin, seq->machine);
ymax = max_ii(ymax, seq->machine);
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index b3d6c395e89..dd3aac1eae9 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -32,8 +32,6 @@
#include "BLF_api.h"
-#include "spreadsheet_intern.hh"
-
#include "spreadsheet_context.hh"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_dataset_draw.hh"
@@ -304,6 +302,7 @@ static float get_default_column_width(const ColumnValues &values)
switch (values.type()) {
case SPREADSHEET_VALUE_TYPE_BOOL:
return 2.0f;
+ case SPREADSHEET_VALUE_TYPE_INT8:
case SPREADSHEET_VALUE_TYPE_INT32:
return float_width;
case SPREADSHEET_VALUE_TYPE_FLOAT:
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
index a29aa1fd026..46e98acb8e8 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -23,6 +23,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type)
if (type.is<bool>()) {
return SPREADSHEET_VALUE_TYPE_BOOL;
}
+ if (type.is<int8_t>()) {
+ return SPREADSHEET_VALUE_TYPE_INT8;
+ }
if (type.is<int>()) {
return SPREADSHEET_VALUE_TYPE_INT32;
}
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 4afa70d9ef6..f5315b616d0 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
- if (component_->attribute_domain_size(domain_) == 0) {
+ if (component_->attribute_domain_num(domain_) == 0) {
return;
}
@@ -81,6 +81,9 @@ void GeometryDataSource::foreach_default_column_ids(
if (attribute_id.is_anonymous()) {
return true;
}
+ if (!bke::allow_procedural_attribute_access(attribute_id.name())) {
+ return true;
+ }
SpreadsheetColumnID column_id;
column_id.name = (char *)attribute_id.name().data();
fn(column_id, false);
@@ -110,8 +113,8 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
- const int domain_size = component_->attribute_domain_size(domain_);
- if (domain_size == 0) {
+ const int domain_num = component_->attribute_domain_num(domain_);
+ if (domain_num == 0) {
return {};
}
@@ -129,7 +132,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
Span<InstanceReference> references = instances.references();
return std::make_unique<ColumnValues>(
column_id.name,
- VArray<InstanceReference>::ForFunc(domain_size,
+ VArray<InstanceReference>::ForFunc(domain_num,
[reference_handles, references](int64_t index) {
return references[reference_handles[index]];
}));
@@ -137,13 +140,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
Span<float4x4> transforms = instances.instance_transforms();
if (STREQ(column_id.name, "Rotation")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
+ column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
return transforms[index].to_euler();
}));
}
if (STREQ(column_id.name, "Scale")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
+ column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
return transforms[index].scale();
}));
}
@@ -210,7 +213,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
int GeometryDataSource::tot_rows() const
{
- return component_->attribute_domain_size(domain_);
+ return component_->attribute_domain_num(domain_);
}
/**
@@ -256,7 +259,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
BMesh *bm = mesh_orig->edit_mesh->bm;
BM_mesh_elem_table_ensure(bm, BM_VERT);
- int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
if (orig_indices != nullptr) {
/* Use CD_ORIGINDEX layer if it exists. */
VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
@@ -509,7 +512,7 @@ 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>, GArray<>> arrays;
+ Map<std::pair<eAttrDomain, GField>, GArray<>> arrays;
};
static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
@@ -523,18 +526,18 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
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);
+ const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
+ const int domain_num = component.attribute_domain_num(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. */
GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
- GArray<> evaluated_array(field.cpp_type(), domain_size);
+ GArray<> evaluated_array(field.cpp_type(), domain_num);
bke::GeometryComponentFieldContext field_context{component, domain};
- fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ fn::FieldEvaluator field_evaluator{field_context, domain_num};
field_evaluator.add_with_destination(field, evaluated_array);
field_evaluator.evaluate();
return evaluated_array;
@@ -547,7 +550,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
const GeometryComponentType component_type = get_display_component_type(C, object_eval);
GeometrySet geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, object_eval);
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 8b281e5a558..04b4f6d8d06 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -40,7 +40,7 @@ class GeometryDataSource : public DataSource {
Object *object_eval_;
const GeometrySet geometry_set_;
const GeometryComponent *component_;
- AttributeDomain domain_;
+ eAttrDomain domain_;
ExtraColumns extra_columns_;
/* Some data is computed on the fly only when it is requested. Computing it does not change the
@@ -53,7 +53,7 @@ class GeometryDataSource : public DataSource {
GeometryDataSource(Object *object_eval,
GeometrySet geometry_set,
const GeometryComponentType component_type,
- const AttributeDomain domain,
+ const eAttrDomain domain,
ExtraColumns extra_columns)
: object_eval_(object_eval),
geometry_set_(std::move(geometry_set)),
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
index c4b5228758c..ee22f4173ab 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
@@ -27,7 +27,7 @@ class GeometryDataSetTreeView;
class GeometryDataSetTreeViewItem : public ui::AbstractTreeViewItem {
GeometryComponentType component_type_;
- std::optional<AttributeDomain> domain_;
+ std::optional<eAttrDomain> domain_;
BIFIconID icon_;
public:
@@ -35,7 +35,7 @@ class GeometryDataSetTreeViewItem : public ui::AbstractTreeViewItem {
StringRef label,
BIFIconID icon);
GeometryDataSetTreeViewItem(GeometryComponentType component_type,
- AttributeDomain domain,
+ eAttrDomain domain,
StringRef label,
BIFIconID icon);
@@ -113,7 +113,7 @@ GeometryDataSetTreeViewItem::GeometryDataSetTreeViewItem(GeometryComponentType c
this->set_collapsed(false);
}
GeometryDataSetTreeViewItem::GeometryDataSetTreeViewItem(GeometryComponentType component_type,
- AttributeDomain domain,
+ eAttrDomain domain,
StringRef label,
BIFIconID icon)
: component_type_(component_type), domain_(domain), icon_(icon)
@@ -144,7 +144,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row)
/* Using the tree row button instead of a separate right aligned button gives padding
* to the right side of the number, which it didn't have with the button. */
char element_count[7];
- BLI_str_format_attribute_domain_size(element_count, *count);
+ BLI_str_format_decimal_unit(element_count, *count);
UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count);
}
}
@@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const
}
if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) {
- return component->attribute_domain_size(*domain_);
+ return component->attribute_domain_num(*domain_);
}
return 0;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
index 4d6dc3b4166..166c5de9fc3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
@@ -5,12 +5,6 @@
#include "ED_screen.h"
-#include "RNA_access.h"
-#include "RNA_define.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
#include "BLI_listbase.h"
#include "MEM_guardedalloc.h"
@@ -20,8 +14,6 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "ED_screen.h"
-
#include "WM_api.h"
#include "WM_types.h"
@@ -92,7 +84,7 @@ static int select_component_domain_invoke(bContext *C,
{
GeometryComponentType component_type = static_cast<GeometryComponentType>(
RNA_int_get(op->ptr, "component_type"));
- AttributeDomain attribute_domain = static_cast<AttributeDomain>(
+ eAttrDomain attribute_domain = static_cast<eAttrDomain>(
RNA_int_get(op->ptr, "attribute_domain_type"));
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
index eb8f111baa0..e1ff4b59b14 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
@@ -14,8 +14,6 @@
#include "RNA_access.h"
-#include "spreadsheet_intern.hh"
-
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
#include "spreadsheet_layout.hh"
@@ -73,6 +71,35 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
}
}
}
+ else if (column_data.type().is<int8_t>()) {
+ const int value = row_filter.value_int;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ apply_filter_operation(
+ column_data.typed<int8_t>(),
+ [&](const int cell) { return cell == value; },
+ prev_mask,
+ new_indices);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_GREATER: {
+ apply_filter_operation(
+ column_data.typed<int8_t>(),
+ [value](const int cell) { return cell > value; },
+ prev_mask,
+ new_indices);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_LESS: {
+ apply_filter_operation(
+ column_data.typed<int8_t>(),
+ [&](const int cell) { return cell < value; },
+ prev_mask,
+ new_indices);
+ break;
+ }
+ }
+ }
else if (column_data.type().is<int>()) {
const int value = row_filter.value_int;
switch (row_filter.operation) {
@@ -190,33 +217,29 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
}
else if (column_data.type().is<InstanceReference>()) {
const StringRef value = row_filter.value_string;
- switch (row_filter.operation) {
- case SPREADSHEET_ROW_FILTER_EQUAL: {
- apply_filter_operation(
- column_data.typed<InstanceReference>(),
- [&](const InstanceReference cell) {
- switch (cell.type()) {
- case InstanceReference::Type::Object: {
- return value == (reinterpret_cast<ID &>(cell.object()).name + 2);
- }
- case InstanceReference::Type::Collection: {
- return value == (reinterpret_cast<ID &>(cell.collection()).name + 2);
- }
- case InstanceReference::Type::GeometrySet: {
- return false;
- }
- case InstanceReference::Type::None: {
- return false;
- }
- }
- BLI_assert_unreachable();
+
+ apply_filter_operation(
+ column_data.typed<InstanceReference>(),
+ [&](const InstanceReference cell) {
+ switch (cell.type()) {
+ case InstanceReference::Type::Object: {
+ return value == (reinterpret_cast<ID &>(cell.object()).name + 2);
+ }
+ case InstanceReference::Type::Collection: {
+ return value == (reinterpret_cast<ID &>(cell.collection()).name + 2);
+ }
+ case InstanceReference::Type::GeometrySet: {
return false;
- },
- prev_mask,
- new_indices);
- break;
- }
- }
+ }
+ case InstanceReference::Type::None: {
+ return false;
+ }
+ }
+ BLI_assert_unreachable();
+ return false;
+ },
+ prev_mask,
+ new_indices);
}
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
index 7c1ac024c12..d42a371c666 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
@@ -63,6 +63,7 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
const eSpreadsheetColumnValueType data_type)
{
switch (data_type) {
+ case SPREADSHEET_VALUE_TYPE_INT8:
case SPREADSHEET_VALUE_TYPE_INT32:
return std::to_string(row_filter.value_int);
case SPREADSHEET_VALUE_TYPE_FLOAT: {
@@ -200,6 +201,10 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
}
switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) {
+ case SPREADSHEET_VALUE_TYPE_INT8:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_int8", 0, IFACE_("Value"), ICON_NONE);
+ break;
case SPREADSHEET_VALUE_TYPE_INT32:
uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
uiItemR(layout, filter_ptr, "value_int", 0, IFACE_("Value"), ICON_NONE);
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 36eafedbc80..c93ffccd477 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1603,7 +1603,7 @@ void draw_text_main(SpaceText *st, ARegion *region)
return;
}
- /* dpi controlled line height and font size */
+ /* DPI controlled line height and font size. */
st->runtime.lheight_px = (U.widget_unit * st->lheight) / 20;
/* don't draw lines below this */
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 7776119a594..8add6886584 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -6,12 +6,14 @@
*/
#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
-#include "BLI_math.h"
+#include "BLI_math_vector.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_object.h"
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index a02058fde6b..9ed2fec96db 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -590,16 +590,15 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *view3d_mat_drop_tooltip(bContext *C,
wmDrag *drag,
const int xy[2],
- struct wmDropBox *drop)
+ wmDropBox *UNUSED(drop))
{
const char *name = WM_drag_get_item_name(drag);
ARegion *region = CTX_wm_region(C);
- RNA_string_set(drop->ptr, "name", name);
- int mval[2] = {
+ const 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);
+ return ED_object_ot_drop_named_material_tooltip(C, name, mval);
}
static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
@@ -716,7 +715,7 @@ static void view3d_ob_drop_copy_local_id(bContext *UNUSED(C), wmDrag *drag, wmDr
{
ID *id = WM_drag_get_local_ID(drag, ID_OB);
- RNA_string_set(drop->ptr, "name", id->name + 2);
+ RNA_int_set(drop->ptr, "session_uuid", id->session_uuid);
/* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */
BLI_assert(drag->type != WM_DRAG_ASSET);
@@ -751,7 +750,7 @@ static void view3d_ob_drop_copy_external_asset(bContext *UNUSED(C), wmDrag *drag
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);
+ RNA_int_set(drop->ptr, "session_uuid", id->session_uuid);
Base *base = BKE_view_layer_base_find(view_layer, (Object *)id);
if (base != NULL) {
@@ -823,15 +822,15 @@ static void view3d_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *dr
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
- RNA_string_set(drop->ptr, "name", id->name + 2);
+ WM_operator_properties_id_lookup_set_from_id(drop->ptr, id);
}
static void view3d_id_drop_copy_with_type(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
- RNA_string_set(drop->ptr, "name", id->name + 2);
RNA_enum_set(drop->ptr, "type", GS(id->name));
+ WM_operator_properties_id_lookup_set_from_id(drop->ptr, id);
}
static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
@@ -839,7 +838,7 @@ static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBo
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
if (id) {
- RNA_string_set(drop->ptr, "name", id->name + 2);
+ WM_operator_properties_id_lookup_set_from_id(drop->ptr, id);
RNA_struct_property_unset(drop->ptr, "filepath");
}
else if (drag->path[0]) {
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index 96a193cc10d..395df42b2cb 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -8,11 +8,9 @@
*/
#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BLI_rect.h"
-#include "DNA_scene_types.h"
#include "MEM_guardedalloc.h"
@@ -54,7 +52,7 @@ typedef struct SnapCursorDataIntern {
struct SnapObjectContext *snap_context_v3d;
const Scene *scene;
- short snap_elem_hidden;
+ eSnapMode snap_elem_hidden;
float prevpoint_stack[3];
@@ -374,7 +372,7 @@ void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d,
const float normal[3],
const uchar color_line[4],
const uchar color_point[4],
- const short snap_elem_type)
+ const eSnapMode snap_elem_type)
{
if (!loc_prev && !loc_curr) {
return;
@@ -539,9 +537,9 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w
/** \name Update
* \{ */
-static short v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene)
+static eSnapMode v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene)
{
- short snap_elements = snap_state->snap_elem_force;
+ eSnapMode snap_elements = snap_state->snap_elem_force;
if (!snap_elements) {
return scene->toolsettings->snap_mode;
}
@@ -587,7 +585,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
v3d_cursor_snap_context_ensure(scene);
float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3];
- short snap_elem = 0;
+ eSnapMode snap_elem = SCE_SNAP_MODE_NONE;
int snap_elem_index[3] = {-1, -1, -1};
int index = -1;
@@ -596,8 +594,8 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
zero_v3(face_nor);
unit_m3(omat);
- ushort snap_elements = v3d_cursor_snap_elements(state, scene);
- data_intern->snap_elem_hidden = 0;
+ eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE)) {
data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
@@ -613,7 +611,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) {
snap_data->is_enabled = false;
if (!calc_plane_omat) {
- snap_data->snap_elem = 0;
+ snap_data->snap_elem = SCE_SNAP_MODE_NONE;
return;
}
snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
@@ -621,8 +619,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
}
#endif
- if (snap_elements & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
+ if (snap_elements & SCE_SNAP_MODE_GEOM) {
float prev_co[3] = {0.0f};
if (state->prevpoint) {
copy_v3_v3(prev_co, state->prevpoint);
@@ -648,7 +645,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
v3d,
snap_elements,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = edit_mode_type,
.use_occlusion_test = use_occlusion_test,
},
@@ -669,7 +666,8 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
if (calc_plane_omat) {
RegionView3D *rv3d = region->regiondata;
- bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE);
+ bool orient_surface = (snap_elem != SCE_SNAP_MODE_NONE) &&
+ (state->plane_orient == V3D_PLACE_ORIENT_SURFACE);
if (orient_surface) {
copy_m3_m4(omat, obmat);
}
@@ -700,7 +698,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
}
}
- float *co_depth = snap_elem ? co : scene->cursor.location;
+ float *co_depth = (snap_elem != SCE_SNAP_MODE_NONE) ? co : scene->cursor.location;
snap_elem &= ~data_intern->snap_elem_hidden;
if (snap_elem == 0) {
RegionView3D *rv3d = region->regiondata;
@@ -816,7 +814,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust
}
const bool draw_plane = state->draw_plane || state->draw_box;
- if (!snap_data->snap_elem && !draw_plane) {
+ if (snap_data->snap_elem == SCE_SNAP_MODE_NONE && !draw_plane) {
return;
}
@@ -834,7 +832,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 || state->draw_box)) {
+ if (snap_data->snap_elem != SCE_SNAP_MODE_NONE && (state->draw_point || state->draw_box)) {
const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ?
state->prevpoint :
NULL;
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 5b068750d76..b9e4c19295d 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1786,11 +1786,13 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
}
/* Disable other overlays (set all available _HIDE_ flags). */
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
- V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES |
- V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
+ V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
if ((draw_flags & V3D_OFSDRAW_SHOW_OBJECT_EXTRAS) == 0) {
v3d.overlay.flag |= V3D_OVERLAY_HIDE_OBJECT_XTRAS;
}
+ if ((object_type_exclude_viewport_override & (1 << OB_ARMATURE)) != 0) {
+ v3d.overlay.flag |= V3D_OVERLAY_HIDE_BONES;
+ }
v3d.flag |= V3D_HIDE_HELPLINES;
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index e52ff062302..5fbdbef676c 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -608,7 +608,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO;
/* properties */
- RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign");
+ WM_operator_properties_id_lookup(ot, true);
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
FILE_SPECIAL,
@@ -631,13 +631,20 @@ static int background_image_remove_exec(bContext *C, wmOperator *op)
CameraBGImage *bgpic_rem = BLI_findlink(&cam->bg_images, index);
if (bgpic_rem) {
- if (bgpic_rem->source == CAM_BGIMG_SOURCE_IMAGE) {
- id_us_min((ID *)bgpic_rem->ima);
- }
- else if (bgpic_rem->source == CAM_BGIMG_SOURCE_MOVIE) {
- id_us_min((ID *)bgpic_rem->clip);
+ if (ID_IS_OVERRIDE_LIBRARY(cam) &&
+ (bgpic_rem->flag & CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL) == 0) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Cannot remove background image %d from camera '%s', as it is from the linked "
+ "reference data",
+ index,
+ cam->id.name + 2);
+ return OPERATOR_CANCELLED;
}
+ id_us_min((ID *)bgpic_rem->ima);
+ id_us_min((ID *)bgpic_rem->clip);
+
BKE_camera_background_image_remove(cam, bgpic_rem);
WM_event_add_notifier(C, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, cam);
@@ -657,7 +664,7 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = background_image_remove_exec;
- ot->poll = ED_operator_camera;
+ ot->poll = ED_operator_camera_poll;
/* flags */
ot->flag = 0;
@@ -678,10 +685,8 @@ static int drop_world_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
- char name[MAX_ID_NAME - 2];
-
- RNA_string_get(op->ptr, "name", name);
- World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name);
+ World *world = (World *)WM_operator_properties_id_lookup_from_name_or_session_uuid(
+ bmain, op->ptr, ID_WO);
if (world == NULL) {
return OPERATOR_CANCELLED;
}
@@ -718,7 +723,7 @@ void VIEW3D_OT_drop_world(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
/* properties */
- RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign");
+ WM_operator_properties_id_lookup(ot, true);
}
/** \} */
@@ -905,7 +910,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
v3d,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 567022cc9d1..9aae30c4a7e 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -359,7 +359,7 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
v3d,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_CAGE,
},
mval_fl,
@@ -374,7 +374,7 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
depsgraph,
v3d,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_CAGE,
},
ray_start,
@@ -1228,11 +1228,7 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup)
gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true);
gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL);
- RNA_enum_set(gizmo->ptr,
- "snap_elements_force",
- (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */
- SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT));
+ RNA_enum_set(gizmo->ptr, "snap_elements_force", SCE_SNAP_MODE_GEOM);
ED_gizmotypes_snap_3d_flag_set(gizmo, V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE);
WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
index f0557205e8f..aa287403e23 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
@@ -19,8 +19,6 @@
#include "MEM_guardedalloc.h"
-#include "WM_toolsystem.h"
-
#include "RNA_access.h"
#include "RNA_define.h"
diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c
index ea21eed6445..087ca72211e 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_roll.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c
@@ -48,11 +48,7 @@ static void view_roll_angle(ARegion *region,
normalize_qt(quat);
if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) {
- if (ED_view3d_quat_to_axis_view(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll)) {
- if (rv3d->view != RV3D_VIEW_USER) {
- ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_mul);
- }
- }
+ ED_view3d_quat_to_axis_view_and_reset_quat(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
}
else {
rv3d->view = RV3D_VIEW_USER;
diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
index c9ef6422982..989fa152acc 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
@@ -162,10 +162,8 @@ static void viewrotate_apply_snap(ViewOpsData *vod)
if (found) {
/* lock 'quat_best' to an axis view if we can */
- ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
- if (rv3d->view != RV3D_VIEW_USER) {
- ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best);
- }
+ ED_view3d_quat_to_axis_view_and_reset_quat(
+ quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
}
else {
copy_qt_qt(quat_best, viewquat_align);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index 19e064438fc..c4eff01375b 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -412,7 +412,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d,
walk->depsgraph,
walk->v3d,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
/* Avoid having to convert the edit-mesh to a regular mesh. */
.edit_mode_type = SNAP_GEOM_EDIT,
},
@@ -454,7 +454,7 @@ static bool walk_ray_cast(RegionView3D *rv3d,
walk->depsgraph,
walk->v3d,
&(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
},
ray_start,
ray_normal,
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index eefc085bdf8..d3b82476d09 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -695,7 +695,7 @@ static bool view3d_interactive_add_calc_snap(bContext *UNUSED(C),
if (r_is_snap_invert) {
*r_is_snap_invert = snap_data->is_snap_invert;
}
- return snap_data->snap_elem != 0;
+ return snap_data->snap_elem != SCE_SNAP_MODE_NONE;
}
/** \} */
@@ -1303,7 +1303,7 @@ static int idp_rna_snap_target_get_fn(struct PointerRNA *UNUSED(ptr),
struct PropertyRNA *UNUSED(prop))
{
V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get();
- if (!snap_state->snap_elem_force) {
+ if (snap_state->snap_elem_force == SCE_SNAP_MODE_NONE) {
return PLACE_SNAP_TO_DEFAULT;
}
@@ -1316,7 +1316,7 @@ static void idp_rna_snap_target_set_fn(struct PointerRNA *UNUSED(ptr),
struct PropertyRNA *UNUSED(prop),
int value)
{
- short snap_mode = 0; /* #toolsettings->snap_mode. */
+ eSnapMode snap_mode = SCE_SNAP_MODE_NONE; /* #toolsettings->snap_mode. */
const enum ePlace_SnapTo snap_to = value;
if (snap_to == PLACE_SNAP_TO_GEOMETRY) {
snap_mode = SCE_SNAP_MODE_GEOM;
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 85d239507ce..1230515a180 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -30,7 +30,7 @@
void ED_view3d_project_float_v2_m4(const ARegion *region,
const float co[3],
float r_co[2],
- float mat[4][4])
+ const float mat[4][4])
{
float vec4[4];
@@ -52,7 +52,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region,
void ED_view3d_project_float_v3_m4(const ARegion *region,
const float co[3],
float r_co[3],
- float mat[4][4])
+ const float mat[4][4])
{
float vec4[4];
@@ -312,7 +312,7 @@ float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float
return -dot_v3v3(rv3d->viewinv[2], co);
}
-static void view3d_win_to_ray_segment(struct Depsgraph *depsgraph,
+static void view3d_win_to_ray_segment(const struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
const float mval[2],
@@ -642,9 +642,9 @@ void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r
normalize_v3(r_out);
}
-bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph,
+bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph,
const ARegion *region,
- View3D *v3d,
+ const View3D *v3d,
const float mval[2],
float r_ray_start[3],
float r_ray_end[3],
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 7a022441876..8eff9ee472f 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -57,8 +57,6 @@
#include "BKE_tracking.h"
#include "BKE_workspace.h"
-#include "DEG_depsgraph.h"
-
#include "WM_api.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
@@ -1647,7 +1645,7 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
const int name_index = RNA_enum_get(op->ptr, "name");
const struct SelectPick_Params params = {
- .sel_op = ED_select_op_from_operator(op),
+ .sel_op = ED_select_op_from_operator(op->ptr),
};
View3D *v3d = CTX_wm_view3d(C);
@@ -2876,8 +2874,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
Object *obact = CTX_data_active_object(C);
struct SelectPick_Params params = {0};
- ED_select_pick_params_from_operator(op, &params);
+ ED_select_pick_params_from_operator(op->ptr, &params);
+ const bool vert_without_handles = RNA_boolean_get(op->ptr, "vert_without_handles");
bool center = RNA_boolean_get(op->ptr, "center");
bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
/* Only force object select for edit-mode to support vertex parenting,
@@ -2932,7 +2931,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
changed = ED_lattice_select_pick(C, mval, &params);
}
else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
- changed = ED_curve_editnurb_select_pick(C, mval, ED_view3d_select_dist_px(), &params);
+ changed = ED_curve_editnurb_select_pick(
+ C, mval, ED_view3d_select_dist_px(), vert_without_handles, &params);
}
else if (obedit->type == OB_MBALL) {
changed = ED_mball_select_pick(C, mval, &params);
@@ -2988,6 +2988,7 @@ void VIEW3D_OT_select(wmOperatorType *ot)
ot->invoke = view3d_select_invoke;
ot->exec = view3d_select_exec;
ot->poll = ED_operator_view3d_active;
+ ot->get_name = ED_select_pick_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
@@ -3008,6 +3009,15 @@ void VIEW3D_OT_select(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (edit mode only)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ /* Needed for select-through to usefully drag handles, see: T98254.
+ * NOTE: this option may be removed and become default behavior, see design task: T98552. */
+ prop = RNA_def_boolean(ot->srna,
+ "vert_without_handles",
+ 0,
+ "Control Point Without Handles",
+ "Only select the curve control point, not it's handles");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
prop = RNA_def_int_vector(ot->srna,
"location",
2,
@@ -4740,6 +4750,7 @@ void VIEW3D_OT_select_circle(wmOperatorType *ot)
ot->exec = view3d_circle_select_exec;
ot->poll = view3d_selectable_data;
ot->cancel = view3d_circle_select_cancel;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index e6895c0f4a3..306394ce53d 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -106,7 +106,7 @@ void ED_view3d_dist_range_get(const View3D *v3d, float r_dist_range[2])
r_dist_range[1] = v3d->clip_end * 10.0f;
}
-bool ED_view3d_clip_range_get(Depsgraph *depsgraph,
+bool ED_view3d_clip_range_get(const Depsgraph *depsgraph,
const View3D *v3d,
const RegionView3D *rv3d,
float *r_clipsta,
@@ -1317,22 +1317,59 @@ bool ED_view3d_quat_to_axis_view(const float quat[4],
*r_view = RV3D_VIEW_USER;
*r_view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
- /* quat values are all unit length */
- for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) {
- for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270;
- view_axis_roll++) {
- if (fabsf(angle_signed_qtqt(
- quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll])) < epsilon) {
- *r_view = view;
- *r_view_axis_roll = view_axis_roll;
- return true;
+ /* Quaternion values are all unit length. */
+
+ if (epsilon < M_PI_4) {
+ /* Under 45 degrees, just pick the closest value. */
+ for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) {
+ for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270;
+ view_axis_roll++) {
+ if (fabsf(angle_signed_qtqt(
+ quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll])) < epsilon) {
+ *r_view = view;
+ *r_view_axis_roll = view_axis_roll;
+ return true;
+ }
+ }
+ }
+ }
+ else {
+ /* Epsilon over 45 degrees, check all & find use the closest. */
+ float delta_best = FLT_MAX;
+ for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) {
+ for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270;
+ view_axis_roll++) {
+ const float delta_test = fabsf(
+ angle_signed_qtqt(quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll]));
+ if (delta_best > delta_test) {
+ delta_best = delta_test;
+ *r_view = view;
+ *r_view_axis_roll = view_axis_roll;
+ }
}
}
+ if (*r_view != RV3D_VIEW_USER) {
+ return true;
+ }
}
return false;
}
+bool ED_view3d_quat_to_axis_view_and_reset_quat(float quat[4],
+ const float epsilon,
+ char *r_view,
+ char *r_view_axis_roll)
+{
+ const bool is_axis_view = ED_view3d_quat_to_axis_view(quat, epsilon, r_view, r_view_axis_roll);
+ if (is_axis_view) {
+ /* Reset `quat` to it's view axis, so axis-aligned views are always *exactly* aligned. */
+ BLI_assert(*r_view != RV3D_VIEW_USER);
+ ED_view3d_quat_from_axis_view(*r_view, *r_view_axis_roll, quat);
+ }
+ return is_axis_view;
+}
+
char ED_view3d_lock_view_from_index(int index)
{
switch (index) {
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 7a2a2a8a5e8..960d9a25ca7 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1172,12 +1172,20 @@ int transformEvent(TransInfo *t, const wmEvent *event)
MOD_CONSTRAINT_SELECT_PLANE;
if (t->con.mode & CON_APPLY) {
stopConstraint(t);
+ initSelectConstraint(t);
+
+ /* In this case we might just want to remove the constraint,
+ * so set #TREDRAW_SOFT to only select the constraint on the next mouse move event.
+ * This way we can kind of "cancel" due to confirmation without constraint. */
+ t->redraw = TREDRAW_SOFT;
}
+ else {
+ initSelectConstraint(t);
- initSelectConstraint(t);
- /* Use #TREDRAW_SOFT so that #selectConstraint is only called on the next event.
- * This allows us to "deselect" the constraint. */
- t->redraw = TREDRAW_SOFT;
+ /* When first called, set #TREDRAW_HARD to select constraint immediately in
+ * #selectConstraint. */
+ BLI_assert(t->redraw == TREDRAW_HARD);
+ }
}
}
handled = true;
@@ -1417,9 +1425,6 @@ static void drawTransformView(const struct bContext *C, ARegion *region, void *a
/* edge slide, vert slide */
drawEdgeSlide(t);
drawVertSlide(t);
-
- /* Rotation */
- drawDial3d(t);
}
}
@@ -1571,6 +1576,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
/* do we check for parameter? */
if (transformModeUseSnap(t)) {
if (!(t->modifiers & MOD_SNAP) != !(t->tsnap.flag & SCE_SNAP)) {
+ /* Type is #eSnapFlag, but type must match various snap attributes in #ToolSettings. */
char *snap_flag_ptr;
wmMsgParams_RNA msg_key_params = {{0}};
@@ -1778,13 +1784,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false;
- /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate
- * (which can have type = 0) */
- /* For gizmo only, so assume LEFTMOUSE. */
- if (t->launch_event == 0) {
- t->launch_event = LEFTMOUSE;
- }
-
unit_m3(t->spacemtx);
initTransInfo(C, t, op, event);
@@ -2053,3 +2052,17 @@ bool checkUseAxisMatrix(TransInfo *t)
return false;
}
+
+bool transform_apply_matrix(TransInfo *t, float mat[4][4])
+{
+ if (t->transform_matrix != NULL) {
+ t->transform_matrix(t, mat);
+ return true;
+ }
+ return false;
+}
+
+void transform_final_value_get(const TransInfo *t, float *value, const int value_num)
+{
+ memcpy(value, t->values_final, sizeof(float) * value_num);
+}
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index a3df6a44682..a35942aedec 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -13,6 +13,7 @@
#include "DNA_listBase.h"
#include "DNA_object_enums.h"
+#include "DNA_scene_types.h"
#include "DEG_depsgraph.h"
@@ -153,7 +154,8 @@ typedef enum {
} eTModifier;
/** #TransSnap.status */
-typedef enum {
+typedef enum eTSnap {
+ SNAP_RESETTED = 0,
SNAP_FORCED = 1 << 0,
TARGET_INIT = 1 << 1,
/* Special flag for snap to grid. */
@@ -294,11 +296,14 @@ typedef struct TransSnapPoint {
} TransSnapPoint;
typedef struct TransSnap {
- char flag;
- char mode;
- short target;
- short modePoint;
- short modeSelect;
+ /* Snapping options stored as flags */
+ eSnapFlag flag;
+ /* Method(s) used for snapping source to target */
+ eSnapMode mode;
+ /* Part of source to snap to target */
+ eSnapSourceSelect source_select;
+ /* Determines which objects are possible target */
+ eSnapTargetSelect target_select;
bool align;
bool project;
bool snap_self;
@@ -306,7 +311,7 @@ typedef struct TransSnap {
bool use_backface_culling;
eTSnap status;
/* Snapped Element Type (currently for objects only). */
- char snapElem;
+ eSnapMode snapElem;
/** snapping from this point (in global-space). */
float snapTarget[3];
/** to this point (in global-space). */
@@ -533,6 +538,13 @@ typedef struct TransInfo {
/* Event handler function that determines whether the viewport needs to be redrawn. */
eRedrawFlag (*handleEvent)(struct TransInfo *, const struct wmEvent *);
+ /**
+ * Optional callback to transform a single matrix.
+ *
+ * \note used by the gizmo to transform the matrix used to position it.
+ */
+ void (*transform_matrix)(struct TransInfo *t, float mat_xform[4][4]);
+
/** Constraint Data. */
TransCon con;
@@ -713,6 +725,12 @@ void removeAspectRatio(TransInfo *t, float vec[2]);
*/
struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf);
+/**
+ * Transform a single matrix using the current `t->final_values`.
+ */
+bool transform_apply_matrix(TransInfo *t, float mat[4][4]);
+void transform_final_value_get(const TransInfo *t, float *value, int value_num);
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -725,7 +743,6 @@ struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf);
bool gimbal_axis_pose(struct Object *ob, const struct bPoseChannel *pchan, float gmat[3][3]);
bool gimbal_axis_object(struct Object *ob, float gmat[3][3]);
-void drawDial3d(const TransInfo *t);
/** \} */
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index dbe67bd0d66..d9b971c5478 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr
/** \name UV Coordinates
* \{ */
+/**
+ * Find the correction for the scaling factor when "Constrain to Bounds" is active.
+ * \param numerator: How far the UV boundary (unit square) is from the origin of the scale.
+ * \param denominator: How far the AABB is from the origin of the scale.
+ * \param scale: Scale parameter to update.
+ */
+static void constrain_scale_to_boundary(const float numerator,
+ const float denominator,
+ float *scale)
+{
+ if (denominator == 0.0f) {
+ /* The origin of the scale is on the edge of the boundary. */
+ if (numerator < 0.0f) {
+ /* Negative scale will wrap around and put us outside the boundary. */
+ *scale = 0.0f; /* Hold at the boundary instead. */
+ }
+ return; /* Nothing else we can do without more info. */
+ }
+
+ const float correction = numerator / denominator;
+ if (correction < 0.0f || !isfinite(correction)) {
+ /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */
+ return;
+ }
+
+ if (denominator < 0.0f) {
+ /* Scale origin is outside boundary, only make scale bigger. */
+ if (*scale < correction) {
+ *scale = correction;
+ }
+ return;
+ }
+
+ /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */
+ if (*scale > correction) {
+ *scale = correction;
+ }
+}
+
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
{
bool clipx = true, clipy = true;
@@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
}
if (resize) {
- if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] &&
- t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) {
- vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]);
- }
- else if (max[0] > (base_offset[0] + t->aspect[0]) &&
- t->center_global[0] < (base_offset[0] + t->aspect[0])) {
- vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) /
- (t->center_global[0] - max[0]);
- }
- else {
- clipx = 0;
- }
-
- if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] &&
- t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) {
- vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]);
- }
- else if (max[1] > (base_offset[1] + t->aspect[1]) &&
- t->center_global[1] < (base_offset[1] + t->aspect[1])) {
- vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) /
- (t->center_global[1] - max[1]);
- }
- else {
- clipy = 0;
- }
+ /* Assume no change is required. */
+ float scalex = 1.0f;
+ float scaley = 1.0f;
+
+ /* Update U against the left border. */
+ constrain_scale_to_boundary(
+ t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex);
+ /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */
+ constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0],
+ max[0] - t->center_global[0],
+ &scalex);
+
+ /* Do the same for the V co-ordinate, which is called `y`. */
+ constrain_scale_to_boundary(
+ t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley);
+ constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1],
+ max[1] - t->center_global[1],
+ &scaley);
+
+ clipx = (scalex != 1.0f);
+ clipy = (scaley != 1.0f);
+ vec[0] *= scalex;
+ vec[1] *= scaley;
}
else {
if (min[0] < base_offset[0]) {
@@ -1570,10 +1607,9 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
}
}
-void animrecord_check_state(TransInfo *t, struct Object *ob)
+void animrecord_check_state(TransInfo *t, struct ID *id)
{
Scene *scene = t->scene;
- ID *id = &ob->id;
wmTimer *animtimer = t->animtimer;
ScreenAnimData *sad = (animtimer) ? animtimer->customdata : NULL;
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 7080deaec66..037fbe26c77 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -95,7 +95,7 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc);
/**
* For the realtime animation recording feature, handle overlapping data.
*/
-void animrecord_check_state(TransInfo *t, struct Object *ob);
+void animrecord_check_state(TransInfo *t, struct ID *id);
/* transform_convert_action.c */
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 3c1101d48a5..e1b25acb21e 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -1471,7 +1471,7 @@ void recalcData_pose(TransInfo *t)
/* XXX: this currently doesn't work, since flags aren't set yet! */
int targetless_ik = (t->flag & T_AUTOIK);
- animrecord_check_state(t, ob);
+ animrecord_check_state(t, &ob->id);
autokeyframe_pose(t->context, t->scene, ob, t->mode, targetless_ik);
}
diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c
index 54222fbb117..2039daee386 100644
--- a/source/blender/editors/transform/transform_convert_graph.c
+++ b/source/blender/editors/transform/transform_convert_graph.c
@@ -24,12 +24,9 @@
#include "UI_view2d.h"
#include "transform.h"
-#include "transform_snap.h"
-
#include "transform_convert.h"
-#include "transform_snap.h"
-
#include "transform_mode.h"
+#include "transform_snap.h"
typedef struct TransDataGraph {
float unit_scale;
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index 5255cc4412e..b07d2bda0c5 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -261,18 +261,10 @@ void createTransMaskingData(bContext *C, TransInfo *t)
tc->data_len = 0;
- if (!mask) {
+ if (!ED_maskedit_mask_visible_splines_poll(C)) {
return;
}
- if (t->spacetype == SPACE_CLIP) {
- SpaceClip *sc = t->area->spacedata.first;
- MovieClip *clip = ED_space_clip_get_clip(sc);
- if (!clip) {
- return;
- }
- }
-
/* count */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index 685c35489de..2fa8fcf65ba 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -26,12 +26,9 @@
#include "RNA_prototypes.h"
#include "transform.h"
-#include "transform_snap.h"
-
#include "transform_convert.h"
-#include "transform_snap.h"
-
#include "transform_mode.h"
+#include "transform_snap.h"
/** Used for NLA transform (stored in #TransData.extra pointer). */
typedef struct TransDataNla {
diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c
index d5d79bedbf4..8281052c314 100644
--- a/source/blender/editors/transform/transform_convert_node.c
+++ b/source/blender/editors/transform/transform_convert_node.c
@@ -46,7 +46,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const
}
/* use top-left corner as the transform origin for nodes */
- /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */
+ /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */
#ifdef USE_NODE_CENTER
td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f);
td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f);
@@ -194,7 +194,7 @@ void flushTransNodes(TransInfo *t)
loc[1] += 0.5f * BLI_rctf_size_y(&node->totr);
#endif
- /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */
+ /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */
loc[0] /= dpi_fac;
loc[1] /= dpi_fac;
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index d2585493679..5879a65eb4b 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -884,7 +884,7 @@ void recalcData_objects(TransInfo *t)
/* TODO: autokeyframe calls need some setting to specify to add samples
* (FPoints) instead of keyframes? */
if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
- animrecord_check_state(t, ob);
+ animrecord_check_state(t, &ob->id);
autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode);
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 5964af7ed4b..226b0f84f14 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -91,8 +91,8 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag)
/* *** Extend Transform *** */
int cfra = CFRA;
- int left = SEQ_transform_get_left_handle_frame(seq);
- int right = SEQ_transform_get_right_handle_frame(seq);
+ int left = SEQ_time_left_handle_frame_get(seq);
+ int right = SEQ_time_right_handle_frame_get(seq);
if (((seq->flag & SELECT) == 0 || SEQ_transform_is_locked(channels, seq))) {
*r_count = 0;
@@ -173,16 +173,16 @@ static TransData *SeqToTransData(
/* Use seq_tx_get_final_left() and an offset here
* so transform has the left hand location of the strip.
* tdsq->start_offset is used when flushing the tx data back */
- start_left = SEQ_transform_get_left_handle_frame(seq);
+ start_left = SEQ_time_left_handle_frame_get(seq);
td2d->loc[0] = start_left;
tdsq->start_offset = start_left - seq->start; /* use to apply the original location */
break;
case SEQ_LEFTSEL:
- start_left = SEQ_transform_get_left_handle_frame(seq);
+ start_left = SEQ_time_left_handle_frame_get(seq);
td2d->loc[0] = start_left;
break;
case SEQ_RIGHTSEL:
- td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq);
+ td2d->loc[0] = SEQ_time_right_handle_frame_get(seq);
break;
}
@@ -278,76 +278,7 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips
if (SEQ_transform_test_overlap(seqbase, seq)) {
SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene);
}
-
- SEQ_time_update_sequence(t->scene, seqbase, seq);
- }
-}
-
-static bool seq_transform_check_overlap(SeqCollection *transformed_strips)
-{
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
- if (seq->flag & SEQ_OVERLAP) {
- return true;
- }
- }
- return false;
-}
-
-static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips)
-{
- SeqCollection *collection = SEQ_collection_create(__func__);
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
- if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) {
- SEQ_collection_append_strip(seq, collection);
- }
- }
- return collection;
-}
-
-/* Query strips positioned after left edge of transformed strips bound-box. */
-static SeqCollection *query_right_side_strips(ListBase *seqbase, SeqCollection *transformed_strips)
-{
- int minframe = MAXFRAME;
- {
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
- minframe = min_ii(minframe, seq->startdisp);
- }
}
-
- SeqCollection *collection = SEQ_collection_create(__func__);
- LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->flag & SELECT) == 0 && seq->startdisp >= minframe) {
- SEQ_collection_append_strip(seq, collection);
- }
- }
- return collection;
-}
-
-static void seq_transform_update_effects(Scene *scene,
- ListBase *seqbasep,
- SeqCollection *collection)
-{
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, collection) {
- if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) {
- SEQ_time_update_sequence(scene, seqbasep, seq);
- }
- }
-}
-
-/* Check if effect strips with input are transformed. */
-static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips)
-{
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
- if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) {
- return true;
- }
- }
- return false;
}
static ListBase *seqbase_active_get(const TransInfo *t)
@@ -356,241 +287,15 @@ static ListBase *seqbase_active_get(const TransInfo *t)
return SEQ_active_seqbase_get(ed);
}
-/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal
- * to overlap of transformed strips. */
-static void seq_transform_handle_expand_to_fit(Scene *scene,
- ListBase *seqbasep,
- SeqCollection *transformed_strips,
- bool use_sync_markers)
-{
- ListBase *markers = &scene->markers;
-
- SeqCollection *right_side_strips = query_right_side_strips(seqbasep, transformed_strips);
-
- /* Temporarily move right side strips beyond timeline boundary. */
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, right_side_strips) {
- seq->machine += MAXSEQ * 2;
- }
-
- /* Shuffle transformed standalone strips. This is because transformed strips can overlap with
- * strips on left side. */
- SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips);
- SEQ_transform_seqbase_shuffle_time(
- standalone_strips, seqbasep, scene, markers, use_sync_markers);
- SEQ_collection_free(standalone_strips);
-
- /* Move temporarily moved strips back to their original place and tag for shuffling. */
- SEQ_ITERATOR_FOREACH (seq, right_side_strips) {
- seq->machine -= MAXSEQ * 2;
- }
- /* Shuffle again to displace strips on right side. Final effect shuffling is done in
- * SEQ_transform_handle_overlap. */
- SEQ_transform_seqbase_shuffle_time(
- right_side_strips, seqbasep, scene, markers, use_sync_markers);
- seq_transform_update_effects(scene, seqbasep, right_side_strips);
- SEQ_collection_free(right_side_strips);
-}
-
-static SeqCollection *query_overwrite_targets(ListBase *seqbasep,
- SeqCollection *transformed_strips)
-{
- SeqCollection *collection = SEQ_query_unselected_strips(seqbasep);
-
- Sequence *seq, *seq_transformed;
- SEQ_ITERATOR_FOREACH (seq, collection) {
- bool does_overlap = false;
-
- SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) {
- /* Effects of transformed strips can be unselected. These must not be included. */
- if (seq == seq_transformed) {
- SEQ_collection_remove_strip(seq, collection);
- }
- if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) {
- does_overlap = true;
- }
- }
-
- if (!does_overlap) {
- SEQ_collection_remove_strip(seq, collection);
- }
- }
-
- return collection;
-}
-
-typedef enum eOvelapDescrition {
- /* No overlap. */
- STRIP_OVERLAP_NONE,
- /* Overlapping strip covers overlapped completely. */
- STRIP_OVERLAP_IS_FULL,
- /* Overlapping strip is inside overlapped. */
- STRIP_OVERLAP_IS_INSIDE,
- /* Partial overlap between 2 strips. */
- STRIP_OVERLAP_LEFT_SIDE,
- STRIP_OVERLAP_RIGHT_SIDE,
-} eOvelapDescrition;
-
-static eOvelapDescrition overlap_description_get(const Sequence *transformed,
- const Sequence *target)
-{
- if (transformed->startdisp <= target->startdisp && transformed->enddisp >= target->enddisp) {
- return STRIP_OVERLAP_IS_FULL;
- }
- if (transformed->startdisp > target->startdisp && transformed->enddisp < target->enddisp) {
- return STRIP_OVERLAP_IS_INSIDE;
- }
- if (transformed->startdisp <= target->startdisp && target->startdisp <= transformed->enddisp) {
- return STRIP_OVERLAP_LEFT_SIDE;
- }
- if (transformed->startdisp <= target->enddisp && target->enddisp <= transformed->enddisp) {
- return STRIP_OVERLAP_RIGHT_SIDE;
- }
- return STRIP_OVERLAP_NONE;
-}
-
-/* Split strip in 3 parts, remove middle part and fit transformed inside. */
-static void seq_transform_handle_overwrite_split(Scene *scene,
- ListBase *seqbasep,
- const Sequence *transformed,
- Sequence *target)
-{
- /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass
- * NULL here. */
- Main *bmain = NULL;
-
- Sequence *split_strip = SEQ_edit_strip_split(
- bmain, scene, seqbasep, target, transformed->startdisp, SEQ_SPLIT_SOFT, NULL);
- SEQ_edit_strip_split(
- bmain, scene, seqbasep, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL);
- SEQ_edit_flag_for_removal(scene, seqbasep, split_strip);
- SEQ_edit_remove_flagged_sequences(scene, seqbasep);
-}
-
-/* Trim strips by adjusting handle position.
- * This is bit more complicated in case overlap happens on effect. */
-static void seq_transform_handle_overwrite_trim(Scene *scene,
- ListBase *seqbasep,
- const Sequence *transformed,
- Sequence *target,
- const eOvelapDescrition overlap)
-{
- SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain);
-
- /* Expand collection by adding all target's children, effects and their children. */
- if ((target->type & SEQ_TYPE_EFFECT) != 0) {
- SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain);
- }
-
- /* Trim all non effects, that have influence on effect length which is overlapping. */
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, targets) {
- if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) {
- continue;
- }
- if (overlap == STRIP_OVERLAP_LEFT_SIDE) {
- SEQ_transform_set_left_handle_frame(seq, transformed->enddisp);
- }
- else {
- BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE);
- SEQ_transform_set_right_handle_frame(seq, transformed->startdisp);
- }
-
- SEQ_time_update_sequence(scene, seqbasep, seq);
- }
- SEQ_collection_free(targets);
-}
-
-static void seq_transform_handle_overwrite(Scene *scene,
- ListBase *seqbasep,
- SeqCollection *transformed_strips)
-{
- SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips);
- SeqCollection *strips_to_delete = SEQ_collection_create(__func__);
-
- Sequence *target;
- Sequence *transformed;
- SEQ_ITERATOR_FOREACH (target, targets) {
- SEQ_ITERATOR_FOREACH (transformed, transformed_strips) {
- if (transformed->machine != target->machine) {
- continue;
- }
-
- const eOvelapDescrition overlap = overlap_description_get(transformed, target);
-
- if (overlap == STRIP_OVERLAP_IS_FULL) {
- SEQ_collection_append_strip(target, strips_to_delete);
- }
- else if (overlap == STRIP_OVERLAP_IS_INSIDE) {
- seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target);
- }
- else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) {
- seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap);
- }
- }
- }
-
- SEQ_collection_free(targets);
-
- /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()`
- * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */
- if (SEQ_collection_len(strips_to_delete) > 0) {
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, strips_to_delete) {
- SEQ_edit_flag_for_removal(scene, seqbasep, seq);
- }
- SEQ_edit_remove_flagged_sequences(scene, seqbasep);
- }
- SEQ_collection_free(strips_to_delete);
-}
-
-static void seq_transform_handle_overlap_shuffle(Scene *scene,
- ListBase *seqbasep,
- SeqCollection *transformed_strips,
- bool use_sync_markers)
-{
- ListBase *markers = &scene->markers;
-
- /* Shuffle non strips with no effects attached. */
- SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips);
- SEQ_transform_seqbase_shuffle_time(
- standalone_strips, seqbasep, scene, markers, use_sync_markers);
- SEQ_collection_free(standalone_strips);
-}
-
-void SEQ_transform_handle_overlap(Scene *scene,
- ListBase *seqbasep,
- SeqCollection *transformed_strips,
- bool use_sync_markers)
+static bool seq_transform_check_overlap(SeqCollection *transformed_strips)
{
- const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene);
-
- switch (overlap_mode) {
- case SEQ_OVERLAP_EXPAND:
- seq_transform_handle_expand_to_fit(scene, seqbasep, transformed_strips, use_sync_markers);
- break;
- case SEQ_OVERLAP_OVERWRITE:
- seq_transform_handle_overwrite(scene, seqbasep, transformed_strips);
- break;
- case SEQ_OVERLAP_SHUFFLE:
- seq_transform_handle_overlap_shuffle(scene, seqbasep, transformed_strips, use_sync_markers);
- break;
- }
-
- if (seq_transform_check_strip_effects(transformed_strips)) {
- /* Update effect strips based on strips just moved in time. */
- seq_transform_update_effects(scene, seqbasep, transformed_strips);
- }
-
- /* If any effects still overlap, we need to move them up.
- * In some cases other strips can be overlapping still, see T90646. */
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
- if (SEQ_transform_test_overlap(seqbasep, seq)) {
- SEQ_transform_seqbase_shuffle(seqbasep, seq, scene);
+ if (seq->flag & SEQ_OVERLAP) {
+ return true;
}
- seq->flag &= ~SEQ_OVERLAP;
}
+ return false;
}
static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer *tc)
@@ -627,18 +332,17 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
return;
}
+ TransSeq *ts = tc->custom.type.data;
ListBase *seqbasep = seqbase_active_get(t);
Scene *scene = t->scene;
const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
SEQ_MARKER_TRANS) != 0;
if (seq_transform_check_overlap(transformed_strips)) {
- SEQ_transform_handle_overlap(scene, seqbasep, transformed_strips, use_sync_markers);
+ SEQ_transform_handle_overlap(
+ scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers);
}
- seq_transform_update_effects(scene, seqbasep, transformed_strips);
SEQ_collection_free(transformed_strips);
-
- SEQ_sort(ed->seqbasep);
DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS);
free_transform_custom_data(custom_data);
}
@@ -662,7 +366,10 @@ typedef enum SeqInputSide {
static Sequence *effect_input_get(Sequence *effect, SeqInputSide side)
{
Sequence *input = effect->seq1;
- if (effect->seq2 && (effect->seq2->startdisp - effect->seq1->startdisp) * side > 0) {
+ if (effect->seq2 && (SEQ_time_left_handle_frame_get(effect->seq2) -
+ SEQ_time_left_handle_frame_get(effect->seq1)) *
+ side >
+ 0) {
input = effect->seq2;
}
return input;
@@ -825,23 +532,6 @@ void createTransSeqData(TransInfo *t)
/** \name UVs Transform Flush
* \{ */
-/* commented _only_ because the meta may have animation data which
- * needs moving too T28158. */
-
-BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag)
-{
- /* Calculate this strip and all nested strips.
- * Children are ALWAYS transformed first so we don't need to do this in another loop.
- */
-
- ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(sce));
- SEQ_time_update_sequence(sce, seqbase, seq);
-
- if (sel_flag == SELECT) {
- SEQ_offset_animdata(sce, seq, seq->start - old_start);
- }
-}
-
static void view2d_edge_pan_loc_compensate(TransInfo *t, float loc_in[2], float r_loc[2])
{
TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
@@ -914,24 +604,24 @@ static void flushTransSeq(TransInfo *t)
break;
}
case SEQ_LEFTSEL: { /* No vertical transform. */
- int old_startdisp = seq->startdisp;
- SEQ_transform_set_left_handle_frame(seq, new_frame);
- SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
- SEQ_transform_fix_single_image_seq_offsets(seq);
- SEQ_time_update_sequence(t->scene, seqbasep, seq);
- if (abs(seq->startdisp - old_startdisp) > abs(max_offset)) {
- max_offset = seq->startdisp - old_startdisp;
+ int old_startdisp = SEQ_time_left_handle_frame_get(seq);
+ SEQ_time_left_handle_frame_set(t->scene, seq, new_frame);
+ SEQ_transform_handle_xlimits(
+ t->scene, seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
+ SEQ_transform_fix_single_image_seq_offsets(t->scene, seq);
+ if (abs(SEQ_time_left_handle_frame_get(seq) - old_startdisp) > abs(max_offset)) {
+ max_offset = SEQ_time_left_handle_frame_get(seq) - old_startdisp;
}
break;
}
case SEQ_RIGHTSEL: { /* No vertical transform. */
- int old_enddisp = seq->enddisp;
- SEQ_transform_set_right_handle_frame(seq, new_frame);
- SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
- SEQ_transform_fix_single_image_seq_offsets(seq);
- SEQ_time_update_sequence(t->scene, seqbasep, seq);
- if (abs(seq->enddisp - old_enddisp) > abs(max_offset)) {
- max_offset = seq->enddisp - old_enddisp;
+ int old_enddisp = SEQ_time_right_handle_frame_get(seq);
+ SEQ_time_right_handle_frame_set(t->scene, seq, new_frame);
+ SEQ_transform_handle_xlimits(
+ t->scene, seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
+ SEQ_transform_fix_single_image_seq_offsets(t->scene, seq);
+ if (abs(SEQ_time_right_handle_frame_get(seq) - old_enddisp) > abs(max_offset)) {
+ max_offset = SEQ_time_right_handle_frame_get(seq) - old_enddisp;
}
break;
}
@@ -945,15 +635,6 @@ static void flushTransSeq(TransInfo *t)
SEQ_offset_animdata(t->scene, seq, max_offset);
}
- /* Update effect length and position. */
- if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
- for (seq = seqbasep->first; seq; seq = seq->next) {
- if (seq->seq1 || seq->seq2 || seq->seq3) {
- SEQ_time_update_sequence(t->scene, seqbasep, seq);
- }
- }
- }
-
/* need to do the overlap check in a new loop otherwise adjacent strips
* will not be updated and we'll get false positives */
SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc);
diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c
index cbc2cab0a7a..76c6632039a 100644
--- a/source/blender/editors/transform/transform_convert_sequencer_image.c
+++ b/source/blender/editors/transform/transform_convert_sequencer_image.c
@@ -154,6 +154,42 @@ void createTransSeqImageData(TransInfo *t)
SEQ_collection_free(strips);
}
+static bool autokeyframe_sequencer_image(bContext *C,
+ Scene *scene,
+ StripTransform *transform,
+ const int tmode)
+{
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr);
+
+ const bool around_cursor = scene->toolsettings->sequencer_tool_settings->pivot_point ==
+ V3D_AROUND_CURSOR;
+ const bool do_loc = tmode == TFM_TRANSLATION || around_cursor;
+ const bool do_rot = tmode == TFM_ROTATION;
+ const bool do_scale = tmode == TFM_RESIZE;
+
+ bool changed = false;
+ if (do_rot) {
+ prop = RNA_struct_find_property(&ptr, "rotation");
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ }
+ if (do_loc) {
+ prop = RNA_struct_find_property(&ptr, "offset_x");
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ prop = RNA_struct_find_property(&ptr, "offset_y");
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ }
+ if (do_scale) {
+ prop = RNA_struct_find_property(&ptr, "scale_x");
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ prop = RNA_struct_find_property(&ptr, "scale_y");
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ }
+
+ return changed;
+}
+
void recalcData_sequencer_image(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
@@ -187,6 +223,7 @@ void recalcData_sequencer_image(TransInfo *t)
copy_v2_v2(translation, tdseq->orig_origin_position);
sub_v2_v2(translation, origin);
mul_v2_v2(translation, mirror);
+ translation[0] *= t->scene->r.yasp / t->scene->r.xasp;
transform->xofs = tdseq->orig_translation[0] - translation[0];
transform->yofs = tdseq->orig_translation[1] - translation[1];
@@ -197,53 +234,39 @@ void recalcData_sequencer_image(TransInfo *t)
/* Rotation. Scaling can cause negative rotation. */
if (t->mode == TFM_ROTATION) {
- const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)};
- float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1];
- transform->rotation = tdseq->orig_rotation + rotation;
- transform->rotation += DEG2RAD(360.0);
- transform->rotation = fmod(transform->rotation, DEG2RAD(360.0));
+ transform->rotation = tdseq->orig_rotation - t->values_final[0];
+ }
+
+ if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
+ animrecord_check_state(t, &t->scene->id);
+ autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode);
}
+
SEQ_relations_invalidate_cache_preprocessed(t->scene, seq);
}
}
void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
{
- if (t->state == TRANS_CANCEL) {
- return;
- }
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
TransData *td = NULL;
TransData2D *td2d = NULL;
int i;
- PointerRNA ptr;
- PropertyRNA *prop;
-
for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
TransDataSeq *tdseq = td->extra;
Sequence *seq = tdseq->seq;
StripTransform *transform = seq->strip->transform;
- Scene *scene = t->scene;
-
- RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr);
-
- if (t->mode == TFM_ROTATION) {
- prop = RNA_struct_find_property(&ptr, "rotation");
- ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ if (t->state == TRANS_CANCEL) {
+ if (t->mode == TFM_ROTATION) {
+ transform->rotation = tdseq->orig_rotation;
+ }
+ continue;
}
- if (t->mode == TFM_TRANSLATION) {
- prop = RNA_struct_find_property(&ptr, "offset_x");
- ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
- prop = RNA_struct_find_property(&ptr, "offset_y");
- ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
- }
- if (t->mode == TFM_RESIZE) {
- prop = RNA_struct_find_property(&ptr, "scale_x");
- ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
- prop = RNA_struct_find_property(&ptr, "scale_y");
- ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+
+ if (IS_AUTOKEY_ON(t->scene)) {
+ autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c
index 45f17512c09..d447cd71a40 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -621,7 +621,7 @@ static void flushTransTracking(TransInfo *t)
TransData *td;
TransData2D *td2d;
TransDataTracking *tdt;
- int a;
+ int td_index;
if (t->state == TRANS_CANCEL) {
cancelTransTracking(t);
@@ -630,8 +630,9 @@ static void flushTransTracking(TransInfo *t)
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* flush to 2d vector from internally used 3d vector */
- for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len;
- a++, td2d++, td++, tdt++) {
+ for (td_index = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data;
+ td_index < tc->data_len;
+ td_index++, td2d++, td++, tdt++) {
if (tdt->mode == transDataTracking_ModeTracks) {
float loc2d[2];
@@ -655,7 +656,7 @@ static void flushTransTracking(TransInfo *t)
if (!tdt->smarkers) {
tdt->smarkers = MEM_callocN(sizeof(*tdt->smarkers) * tdt->markersnr,
"flushTransTracking markers");
- for (a = 0; a < tdt->markersnr; a++) {
+ for (int a = 0; a < tdt->markersnr; a++) {
copy_v2_v2(tdt->smarkers[a], tdt->markers[a].pos);
}
}
@@ -665,7 +666,7 @@ static void flushTransTracking(TransInfo *t)
sub_v2_v2v2(d2, loc2d, tdt->srelative);
- for (a = 0; a < tdt->markersnr; a++) {
+ for (int a = 0; a < tdt->markersnr; a++) {
add_v2_v2v2(tdt->markers[a].pos, tdt->smarkers[a], d2);
}
@@ -712,23 +713,23 @@ void recalcData_tracking(TransInfo *t)
if (t->mode == TFM_TRANSLATION) {
if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) {
- BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS);
+ BKE_tracking_marker_clamp_pattern_position(marker);
}
if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) {
- BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_POS);
+ BKE_tracking_marker_clamp_search_position(marker);
}
}
else if (t->mode == TFM_RESIZE) {
if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) {
- BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM);
+ BKE_tracking_marker_clamp_search_size(marker);
}
if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) {
- BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM);
+ BKE_tracking_marker_clamp_search_size(marker);
}
}
else if (t->mode == TFM_ROTATION) {
if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) {
- BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS);
+ BKE_tracking_marker_clamp_pattern_position(marker);
}
}
}
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 975dbc2e986..e45cac36736 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -358,6 +358,10 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) {
t->options |= CTX_SEQUENCER_IMAGE;
+
+ /* Needed for autokeying transforms in preview during playback. */
+ bScreen *animscreen = ED_screen_animation_playing(CTX_wm_manager(C));
+ t->animtimer = (animscreen) ? animscreen->animtimer : NULL;
}
setTransformViewAspect(t, t->aspect);
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index aa8dad2b95f..5b749e05052 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -45,7 +45,6 @@
#include "WM_api.h"
#include "WM_message.h"
#include "WM_types.h"
-#include "wm.h"
#include "ED_armature.h"
#include "ED_gizmo_library.h"
@@ -71,6 +70,10 @@
#include "GPU_state.h"
+static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup,
+ const float twmat[4][4],
+ const float scale[3]);
+
/* return codes for select, and drawing flags */
#define MAN_TRANS_X (1 << 0)
@@ -155,6 +158,9 @@ typedef struct GizmoGroup {
float viewinv_m3[3][3];
} prev;
+ /* Only for Rotate operator. */
+ float rotation;
+
struct wmGizmo *gizmos[MAN_AXIS_LAST];
} GizmoGroup;
@@ -1269,103 +1275,31 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup,
WM_msg_subscribe_rna_anon_prop(mbus, EditBone, lock, &msg_sub_value_gz_tag_refresh);
}
-void drawDial3d(const TransInfo *t)
+static void gizmo_3d_dial_matrixbasis_calc(const ARegion *region,
+ float axis[3],
+ float center_global[3],
+ float mval_init[2],
+ float r_mat_basis[4][4])
{
- if (t->mode == TFM_ROTATION && t->spacetype == SPACE_VIEW3D) {
- if (t->options & CTX_PAINT_CURVE) {
- /* Matrices are in the screen space. Not supported. */
- return;
- }
-
- wmGizmo *gz = wm_gizmomap_modal_get(t->region->gizmo_map);
- if (gz == NULL) {
- /* We only draw Dial3d if the operator has been called by a gizmo. */
- return;
- }
-
- float mat_basis[4][4];
- float mat_final[4][4];
- float color[4];
- float increment = 0.0f;
- float line_with = GIZMO_AXIS_LINE_WIDTH + 1.0f;
- float scale = UI_DPI_FAC * U.gizmo_size;
-
- int axis_idx;
-
- const TransCon *tc = &(t->con);
- if (tc->mode & CON_APPLY) {
- if (tc->mode & CON_AXIS0) {
- axis_idx = MAN_AXIS_ROT_X;
- negate_v3_v3(mat_basis[2], t->spacemtx[0]);
- }
- else if (tc->mode & CON_AXIS1) {
- axis_idx = MAN_AXIS_ROT_Y;
- negate_v3_v3(mat_basis[2], t->spacemtx[1]);
- }
- else {
- BLI_assert((tc->mode & CON_AXIS2) != 0);
- axis_idx = MAN_AXIS_ROT_Z;
- negate_v3_v3(mat_basis[2], t->spacemtx[2]);
- }
- }
- else {
- axis_idx = MAN_AXIS_ROT_C;
- copy_v3_v3(mat_basis[2], t->spacemtx[t->orient_axis]);
- scale *= 1.2f;
- line_with -= 1.0f;
- }
-
- copy_v3_v3(mat_basis[3], t->center_global);
- mat_basis[2][3] = -dot_v3v3(mat_basis[2], mat_basis[3]);
-
- if (ED_view3d_win_to_3d_on_plane(
- t->region, mat_basis[2], (float[2]){UNPACK2(t->mouse.imval)}, false, mat_basis[1])) {
- sub_v3_v3(mat_basis[1], mat_basis[3]);
- normalize_v3(mat_basis[1]);
- cross_v3_v3v3(mat_basis[0], mat_basis[1], mat_basis[2]);
- }
- else {
- /* The plane and the mouse direction are parallel.
- * Calculate a matrix orthogonal to the axis. */
- ortho_basis_v3v3_v3(mat_basis[0], mat_basis[1], mat_basis[2]);
- }
-
- mat_basis[0][3] = 0.0f;
- mat_basis[1][3] = 0.0f;
- mat_basis[2][3] = 0.0f;
- mat_basis[3][3] = 1.0f;
-
- copy_m4_m4(mat_final, mat_basis);
- scale *= ED_view3d_pixel_size_no_ui_scale(t->region->regiondata, mat_final[3]);
- mul_mat3_m4_fl(mat_final, scale);
+ copy_v3_v3(r_mat_basis[2], axis);
+ copy_v3_v3(r_mat_basis[3], center_global);
+ r_mat_basis[2][3] = -dot_v3v3(axis, center_global);
- if (activeSnap(t) && (!transformModeUseSnap(t) ||
- (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) {
- increment = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
- }
-
- BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END);
- gizmo_get_axis_color(axis_idx, NULL, color, color);
-
- GPU_depth_test(GPU_DEPTH_NONE);
- GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_smooth(true);
-
- ED_gizmotypes_dial_3d_draw_util(mat_basis,
- mat_final,
- line_with,
- color,
- false,
- &(struct Dial3dParams){
- .draw_options = ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE,
- .angle_delta = t->values_final[0],
- .angle_increment = increment,
- });
-
- GPU_line_smooth(false);
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
- GPU_blend(GPU_BLEND_NONE);
+ if (ED_view3d_win_to_3d_on_plane(region, axis, mval_init, false, r_mat_basis[1])) {
+ sub_v3_v3(r_mat_basis[1], center_global);
+ normalize_v3(r_mat_basis[1]);
+ cross_v3_v3v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]);
+ }
+ else {
+ /* The plane and the mouse direction are parallel.
+ * Calculate a matrix orthogonal to the axis. */
+ ortho_basis_v3v3_v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]);
}
+
+ r_mat_basis[0][3] = 0.0f;
+ r_mat_basis[1][3] = 0.0f;
+ r_mat_basis[2][3] = 0.0f;
+ r_mat_basis[3][3] = 1.0f;
}
/** \} */
@@ -1374,6 +1308,23 @@ void drawDial3d(const TransInfo *t)
/** \name Transform Gizmo
* \{ */
+/** Scale of the two-axis planes. */
+#define MAN_AXIS_SCALE_PLANE_SCALE 0.07f
+/** Offset of the two-axis planes, depends on the gizmos scale. Define to avoid repeating. */
+#define MAN_AXIS_SCALE_PLANE_OFFSET 7.0f
+
+static void rotation_get_fn(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value)
+{
+ const GizmoGroup *ggd = (const GizmoGroup *)gz_prop->custom_func.user_data;
+ *(float *)value = ggd->rotation;
+}
+
+static void rotation_set_fn(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, const void *value)
+{
+ GizmoGroup *ggd = (GizmoGroup *)gz_prop->custom_func.user_data;
+ ggd->rotation = *(const float *)value;
+}
+
static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
{
GizmoGroup *ggd;
@@ -1384,6 +1335,9 @@ static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true);
+ wmGizmoPropertyFnParams params = {
+ .value_get_fn = rotation_get_fn, .value_set_fn = rotation_set_fn, .user_data = ggd};
+
#define GIZMO_NEW_ARROW(v, draw_style) \
{ \
ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \
@@ -1394,6 +1348,7 @@ static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
{ \
ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \
RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \
+ WM_gizmo_target_property_def_func(ggd->gizmos[v], "offset", &params); \
} \
((void)0)
#define GIZMO_NEW_PRIM(v, draw_style) \
@@ -1461,18 +1416,104 @@ static int gizmo_modal(bContext *C,
ARegion *region = CTX_wm_region(C);
RegionView3D *rv3d = region->regiondata;
- struct TransformBounds tbounds;
-
- if (ED_transform_calc_gizmo_stats(C,
- &(struct TransformCalcParams){
- .use_only_center = true,
- },
- &tbounds)) {
- gizmo_prepare_mat(C, rv3d, &tbounds);
- WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]);
+ wmGizmoGroup *gzgroup = widget->parent_gzgroup;
+
+ /* Recalculating the orientation has two problems.
+ * - The matrix calculated based on the transformed selection may not match the matrix
+ * that was set when transform started.
+ * - Inspecting the selection for every update is expensive (for *every* redraw).
+ *
+ * Instead, use #transform_apply_matrix to transform `rv3d->twmat` or the final scale value
+ * when scaling.
+ */
+ if (false) {
+ struct TransformBounds tbounds;
+
+ if (ED_transform_calc_gizmo_stats(C,
+ &(struct TransformCalcParams){
+ .use_only_center = true,
+ },
+ &tbounds)) {
+ gizmo_prepare_mat(C, rv3d, &tbounds);
+ for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
+ WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]);
+ }
+ }
}
+ else {
+ GizmoGroup *ggd = gzgroup->customdata;
+
+ short axis_type = 0;
+ MAN_ITER_AXES_BEGIN (axis, axis_idx) {
+ if (axis == widget) {
+ axis_type = gizmo_get_axis_type(axis_idx);
+ break;
+ }
+ }
+ MAN_ITER_AXES_END;
+
+ /* Showing axes which aren't being manipulated doesn't always work so well.
+ *
+ * For rotate: global axis will reset after finish.
+ * Also, gimbal axis isn't properly recalculated while transforming.
+ */
+ if (axis_type == MAN_AXES_ROTATE) {
+ MAN_ITER_AXES_BEGIN (axis, axis_idx) {
+ if (axis == widget) {
+ continue;
+ }
+
+ bool is_plane_dummy;
+ const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane_dummy);
+ /* Always show the axis-aligned handle as it's distracting when it's disabled. */
+ if (aidx_norm == 3) {
+ continue;
+ }
+ WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
+ }
+ MAN_ITER_AXES_END;
+ }
- ED_region_tag_redraw_editor_overlays(region);
+ wmWindow *win = CTX_wm_window(C);
+ wmOperator *op = NULL;
+ for (int i = 0; i < widget->op_data_len; i++) {
+ wmGizmoOpElem *gzop = WM_gizmo_operator_get(widget, i);
+ op = WM_operator_find_modal_by_type(win, gzop->type);
+ if (op != NULL) {
+ break;
+ }
+ }
+
+ if (op != NULL) {
+ float twmat[4][4];
+ float scale_buf[3];
+ float *scale = NULL;
+ bool update = false;
+ copy_m4_m4(twmat, rv3d->twmat);
+
+ if (axis_type == MAN_AXES_SCALE) {
+ scale = scale_buf;
+ transform_final_value_get(op->customdata, scale, 3);
+ update = true;
+ }
+ else if (axis_type == MAN_AXES_ROTATE) {
+ transform_final_value_get(op->customdata, &ggd->rotation, 1);
+ if (widget != ggd->gizmos[MAN_AXIS_ROT_C]) {
+ ggd->rotation *= -1;
+ }
+ RNA_float_set(
+ widget->ptr, "incremental_angle", transform_snap_increment_get(op->customdata));
+ }
+ else if (transform_apply_matrix(op->customdata, twmat)) {
+ update = true;
+ }
+
+ if (update) {
+ gizmo_refresh_from_matrix(gzgroup, twmat, scale);
+ ED_region_tag_redraw_editor_overlays(region);
+ }
+ }
+ }
return OPERATOR_RUNNING_MODAL;
}
@@ -1524,9 +1565,8 @@ static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
case MAN_AXIS_SCALE_XY:
case MAN_AXIS_SCALE_YZ:
case MAN_AXIS_SCALE_ZX: {
- const float ofs_ax = 7.0f;
- const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
- WM_gizmo_set_scale(axis, 0.07f);
+ const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
+ WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE);
WM_gizmo_set_matrix_offset_location(axis, ofs);
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
break;
@@ -1638,46 +1678,23 @@ static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
gizmogroup_init_properties_from_twtype(gzgroup);
}
-static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+/**
+ * Set properties for axes.
+ *
+ * \param twmat: The transform matrix (typically #RegionView3D.twmat).
+ * \param scale: Optional scale, to show scale while modally dragging the scale handles.
+ */
+static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup,
+ const float twmat[4][4],
+ const float scale[3])
{
GizmoGroup *ggd = gzgroup->customdata;
- Scene *scene = CTX_data_scene(C);
- ScrArea *area = CTX_wm_area(C);
- View3D *v3d = area->spacedata.first;
- ARegion *region = CTX_wm_region(C);
- RegionView3D *rv3d = region->regiondata;
- struct TransformBounds tbounds;
-
- if (ggd->use_twtype_refresh) {
- ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init;
- if (ggd->twtype != ggd->twtype_prev) {
- ggd->twtype_prev = ggd->twtype;
- gizmogroup_init_properties_from_twtype(gzgroup);
- }
- }
-
- const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init);
-
- /* skip, we don't draw anything anyway */
- if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C,
- &(struct TransformCalcParams){
- .use_only_center = true,
- .orientation_index = orient_index + 1,
- },
- &tbounds) == 0))) {
- return;
- }
-
- gizmo_prepare_mat(C, rv3d, &tbounds);
-
- /* *** set properties for axes *** */
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
const short axis_type = gizmo_get_axis_type(axis_idx);
const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL);
- WM_gizmo_set_matrix_location(axis, rv3d->twmat[3]);
-
+ WM_gizmo_set_matrix_location(axis, twmat[3]);
switch (axis_idx) {
case MAN_AXIS_TRANS_X:
case MAN_AXIS_TRANS_Y:
@@ -1690,8 +1707,17 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
gizmo_line_range(ggd->twtype, axis_type, &start_co[2], &len);
- WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
- RNA_float_set(axis->ptr, "length", len);
+ const float *z_axis = twmat[aidx_norm];
+ if (axis_type == MAN_AXES_SCALE) {
+ /* Scale handles are cubes that don't look right when not aligned with other axes.
+ * This is noticeable when the axis is rotated to something besides the global-axis. */
+ const int aidx_norm_y = (aidx_norm + 2) % 3;
+ const float *y_axis = twmat[aidx_norm_y];
+ WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
+ }
+ else {
+ WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis);
+ }
if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
if (ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
@@ -1699,24 +1725,56 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
start_co[2] += 0.215f;
}
}
+
+ if (scale) {
+ if (axis_type == MAN_AXES_SCALE) {
+ len = ((start_co[2] + len) * scale[aidx_norm]) - start_co[2];
+ }
+ }
+
+ RNA_float_set(axis->ptr, "length", len);
+
WM_gizmo_set_matrix_offset_location(axis, start_co);
+
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
+
break;
}
case MAN_AXIS_ROT_X:
case MAN_AXIS_ROT_Y:
case MAN_AXIS_ROT_Z:
- WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
- break;
+ case MAN_AXIS_ROT_C: {
+ if (axis_idx != MAN_AXIS_ROT_C) {
+ WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]);
+ }
+
+ /* Remove #ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE. It is used only for modal drawing. */
+ PropertyRNA *prop = RNA_struct_find_property(axis->ptr, "draw_options");
+ RNA_property_enum_set(axis->ptr,
+ prop,
+ RNA_property_enum_get(axis->ptr, prop) &
+ ~ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
+ } break;
case MAN_AXIS_TRANS_XY:
case MAN_AXIS_TRANS_YZ:
case MAN_AXIS_TRANS_ZX:
case MAN_AXIS_SCALE_XY:
case MAN_AXIS_SCALE_YZ:
case MAN_AXIS_SCALE_ZX: {
- const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
- const float *z_axis = rv3d->twmat[aidx_norm];
+ const int aidx_norm_x = (aidx_norm + 1) % 3;
+ const int aidx_norm_y = (aidx_norm + 2) % 3;
+ const float *y_axis = twmat[aidx_norm_y];
+ const float *z_axis = twmat[aidx_norm];
WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
+
+ if (axis_type == MAN_AXES_SCALE) {
+ float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
+ if (scale) {
+ ofs[0] *= scale[aidx_norm_x];
+ ofs[1] *= scale[aidx_norm_y];
+ }
+ WM_gizmo_set_matrix_offset_location(axis, ofs);
+ }
break;
}
}
@@ -1733,6 +1791,49 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
}
+static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ ARegion *region = CTX_wm_region(C);
+
+ {
+ wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map);
+ if (gz && gz->parent_gzgroup == gzgroup) {
+ return;
+ }
+ }
+
+ GizmoGroup *ggd = gzgroup->customdata;
+ Scene *scene = CTX_data_scene(C);
+ ScrArea *area = CTX_wm_area(C);
+ View3D *v3d = area->spacedata.first;
+ RegionView3D *rv3d = region->regiondata;
+ struct TransformBounds tbounds;
+
+ if (ggd->use_twtype_refresh) {
+ ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init;
+ if (ggd->twtype != ggd->twtype_prev) {
+ ggd->twtype_prev = ggd->twtype;
+ gizmogroup_init_properties_from_twtype(gzgroup);
+ }
+ }
+
+ const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init);
+
+ /* skip, we don't draw anything anyway */
+ if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C,
+ &(struct TransformCalcParams){
+ .use_only_center = true,
+ .orientation_index = orient_index + 1,
+ },
+ &tbounds) == 0))) {
+ return;
+ }
+
+ gizmo_prepare_mat(C, rv3d, &tbounds);
+
+ gizmo_refresh_from_matrix(gzgroup, rv3d->twmat, NULL);
+}
+
static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C,
wmGizmoGroup *gzgroup,
struct wmMsgBus *mbus)
@@ -1756,11 +1857,22 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr
copy_m3_m4(viewinv_m3, rv3d->viewinv);
float idot[3];
+ /* Re-calculate hidden unless modal. */
+ bool is_modal = false;
+ {
+ wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map);
+ if (gz && gz->parent_gzgroup == gzgroup) {
+ is_modal = true;
+ }
+ }
+
/* when looking through a selected camera, the gizmo can be at the
* exact same position as the view, skip so we don't break selection */
if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 5e-7f) {
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
- WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
+ if (!is_modal) {
+ WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
+ }
}
MAN_ITER_AXES_END;
return;
@@ -1771,12 +1883,15 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
const short axis_type = gizmo_get_axis_type(axis_idx);
/* XXX maybe unset _HIDDEN flag on redraw? */
-
if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) {
- WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
+ if (!is_modal) {
+ WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
+ }
}
else {
- WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
+ if (!is_modal) {
+ WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
+ }
continue;
}
@@ -1785,13 +1900,15 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr
WM_gizmo_set_color(axis, color);
WM_gizmo_set_color_highlight(axis, color_hi);
- switch (axis_idx) {
- case MAN_AXIS_TRANS_C:
- case MAN_AXIS_ROT_C:
- case MAN_AXIS_SCALE_C:
- case MAN_AXIS_ROT_T:
- WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
- break;
+ if (!is_modal) {
+ switch (axis_idx) {
+ case MAN_AXIS_TRANS_C:
+ case MAN_AXIS_ROT_C:
+ case MAN_AXIS_SCALE_C:
+ case MAN_AXIS_ROT_T:
+ WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
+ break;
+ }
}
}
MAN_ITER_AXES_END;
@@ -1879,6 +1996,17 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C,
}
}
}
+ else if (ELEM(axis_idx, MAN_AXIS_ROT_X, MAN_AXIS_ROT_Y, MAN_AXIS_ROT_Z, MAN_AXIS_ROT_C)) {
+ gizmo_3d_dial_matrixbasis_calc(CTX_wm_region(C),
+ gz->matrix_basis[2],
+ gz->matrix_basis[3],
+ (float[2]){UNPACK2(event->mval)},
+ gz->matrix_basis);
+ PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "draw_options");
+ RNA_property_enum_set(
+ gz->ptr, prop, RNA_property_enum_get(gz->ptr, prop) | ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
+ RNA_float_set(gz->ptr, "incremental_angle", 0.0f);
+ }
}
static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d)
@@ -1944,8 +2072,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt)
gzgt->name = "3D View: Transform Gizmo";
gzgt->idname = "VIEW3D_GGT_xform_gizmo";
- gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE |
- WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
+ gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
+ WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index 2fd81486bb6..83f1bd35f81 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -35,7 +35,7 @@
/* Own include. */
#include "transform_mode.h"
-int transform_mode_really_used(bContext *C, int mode)
+eTfmMode transform_mode_really_used(bContext *C, eTfmMode mode)
{
if (mode == TFM_BONESIZE) {
Object *ob = CTX_data_active_object(C);
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 6930b87090c..eac6734ed88 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -25,7 +25,7 @@ typedef struct TransDataGenericSlideVert {
/* transform_mode.c */
-int transform_mode_really_used(struct bContext *C, int mode);
+eTfmMode transform_mode_really_used(struct bContext *C, eTfmMode mode);
bool transdata_check_local_center(const TransInfo *t, short around);
/**
* Informs if the mode can be switched during modal.
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 4b86adc4f46..94caaa288e5 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -322,10 +322,30 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
+static void applyRotationMatrix(TransInfo *t, float mat_xform[4][4])
+{
+ float axis_final[3];
+ const float angle_final = t->values_final[0];
+ if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
+ t->con.applyRot(t, NULL, NULL, axis_final, NULL);
+ }
+ else {
+ negate_v3_v3(axis_final, t->spacemtx[t->orient_axis]);
+ }
+
+ float mat3[3][3];
+ float mat4[4][4];
+ axis_angle_normalized_to_mat3(mat3, axis_final, angle_final);
+ copy_m4_m3(mat4, mat3);
+ transform_pivot_set_m4(mat4, t->center_global);
+ mul_m4_m4m4(mat_xform, mat4, mat_xform);
+}
+
void initRotation(TransInfo *t)
{
t->mode = TFM_ROTATION;
t->transform = applyRotation;
+ t->transform_matrix = applyRotationMatrix;
t->tsnap.applySnap = ApplySnapRotation;
t->tsnap.distance = RotationBetween;
diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c
index 2e405449bc3..3716826800b 100644
--- a/source/blender/editors/transform/transform_mode_trackball.c
+++ b/source/blender/editors/transform/transform_mode_trackball.c
@@ -75,19 +75,25 @@ static void transdata_elem_trackball_fn(void *__restrict iter_data_v,
/** \name Transform (Rotation - Trackball)
* \{ */
-static void applyTrackballValue(TransInfo *t,
- const float axis1[3],
- const float axis2[3],
- const float angles[2])
+static void applyTrackballValue_calc_axis_angle(const TransInfo *t,
+ const float phi[2],
+ float r_axis[3],
+ float *r_angle)
+{
+ float axis1[3], axis2[3];
+ normalize_v3_v3(axis1, t->persinv[0]);
+ normalize_v3_v3(axis2, t->persinv[1]);
+
+ mul_v3_v3fl(r_axis, axis1, phi[0]);
+ madd_v3_v3fl(r_axis, axis2, phi[1]);
+ *r_angle = normalize_v3(r_axis);
+}
+
+static void applyTrackballValue(TransInfo *t, const float axis[3], const float angle)
{
float mat_final[3][3];
- float axis[3];
- float angle;
int i;
- mul_v3_v3fl(axis, axis1, angles[0]);
- madd_v3_v3fl(axis, axis2, angles[1]);
- angle = normalize_v3(axis);
axis_angle_normalized_to_mat3(mat_final, axis, angle);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -120,17 +126,8 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
size_t ofs = 0;
- float axis1[3], axis2[3];
-#if 0 /* UNUSED */
- float mat[3][3], totmat[3][3], smat[3][3];
-#endif
float phi[2];
- copy_v3_v3(axis1, t->persinv[0]);
- copy_v3_v3(axis2, t->persinv[1]);
- normalize_v3(axis1);
- normalize_v3(axis2);
-
copy_v2_v2(phi, t->values);
transform_snap_increment(t, phi);
@@ -165,27 +162,35 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
-#if 0 /* UNUSED */
- axis_angle_normalized_to_mat3(smat, axis1, phi[0]);
- axis_angle_normalized_to_mat3(totmat, axis2, phi[1]);
+ float axis_final[3], angle_final;
+ applyTrackballValue_calc_axis_angle(t, phi, axis_final, &angle_final);
+ applyTrackballValue(t, axis_final, angle_final);
- mul_m3_m3m3(mat, smat, totmat);
+ recalcData(t);
- /* TRANSFORM_FIX_ME */
- // copy_m3_m3(t->mat, mat); /* used in gizmo. */
-#endif
+ ED_area_status_text(t->area, str);
+}
- applyTrackballValue(t, axis1, axis2, phi);
+static void applyTrackballMatrix(TransInfo *t, float mat_xform[4][4])
+{
+ const float phi[2] = {UNPACK2(t->values_final)};
- recalcData(t);
+ float axis_final[3], angle_final;
+ applyTrackballValue_calc_axis_angle(t, phi, axis_final, &angle_final);
- ED_area_status_text(t->area, str);
+ float mat3[3][3], mat4[4][4];
+ axis_angle_normalized_to_mat3(mat3, axis_final, angle_final);
+
+ copy_m4_m3(mat4, mat3);
+ transform_pivot_set_m4(mat4, t->center_global);
+ mul_m4_m4m4(mat_xform, mat4, mat_xform);
}
void initTrackball(TransInfo *t)
{
t->mode = TFM_TRACKBALL;
t->transform = applyTrackball;
+ t->transform_matrix = applyTrackballMatrix;
initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index c881cde351e..3c6b6ea4117 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -469,7 +469,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
add_v3_v3(global_dir, values_ofs);
}
- t->tsnap.snapElem = 0;
+ t->tsnap.snapElem = SCE_SNAP_MODE_NONE;
applySnapping(t, global_dir);
transform_snap_grid(t, global_dir);
@@ -486,7 +486,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
/* Test for mixed snap with grid. */
float snap_dist_sq = FLT_MAX;
- if (t->tsnap.snapElem != 0) {
+ if (t->tsnap.snapElem != SCE_SNAP_MODE_NONE) {
snap_dist_sq = len_squared_v3v3(t->values, global_dir);
}
if ((snap_dist_sq == FLT_MAX) || (len_squared_v3v3(global_dir, incr_dir) < snap_dist_sq)) {
@@ -518,6 +518,13 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
+static void applyTranslationMatrix(TransInfo *t, float mat_xform[4][4])
+{
+ float delta[3];
+ mul_v3_m3v3(delta, t->spacemtx, t->values_final);
+ add_v3_v3(mat_xform[3], delta);
+}
+
void initTranslation(TransInfo *t)
{
if (t->spacetype == SPACE_ACTION) {
@@ -530,6 +537,7 @@ void initTranslation(TransInfo *t)
}
t->transform = applyTranslation;
+ t->transform_matrix = applyTranslationMatrix;
t->tsnap.applySnap = ApplySnapTranslation;
t->tsnap.distance = transform_snap_distance_len_squared_fn;
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 600f525b428..cd8a2f17554 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -419,7 +419,7 @@ static int transform_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* XXX, workaround: active needs to be calculated before transforming,
* since we're not reading from 'td->center' in this case. see: T40241 */
- if (t->tsnap.target == SCE_SNAP_TARGET_ACTIVE) {
+ if (t->tsnap.source_select == SCE_SNAP_SOURCE_ACTIVE) {
/* In camera view, tsnap callback is not set
* (see #initSnappingMode() in transform_snap.c, and T40348). */
if (t->tsnap.targetSnap && ((t->tsnap.status & TARGET_INIT) == 0)) {
@@ -648,7 +648,11 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
RNA_def_property_flag(prop, PROP_HIDDEN);
if (flags & P_GEO_SNAP) {
- prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_target_items, 0, "Target", "");
+ /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid
+ * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is
+ * geometry to which moved geometry is snapped). Use "Source snap point" and "Point on
+ * source that will snap to target" for name and description, respectively. */
+ prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_float_vector(
ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index e119b264ae7..649217092aa 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -79,8 +79,8 @@ static void TargetSnapActive(TransInfo *t);
/** \name Implementations
* \{ */
-static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select);
-static NodeBorder snapNodeBorder(int snap_node_mode);
+static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select);
+static NodeBorder snapNodeBorder(eSnapMode snap_node_mode);
#if 0
int BIF_snappingSupported(Object *obedit)
@@ -377,56 +377,59 @@ void applyProject(TransInfo *t)
if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) ==
V3D_PROJ_RET_OK) {
- if (ED_transform_snap_object_project_view3d(
- t->tsnap.object_context,
- t->depsgraph,
- t->region,
- t->view,
- SCE_SNAP_MODE_FACE,
- &(const struct SnapObjectParams){
- .snap_select = t->tsnap.modeSelect,
- .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
- .use_occlusion_test = false,
- .use_backface_culling = t->tsnap.use_backface_culling,
- },
- mval_fl,
- NULL,
- 0,
- loc,
- no)) {
+ eSnapMode hit = ED_transform_snap_object_project_view3d(
+ t->tsnap.object_context,
+ t->depsgraph,
+ t->region,
+ t->view,
+ SCE_SNAP_MODE_FACE,
+ &(const struct SnapObjectParams){
+ .snap_target_select = t->tsnap.target_select,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
+ .use_occlusion_test = false,
+ .use_backface_culling = t->tsnap.use_backface_culling,
+ },
+ mval_fl,
+ NULL,
+ 0,
+ loc,
+ no);
+ if (hit != SCE_SNAP_MODE_FACE) {
+ return;
+ }
+
#if 0
- if (tc->use_local_mat) {
- mul_m4_v3(tc->imat, loc);
- }
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->imat, loc);
+ }
#endif
- sub_v3_v3v3(tvec, loc, iloc);
+ sub_v3_v3v3(tvec, loc, iloc);
- mul_m3_v3(td->smtx, tvec);
+ mul_m3_v3(td->smtx, tvec);
- add_v3_v3(td->loc, tvec);
+ add_v3_v3(td->loc, tvec);
- if (t->tsnap.align && (t->options & CTX_OBJECT)) {
- /* handle alignment as well */
- const float *original_normal;
- float mat[3][3];
+ if (t->tsnap.align && (t->options & CTX_OBJECT)) {
+ /* handle alignment as well */
+ const float *original_normal;
+ float mat[3][3];
- /* In pose mode, we want to align normals with Y axis of bones... */
- original_normal = td->axismtx[2];
+ /* In pose mode, we want to align normals with Y axis of bones... */
+ original_normal = td->axismtx[2];
- rotation_between_vecs_to_mat3(mat, original_normal, no);
+ rotation_between_vecs_to_mat3(mat, original_normal, no);
- transform_data_ext_rotate(td, mat, true);
+ transform_data_ext_rotate(td, mat, true);
- /* TODO: support constraints for rotation too? see #ElementRotation. */
- }
+ /* TODO: support constraints for rotation too? see #ElementRotation. */
}
}
+ }
#if 0 /* TODO: support this? */
- constraintTransLim(t, td);
+ constraintTransLim(t, td);
#endif
- }
}
}
@@ -522,13 +525,13 @@ void applySnapping(TransInfo *t, float *vec)
void resetSnapping(TransInfo *t)
{
- t->tsnap.status = 0;
- t->tsnap.snapElem = 0;
+ t->tsnap.status = SNAP_RESETTED;
+ t->tsnap.snapElem = SCE_SNAP_MODE_NONE;
t->tsnap.align = false;
- t->tsnap.project = 0;
- t->tsnap.mode = 0;
- t->tsnap.modeSelect = 0;
- t->tsnap.target = 0;
+ t->tsnap.project = false;
+ t->tsnap.mode = SCE_SNAP_MODE_NONE;
+ t->tsnap.target_select = SCE_SNAP_TARGET_ALL;
+ t->tsnap.source_select = SCE_SNAP_SOURCE_CLOSEST;
t->tsnap.last = 0;
t->tsnap.snapNormal[0] = 0;
@@ -581,7 +584,7 @@ static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data))
return true;
}
-static char snap_flag_from_spacetype(TransInfo *t)
+static eSnapFlag snap_flag_from_spacetype(TransInfo *t)
{
ToolSettings *ts = t->settings;
if (t->spacetype == SPACE_NODE) {
@@ -596,7 +599,7 @@ static char snap_flag_from_spacetype(TransInfo *t)
return ts->snap_flag;
}
-static short snap_mode_from_spacetype(TransInfo *t)
+static eSnapMode snap_mode_from_spacetype(TransInfo *t)
{
ToolSettings *ts = t->settings;
@@ -605,7 +608,7 @@ static short snap_mode_from_spacetype(TransInfo *t)
}
if (t->spacetype == SPACE_IMAGE) {
- short snap_mode = ts->snap_uv_mode;
+ eSnapMode snap_mode = ts->snap_uv_mode;
if ((snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) &&
(t->mode == TFM_TRANSLATION)) {
snap_mode &= ~SCE_SNAP_MODE_INCREMENT;
@@ -623,7 +626,7 @@ static short snap_mode_from_spacetype(TransInfo *t)
return SCE_SNAP_MODE_INCREMENT;
}
- short snap_mode = ts->snap_mode;
+ eSnapMode snap_mode = ts->snap_mode;
if ((snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) &&
(t->mode == TFM_TRANSLATION)) {
/* Special case in which snap to increments is transformed to snap to grid. */
@@ -641,7 +644,7 @@ static short snap_mode_from_spacetype(TransInfo *t)
return SCE_SNAP_MODE_INCREMENT;
}
-static short snap_select_type_get(TransInfo *t)
+static eSnapTargetSelect snap_select_type_get(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
Base *base_act = view_layer->basact;
@@ -652,7 +655,7 @@ static short snap_select_type_get(TransInfo *t)
* TODO: perform self snap in gpencil_strokes.
*
* When we're moving the origins, allow snapping onto our own geometry (see T69132). */
- return SNAP_ALL;
+ return SCE_SNAP_TARGET_ALL;
}
const int obedit_type = t->obedit_type;
@@ -669,44 +672,44 @@ static short snap_select_type_get(TransInfo *t)
if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
/* Exclude editmesh if using proportional edit */
- return SNAP_NOT_EDITED;
+ return SCE_SNAP_TARGET_NOT_EDITED;
}
if (!t->tsnap.snap_self) {
- return SNAP_NOT_ACTIVE;
+ return SCE_SNAP_TARGET_NOT_ACTIVE;
}
- return SNAP_NOT_SELECTED;
+ return SCE_SNAP_TARGET_NOT_SELECTED;
}
- return SNAP_ALL;
+ return SCE_SNAP_TARGET_ALL;
}
if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
/* Particles edit mode. */
- return SNAP_ALL;
+ return SCE_SNAP_TARGET_ALL;
}
/* Object or pose mode. */
- return SNAP_NOT_SELECTED;
+ return SCE_SNAP_TARGET_NOT_SELECTED;
}
if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
- return SNAP_NOT_SELECTED;
+ return SCE_SNAP_TARGET_NOT_SELECTED;
}
- return SNAP_ALL;
+ return SCE_SNAP_TARGET_ALL;
}
static void initSnappingMode(TransInfo *t)
{
ToolSettings *ts = t->settings;
t->tsnap.mode = snap_mode_from_spacetype(t);
- t->tsnap.modeSelect = snap_select_type_get(t);
+ t->tsnap.target_select = snap_select_type_get(t);
if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) {
/* Force project off when not supported. */
- t->tsnap.project = 0;
+ t->tsnap.project = false;
}
if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE, SPACE_SEQ)) {
@@ -752,7 +755,7 @@ void initSnapping(TransInfo *t, wmOperator *op)
{
resetSnapping(t);
t->tsnap.flag = snap_flag_from_spacetype(t);
- short snap_target = t->settings->snap_target;
+ eSnapSourceSelect snap_source = t->settings->snap_target;
/* if snap property exists */
PropertyRNA *prop;
@@ -763,7 +766,10 @@ void initSnapping(TransInfo *t, wmOperator *op)
if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
RNA_property_is_set(op->ptr, prop)) {
- snap_target = RNA_property_enum_get(op->ptr, prop);
+ /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid
+ * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is
+ * geometry to which moved geometry is snapped). */
+ snap_source = RNA_property_enum_get(op->ptr, prop);
}
if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) &&
@@ -803,7 +809,7 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
}
- t->tsnap.target = snap_target;
+ t->tsnap.source_select = snap_source;
initSnappingMode(t);
}
@@ -844,11 +850,11 @@ static void setSnappingCallback(TransInfo *t)
return;
}
- switch (t->tsnap.target) {
- case SCE_SNAP_TARGET_CLOSEST:
+ switch (t->tsnap.source_select) {
+ case SCE_SNAP_SOURCE_CLOSEST:
t->tsnap.targetSnap = TargetSnapClosest;
break;
- case SCE_SNAP_TARGET_CENTER:
+ case SCE_SNAP_SOURCE_CENTER:
if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
t->tsnap.targetSnap = TargetSnapCenter;
break;
@@ -856,10 +862,10 @@ static void setSnappingCallback(TransInfo *t)
/* Can't do TARGET_CENTER with these modes,
* use TARGET_MEDIAN instead. */
ATTR_FALLTHROUGH;
- case SCE_SNAP_TARGET_MEDIAN:
+ case SCE_SNAP_SOURCE_MEDIAN:
t->tsnap.targetSnap = TargetSnapMedian;
break;
- case SCE_SNAP_TARGET_ACTIVE:
+ case SCE_SNAP_SOURCE_ACTIVE:
t->tsnap.targetSnap = TargetSnapActive;
break;
}
@@ -973,7 +979,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec))
float no[3];
float mval[2];
bool found = false;
- short snap_elem = 0;
+ eSnapMode snap_elem = SCE_SNAP_MODE_NONE;
float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */
mval[0] = t->mval[0];
@@ -982,7 +988,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec))
if (t->tsnap.mode & SCE_SNAP_MODE_GEOM) {
zero_v3(no); /* objects won't set this */
snap_elem = snapObjectsTransform(t, mval, &dist_px, loc, no);
- found = snap_elem != 0;
+ found = (snap_elem != SCE_SNAP_MODE_NONE);
}
if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) {
found = peelObjectsTransform(
@@ -1003,7 +1009,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec))
t->tsnap.status &= ~POINT_INIT;
}
- t->tsnap.snapElem = (char)snap_elem;
+ t->tsnap.snapElem = snap_elem;
}
static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec))
@@ -1020,7 +1026,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec))
objects,
objects_len,
t->mval,
- true,
+ t->tsnap.target_select == SCE_SNAP_TARGET_NOT_SELECTED,
&dist_sq,
t->tsnap.snapPoint)) {
t->tsnap.snapPoint[0] *= t->aspect[0];
@@ -1190,7 +1196,7 @@ static void TargetSnapActive(TransInfo *t)
}
/* No active, default to median */
else {
- t->tsnap.target = SCE_SNAP_TARGET_MEDIAN;
+ t->tsnap.source_select = SCE_SNAP_SOURCE_MEDIAN;
t->tsnap.targetSnap = TargetSnapMedian;
TargetSnapMedian(t);
}
@@ -1302,7 +1308,7 @@ static void TargetSnapClosest(TransInfo *t)
/** \name Snap Objects
* \{ */
-short snapObjectsTransform(
+eSnapMode snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
{
float *target = (t->tsnap.status & TARGET_INIT) ? t->tsnap.snapTarget : t->center_global;
@@ -1313,7 +1319,7 @@ short snapObjectsTransform(
t->view,
t->tsnap.mode,
&(const struct SnapObjectParams){
- .snap_select = t->tsnap.modeSelect,
+ .snap_target_select = t->tsnap.target_select,
.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
.use_backface_culling = t->tsnap.use_backface_culling,
@@ -1346,7 +1352,7 @@ bool peelObjectsTransform(TransInfo *t,
t->region,
t->view,
&(const struct SnapObjectParams){
- .snap_select = t->tsnap.modeSelect,
+ .snap_target_select = t->tsnap.target_select,
.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
},
mval,
@@ -1414,16 +1420,16 @@ bool peelObjectsTransform(TransInfo *t,
/** \name snap Nodes
* \{ */
-static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select)
+static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select)
{
/* node is use for snapping only if a) snap mode matches and b) node is inside the view */
- return ((snap_select == SNAP_NOT_SELECTED && !(node->flag & NODE_SELECT)) ||
- (snap_select == SNAP_ALL && !(node->flag & NODE_ACTIVE))) &&
+ return ((snap_target_select == SCE_SNAP_TARGET_NOT_SELECTED && !(node->flag & NODE_SELECT)) ||
+ (snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) &&
(node->totr.xmin < v2d->cur.xmax && node->totr.xmax > v2d->cur.xmin &&
node->totr.ymin < v2d->cur.ymax && node->totr.ymax > v2d->cur.ymin);
}
-static NodeBorder snapNodeBorder(int snap_node_mode)
+static NodeBorder snapNodeBorder(eSnapMode snap_node_mode)
{
NodeBorder flag = 0;
if (snap_node_mode & SCE_SNAP_MODE_NODE_X) {
@@ -1499,7 +1505,7 @@ static bool snapNodes(ToolSettings *ts,
SpaceNode *snode,
ARegion *region,
const int mval[2],
- eSnapSelect snap_select,
+ eSnapTargetSelect snap_target_select,
float r_loc[2],
float *r_dist_px,
char *r_node_border)
@@ -1511,7 +1517,7 @@ static bool snapNodes(ToolSettings *ts,
*r_node_border = 0;
for (node = ntree->nodes.first; node; node = node->next) {
- if (snapNodeTest(&region->v2d, node, snap_select)) {
+ if (snapNodeTest(&region->v2d, node, snap_target_select)) {
retval |= snapNode(ts, snode, region, node, mval, r_loc, r_dist_px, r_node_border);
}
}
@@ -1526,7 +1532,7 @@ bool snapNodesTransform(
t->area->spacedata.first,
t->region,
mval,
- t->tsnap.modeSelect,
+ t->tsnap.target_select,
r_loc,
r_dist_px,
r_node_border);
@@ -1548,7 +1554,7 @@ static void snap_grid_apply(
float in[3];
if (t->con.mode & CON_APPLY) {
- BLI_assert(t->tsnap.snapElem == 0);
+ BLI_assert(t->tsnap.snapElem == SCE_SNAP_MODE_NONE);
t->con.applyVec(t, NULL, NULL, loc, in);
}
else {
@@ -1676,6 +1682,16 @@ bool transform_snap_increment(const TransInfo *t, float *r_val)
return transform_snap_increment_ex(t, false, r_val);
}
+float transform_snap_increment_get(const TransInfo *t)
+{
+ if (activeSnap(t) && (!transformModeUseSnap(t) ||
+ (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) {
+ return (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
+ }
+
+ return 0.0f;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index cde97d14be4..6db027df067 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -19,12 +19,12 @@ bool peelObjectsTransform(struct TransInfo *t,
float r_no[3],
float *r_thickness);
-short snapObjectsTransform(struct TransInfo *t,
- const float mval[2],
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3]);
+eSnapMode snapObjectsTransform(struct TransInfo *t,
+ const float mval[2],
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3]);
bool snapNodesTransform(struct TransInfo *t,
const int mval[2],
/* return args */
@@ -36,6 +36,7 @@ bool transformModeUseSnap(const TransInfo *t);
bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val);
bool transform_snap_increment(const TransInfo *t, float *val);
+float transform_snap_increment_get(const TransInfo *t);
bool transform_snap_grid(TransInfo *t, float *val);
bool activeSnap(const TransInfo *t);
diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc
index fade7f47d9c..cf99d4b2ef3 100644
--- a/source/blender/editors/transform/transform_snap_object.cc
+++ b/source/blender/editors/transform/transform_snap_object.cc
@@ -157,7 +157,7 @@ struct SnapObjectContext {
enum eViewProj view_proj;
float clip_plane[MAX_CLIPPLANE_LEN][4];
short clip_plane_len;
- short snap_to_flag;
+ eSnapMode snap_to_flag;
bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */
} runtime;
};
@@ -168,8 +168,13 @@ struct SnapObjectContext {
/** \name Utilities
* \{ */
-/* Mesh used for snapping.
- * If nullptr the BMesh should be used. */
+/**
+ * Mesh used for snapping.
+ *
+ * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used.
+ * - In rare cases there is no evaluated mesh available and a null result doesn't imply an
+ * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536.
+ */
static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
{
const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
@@ -278,8 +283,10 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx,
}
}
else {
- /* Any existing #SnapData_EditMesh is now invalid. */
- sctx->editmesh_caches.remove(BKE_editmesh_from_object(ob_eval));
+ if (ob_eval->type == OB_MESH) {
+ /* Any existing #SnapData_EditMesh is now invalid. */
+ sctx->editmesh_caches.remove(BKE_editmesh_from_object(ob_eval));
+ }
std::unique_ptr<SnapData_Mesh> sod_ptr = std::make_unique<SnapData_Mesh>();
sod = sod_ptr.get();
@@ -412,7 +419,7 @@ using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx,
void *data);
static bool snap_object_is_snappable(const SnapObjectContext *sctx,
- const eSnapSelect snap_select,
+ const eSnapTargetSelect snap_select,
const Base *base_act,
const Base *base,
const bool is_in_object_mode)
@@ -421,7 +428,7 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return false;
}
- if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
+ if ((snap_select == SCE_SNAP_TARGET_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
return true;
}
@@ -429,15 +436,15 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return false;
}
- if (snap_select == SNAP_NOT_ACTIVE) {
+ if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) {
return base_act != base;
}
- if (snap_select == SNAP_NOT_EDITED) {
+ if (snap_select == SCE_SNAP_TARGET_NOT_EDITED) {
return base->object->mode != OB_MODE_EDIT;
}
- if (snap_select == SNAP_NOT_SELECTED) {
+ if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) {
if (is_in_object_mode) {
return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL));
}
@@ -446,7 +453,7 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return true;
}
- if (snap_select == SNAP_SELECTABLE) {
+ if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) {
return (base->flag & BASE_SELECTABLE) != 0;
}
@@ -462,12 +469,12 @@ static void iter_snap_objects(SnapObjectContext *sctx,
void *data)
{
ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph);
- const eSnapSelect snap_select = params->snap_select;
+ const eSnapTargetSelect snap_target_select = params->snap_target_select;
Base *base_act = view_layer->basact;
const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if (!snap_object_is_snappable(sctx, snap_select, base_act, base, is_in_object_mode)) {
+ if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base, is_in_object_mode)) {
continue;
}
@@ -998,6 +1005,9 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
if (me_eval == nullptr) {
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
+ if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */
+ return;
+ }
BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)),
"Make sure there is only one pointer for looptris");
retval = raycastEditMesh(sctx,
@@ -1525,18 +1535,18 @@ static void nearest2d_data_init_editmesh(SnapData_EditMesh *sod,
/** \name Internal Object Snapping API
* \{ */
-static short snap_mesh_polygon(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index)
+static eSnapMode snap_mesh_polygon(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3],
+ int *r_index)
{
- short elem = 0;
+ eSnapMode elem = SCE_SNAP_MODE_NONE;
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
@@ -1655,23 +1665,23 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
return elem;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
-static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- const float obmat[4][4],
- float original_dist_px,
- const float prev_co[3],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index)
+static eSnapMode snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ float original_dist_px,
+ const float prev_co[3],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3],
+ int *r_index)
{
- short elem = SCE_SNAP_MODE_EDGE;
+ eSnapMode elem = SCE_SNAP_MODE_EDGE;
if (ob_eval->type != OB_MESH) {
return elem;
@@ -1818,19 +1828,19 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
return elem;
}
-static short snapArmature(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- const float obmat[4][4],
- bool is_object_active,
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float *UNUSED(r_no),
- int *r_index)
+static eSnapMode snapArmature(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ bool is_object_active,
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float *UNUSED(r_no),
+ int *r_index)
{
- short retval = 0;
+ eSnapMode retval = SCE_SNAP_MODE_NONE;
if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */
return retval;
@@ -1867,7 +1877,7 @@ static short snapArmature(SnapObjectContext *sctx,
const bool is_posemode = is_object_active && (ob_eval->mode & OB_MODE_POSE);
const bool skip_selected = (is_editmode || is_posemode) &&
- (params->snap_select == SNAP_NOT_SELECTED);
+ (params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED);
const bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP;
if (arm->edbo) {
@@ -1982,25 +1992,25 @@ static short snapArmature(SnapObjectContext *sctx,
return retval;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
-static short snapCurve(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float *UNUSED(r_no),
- int *r_index)
+static eSnapMode snapCurve(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float *UNUSED(r_no),
+ int *r_index)
{
bool has_snap = false;
/* only vertex snapping mode (eg control points and handles) supported for now) */
if ((sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
Curve *cu = static_cast<Curve *>(ob_eval->data);
@@ -2024,7 +2034,7 @@ static short snapCurve(SnapObjectContext *sctx,
sctx->runtime.win_size,
sctx->runtime.mval,
dist_px_sq)) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
}
@@ -2046,7 +2056,7 @@ static short snapCurve(SnapObjectContext *sctx,
}
bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP;
- bool skip_selected = params->snap_select == SNAP_NOT_SELECTED;
+ bool skip_selected = params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED;
LISTBASE_FOREACH (Nurb *, nu, (use_obedit ? &cu->editnurb->nurbs : &cu->nurb)) {
for (int u = 0; u < nu->pntsu; u++) {
@@ -2069,11 +2079,14 @@ static short snapCurve(SnapObjectContext *sctx,
nu->bezt[u].vec[1],
&dist_px_sq,
r_loc);
+
/* Don't snap if handle is selected (moving),
* or if it is aligning to a moving handle. */
- is_selected = (!(nu->bezt[u].f1 & SELECT) &&
- !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) != 0;
- if (!(is_selected && skip_selected)) {
+ bool is_selected_h1 = (nu->bezt[u].f1 & SELECT) != 0;
+ bool is_selected_h2 = (nu->bezt[u].f3 & SELECT) != 0;
+ bool is_autoalign_h1 = (nu->bezt[u].h1 & HD_ALIGN) != 0;
+ bool is_autoalign_h2 = (nu->bezt[u].h2 & HD_ALIGN) != 0;
+ if (!skip_selected || !(is_selected_h1 || (is_autoalign_h1 && is_selected_h2))) {
has_snap |= test_projected_vert_dist(&neasrest_precalc,
clip_planes_local,
clip_plane_len,
@@ -2083,9 +2096,7 @@ static short snapCurve(SnapObjectContext *sctx,
r_loc);
}
- is_selected = (!(nu->bezt[u].f3 & SELECT) &&
- !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) != 0;
- if (!(is_selected && skip_selected)) {
+ if (!skip_selected || !(is_selected_h2 || (is_autoalign_h2 && is_selected_h1))) {
has_snap |= test_projected_vert_dist(&neasrest_precalc,
clip_planes_local,
clip_plane_len,
@@ -2151,21 +2162,21 @@ static short snapCurve(SnapObjectContext *sctx,
return SCE_SNAP_MODE_VERTEX;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
/* may extend later (for now just snaps to empty center) */
-static short snap_object_center(const SnapObjectContext *sctx,
- Object *ob_eval,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float *UNUSED(r_no),
- int *r_index)
+static eSnapMode snap_object_center(const SnapObjectContext *sctx,
+ Object *ob_eval,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float *UNUSED(r_no),
+ int *r_index)
{
- short retval = 0;
+ eSnapMode retval = SCE_SNAP_MODE_NONE;
if (ob_eval->transflag & OB_DUPLI) {
return retval;
@@ -2207,20 +2218,20 @@ static short snap_object_center(const SnapObjectContext *sctx,
return retval;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
-static short snapCamera(const SnapObjectContext *sctx,
- Object *object,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float *r_no,
- int *r_index)
+static eSnapMode snapCamera(const SnapObjectContext *sctx,
+ Object *object,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float *r_no,
+ int *r_index)
{
- short retval = 0;
+ eSnapMode retval = SCE_SNAP_MODE_NONE;
Scene *scene = sctx->scene;
@@ -2301,28 +2312,28 @@ static short snapCamera(const SnapObjectContext *sctx,
return retval;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
-static short snapMesh(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- const Mesh *me_eval,
- const float obmat[4][4],
- bool use_hide,
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index)
+static eSnapMode snapMesh(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ const Mesh *me_eval,
+ const float obmat[4][4],
+ bool use_hide,
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3],
+ int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if (me_eval->totvert == 0) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
float lpmat[4][4];
@@ -2335,7 +2346,7 @@ static short snapMesh(SnapObjectContext *sctx,
if (bb &&
!snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
SnapData_Mesh *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
@@ -2376,7 +2387,7 @@ static short snapMesh(SnapObjectContext *sctx,
nearest.dist_sq = dist_px_sq;
int last_index = nearest.index;
- short elem = SCE_SNAP_MODE_VERTEX;
+ eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
@@ -2480,31 +2491,31 @@ static short snapMesh(SnapObjectContext *sctx,
return elem;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
-static short snapEditMesh(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- Object *ob_eval,
- BMEditMesh *em,
- const float obmat[4][4],
- /* read/write args */
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index)
+static eSnapMode snapEditMesh(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ Object *ob_eval,
+ BMEditMesh *em,
+ const float obmat[4][4],
+ /* read/write args */
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3],
+ int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
if (em->bm->totvert == 0) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
}
else {
if (em->bm->totedge == 0) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
}
@@ -2520,7 +2531,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!snap_bound_box_check_dist(
sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) {
@@ -2595,7 +2606,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
nearest.index = -1;
nearest.dist_sq = dist_px_sq;
- short elem = SCE_SNAP_MODE_VERTEX;
+ eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
@@ -2661,7 +2672,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
return elem;
}
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
struct SnapObjUserData {
@@ -2673,7 +2684,7 @@ struct SnapObjUserData {
int *r_index;
Object **r_ob;
float (*r_obmat)[4];
- short ret;
+ eSnapMode ret;
};
/**
@@ -2687,7 +2698,7 @@ static void snap_obj_fn(SnapObjectContext *sctx,
void *data)
{
SnapObjUserData *dt = static_cast<SnapObjUserData *>(data);
- short retval = 0;
+ eSnapMode retval = SCE_SNAP_MODE_NONE;
switch (ob_eval->type) {
case OB_MESH: {
@@ -2696,6 +2707,9 @@ static void snap_obj_fn(SnapObjectContext *sctx,
const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
if (me_eval == nullptr) {
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
+ if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */
+ return;
+ }
BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)),
"Make sure there is only one pointer for looptris");
retval = snapEditMesh(
@@ -2731,11 +2745,8 @@ static void snap_obj_fn(SnapObjectContext *sctx,
dt->r_index);
break;
case OB_CURVES_LEGACY:
- retval = snapCurve(
- sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
- break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */
case OB_SURF:
- if (BKE_object_is_in_editmode(ob_eval)) {
+ if (ob_eval->type == OB_CURVES_LEGACY || BKE_object_is_in_editmode(ob_eval)) {
retval = snapCurve(
sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
if (params->edit_mode_type != SNAP_GEOM_FINAL) {
@@ -2804,18 +2815,18 @@ static void snap_obj_fn(SnapObjectContext *sctx,
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
*/
-static short snapObjectsRay(SnapObjectContext *sctx,
- const SnapObjectParams *params,
- /* read/write args */
- /* Parameters below cannot be const, because they are assigned to a
- * non-const variable (readability-non-const-parameter). */
- float *dist_px /* NOLINT */,
- /* return args */
- float r_loc[3] /* NOLINT */,
- float r_no[3] /* NOLINT */,
- int *r_index /* NOLINT */,
- Object **r_ob,
- float r_obmat[4][4])
+static eSnapMode snapObjectsRay(SnapObjectContext *sctx,
+ const SnapObjectParams *params,
+ /* read/write args */
+ /* Parameters below cannot be const, because they are assigned to a
+ * non-const variable (readability-non-const-parameter). */
+ float *dist_px /* NOLINT */,
+ /* return args */
+ float r_loc[3] /* NOLINT */,
+ float r_no[3] /* NOLINT */,
+ int *r_index /* NOLINT */,
+ Object **r_ob,
+ float r_obmat[4][4])
{
SnapObjUserData data = {};
data.dist_px = dist_px;
@@ -2824,7 +2835,7 @@ static short snapObjectsRay(SnapObjectContext *sctx,
data.r_ob = r_ob;
data.r_index = r_index;
data.r_obmat = r_obmat;
- data.ret = 0;
+ data.ret = SCE_SNAP_MODE_NONE;
iter_snap_objects(sctx, params, snap_obj_fn, &data);
@@ -2997,31 +3008,29 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx,
sctx, depsgraph, v3d, params, ray_origin, ray_direction, ray_depth, r_co, r_no);
}
-static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx,
- Depsgraph *depsgraph,
- const ARegion *region,
- const View3D *v3d,
- const ushort snap_to_flag,
- const SnapObjectParams *params,
- const float mval[2],
- const float prev_co[3],
- float *dist_px,
- float r_loc[3],
- float r_no[3],
- int *r_index,
- Object **r_ob,
- float r_obmat[4][4],
- float r_face_nor[3])
+static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx,
+ Depsgraph *depsgraph,
+ const ARegion *region,
+ const View3D *v3d,
+ const eSnapMode snap_to_flag,
+ const SnapObjectParams *params,
+ const float mval[2],
+ const float prev_co[3],
+ float *dist_px,
+ float r_loc[3],
+ float r_no[3],
+ int *r_index,
+ Object **r_ob,
+ float r_obmat[4][4],
+ float r_face_nor[3])
{
sctx->runtime.depsgraph = depsgraph;
sctx->runtime.region = region;
sctx->runtime.v3d = v3d;
- BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) !=
- 0);
+ BLI_assert((snap_to_flag & SCE_SNAP_MODE_GEOM) != 0);
- short retval = 0;
+ eSnapMode retval = SCE_SNAP_MODE_NONE;
bool has_hit = false;
Object *ob_eval = nullptr;
@@ -3040,7 +3049,7 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext
float ray_start[3], ray_normal[3];
if (!ED_view3d_win_to_ray_clipped_ex(
depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) {
- return 0;
+ return SCE_SNAP_MODE_NONE;
}
float dummy_ray_depth = BVH_RAYCAST_DIST_MAX;
@@ -3084,7 +3093,7 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext
if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT |
SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
- short elem_test, elem = 0;
+ eSnapMode elem_test, elem = SCE_SNAP_MODE_NONE;
float dist_px_tmp = *dist_px;
copy_m4_m4(sctx->runtime.pmat, rv3d->persmat);
@@ -3094,9 +3103,11 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext
sctx->runtime.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO;
/* First snap to edge instead of middle or perpendicular. */
- sctx->runtime.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE);
+ sctx->runtime.snap_to_flag = static_cast<eSnapMode>(
+ snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE));
if (snap_to_flag & (SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
- sctx->runtime.snap_to_flag |= SCE_SNAP_MODE_EDGE;
+ sctx->runtime.snap_to_flag = static_cast<eSnapMode>(sctx->runtime.snap_to_flag |
+ SCE_SNAP_MODE_EDGE);
}
planes_from_projmat(sctx->runtime.pmat,
@@ -3180,21 +3191,21 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext
return retval;
}
-short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
- Depsgraph *depsgraph,
- const ARegion *region,
- const View3D *v3d,
- const ushort snap_to,
- const SnapObjectParams *params,
- const float mval[2],
- const float prev_co[3],
- float *dist_px,
- float r_loc[3],
- float r_no[3],
- int *r_index,
- Object **r_ob,
- float r_obmat[4][4],
- float r_face_nor[3])
+eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
+ Depsgraph *depsgraph,
+ const ARegion *region,
+ const View3D *v3d,
+ const eSnapMode snap_to,
+ const SnapObjectParams *params,
+ const float mval[2],
+ const float prev_co[3],
+ float *dist_px,
+ float r_loc[3],
+ float r_no[3],
+ int *r_index,
+ Object **r_ob,
+ float r_obmat[4][4],
+ float r_face_nor[3])
{
return transform_snap_context_project_view3d_mixed_impl(sctx,
depsgraph,
@@ -3213,17 +3224,17 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
r_face_nor);
}
-short ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
- Depsgraph *depsgraph,
- const ARegion *region,
- const View3D *v3d,
- const ushort snap_to,
- const SnapObjectParams *params,
- const float mval[2],
- const float prev_co[3],
- float *dist_px,
- float r_loc[3],
- float r_no[3])
+eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
+ Depsgraph *depsgraph,
+ const ARegion *region,
+ const View3D *v3d,
+ const eSnapMode snap_to,
+ const SnapObjectParams *params,
+ const float mval[2],
+ const float prev_co[3],
+ float *dist_px,
+ float r_loc[3],
+ float r_no[3])
{
return ED_transform_snap_object_project_view3d_ex(sctx,
depsgraph,
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index 7dc361ff5bb..dbcae2b6320 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -25,6 +25,7 @@
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "transform.h"
#include "transform_snap.h"
@@ -65,14 +66,14 @@ static void seq_snap_source_points_build(TransSeqSnapData *snap_data, SeqCollect
SEQ_ITERATOR_FOREACH (seq, snap_sources) {
int left = 0, right = 0;
if (seq->flag & SEQ_LEFTSEL) {
- left = right = seq->startdisp;
+ left = right = SEQ_time_left_handle_frame_get(seq);
}
else if (seq->flag & SEQ_RIGHTSEL) {
- left = right = seq->enddisp;
+ left = right = SEQ_time_right_handle_frame_get(seq);
}
else {
- left = seq->startdisp;
- right = seq->enddisp;
+ left = SEQ_time_left_handle_frame_get(seq);
+ right = SEQ_time_right_handle_frame_get(seq);
}
snap_data->source_snap_points[i] = left;
@@ -193,21 +194,24 @@ static void seq_snap_target_points_build(Scene *scene,
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, snap_targets) {
- snap_data->target_snap_points[i] = seq->startdisp;
- snap_data->target_snap_points[i + 1] = seq->enddisp;
+ snap_data->target_snap_points[i] = SEQ_time_left_handle_frame_get(seq);
+ snap_data->target_snap_points[i + 1] = SEQ_time_right_handle_frame_get(seq);
i += 2;
if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) {
- int content_start = min_ii(seq->enddisp, seq->start);
- int content_end = max_ii(seq->startdisp, seq->start + seq->len);
+ int content_start = min_ii(SEQ_time_right_handle_frame_get(seq), seq->start);
+ int content_end = max_ii(SEQ_time_left_handle_frame_get(seq), 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) {
- content_start = seq->startdisp;
- content_end = seq->enddisp;
+ content_start = SEQ_time_left_handle_frame_get(seq);
+ content_end = SEQ_time_right_handle_frame_get(seq);
}
- CLAMP(content_start, seq->startdisp, seq->enddisp);
- CLAMP(content_end, seq->startdisp, seq->enddisp);
+ CLAMP(content_start,
+ SEQ_time_left_handle_frame_get(seq),
+ SEQ_time_right_handle_frame_get(seq));
+ CLAMP(
+ content_end, SEQ_time_left_handle_frame_get(seq), SEQ_time_right_handle_frame_get(seq));
snap_data->target_snap_points[i] = content_start;
snap_data->target_snap_points[i + 1] = content_end;
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 89d80d582f8..5c2a3374aa1 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -91,6 +91,7 @@ set(SRC
../include/ED_uvedit.h
../include/ED_view3d.h
../include/ED_view3d_offscreen.h
+ ../include/UI_grid_view.hh
../include/UI_icons.h
../include/UI_interface.h
../include/UI_interface.hh
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index f07364cd4d3..3cfe6bd61a4 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -19,6 +19,7 @@
#include "BKE_collection.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
@@ -40,6 +41,7 @@
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_paint.h"
+#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_util.h"
@@ -137,8 +139,12 @@ void ED_editors_init(bContext *C)
ED_object_posemode_enter_ex(bmain, ob);
}
- /* Other edit/paint/etc. modes are only settable for objects in active scene currently. */
- if (!BKE_collection_has_object_recursive(scene->master_collection, ob)) {
+ /* Other edit/paint/etc. modes are only settable for objects visible in active scene currently.
+ * Otherwise, they (and their obdata) may not be (fully) evaluated, which is mandatory for some
+ * modes like Sculpt.
+ * Ref. T98225. */
+ if (!BKE_collection_has_object_recursive(scene->master_collection, ob) ||
+ !BKE_scene_has_object(scene, ob) || (ob->visibility_flag & OB_HIDE_VIEWPORT) != 0) {
continue;
}
@@ -182,6 +188,18 @@ void ED_editors_init(bContext *C)
ED_space_image_paint_update(bmain, wm, scene);
}
+ /* Enforce a full redraw for the first time areas/regions get drawn. Further region init/refresh
+ * just triggers non-rebuild redraws (#RGN_DRAW_NO_REBUILD). Usually a full redraw would be
+ * triggered by a `NC_WM | ND_FILEREAD` notifier, but if a startup script calls an operator that
+ * redraws the window, notifiers are not handled before the operator runs. See T98461. */
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ const bScreen *screen = WM_window_get_active_screen(win);
+
+ ED_screen_areas_iter (win, screen, area) {
+ ED_area_tag_redraw(area);
+ }
+ }
+
ED_assetlist_storage_tag_main_data_dirty();
SWAP(int, reports->flag, reports_flag_prev);
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index 803e65590a0..af3589e50f0 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -67,19 +67,19 @@ static bool lib_id_preview_editing_poll(bContext *C)
static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
{
- char path[FILE_MAX];
+ char filepath[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", path);
+ RNA_string_get(op->ptr, "filepath", filepath);
- if (!BLI_is_file(path)) {
- BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path);
+ if (!BLI_is_file(filepath)) {
+ BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
return OPERATOR_CANCELLED;
}
PointerRNA idptr = CTX_data_pointer_get(C, "id");
ID *id = (ID *)idptr.data;
- BKE_previewimg_id_custom_set(id, path);
+ BKE_previewimg_id_custom_set(id, filepath);
WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr);
diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c
index 53f9aca8e8d..660afa4c3d7 100644
--- a/source/blender/editors/util/select_utils.c
+++ b/source/blender/editors/util/select_utils.c
@@ -66,15 +66,18 @@ eSelectOp ED_select_op_modal(const eSelectOp sel_op, const bool is_first)
return sel_op;
}
-int ED_select_similar_compare_float(const float delta, const float thresh, const int compare)
+bool ED_select_similar_compare_float(const float delta,
+ const float thresh,
+ const eSimilarCmp compare)
{
+ BLI_assert(thresh >= 0.0f);
switch (compare) {
case SIM_CMP_EQ:
return (fabsf(delta) <= thresh);
case SIM_CMP_GT:
- return ((delta + thresh) >= 0.0);
+ return ((delta + thresh) >= 0.0f);
case SIM_CMP_LT:
- return ((delta - thresh) <= 0.0);
+ return ((delta - thresh) <= 0.0f);
default:
BLI_assert_unreachable();
return 0;
@@ -84,8 +87,10 @@ int ED_select_similar_compare_float(const float delta, const float thresh, const
bool ED_select_similar_compare_float_tree(const KDTree_1d *tree,
const float length,
const float thresh,
- const int compare)
+ const eSimilarCmp compare)
{
+ BLI_assert(compare == SIM_CMP_EQ || length >= 0.0f); /* See precision note below. */
+
/* Length of the edge we want to compare against. */
float nearest_edge_length;
@@ -112,6 +117,7 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree,
KDTreeNearest_1d nearest;
if (BLI_kdtree_1d_find_nearest(tree, &nearest_edge_length, &nearest) != -1) {
+ BLI_assert(compare == SIM_CMP_EQ || nearest.co[0] >= 0.0f); /* See precision note above. */
float delta = length - nearest.co[0];
return ED_select_similar_compare_float(delta, thresh, compare);
}
@@ -119,11 +125,11 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree,
return false;
}
-eSelectOp ED_select_op_from_operator(wmOperator *op)
+eSelectOp ED_select_op_from_operator(PointerRNA *ptr)
{
- const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool deselect = RNA_boolean_get(op->ptr, "deselect");
- const bool toggle = RNA_boolean_get(op->ptr, "toggle");
+ const bool extend = RNA_boolean_get(ptr, "extend");
+ const bool deselect = RNA_boolean_get(ptr, "deselect");
+ const bool toggle = RNA_boolean_get(ptr, "toggle");
if (extend) {
return SEL_OP_ADD;
@@ -137,10 +143,56 @@ eSelectOp ED_select_op_from_operator(wmOperator *op)
return SEL_OP_SET;
}
-void ED_select_pick_params_from_operator(wmOperator *op, struct SelectPick_Params *params)
+void ED_select_pick_params_from_operator(PointerRNA *ptr, struct SelectPick_Params *params)
{
memset(params, 0x0, sizeof(*params));
- params->sel_op = ED_select_op_from_operator(op);
- params->deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
- params->select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough");
+ params->sel_op = ED_select_op_from_operator(ptr);
+ params->deselect_all = RNA_boolean_get(ptr, "deselect_all");
+ params->select_passthrough = RNA_boolean_get(ptr, "select_passthrough");
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Naming Callbacks
+ * \{ */
+
+const char *ED_select_pick_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr)
+{
+ struct SelectPick_Params params = {0};
+ ED_select_pick_params_from_operator(ptr, &params);
+ switch (params.sel_op) {
+ case SEL_OP_ADD:
+ return "Select (Extend)";
+ case SEL_OP_SUB:
+ return "Select (Deselect)";
+ case SEL_OP_XOR:
+ return "Select (Toggle)";
+ case SEL_OP_AND:
+ BLI_assert_unreachable();
+ ATTR_FALLTHROUGH;
+ case SEL_OP_SET:
+ break;
+ }
+ return "Select";
}
+
+const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr)
+{
+ /* Matches options in #WM_operator_properties_select_operation_simple */
+ const eSelectOp sel_op = RNA_enum_get(ptr, "mode");
+ switch (sel_op) {
+ case SEL_OP_ADD:
+ return "Circle Select (Extend)";
+ case SEL_OP_SUB:
+ return "Circle Select (Deselect)";
+ case SEL_OP_XOR:
+ ATTR_FALLTHROUGH;
+ case SEL_OP_AND:
+ BLI_assert_unreachable();
+ ATTR_FALLTHROUGH;
+ case SEL_OP_SET:
+ break;
+ }
+ return "Circle Select";
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 181982f0b2c..04128cf378c 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -182,5 +182,6 @@ void UV_OT_select_circle(struct wmOperatorType *ot);
void UV_OT_select_more(struct wmOperatorType *ot);
void UV_OT_select_less(struct wmOperatorType *ot);
void UV_OT_select_overlap(struct wmOperatorType *ot);
+void UV_OT_select_similar(struct wmOperatorType *ot);
/* Used only when UV sync select is disabled. */
void UV_OT_select_mode(struct wmOperatorType *ot);
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index fe6f9f0d513..0b5d6592426 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -2044,6 +2044,7 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_select_pinned);
WM_operatortype_append(UV_OT_select_box);
WM_operatortype_append(UV_OT_select_lasso);
+ WM_operatortype_append(UV_OT_select_similar);
WM_operatortype_append(UV_OT_select_circle);
WM_operatortype_append(UV_OT_select_more);
WM_operatortype_append(UV_OT_select_less);
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 13dac431b57..8dcf2ceb679 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -12,6 +12,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_image_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@@ -22,6 +23,7 @@
#include "BLI_blenlib.h"
#include "BLI_hash.h"
#include "BLI_kdopbvh.h"
+#include "BLI_kdtree.h"
#include "BLI_lasso_2d.h"
#include "BLI_math.h"
#include "BLI_polyfill_2d.h"
@@ -31,6 +33,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_report.h"
@@ -75,6 +78,16 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
const ToolSettings *ts,
Object *obedit);
+typedef enum {
+ UV_SSIM_AREA_UV = 1000,
+ UV_SSIM_AREA_3D,
+ UV_SSIM_LENGTH_UV,
+ UV_SSIM_LENGTH_3D,
+ UV_SSIM_SIDES,
+ UV_SSIM_PIN,
+ UV_SSIM_MATERIAL,
+} eUVSelectSimilar;
+
/* -------------------------------------------------------------------- */
/** \name Active Selection Tracking
*
@@ -586,12 +599,25 @@ bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_lo
if (ts->selectmode & SCE_SELECT_FACE) {
return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT);
}
+ if (ts->selectmode & SCE_SELECT_EDGE) {
+ /* Are you looking for `uvedit_edge_select_test(...)` instead? */
+ }
return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
}
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+ if (ts->selectmode & SCE_SELECT_FACE) {
+ /* Are you looking for `uvedit_face_select_test(...)` instead? */
+ }
+
+ if (ts->selectmode & SCE_SELECT_EDGE) {
+ /* Are you looking for `uvedit_edge_select_test(...)` instead? */
+ }
+
return (luv->flag & MLOOPUV_VERTSEL) != 0;
}
+
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
{
return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
@@ -653,6 +679,9 @@ void uvedit_uv_select_shared_vert(const Scene *scene,
e_first = e_iter = l->e;
do {
BMLoop *l_radial_iter = e_iter->l;
+ if (!l_radial_iter) {
+ continue; /* Skip wire edges with no loops. */
+ }
do {
if (l_radial_iter->v == l->v) {
if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
@@ -696,6 +725,10 @@ void uvedit_uv_select_enable(const Scene *scene,
{
const ToolSettings *ts = scene->toolsettings;
+ if (ts->selectmode & SCE_SELECT_EDGE) {
+ /* Are you looking for `uvedit_edge_select_set(...)` instead? */
+ }
+
if (ts->uv_flag & UV_SYNC_SELECTION) {
if (ts->selectmode & SCE_SELECT_FACE) {
BM_face_select_set(em->bm, l->f, true);
@@ -2422,7 +2455,7 @@ static bool uv_mouse_select_multi(bContext *C,
UvNearestHit hit = UV_NEAREST_HIT_INIT_DIST_PX(&region->v2d, 75.0f);
int selectmode, sticky;
bool found_item = false;
- /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */
+ /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
int flush = 0;
/* Penalty (in pixels) applied to elements that are already selected
@@ -2511,8 +2544,15 @@ static bool uv_mouse_select_multi(bContext *C,
else if (selectmode == UV_SELECT_EDGE) {
is_selected = uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset);
}
- else { /* Vertex or island. */
- is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
+ else {
+ /* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above),
+ * `hit.l` is NULL, use `hit.efa` instead. */
+ if (hit.l != NULL) {
+ is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
+ }
+ else {
+ is_selected = uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset);
+ }
}
}
@@ -2645,7 +2685,7 @@ static int uv_select_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "location", co);
struct SelectPick_Params params = {0};
- ED_select_pick_params_from_operator(op, &params);
+ ED_select_pick_params_from_operator(op->ptr, &params);
const bool changed = uv_mouse_select(C, co, &params);
@@ -2680,6 +2720,7 @@ void UV_OT_select(wmOperatorType *ot)
ot->exec = uv_select_exec;
ot->invoke = uv_select_invoke;
ot->poll = ED_operator_uvedit; /* requires space image */
+ ot->get_name = ED_select_pick_get_name;
/* properties */
PropertyRNA *prop;
@@ -2724,7 +2765,7 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
const ToolSettings *ts = scene->toolsettings;
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
bool found_item = false;
- /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */
+ /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
int flush = 0;
/* Find edge. */
@@ -3278,8 +3319,6 @@ static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, co
BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
- /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
-
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (select) {
@@ -3349,8 +3388,6 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co
/* now select tagged verts */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
-
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
@@ -3369,8 +3406,6 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co
}
BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
- /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
-
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
uv_select_flush_from_tag_sticky_loc_internal(
@@ -3849,6 +3884,7 @@ void UV_OT_select_circle(wmOperatorType *ot)
ot->exec = uv_circle_select_exec;
ot->poll = ED_operator_uvedit_space_image; /* requires space image */
ot->cancel = WM_gesture_circle_cancel;
+ ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
@@ -4406,6 +4442,550 @@ void UV_OT_select_overlap(wmOperatorType *ot)
}
/** \} */
+/** \name Select Similar Operator
+ * \{ */
+
+static float get_uv_vert_needle(const eUVSelectSimilar type,
+ BMVert *vert,
+ const float ob_m3[3][3],
+ MLoopUV *luv,
+ const int cd_loop_uv_offset)
+{
+ float result = 0.0f;
+ switch (type) {
+ case UV_SSIM_AREA_UV: {
+ BMFace *f;
+ BMIter iter;
+ BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
+ result += BM_face_calc_area_uv(f, cd_loop_uv_offset);
+ }
+ } break;
+ case UV_SSIM_AREA_3D: {
+ BMFace *f;
+ BMIter iter;
+ BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
+ result += BM_face_calc_area_with_mat3(f, ob_m3);
+ }
+ } break;
+ case UV_SSIM_SIDES: {
+ BMEdge *e;
+ BMIter iter;
+ BM_ITER_ELEM (e, &iter, vert, BM_EDGES_OF_VERT) {
+ result += 1.0f;
+ }
+ } break;
+ case UV_SSIM_PIN:
+ return (luv->flag & MLOOPUV_PINNED) ? 1.0f : 0.0f;
+ default:
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ return result;
+}
+
+static float get_uv_edge_needle(const eUVSelectSimilar type,
+ BMEdge *edge,
+ const float ob_m3[3][3],
+ MLoopUV *luv_a,
+ MLoopUV *luv_b,
+ const int cd_loop_uv_offset)
+{
+ float result = 0.0f;
+ switch (type) {
+ case UV_SSIM_AREA_UV: {
+ BMFace *f;
+ BMIter iter;
+ BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
+ result += BM_face_calc_area_uv(f, cd_loop_uv_offset);
+ }
+ } break;
+ case UV_SSIM_AREA_3D: {
+ BMFace *f;
+ BMIter iter;
+ BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
+ result += BM_face_calc_area_with_mat3(f, ob_m3);
+ }
+ } break;
+ case UV_SSIM_LENGTH_UV:
+ return len_v2v2(luv_a->uv, luv_b->uv);
+ case UV_SSIM_LENGTH_3D:
+ return len_v3v3(edge->v1->co, edge->v2->co);
+ case UV_SSIM_SIDES: {
+ BMEdge *e;
+ BMIter iter;
+ BM_ITER_ELEM (e, &iter, edge, BM_FACES_OF_EDGE) {
+ result += 1.0f;
+ }
+ } break;
+ case UV_SSIM_PIN:
+ if (luv_a->flag & MLOOPUV_PINNED) {
+ result += 1.0f;
+ }
+ if (luv_b->flag & MLOOPUV_PINNED) {
+ result += 1.0f;
+ }
+ break;
+ default:
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ return result;
+}
+
+static float get_uv_face_needle(const eUVSelectSimilar type,
+ BMFace *face,
+ const float ob_m3[3][3],
+ const int cd_loop_uv_offset)
+{
+ float result = 0.0f;
+ switch (type) {
+ case UV_SSIM_AREA_UV:
+ return BM_face_calc_area_uv(face, cd_loop_uv_offset);
+ case UV_SSIM_AREA_3D:
+ return BM_face_calc_area_with_mat3(face, ob_m3);
+ case UV_SSIM_SIDES:
+ return face->len;
+ case UV_SSIM_PIN: {
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_PINNED) {
+ result += 1.0f;
+ }
+ }
+ } break;
+ case UV_SSIM_MATERIAL:
+ return face->mat_nr;
+ default:
+ BLI_assert_unreachable();
+ return false;
+ }
+ return result;
+}
+
+static int uv_select_similar_vert_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type");
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare");
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ int max_verts_selected_all = 0;
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ max_verts_selected_all += face->len;
+ }
+ /* TODO: Get a tighter bounds */
+ }
+
+ int tree_index = 0;
+ KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_verts_selected_all);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+ if (bm->totvertsel == 0) {
+ continue;
+ }
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
+ if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ float needle = get_uv_vert_needle(type, l->v, ob_m3, luv, cd_loop_uv_offset);
+ BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
+ }
+ }
+ }
+
+ if (tree_1d != NULL) {
+ BLI_kdtree_1d_deduplicate(tree_1d);
+ BLI_kdtree_1d_balance(tree_1d);
+ }
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+ if (bm->totvertsel == 0) {
+ continue;
+ }
+
+ bool changed = false;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
+ if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue; /* Already selected. */
+ }
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ const float needle = get_uv_vert_needle(type, l->v, ob_m3, luv, cd_loop_uv_offset);
+ bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
+ if (select) {
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ }
+ if (changed) {
+ uv_select_tag_update_for_object(depsgraph, ts, ob);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(objects);
+ BLI_kdtree_1d_free(tree_1d);
+ return OPERATOR_FINISHED;
+}
+
+static int uv_select_similar_edge_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type");
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare");
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ int max_edges_selected_all = 0;
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ max_edges_selected_all += face->len;
+ }
+ /* TODO: Get a tighter bounds. */
+ }
+
+ int tree_index = 0;
+ KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_edges_selected_all);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+ if (bm->totvertsel == 0) {
+ continue;
+ }
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
+ if (!uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ float needle = get_uv_edge_needle(type, l->e, ob_m3, luv_a, luv_b, cd_loop_uv_offset);
+ if (tree_1d) {
+ BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
+ }
+ }
+ }
+ }
+
+ if (tree_1d != NULL) {
+ BLI_kdtree_1d_deduplicate(tree_1d);
+ BLI_kdtree_1d_balance(tree_1d);
+ }
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+ if (bm->totvertsel == 0) {
+ continue;
+ }
+
+ bool changed = false;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
+ if (uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ continue; /* Already selected. */
+ }
+
+ MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ float needle = get_uv_edge_needle(type, l->e, ob_m3, luv_a, luv_b, cd_loop_uv_offset);
+ bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
+ if (select) {
+ uvedit_edge_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ }
+ if (changed) {
+ uv_select_tag_update_for_object(depsgraph, ts, ob);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(objects);
+ BLI_kdtree_1d_free(tree_1d);
+ return OPERATOR_FINISHED;
+}
+
+static int uv_select_similar_face_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type");
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare");
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ int max_faces_selected_all = 0;
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ max_faces_selected_all += em->bm->totfacesel;
+ /* TODO: Get a tighter bounds */
+ }
+
+ int tree_index = 0;
+ KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_faces_selected_all);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ if (!uvedit_face_select_test(scene, face, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ float needle = get_uv_face_needle(type, face, ob_m3, cd_loop_uv_offset);
+ if (tree_1d) {
+ BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
+ }
+ }
+ }
+
+ if (tree_1d != NULL) {
+ BLI_kdtree_1d_deduplicate(tree_1d);
+ BLI_kdtree_1d_balance(tree_1d);
+ }
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *ob = objects[ob_index];
+
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BMesh *bm = em->bm;
+ bool changed = false;
+ bool do_history = false;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, ob->obmat);
+
+ BMFace *face;
+ BMIter iter;
+ BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, face)) {
+ continue;
+ }
+ if (uvedit_face_select_test(scene, face, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ float needle = get_uv_face_needle(type, face, ob_m3, cd_loop_uv_offset);
+
+ bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
+ if (select) {
+ uvedit_face_select_set(scene, em, face, select, do_history, cd_loop_uv_offset);
+ changed = true;
+ }
+ }
+ if (changed) {
+ uv_select_tag_update_for_object(depsgraph, ts, ob);
+ }
+ }
+
+ MEM_SAFE_FREE(objects);
+ BLI_kdtree_1d_free(tree_1d);
+ return OPERATOR_FINISHED;
+}
+
+/* Select similar UV faces/edges/verts based on current selection. */
+static int uv_select_similar_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
+
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_float_set(op->ptr, prop, ts->select_thresh);
+ }
+ else {
+ ts->select_thresh = RNA_property_float_get(op->ptr, prop);
+ }
+
+ int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
+ if (selectmode & UV_SELECT_EDGE) {
+ return uv_select_similar_edge_exec(C, op);
+ }
+ else if (selectmode & UV_SELECT_FACE) {
+ return uv_select_similar_face_exec(C, op);
+ }
+ if (selectmode & UV_SELECT_ISLAND) {
+ // return uv_select_similar_island_exec(C, op);
+ }
+
+ return uv_select_similar_vert_exec(C, op);
+}
+
+static EnumPropertyItem prop_vert_similar_types[] = {{UV_SSIM_PIN, "PIN", 0, "Pinned", ""}, {0}};
+
+static EnumPropertyItem prop_edge_similar_types[] = {
+ {UV_SSIM_LENGTH_UV, "LENGTH", 0, "Length", ""},
+ {UV_SSIM_LENGTH_3D, "LENGTH_3D", 0, "Length 3D", ""},
+ {UV_SSIM_PIN, "PIN", 0, "Pinned", ""},
+ {0}};
+
+static EnumPropertyItem prop_face_similar_types[] = {
+ {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""},
+ {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""},
+ {UV_SSIM_SIDES, "SIDES", 0, "Polygon Sides", ""},
+ {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {0}};
+
+static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
+ {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
+ {SIM_CMP_LT, "LESS", 0, "Less", ""},
+ {0}};
+
+static const EnumPropertyItem *uv_select_similar_type_itemf(bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *UNUSED(r_free))
+{
+ const ToolSettings *ts = CTX_data_tool_settings(C);
+ if (ts) {
+ int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
+ if (selectmode & UV_SELECT_EDGE) {
+ return prop_edge_similar_types;
+ }
+ if (selectmode & UV_SELECT_FACE) {
+ return prop_face_similar_types;
+ }
+ }
+
+ return prop_vert_similar_types;
+}
+void UV_OT_select_similar(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Similar";
+ ot->description = "Select similar UVs by property types";
+ ot->idname = "UV_OT_select_similar";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = uv_select_similar_exec;
+ ot->poll = ED_operator_uvedit_space_image;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop = ot->prop = RNA_def_enum(
+ ot->srna, "type", prop_vert_similar_types, SIMVERT_NORMAL, "Type", "");
+ RNA_def_enum_funcs(prop, uv_select_similar_type_itemf);
+ RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
+ RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+}
+
+/** \} */
/* -------------------------------------------------------------------- */
/** \name Selected Elements as Arrays (Vertex, Edge & Faces)
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index bacf321fce1..55e44607f6f 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -1716,7 +1716,7 @@ static void stitch_draw_vbo(GPUVertBuf *vbo, GPUPrimType prim_type, const float
GPU_batch_discard(batch);
}
-/* TODO: make things pretier : store batches inside StitchPreviewer instead of the bare verts pos
+/* TODO: make things prettier : store batches inside StitchPreviewer instead of the bare verts pos
*/
static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
{
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 34fae2ffb2a..3618286ec01 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -103,7 +103,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit)
int cd_loop_uv_offset;
if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
- ED_mesh_uv_texture_add(obedit->data, NULL, true, true, NULL);
+ ED_mesh_uv_add(obedit->data, NULL, true, true, NULL);
}
/* Happens when there are no faces. */
@@ -267,60 +267,104 @@ static bool uvedit_have_selection_multi(const Scene *scene,
return have_select;
}
+void ED_uvedit_get_aspect_from_material(Object *ob,
+ const int material_index,
+ float *r_aspx,
+ float *r_aspy)
+{
+ if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) {
+ *r_aspx = 1.0f;
+ *r_aspy = 1.0f;
+ return;
+ }
+ Image *ima;
+ ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL);
+ ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
+}
+
void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
{
BMEditMesh *em = BKE_editmesh_from_object(ob);
BLI_assert(em != NULL);
bool sloppy = true;
bool selected = false;
- BMFace *efa;
- Image *ima;
+ BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
+ if (!efa) {
+ *r_aspx = 1.0f;
+ *r_aspy = 1.0f;
+ return;
+ }
- efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
+ ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy);
+}
- if (efa) {
- ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
+static bool uvedit_is_face_affected(const Scene *scene,
+ BMFace *efa,
+ const UnwrapOptions *options,
+ const int cd_loop_uv_offset)
+{
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ return false;
+ }
- ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
+ if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ return false;
}
- else {
- *r_aspx = 1.0f;
- *r_aspy = 1.0f;
+
+ if (options->topology_from_uvs && options->only_selected_uvs &&
+ !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert.
+ */
+static void uvedit_prepare_pinned_indices(ParamHandle *handle,
+ BMFace *efa,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_PINNED) {
+ int bmvertindex = BM_elem_index_get(l->v);
+ GEO_uv_prepare_pin_index(handle, bmvertindex, luv->uv);
+ }
}
}
static void construct_param_handle_face_add(ParamHandle *handle,
const Scene *scene,
BMFace *efa,
- int face_index,
+ ParamKey face_index,
const int cd_loop_uv_offset)
{
- ParamKey key;
ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len);
- ParamBool *pin = BLI_array_alloca(pin, efa->len);
- ParamBool *select = BLI_array_alloca(select, efa->len);
- float **co = BLI_array_alloca(co, efa->len);
+ bool *pin = BLI_array_alloca(pin, efa->len);
+ bool *select = BLI_array_alloca(select, efa->len);
+ const float **co = BLI_array_alloca(co, efa->len);
float **uv = BLI_array_alloca(uv, efa->len);
int i;
BMIter liter;
BMLoop *l;
- key = (ParamKey)face_index;
-
/* let parametrizer split the ngon, it can make better decisions
* about which split is best for unwrapping than poly-fill. */
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- vkeys[i] = (ParamKey)BM_elem_index_get(l->v);
+ vkeys[i] = GEO_uv_find_pin_index(handle, BM_elem_index_get(l->v), luv->uv);
co[i] = l->v->co;
uv[i] = luv->uv;
pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
}
- GEO_uv_parametrizer_face_add(handle, key, i, vkeys, co, uv, pin, select);
+ GEO_uv_parametrizer_face_add(handle, face_index, i, vkeys, co, uv, pin, select);
}
/* See: construct_param_handle_multi to handle multiple objects at once. */
@@ -330,16 +374,12 @@ static ParamHandle *construct_param_handle(const Scene *scene,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
{
- ParamHandle *handle;
BMFace *efa;
- BMLoop *l;
BMEdge *eed;
- BMIter iter, liter;
+ BMIter iter;
int i;
- const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
-
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
float aspx, aspy;
@@ -354,30 +394,17 @@ static ParamHandle *construct_param_handle(const Scene *scene,
/* we need the vert indices */
BM_mesh_elem_index_ensure(bm, BM_VERT);
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
-
- if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
- (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
- continue;
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset);
}
+ }
- if (options->topology_from_uvs) {
- bool is_loopsel = false;
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (options->only_selected_uvs &&
- (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
- continue;
- }
- is_loopsel = true;
- break;
- }
- if (is_loopsel == false) {
- continue;
- }
+ BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
}
-
- construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
}
if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
@@ -408,14 +435,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
const UnwrapOptions *options,
int *count_fail)
{
- ParamHandle *handle;
BMFace *efa;
- BMLoop *l;
BMEdge *eed;
- BMIter iter, liter;
+ BMIter iter;
int i;
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
Object *ob = objects[0];
@@ -444,29 +469,15 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
}
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
-
- if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
- (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
- continue;
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset);
}
+ }
- if (options->topology_from_uvs) {
- bool is_loopsel = false;
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (options->only_selected_uvs &&
- (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
- continue;
- }
- is_loopsel = true;
- break;
- }
- if (is_loopsel == false) {
- continue;
- }
+ BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
}
-
- construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
}
if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
@@ -493,8 +504,8 @@ static void texface_from_original_index(const Scene *scene,
BMFace *efa,
int index,
float **r_uv,
- ParamBool *r_pin,
- ParamBool *r_select)
+ bool *r_pin,
+ bool *r_select)
{
BMLoop *l;
BMIter liter;
@@ -530,7 +541,6 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
{
- ParamHandle *handle;
/* index pointers */
MPoly *mpoly;
MLoop *mloop;
@@ -562,7 +572,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
float aspx, aspy;
@@ -627,8 +637,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
/* Prepare and feed faces to the solver */
for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) {
ParamKey key, vkeys[4];
- ParamBool pin[4], select[4];
- float *co[4];
+ bool pin[4], select[4];
+ const float *co[4];
float *uv[4];
BMFace *origFace = faceMap[i];
@@ -646,7 +656,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
mloop = &subsurfedLoops[mpoly->loopstart];
- /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
+ /* We will not check for v4 here. Sub-surface faces always have 4 vertices. */
BLI_assert(mpoly->totloop == 4);
key = (ParamKey)i;
vkeys[0] = (ParamKey)mloop[0].v;
@@ -1014,8 +1024,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
bool rotate = true;
bool ignore_pinned = false;
- ParamHandle *handle;
- handle = construct_param_handle(scene, ob, bm, &options, NULL);
+ ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL);
GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1034,8 +1043,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
bool rotate,
bool ignore_pinned)
{
- ParamHandle *handle;
- handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
+ ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1241,7 +1249,7 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
handle = construct_param_handle(scene, obedit, em->bm, &options, NULL);
}
- GEO_uv_parametrizer_lscm_begin(handle, PARAM_TRUE, abf);
+ GEO_uv_parametrizer_lscm_begin(handle, true, abf);
/* Create or increase size of g_live_unwrap.handles array */
if (g_live_unwrap.handles == NULL) {
@@ -1527,49 +1535,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
}
}
-static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+static void shrink_loop_uv_by_aspect_ratio(BMFace *efa,
+ const int cd_loop_uv_offset,
+ const float aspect_y)
{
+ BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */
+ BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */
+
BMLoop *l;
- BMIter iter, liter;
- MLoopUV *luv;
- BMFace *efa;
- float scale, aspx, aspy;
+ BMIter iter;
+ BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (aspect_y > 1.0f) {
+ /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */
+ luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y);
+ }
+ else {
+ /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */
+ luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y);
+ }
+ }
+}
+static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+{
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
-
+ float aspx, aspy;
ED_uvedit_get_aspect(ob, &aspx, &aspy);
+ const float aspect_y = aspx / aspy;
+ if (aspect_y == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ return;
+ }
+ BMFace *efa;
+ BMIter iter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
+ }
+ }
+}
- if (aspx == aspy) {
+static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em)
+{
+ const int materials_num = ob->totcol;
+ if (materials_num == 0) {
+ /* Without any materials, there is no aspect_y information and nothing to do. */
return;
}
- if (aspx > aspy) {
- scale = aspy / aspx;
+ float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num);
+ /* Lazily initialize aspect ratio for materials. */
+ copy_vn_fl(material_aspect_y, materials_num, -1.0f);
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- continue;
- }
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
- }
+ BMFace *efa;
+ BMIter iter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
}
- }
- else {
- scale = aspx / aspy;
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- continue;
- }
+ const int material_index = efa->mat_nr;
+ if (UNLIKELY(material_index < 0 || material_index >= materials_num)) {
+ /* The index might be for a material slot which is not currently setup. */
+ continue;
+ }
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
- }
+ float aspect_y = material_aspect_y[material_index];
+ if (aspect_y == -1.0f) {
+ /* Lazily initialize aspect ratio for materials. */
+ float aspx, aspy;
+ ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ material_aspect_y[material_index] = aspect_y;
+ }
+
+ if (aspect_y == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ continue;
}
+ shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
}
}
@@ -1613,7 +1660,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
uv_map_clip_correct_properties_ex(ot, true);
}
-static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
+/**
+ * \param per_face_aspect: Calculate the aspect ratio per-face,
+ * otherwise use a single aspect for all UV's based on the material of the active face.
+ * TODO: using per-face aspect may split UV islands so more advanced UV projection methods
+ * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves.
+ * For now keep using a single aspect for all faces in this case.
+ */
+static void uv_map_clip_correct_multi(Object **objects,
+ uint objects_len,
+ wmOperator *op,
+ bool per_face_aspect)
{
BMFace *efa;
BMLoop *l;
@@ -1633,9 +1690,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
BMEditMesh *em = BKE_editmesh_from_object(ob);
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- /* correct for image aspect ratio */
+ /* Correct for image aspect ratio. */
if (correct_aspect) {
- correct_uv_aspect(ob, em);
+ if (per_face_aspect) {
+ correct_uv_aspect_per_face(ob, em);
+ }
+ else {
+ correct_uv_aspect(ob, em);
+ }
}
if (scale_to_bounds) {
@@ -1678,6 +1740,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
dy = 1.0f / dy;
}
+ if (dx == 1.0f && dy == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ return;
+ }
+
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1702,7 +1769,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
static void uv_map_clip_correct(Object *ob, wmOperator *op)
{
- uv_map_clip_correct_multi(&ob, 1, op);
+ uv_map_clip_correct_multi(&ob, 1, op, true);
}
/** \} */
@@ -1733,7 +1800,7 @@ static void uvedit_unwrap(const Scene *scene,
handle = construct_param_handle(scene, obedit, em->bm, options, result_info);
}
- GEO_uv_parametrizer_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
+ GEO_uv_parametrizer_lscm_begin(handle, false, scene->toolsettings->unwrapper == 0);
GEO_uv_parametrizer_lscm_solve(handle,
result_info ? &result_info->count_changed : NULL,
result_info ? &result_info->count_failed : NULL);
@@ -2283,7 +2350,9 @@ static int smart_project_exec(bContext *C, wmOperator *op)
.use_seams = true,
});
- uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
+ /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
+ const bool per_face_aspect = false;
+ uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect);
}
MEM_freeN(objects_changed);
@@ -2485,7 +2554,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
}
if (changed_multi) {
- uv_map_clip_correct_multi(objects, objects_len, op);
+ uv_map_clip_correct_multi(objects, objects_len, op, true);
}
MEM_freeN(objects);
@@ -2968,7 +3037,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
* since we are not in edit mode we need to ensure only the uv flags are tested */
scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION;
- ED_mesh_uv_texture_ensure(me, NULL);
+ ED_mesh_uv_ensure(me, NULL);
BM_mesh_bm_from_me(bm,
me,