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.c14
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c2
-rw-r--r--source/blender/editors/animation/anim_deps.c2
-rw-r--r--source/blender/editors/animation/anim_draw.c24
-rw-r--r--source/blender/editors/animation/anim_filter.c13
-rw-r--r--source/blender/editors/animation/anim_markers.c15
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c8
-rw-r--r--source/blender/editors/animation/anim_ops.c14
-rw-r--r--source/blender/editors/animation/drivers.c2
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c2
-rw-r--r--source/blender/editors/animation/keyframes_edit.c6
-rw-r--r--source/blender/editors/animation/keyframes_general.c109
-rw-r--r--source/blender/editors/animation/keyframing.c33
-rw-r--r--source/blender/editors/armature/armature_add.c6
-rw-r--r--source/blender/editors/armature/armature_naming.c12
-rw-r--r--source/blender/editors/armature/pose_lib.c12
-rw-r--r--source/blender/editors/armature/pose_lib_2.c2
-rw-r--r--source/blender/editors/armature/pose_slide.c4
-rw-r--r--source/blender/editors/armature/pose_transform.c6
-rw-r--r--source/blender/editors/curve/editcurve.c5
-rw-r--r--source/blender/editors/curve/editcurve_add.c2
-rw-r--r--source/blender/editors/curve/editcurve_paint.c2
-rw-r--r--source/blender/editors/curve/editcurve_pen.c2
-rw-r--r--source/blender/editors/curve/editfont.c66
-rw-r--r--source/blender/editors/curves/CMakeLists.txt13
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc84
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc496
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc30
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c3
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c2
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c8
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c21
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c47
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.cc8
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c63
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.cc4
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h6
-rw-r--r--source/blender/editors/include/ED_curves.h13
-rw-r--r--source/blender/editors/include/ED_curves_sculpt.h25
-rw-r--r--source/blender/editors/include/ED_gpencil.h7
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h23
-rw-r--r--source/blender/editors/include/ED_mesh.h4
-rw-r--r--source/blender/editors/include/ED_paint.h7
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h43
-rw-r--r--source/blender/editors/include/UI_abstract_view.hh280
-rw-r--r--source/blender/editors/include/UI_grid_view.hh55
-rw-r--r--source/blender/editors/include/UI_icons.h2
-rw-r--r--source/blender/editors/include/UI_interface.h84
-rw-r--r--source/blender/editors/include/UI_interface.hh6
-rw-r--r--source/blender/editors/include/UI_tree_view.hh181
-rw-r--r--source/blender/editors/include/UI_view2d.h6
-rw-r--r--source/blender/editors/interface/CMakeLists.txt25
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_color.c (renamed from source/blender/editors/interface/interface_eyedropper_color.c)4
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_colorband.c (renamed from source/blender/editors/interface/interface_eyedropper_colorband.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_datablock.c (renamed from source/blender/editors/interface/interface_eyedropper_datablock.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_depth.c (renamed from source/blender/editors/interface/interface_eyedropper_depth.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_driver.c (renamed from source/blender/editors/interface/interface_eyedropper_driver.c)6
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c (renamed from source/blender/editors/interface/interface_eyedropper_gpencil_color.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_intern.h (renamed from source/blender/editors/interface/interface_eyedropper_intern.h)4
-rw-r--r--source/blender/editors/interface/eyedroppers/interface_eyedropper.c (renamed from source/blender/editors/interface/interface_eyedropper.c)2
-rw-r--r--source/blender/editors/interface/interface.cc76
-rw-r--r--source/blender/editors/interface/interface_context_menu.c10
-rw-r--r--source/blender/editors/interface/interface_drag.cc4
-rw-r--r--source/blender/editors/interface/interface_dropboxes.cc32
-rw-r--r--source/blender/editors/interface/interface_handlers.c137
-rw-r--r--source/blender/editors/interface/interface_intern.h32
-rw-r--r--source/blender/editors/interface/interface_ops.c64
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/interface_query.cc32
-rw-r--r--source/blender/editors/interface/interface_region_menu_pie.cc2
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c2
-rw-r--r--source/blender/editors/interface/interface_template_attribute_search.cc10
-rw-r--r--source/blender/editors/interface/interface_templates.c59
-rw-r--r--source/blender/editors/interface/interface_view.cc229
-rw-r--r--source/blender/editors/interface/interface_widgets.c57
-rw-r--r--source/blender/editors/interface/view2d.cc35
-rw-r--r--source/blender/editors/interface/views/abstract_view.cc109
-rw-r--r--source/blender/editors/interface/views/abstract_view_item.cc373
-rw-r--r--source/blender/editors/interface/views/grid_view.cc (renamed from source/blender/editors/interface/grid_view.cc)92
-rw-r--r--source/blender/editors/interface/views/interface_view.cc196
-rw-r--r--source/blender/editors/interface/views/tree_view.cc (renamed from source/blender/editors/interface/tree_view.cc)412
-rw-r--r--source/blender/editors/io/io_alembic.c8
-rw-r--r--source/blender/editors/io/io_gpencil_export.c12
-rw-r--r--source/blender/editors/io/io_gpencil_import.c6
-rw-r--r--source/blender/editors/io/io_obj.c8
-rw-r--r--source/blender/editors/mask/mask_add.c8
-rw-r--r--source/blender/editors/mask/mask_draw.c2
-rw-r--r--source/blender/editors/mask/mask_editaction.c2
-rw-r--r--source/blender/editors/mask/mask_ops.c8
-rw-r--r--source/blender/editors/mask/mask_query.c3
-rw-r--r--source/blender/editors/mask/mask_select.c2
-rw-r--r--source/blender/editors/mask/mask_shapekey.c8
-rw-r--r--source/blender/editors/mesh/editface.cc51
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c2
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c2
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c231
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_add.cc183
-rw-r--r--source/blender/editors/object/object_bake_api.c2
-rw-r--r--source/blender/editors/object/object_edit.c8
-rw-r--r--source/blender/editors/object/object_modifier.cc27
-rw-r--r--source/blender/editors/object/object_relations.c19
-rw-r--r--source/blender/editors/object/object_shapekey.c43
-rw-r--r--source/blender/editors/object/object_transform.cc2
-rw-r--r--source/blender/editors/object/object_vgroup.c8
-rw-r--r--source/blender/editors/physics/particle_edit.c7
-rw-r--r--source/blender/editors/physics/particle_object.c1
-rw-r--r--source/blender/editors/physics/physics_fluid.c8
-rw-r--r--source/blender/editors/render/render_opengl.cc18
-rw-r--r--source/blender/editors/render/render_preview.cc2
-rw-r--r--source/blender/editors/render/render_view.cc8
-rw-r--r--source/blender/editors/screen/screen_context.c66
-rw-r--r--source/blender/editors/screen/screen_ops.c30
-rw-r--r--source/blender/editors/screen/screen_user_menu.c9
-rw-r--r--source/blender/editors/screen/workspace_edit.c107
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt7
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_add.cc279
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_brush.cc66
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_comb.cc109
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_delete.cc43
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_density.cc912
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc91
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh39
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc983
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc325
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_puff.cc382
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection.cc51
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc30
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_slide.cc480
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc261
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc50
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.cc11
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h9
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c50
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c61
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.cc4
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc48
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c53
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_image.cc2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c52
-rw-r--r--source/blender/editors/sound/sound_ops.c15
-rw-r--r--source/blender/editors/space_action/action_edit.c392
-rw-r--r--source/blender/editors/space_action/action_select.c234
-rw-r--r--source/blender/editors/space_action/space_action.c12
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c6
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c3
-rw-r--r--source/blender/editors/space_clip/clip_draw.c22
-rw-r--r--source/blender/editors/space_clip/clip_editor.c6
-rw-r--r--source/blender/editors/space_clip/clip_graph_ops.c8
-rw-r--r--source/blender/editors/space_clip/clip_intern.h3
-rw-r--r--source/blender/editors/space_clip/clip_ops.c10
-rw-r--r--source/blender/editors/space_clip/clip_utils.c12
-rw-r--r--source/blender/editors/space_clip/space_clip.c3
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c154
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c4
-rw-r--r--source/blender/editors/space_console/console_ops.c12
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc58
-rw-r--r--source/blender/editors/space_file/file_ops.c2
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c2
-rw-r--r--source/blender/editors/space_graph/graph_draw.c5
-rw-r--r--source/blender/editors/space_graph/graph_edit.c151
-rw-r--r--source/blender/editors/space_graph/graph_ops.c23
-rw-r--r--source/blender/editors/space_graph/graph_select.c40
-rw-r--r--source/blender/editors/space_graph/graph_slider_ops.c4
-rw-r--r--source/blender/editors/space_graph/graph_utils.c11
-rw-r--r--source/blender/editors/space_graph/graph_view.c7
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_image/image_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_draw.c3
-rw-r--r--source/blender/editors/space_image/image_edit.c9
-rw-r--r--source/blender/editors/space_image/image_ops.c88
-rw-r--r--source/blender/editors/space_image/image_undo.cc (renamed from source/blender/editors/space_image/image_undo.c)316
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c6
-rw-r--r--source/blender/editors/space_nla/nla_channels.c12
-rw-r--r--source/blender/editors/space_nla/nla_draw.c8
-rw-r--r--source/blender/editors/space_nla/nla_edit.c94
-rw-r--r--source/blender/editors/space_nla/nla_ops.c24
-rw-r--r--source/blender/editors/space_nla/nla_select.c16
-rw-r--r--source/blender/editors/space_nla/space_nla.c6
-rw-r--r--source/blender/editors/space_node/drawnode.cc2
-rw-r--r--source/blender/editors/space_node/node_add.cc6
-rw-r--r--source/blender/editors/space_node/node_draw.cc18
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc8
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.cc84
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc4
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc67
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh14
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc440
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc264
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_drag_drop.c3
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c525
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c18
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c12
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c12
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh6
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc218
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc4
-rw-r--r--source/blender/editors/space_text/text_format_lua.c13
-rw-r--r--source/blender/editors/space_text/text_format_osl.c11
-rw-r--r--source/blender/editors/space_text/text_format_pov.c11
-rw-r--r--source/blender/editors/space_text/text_format_pov_ini.c11
-rw-r--r--source/blender/editors/space_text/text_format_py.c13
-rw-r--r--source/blender/editors/space_text/text_ops.c17
-rw-r--r--source/blender/editors/space_topbar/space_topbar.c3
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c7
-rw-r--r--source/blender/editors/transform/CMakeLists.txt1
-rw-r--r--source/blender/editors/transform/transform.c9
-rw-r--r--source/blender/editors/transform/transform.h37
-rw-r--r--source/blender/editors/transform/transform_constraints.c2
-rw-r--r--source/blender/editors/transform/transform_convert.c730
-rw-r--r--source/blender/editors/transform/transform_convert.h137
-rw-r--r--source/blender/editors/transform/transform_convert_action.c126
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c30
-rw-r--r--source/blender/editors/transform/transform_convert_cursor.c33
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c13
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c13
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c47
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c13
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c17
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c13
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c41
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c15
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_skin.c11
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c15
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_vert_cdata.c296
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c32
-rw-r--r--source/blender/editors/transform/transform_convert_node.c13
-rw-r--r--source/blender/editors/transform/transform_convert_object.c19
-rw-r--r--source/blender/editors/transform/transform_convert_object_texspace.c13
-rw-r--r--source/blender/editors/transform/transform_convert_paintcurve.c11
-rw-r--r--source/blender/editors/transform/transform_convert_particle.c13
-rw-r--r--source/blender/editors/transform/transform_convert_sculpt.c13
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c17
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c23
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c13
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/editors/transform/transform_mode.c27
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c10
-rw-r--r--source/blender/editors/transform/transform_mode_edge_bevelweight.c11
-rw-r--r--source/blender/editors/transform/transform_mode_edge_crease.c11
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c4
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c2
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c102
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c2
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_timescale.c4
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c53
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c4
-rw-r--r--source/blender/editors/transform/transform_ops.c60
-rw-r--r--source/blender/editors/transform/transform_snap.c385
-rw-r--r--source/blender/editors/transform/transform_snap.h7
-rw-r--r--source/blender/editors/transform/transform_snap_object.cc517
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c7
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c2
-rw-r--r--source/blender/editors/util/numinput.c13
-rw-r--r--source/blender/editors/uvedit/uvedit_islands.c10
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c391
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c299
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c71
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c10
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c17
298 files changed, 11151 insertions, 5581 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 9eeabf2d05e..729e8533d50 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -3123,7 +3123,7 @@ static bAnimChannelType ACF_DSSIMULATION = {
/* TODO: just get this from RNA? */
static int acf_dsgpencil_icon(bAnimListElem *UNUSED(ale))
{
- return ICON_GREASEPENCIL;
+ return ICON_OUTLINER_DATA_GREASEPENCIL;
}
/* Get the appropriate flag(s) for the setting when it is valid. */
@@ -4747,13 +4747,13 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
RNA_id_pointer_create(id, &id_ptr);
/* Get NLA context for value remapping */
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
- (float)CFRA);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (float)scene->r.cfra);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
&nla_cache, &id_ptr, adt, &anim_eval_context);
/* get current frame and apply NLA-mapping to it (if applicable) */
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
/* Get flags for keyframing. */
flag = ANIM_get_keyframing_flags(scene, true);
@@ -4803,8 +4803,8 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
RNA_id_pointer_create((ID *)key, &id_ptr);
/* Get NLA context for value remapping */
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
- (float)CFRA);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (float)scene->r.cfra);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
&nla_cache, &id_ptr, key->adt, &anim_eval_context);
@@ -4872,7 +4872,7 @@ static void achannel_setting_slider_nla_curve_cb(bContext *C,
float cfra;
/* get current frame - *no* NLA mapping should be done */
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene, true);
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index b223a1493fd..8464f280c29 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -2033,7 +2033,7 @@ static void setflag_anim_channels(bAnimContext *ac,
if ((ac->spacetype == SPACE_GRAPH) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
/* graph editor (case 2) */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
}
else {
/* standard case */
diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c
index d80eac2422e..ff53ad42e84 100644
--- a/source/blender/editors/animation/anim_deps.c
+++ b/source/blender/editors/animation/anim_deps.c
@@ -356,7 +356,7 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
if (ale->update & ANIM_UPDATE_HANDLES) {
ale->update &= ~ANIM_UPDATE_HANDLES;
if (fcu) {
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
}
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index ee1522c7b76..d9dcbf1d57e 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -120,9 +120,9 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -25, -100);
- if (SFRA < EFRA) {
- immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax);
- immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
+ if (scene->r.sfra < scene->r.efra) {
+ immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)scene->r.sfra, v2d->cur.ymax);
+ immRectf(pos, (float)scene->r.efra, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
}
else {
immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
@@ -135,11 +135,11 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d)
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, (float)SFRA, v2d->cur.ymin);
- immVertex2f(pos, (float)SFRA, v2d->cur.ymax);
+ immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymin);
+ immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymax);
- immVertex2f(pos, (float)EFRA, v2d->cur.ymin);
- immVertex2f(pos, (float)EFRA, v2d->cur.ymax);
+ immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymin);
+ immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymax);
immEnd();
immUnbindProgram();
@@ -530,7 +530,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
bool donenext = false, doneprev = false;
int nextcount = 0, prevcount = 0;
- cfranext = cfraprev = (float)(CFRA);
+ cfranext = cfraprev = (float)(scene->r.cfra);
/* seed up dummy dopesheet context with flags to perform necessary filtering */
if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
@@ -559,7 +559,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
aknext = ED_keylist_find_next(keylist, cfranext);
if (aknext) {
- if (CFRA == (int)aknext->cfra) {
+ if (scene->r.cfra == (int)aknext->cfra) {
/* make this the new starting point for the search and ignore */
cfranext = aknext->cfra;
}
@@ -577,7 +577,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
akprev = ED_keylist_find_prev(keylist, cfraprev);
if (akprev) {
- if (CFRA == (int)akprev->cfra) {
+ if (scene->r.cfra == (int)akprev->cfra) {
/* make this the new starting point for the search */
}
else {
@@ -599,14 +599,14 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
*r_prevfra = cfraprev;
}
else {
- *r_prevfra = CFRA - (cfranext - CFRA);
+ *r_prevfra = scene->r.cfra - (cfranext - scene->r.cfra);
}
if (donenext) {
*r_nextfra = cfranext;
}
else {
- *r_nextfra = CFRA + (CFRA - cfraprev);
+ *r_nextfra = scene->r.cfra + (scene->r.cfra - cfraprev);
}
return true;
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index a75944fa2f2..d9eeed94868 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -1807,11 +1807,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data,
ListBase tmp_data = {NULL, NULL};
size_t tmp_items = 0;
- /* add gpencil animation channels */
- BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) {
- tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
+ if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
+ /* add gpencil animation channels */
+ BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) {
+ tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
+ }
+ END_ANIMFILTER_SUBCHANNELS;
}
- END_ANIMFILTER_SUBCHANNELS;
/* did we find anything? */
if (tmp_items) {
@@ -1925,6 +1927,9 @@ static size_t animdata_filter_ds_gpencil(
tmp_items += animfilter_block_data(ac, &tmp_data, ads, &gpd->id, filter_mode);
/* add Grease Pencil layers */
+ if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
+ tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
+ }
/* TODO: do these need a separate expander?
* XXX: what order should these go in? */
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index ad06b185132..3608140a29d 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -104,7 +104,7 @@ int ED_markers_post_apply_transform(
ListBase *markers, Scene *scene, int mode, float value, char side)
{
TimeMarker *marker;
- float cfra = (float)CFRA;
+ float cfra = (float)scene->r.cfra;
int changed_tot = 0;
/* sanity check - no markers, or locked markers */
@@ -1497,8 +1497,8 @@ static void ED_markers_select_leftright(bAnimContext *ac,
}
LISTBASE_FOREACH (TimeMarker *, marker, markers) {
- if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= CFRA) ||
- (mode == MARKERS_LRSEL_RIGHT && marker->frame >= CFRA)) {
+ if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= scene->r.cfra) ||
+ (mode == MARKERS_LRSEL_RIGHT && marker->frame >= scene->r.cfra)) {
marker->flag |= SELECT;
}
}
@@ -1588,12 +1588,13 @@ static void MARKER_OT_delete(wmOperatorType *ot)
ot->idname = "MARKER_OT_delete";
/* api callbacks */
- ot->invoke = WM_operator_confirm;
+ ot->invoke = WM_operator_confirm_or_exec;
ot->exec = ed_marker_delete_exec;
ot->poll = ed_markers_poll_selected_no_locked_markers;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ WM_operator_properties_confirm_or_exec(ot);
}
/** \} */
@@ -1754,11 +1755,11 @@ static int ed_marker_camera_bind_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- marker = ED_markers_find_nearest_marker(markers, CFRA);
- if ((marker == NULL) || (marker->frame != CFRA)) {
+ marker = ED_markers_find_nearest_marker(markers, scene->r.cfra);
+ if ((marker == NULL) || (marker->frame != scene->r.cfra)) {
marker = MEM_callocN(sizeof(TimeMarker), "Camera TimeMarker");
marker->flag = SELECT;
- marker->frame = CFRA;
+ marker->frame = scene->r.cfra;
BLI_addtail(markers, marker);
/* deselect all others, so that the user can then move it without problems */
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index b15bd3db678..23c1d68b4d6 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -391,7 +391,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
return;
}
- const int cfra = CFRA;
+ const int cfra = scene->r.cfra;
int sfra = INT_MAX, efra = INT_MIN;
switch (range) {
case ANIMVIZ_CALC_RANGE_CURRENT_FRAME:
@@ -485,7 +485,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
sfra,
efra,
efra - sfra + 1);
- for (CFRA = sfra; CFRA <= efra; CFRA++) {
+ for (scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) {
if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) {
/* For current frame, only update tagged. */
BKE_scene_graph_update_tagged(depsgraph, bmain);
@@ -496,14 +496,14 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
}
/* perform baking for targets */
- motionpaths_calc_bake_targets(targets, CFRA);
+ motionpaths_calc_bake_targets(targets, scene->r.cfra);
}
/* reset original environment */
/* NOTE: We don't always need to reevaluate the main scene, as the depsgraph
* may be a temporary one that works on a subset of the data.
* We always have to restore the current frame though. */
- CFRA = cfra;
+ scene->r.cfra = cfra;
if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) {
motionpaths_calc_update_scene(depsgraph);
}
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index f3972cb45df..c7e755fb6df 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -142,14 +142,14 @@ static void change_frame_apply(bContext *C, wmOperator *op)
/* set the new frame number */
if (scene->r.flag & SCER_SHOW_SUBFRAME) {
- CFRA = (int)frame;
- SUBFRA = frame - (int)frame;
+ scene->r.cfra = (int)frame;
+ scene->r.subframe = frame - (int)frame;
}
else {
- CFRA = round_fl_to_int(frame);
- SUBFRA = 0.0f;
+ scene->r.cfra = round_fl_to_int(frame);
+ scene->r.subframe = 0.0f;
}
- FRAMENUMBER_MIN_CLAMP(CFRA);
+ FRAMENUMBER_MIN_CLAMP(scene->r.cfra);
/* do updates */
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
@@ -382,7 +382,7 @@ static int anim_set_sfra_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- frame = CFRA;
+ frame = scene->r.cfra;
/* if Preview Range is defined, set the 'start' frame for that */
if (PRVRANGEON) {
@@ -437,7 +437,7 @@ static int anim_set_efra_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- frame = CFRA;
+ frame = scene->r.cfra;
/* if Preview Range is defined, set the 'end' frame for that */
if (PRVRANGEON) {
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index c6f68228807..effedd4307d 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -124,7 +124,7 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[],
insert_vert_fcurve(
fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF);
fcu->extend = FCURVE_EXTRAPOLATE_LINEAR;
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
}
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index 6f31472907b..d2f0ee622c4 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -1020,7 +1020,7 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c
/* adding or removing the Cycles modifier requires an update to handles */
if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) {
- calchandles_fcurve(curve);
+ BKE_fcurve_handles_recalc(curve);
}
/* did we succeed? */
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index f8277cf6a85..706db498a82 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -444,7 +444,7 @@ void ANIM_animdata_keyframe_callback(bAnimContext *ac,
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -831,7 +831,7 @@ static short snap_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt)
{
const Scene *scene = ked->scene;
if (bezt->f2 & SELECT) {
- bezt->vec[1][0] = (float)CFRA;
+ bezt->vec[1][0] = (float)scene->r.cfra;
}
return 0;
}
@@ -929,7 +929,7 @@ static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt)
const Scene *scene = ked->scene;
if (bezt->f2 & SELECT) {
- mirror_bezier_xaxis_ex(bezt, CFRA);
+ mirror_bezier_xaxis_ex(bezt, scene->r.cfra);
}
return 0;
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index 00e2f221117..7723c221a40 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -47,77 +47,6 @@
/* **************************************************** */
-void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc)
-{
- /* sanity check */
- if (fcu == NULL) {
- return;
- }
-
- /* verify the index:
- * 1) cannot be greater than the number of available keyframes
- * 2) negative indices are for specifying a value from the end of the array
- */
- if (abs(index) >= fcu->totvert) {
- return;
- }
- if (index < 0) {
- index += fcu->totvert;
- }
-
- /* Delete this keyframe */
- memmove(
- &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1));
- fcu->totvert--;
-
- if (fcu->totvert == 0) {
- MEM_SAFE_FREE(fcu->bezt);
- }
-
- /* recalc handles - only if it won't cause problems */
- if (do_recalc) {
- calchandles_fcurve(fcu);
- }
-}
-
-bool delete_fcurve_keys(FCurve *fcu)
-{
- bool changed = false;
-
- if (fcu->bezt == NULL) { /* ignore baked curves */
- return false;
- }
-
- /* Delete selected BezTriples */
- for (int i = 0; i < fcu->totvert; i++) {
- if (fcu->bezt[i].f2 & SELECT) {
- if (i == fcu->active_keyframe_index) {
- BKE_fcurve_active_keyframe_set(fcu, NULL);
- }
- memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1));
- fcu->totvert--;
- i--;
- changed = true;
- }
- }
-
- /* Free the array of BezTriples if there are not keyframes */
- if (fcu->totvert == 0) {
- clear_fcurve_keys(fcu);
- }
-
- return changed;
-}
-
-void clear_fcurve_keys(FCurve *fcu)
-{
- MEM_SAFE_FREE(fcu->bezt);
-
- fcu->totvert = 0;
-}
-
-/* ---------------- */
-
bool duplicate_fcurve_keys(FCurve *fcu)
{
bool changed = false;
@@ -282,7 +211,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo
}
if (fcu->bezt->vec[1][1] == default_value) {
- clear_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_all(fcu);
/* check if curve is really unused and if it is, return signal for deletion */
if (BKE_fcurve_is_empty(fcu)) {
@@ -679,7 +608,7 @@ void smooth_fcurve(FCurve *fcu)
}
/* recalculate handles */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* ---------------- */
@@ -762,7 +691,7 @@ void sample_fcurve(FCurve *fcu)
}
/* recalculate channel's handles? */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* **************************************************** */
@@ -921,7 +850,7 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data)
}
/* in case 'relative' paste method is used */
- animcopy_cfra = CFRA;
+ animcopy_cfra = scene->r.cfra;
/* everything went fine */
return 0;
@@ -1121,7 +1050,7 @@ static void paste_animedit_keys_fcurve(
case KEYFRAME_PASTE_MERGE_OVER:
/* remove all keys */
- clear_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_all(fcu);
break;
case KEYFRAME_PASTE_MERGE_OVER_RANGE:
@@ -1148,7 +1077,7 @@ static void paste_animedit_keys_fcurve(
}
/* remove frames in the range */
- delete_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_selected(fcu);
}
break;
}
@@ -1182,7 +1111,7 @@ static void paste_animedit_keys_fcurve(
}
/* recalculate F-Curve's handles? */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = {
@@ -1217,11 +1146,11 @@ const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = {
{0, NULL, 0, NULL, NULL},
};
-short paste_animedit_keys(bAnimContext *ac,
- ListBase *anim_data,
- const eKeyPasteOffset offset_mode,
- const eKeyMergeMode merge_mode,
- bool flip)
+eKeyPasteError paste_animedit_keys(bAnimContext *ac,
+ ListBase *anim_data,
+ const eKeyPasteOffset offset_mode,
+ const eKeyMergeMode merge_mode,
+ bool flip)
{
bAnimListElem *ale;
@@ -1235,25 +1164,23 @@ short paste_animedit_keys(bAnimContext *ac,
/* check if buffer is empty */
if (BLI_listbase_is_empty(&animcopybuf)) {
- BKE_report(ac->reports, RPT_ERROR, "No animation data in buffer to paste");
- return -1;
+ return KEYFRAME_PASTE_NOTHING_TO_PASTE;
}
if (BLI_listbase_is_empty(anim_data)) {
- BKE_report(ac->reports, RPT_ERROR, "No selected F-Curves to paste into");
- return -1;
+ return KEYFRAME_PASTE_NOWHERE_TO_PASTE;
}
/* methods of offset */
switch (offset_mode) {
case KEYFRAME_PASTE_OFFSET_CFRA_START:
- offset = (float)(CFRA - animcopy_firstframe);
+ offset = (float)(scene->r.cfra - animcopy_firstframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_END:
- offset = (float)(CFRA - animcopy_lastframe);
+ offset = (float)(scene->r.cfra - animcopy_lastframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
- offset = (float)(CFRA - animcopy_cfra);
+ offset = (float)(scene->r.cfra - animcopy_cfra);
break;
case KEYFRAME_PASTE_OFFSET_NONE:
offset = 0.0f;
@@ -1335,7 +1262,7 @@ short paste_animedit_keys(bAnimContext *ac,
ANIM_animdata_update(ac, anim_data);
- return 0;
+ return KEYFRAME_PASTE_OK;
}
/* **************************************************** */
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index aa99a4e50c3..9084b9bb214 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -639,7 +639,7 @@ int insert_vert_fcurve(
* - we may calculate twice (due to auto-handle needing to be calculated twice)
*/
if ((flag & INSERTKEY_FAST) == 0) {
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* return the index at which the keyframe was added */
@@ -1282,10 +1282,12 @@ static bool insert_keyframe_value(ReportList *reports,
/* delete keyframe immediately before/after newly added */
switch (insert_mode) {
case KEYNEEDED_DELPREV:
- delete_fcurve_key(fcu, fcu->totvert - 2, 1);
+ BKE_fcurve_delete_key(fcu, fcu->totvert - 2);
+ BKE_fcurve_handles_recalc(fcu);
break;
case KEYNEEDED_DELNEXT:
- delete_fcurve_key(fcu, 1, 1);
+ BKE_fcurve_delete_key(fcu, 1);
+ BKE_fcurve_handles_recalc(fcu);
break;
}
@@ -1683,7 +1685,8 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra)
i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards) */
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
/* Only delete curve too if it won't be doing anything anymore */
if (BKE_fcurve_is_empty(fcu)) {
@@ -1947,7 +1950,8 @@ static int insert_key_exec(bContext *C, wmOperator *op)
Object *obedit = CTX_data_edit_object(C);
bool ob_edit_mode = false;
- float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */
+ float cfra = (float)
+ scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */
int num_channels;
const bool confirm = op->flag & OP_IS_INVOKE;
@@ -2168,7 +2172,8 @@ static int delete_key_exec(bContext *C, wmOperator *op)
static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *ks)
{
Scene *scene = CTX_data_scene(C);
- float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */
+ float cfra = (float)
+ scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */
int num_channels;
const bool confirm = op->flag & OP_IS_INVOKE;
@@ -2344,7 +2349,7 @@ void ANIM_OT_keyframe_clear_v3d(wmOperatorType *ot)
static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- float cfra = (float)CFRA;
+ float cfra = (float)scene->r.cfra;
int selected_objects_len = 0;
int selected_objects_success_len = 0;
@@ -2494,7 +2499,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
char *path;
uiBut *but;
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
- CTX_data_depsgraph_pointer(C), (float)CFRA);
+ CTX_data_depsgraph_pointer(C), (float)scene->r.cfra);
bool changed = false;
int index;
const bool all = RNA_boolean_get(op->ptr, "all");
@@ -2663,7 +2668,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op)
PropertyRNA *prop = NULL;
Main *bmain = CTX_data_main(C);
char *path;
- float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */
+ float cfra = (float)
+ scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */
bool changed = false;
int index;
const bool all = RNA_boolean_get(op->ptr, "all");
@@ -2706,7 +2712,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op)
i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards) */
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
changed = true;
}
}
@@ -2835,7 +2842,7 @@ void ANIM_OT_keyframe_clear_button(wmOperatorType *ot)
bool autokeyframe_cfra_can_key(const Scene *scene, ID *id)
{
- float cfra = (float)CFRA; /* XXX for now, this will do */
+ float cfra = (float)scene->r.cfra; /* XXX for now, this will do */
/* only filter if auto-key mode requires this */
if (IS_AUTOKEY_ON(scene) == 0) {
@@ -3065,7 +3072,7 @@ bool ED_autokeyframe_object(bContext *C, Scene *scene, Object *ob, KeyingSet *ks
* 3) Free the extra info.
*/
ANIM_relative_keyingset_add_source(&dsources, &ob->id, NULL, NULL);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra);
BLI_freelistN(&dsources);
return true;
@@ -3085,7 +3092,7 @@ bool ED_autokeyframe_pchan(
* 3) Free the extra info.
*/
ANIM_relative_keyingset_add_source(&dsources, &ob->id, &RNA_PoseBone, pchan);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra);
BLI_freelistN(&dsources);
return true;
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index dd43e3e6613..2071f056f9e 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -422,7 +422,7 @@ static void updateDuplicateActionConstraintSettings(
float mat[4][4];
bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
- BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+ BKE_constraint_custom_object_space_init(&cob, curcon);
unit_m4(mat);
bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
@@ -576,7 +576,7 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob,
unit_m4(local_mat);
bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
- BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+ BKE_constraint_custom_object_space_init(&cob, curcon);
BKE_constraint_mat_convertspace(
ob, pchan, &cob, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
@@ -631,7 +631,7 @@ static void updateDuplicateTransformConstraintSettings(Object *ob,
float target_mat[4][4], own_mat[4][4], imat[4][4];
bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
- BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+ BKE_constraint_custom_object_space_init(&cob, curcon);
unit_m4(own_mat);
BKE_constraint_mat_convertspace(
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 1f02e24666d..4f329dbe449 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -13,6 +13,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -281,6 +282,17 @@ void ED_armature_bone_rename(Main *bmain,
}
}
+ /* fix camera focus */
+ if (ob->type == OB_CAMERA) {
+ Camera *cam = (Camera *)ob->data;
+ if ((cam->dof.focus_object != NULL) && (cam->dof.focus_object->data == arm)) {
+ if (STREQ(cam->dof.focus_subtarget, oldname)) {
+ BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME);
+ DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ }
+ }
+
/* fix grease pencil modifiers and vertex groups */
if (ob->type == OB_GPENCIL) {
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index ea54c3014ca..ff187a52154 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -24,6 +24,7 @@
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -448,7 +449,7 @@ static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *U
ICON_NONE,
"POSELIB_OT_pose_add",
"frame",
- CFRA);
+ scene->r.cfra);
/* Replace existing - sub-menu. */
uiItemMenuF(
@@ -615,7 +616,8 @@ static int poselib_remove_exec(bContext *C, wmOperator *op)
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* check if remove */
if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) {
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
break;
}
}
@@ -1113,7 +1115,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData
/* perform actual auto-keying now */
if (autokey) {
/* insert keyframes for all relevant bones in one go */
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra);
BLI_freelistN(&dsources);
}
@@ -1578,7 +1580,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con
case EVT_PADMINUS:
if (pld->searchstr[0]) {
/* searching... */
- poselib_preview_handle_search(pld, event->type, event->ascii);
+ poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event));
}
else {
/* view manipulation (see above) */
@@ -1589,7 +1591,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con
/* otherwise, assume that searching might be able to handle it */
default:
- poselib_preview_handle_search(pld, event->type, event->ascii);
+ poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event));
break;
}
diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c
index 9ee289145c4..d866062cec0 100644
--- a/source/blender/editors/armature/pose_lib_2.c
+++ b/source/blender/editors/armature/pose_lib_2.c
@@ -134,7 +134,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd)
}
/* Perform actual auto-keying. */
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra);
BLI_freelistN(&dsources);
/* send notifiers for this */
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 0efa32ec63a..38c99c2ef60 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -2061,12 +2061,12 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
if (mode == POSE_PROPAGATE_SMART_HOLDS) {
/* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
* from the keyframe that occurs after the current frame. */
- modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA);
+ modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)scene->r.cfra);
}
/* Go through propagating pose to keyframes, curve by curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
- pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData);
+ pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)scene->r.cfra, modeData);
}
}
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index f0b0218d7e0..cfc6b0b6b6e 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -1201,7 +1201,7 @@ static int pose_clear_transform_generic_exec(bContext *C,
KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
/* insert keyframes */
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra);
/* now recalculate paths */
if (ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
@@ -1343,8 +1343,8 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
- (float)CFRA);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (float)scene->r.cfra);
const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4c0df48f65b..24302aca59b 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -5562,7 +5562,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Curve *cu;
float location[3];
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
- (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
+ (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST));
Nurb *nu;
BezTriple *bezt;
@@ -5595,12 +5595,13 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
vc.depsgraph,
vc.region,
vc.v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE :
SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
},
+ NULL,
mval,
NULL,
NULL,
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index ba5a7409ba7..ee6376ca95f 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -87,6 +87,8 @@ static const char *get_surf_defname(int type)
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle");
case CU_PRIM_PATCH:
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch");
+ case CU_PRIM_TUBE:
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCylinder");
case CU_PRIM_SPHERE:
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere");
case CU_PRIM_DONUT:
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 6946c09e6f1..7632f1b1e64 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -1162,7 +1162,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
curve_draw_event_add_first(op, event);
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (cdd->state == CURVE_DRAW_PAINTING) {
const float mval_fl[2] = {UNPACK2(event->mval)};
if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {
diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c
index 729ad46877a..27f4e4fca61 100644
--- a/source/blender/editors/curve/editcurve_pen.c
+++ b/source/blender/editors/curve/editcurve_pen.c
@@ -1622,7 +1622,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
/* Check if dragging */
if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
cpd->dragging = true;
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 611dbb2e80c..ceed12dcff1 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -59,7 +59,7 @@ static int kill_selection(Object *obedit, int ins);
/** \name Internal Utilities
* \{ */
-static char32_t findaccent(char32_t char1, uint code)
+static char32_t findaccent(char32_t char1, const char code)
{
char32_t new = 0;
@@ -1638,12 +1638,11 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Object *obedit = CTX_data_edit_object(C);
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
- static int accentcode = 0;
- uintptr_t ascii = event->ascii;
+ static bool accentcode = false;
const bool alt = event->modifier & KM_ALT;
const bool shift = event->modifier & KM_SHIFT;
const bool ctrl = event->modifier & KM_CTRL;
- int event_type = event->type, event_val = event->val;
+ char32_t insert_char_override = 0;
char32_t inserted_text[2] = {0};
if (RNA_struct_property_is_set(op->ptr, "text")) {
@@ -1652,48 +1651,47 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (RNA_struct_property_is_set(op->ptr, "accent")) {
if (ef->len != 0 && ef->pos > 0) {
- accentcode = 1;
+ accentcode = true;
}
return OPERATOR_FINISHED;
}
- /* tab should exit editmode, but we allow it to be typed using modifier keys */
- if (event_type == EVT_TABKEY) {
- if ((alt || ctrl || shift) == 0) {
- return OPERATOR_PASS_THROUGH;
- }
-
- ascii = 9;
- }
-
- if (event_type == EVT_BACKSPACEKEY) {
+ if (event->type == EVT_BACKSPACEKEY) {
if (alt && ef->len != 0 && ef->pos > 0) {
- accentcode = 1;
+ accentcode = true;
}
return OPERATOR_PASS_THROUGH;
}
- if (event_val && (ascii || event->utf8_buf[0])) {
- /* handle case like TAB (== 9) */
- if ((ascii > 31 && ascii < 254 && ascii != 127) || (ELEM(ascii, 13, 10)) || (ascii == 8) ||
- (event->utf8_buf[0])) {
+ /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */
+ if (event->type == EVT_TABKEY) {
+ if ((alt || ctrl || shift) == 0) {
+ return OPERATOR_PASS_THROUGH;
+ }
+ insert_char_override = '\t';
+ }
+ if (insert_char_override || event->utf8_buf[0]) {
+ if (insert_char_override) {
+ /* Handle case like TAB ('\t'). */
+ inserted_text[0] = insert_char_override;
+ insert_into_textbuf(obedit, insert_char_override);
+ text_update_edited(C, obedit, FO_EDIT);
+ }
+ else {
+ BLI_assert(event->utf8_buf[0]);
if (accentcode) {
if (ef->pos > 0) {
- inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii);
+ inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1],
+ BLI_str_utf8_as_unicode(event->utf8_buf));
ef->textbuf[ef->pos - 1] = inserted_text[0];
}
- accentcode = 0;
+ accentcode = false;
}
else if (event->utf8_buf[0]) {
inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf);
- ascii = inserted_text[0];
- insert_into_textbuf(obedit, ascii);
- accentcode = 0;
- }
- else if (ascii) {
- insert_into_textbuf(obedit, ascii);
- accentcode = 0;
+ insert_into_textbuf(obedit, inserted_text[0]);
+ accentcode = false;
}
else {
BLI_assert(0);
@@ -1702,11 +1700,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
kill_selection(obedit, 1);
text_update_edited(C, obedit, FO_EDIT);
}
- else {
- inserted_text[0] = ascii;
- insert_into_textbuf(obedit, ascii);
- text_update_edited(C, obedit, FO_EDIT);
- }
}
else {
return OPERATOR_PASS_THROUGH;
@@ -1720,11 +1713,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_string_set(op->ptr, "text", inserted_utf8);
}
- /* reset property? */
- if (event_val == 0) {
- accentcode = 0;
- }
-
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt
index 3c31e8014ff..303d2fb71dc 100644
--- a/source/blender/editors/curves/CMakeLists.txt
+++ b/source/blender/editors/curves/CMakeLists.txt
@@ -8,6 +8,7 @@ set(INC
../../depsgraph
../../functions
../../geometry
+ ../../gpu
../../makesdna
../../makesrna
../../windowmanager
@@ -27,5 +28,17 @@ set(LIB
bf_blenlib
)
+if(WITH_TBB)
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_TBB)
+ if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+ endif()
+endif()
+
blender_add_lib(bf_editor_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 552ef1d96c8..79916253207 100644
--- a/source/blender/editors/curves/intern/curves_add.cc
+++ b/source/blender/editors/curves/intern/curves_add.cc
@@ -6,12 +6,96 @@
#include "BLI_rand.hh"
+#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "ED_curves.h"
+#include "ED_node.h"
+#include "ED_object.h"
+
+#include "DNA_modifier_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
namespace blender::ed::curves {
+static bool has_surface_deformation_node(const bNodeTree &ntree)
+{
+ LISTBASE_FOREACH (const bNode *, node, &ntree.nodes) {
+ if (node->type == GEO_NODE_DEFORM_CURVES_ON_SURFACE) {
+ return true;
+ }
+ if (node->type == NODE_GROUP) {
+ if (node->id != nullptr) {
+ if (has_surface_deformation_node(*reinterpret_cast<const bNodeTree *>(node->id))) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool has_surface_deformation_node(const Object &curves_ob)
+{
+ LISTBASE_FOREACH (const ModifierData *, md, &curves_ob.modifiers) {
+ if (md->type != eModifierType_Nodes) {
+ continue;
+ }
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ if (nmd->node_group == nullptr) {
+ continue;
+ }
+ if (has_surface_deformation_node(*nmd->node_group)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
+{
+ if (has_surface_deformation_node(curves_ob)) {
+ return;
+ }
+
+ Main *bmain = CTX_data_main(&C);
+ Scene *scene = CTX_data_scene(&C);
+
+ ModifierData *md = ED_object_modifier_add(
+ nullptr, bmain, scene, &curves_ob, "Surface Deform", eModifierType_Nodes);
+ NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
+ nmd.node_group = ntreeAddTree(bmain, "Surface Deform", "GeometryNodeTree");
+
+ bNodeTree *ntree = nmd.node_group;
+ ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry");
+ ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
+ bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT);
+ bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT);
+ bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE);
+
+ ED_node_tree_propagate_change(&C, bmain, nmd.node_group);
+
+ nodeAddLink(ntree,
+ group_input,
+ static_cast<bNodeSocket *>(group_input->outputs.first),
+ deform_node,
+ nodeFindSocket(deform_node, SOCK_IN, "Curves"));
+ nodeAddLink(ntree,
+ deform_node,
+ nodeFindSocket(deform_node, SOCK_OUT, "Curves"),
+ group_output,
+ static_cast<bNodeSocket *>(group_output->inputs.first));
+
+ group_input->locx = -200;
+ group_output->locx = 200;
+ deform_node->locx = 0;
+
+ ED_node_tree_propagate_change(&C, bmain, nmd.node_group);
+}
+
bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve)
{
bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size);
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index 25bcba6cfb3..740e53b59f8 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -25,6 +25,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_legacy_convert.h"
#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -72,7 +73,7 @@ static bool object_has_editable_curves(const Main &bmain, const Object &object)
return true;
}
-static VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
+VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
{
VectorSet<Curves *> unique_curves;
@@ -93,6 +94,47 @@ static VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
return unique_curves;
}
+static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface)
+{
+ Object *object = CTX_data_active_object(C);
+ if (object == nullptr || object->type != OB_CURVES) {
+ return false;
+ }
+ if (check_editable) {
+ if (!ED_operator_object_active_editable_ex(C, object)) {
+ return false;
+ }
+ }
+ if (check_surface) {
+ Curves &curves = *static_cast<Curves *>(object->data);
+ if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
+ CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool editable_curves_with_surface_poll(bContext *C)
+{
+ return curves_poll_impl(C, true, true);
+}
+
+bool curves_with_surface_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, true);
+}
+
+bool editable_curves_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, false);
+}
+
+bool curves_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, false);
+}
+
using bke::CurvesGeometry;
namespace convert_to_particle_system {
@@ -243,17 +285,14 @@ static void try_convert_single_object(Object &curves_ob,
}
/* Prepare transformation matrices. */
- const float4x4 curves_to_world_mat = curves_ob.obmat;
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- 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;
+ const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
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 float3 &root_pos_cu = positions_cu[points.first()];
- const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
+ const float3 root_pos_su = transforms.curves_to_surface * root_pos_cu;
BVHTreeNearest nearest;
nearest.dist_sq = FLT_MAX;
@@ -293,7 +332,7 @@ static void try_convert_single_object(Object &curves_ob,
for (const int key_i : hair_keys.index_range()) {
const float3 &key_pos_cu = positions_cu[points[key_i]];
- const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
+ const float3 key_pos_su = transforms.curves_to_surface * key_pos_cu;
const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
HairKey &key = hair_keys[key_i];
@@ -339,16 +378,6 @@ static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static bool curves_convert_to_particle_system_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr || ob->type != OB_CURVES) {
- return false;
- }
- Curves &curves = *static_cast<Curves *>(ob->data);
- return curves.surface != nullptr;
-}
-
} // namespace convert_to_particle_system
static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
@@ -357,7 +386,7 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
ot->idname = "CURVES_OT_convert_to_particle_system";
ot->description = "Add a new or update an existing hair particle system on the surface object";
- ot->poll = convert_to_particle_system::curves_convert_to_particle_system_poll;
+ ot->poll = curves_with_surface_poll;
ot->exec = convert_to_particle_system::curves_convert_to_particle_system_exec;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -467,7 +496,6 @@ static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNU
}
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);
@@ -504,174 +532,169 @@ enum class AttachMode {
Deform,
};
-static bool snap_curves_to_surface_poll(bContext *C)
+static void snap_curves_to_surface_exec_object(Object &curves_ob,
+ const Object &surface_ob,
+ const AttachMode attach_mode,
+ bool *r_invalid_uvs,
+ bool *r_missing_uvs)
{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr || ob->type != OB_CURVES) {
- return false;
- }
- if (!ED_operator_object_active_editable_ex(C, ob)) {
- return false;
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+
+ Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
+
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id.surface_uv_map != nullptr) {
+ const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh);
+ surface_uv_map = surface_attributes
+ .lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
+ .typed<float2>();
}
- Curves &curves = *static_cast<Curves *>(ob->data);
- if (curves.surface == nullptr) {
- return false;
+
+ MutableSpan<float3> positions_cu = curves.positions_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)};
+
+ const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
+
+ switch (attach_mode) {
+ case AttachMode::Nearest: {
+ BVHTreeFromMesh surface_bvh;
+ BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+
+ 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 float3 old_first_point_pos_su = transforms.curves_to_surface *
+ old_first_point_pos_cu;
+
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(surface_bvh.tree,
+ old_first_point_pos_su,
+ &nearest,
+ surface_bvh.nearest_callback,
+ &surface_bvh);
+ const int looptri_index = nearest.index;
+ if (looptri_index == -1) {
+ continue;
+ }
+
+ const float3 new_first_point_pos_su = nearest.co;
+ const float3 new_first_point_pos_cu = transforms.surface_to_curves *
+ new_first_point_pos_su;
+ const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
+
+ for (float3 &pos_cu : positions_cu.slice(points)) {
+ pos_cu += pos_diff_cu;
+ }
+
+ 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()) {
+ *r_missing_uvs = true;
+ 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 float2 uv = surface_uv_coords[curve_i];
+ ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
+ if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
+ *r_invalid_uvs = true;
+ continue;
+ }
+
+ 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;
+ const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
+
+ float3 new_first_point_pos_su;
+ interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
+ const float3 new_first_point_pos_cu = transforms.surface_to_curves *
+ new_first_point_pos_su;
+
+ const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
+ for (float3 &pos_cu : positions_cu.slice(points)) {
+ pos_cu += pos_diff_cu;
+ }
+ }
+ });
+ break;
+ }
}
- return true;
+
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
}
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_uv = false;
+ bool found_invalid_uvs = false;
+ bool found_missing_uvs = false;
CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
if (curves_ob->type != OB_CURVES) {
continue;
}
Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
if (curves_id.surface == nullptr) {
continue;
}
- Object &surface_ob = *curves_id.surface;
- if (surface_ob.type != OB_MESH) {
+ if (curves_id.surface->type != OB_MESH) {
continue;
}
- 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<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)};
-
- const float4x4 curves_to_world_mat = curves_ob->obmat;
- const float4x4 world_to_curves_mat = curves_to_world_mat.inverted();
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- 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;
- const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat;
-
- switch (attach_mode) {
- case AttachMode::Nearest: {
- BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
-
- 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 float3 old_first_point_pos_su = curves_to_surface_mat * old_first_point_pos_cu;
-
- BVHTreeNearest nearest;
- nearest.index = -1;
- nearest.dist_sq = FLT_MAX;
- BLI_bvhtree_find_nearest(surface_bvh.tree,
- old_first_point_pos_su,
- &nearest,
- surface_bvh.nearest_callback,
- &surface_bvh);
- const int looptri_index = nearest.index;
- if (looptri_index == -1) {
- continue;
- }
-
- const float3 new_first_point_pos_su = nearest.co;
- const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su;
- const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
-
- for (float3 &pos_cu : positions_cu.slice(points)) {
- pos_cu += pos_diff_cu;
- }
-
- 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 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 = *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;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
-
- float3 new_first_point_pos_su;
- interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
- const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su;
-
- const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
- for (float3 &pos_cu : positions_cu.slice(points)) {
- pos_cu += pos_diff_cu;
- }
- }
- });
- break;
- }
- }
-
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ snap_curves_to_surface_exec_object(
+ *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
}
CTX_DATA_END;
- if (found_invalid_uv) {
+ if (found_missing_uvs) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Curves do not have attachment information that can be used for deformation");
+ }
+ if (found_invalid_uvs) {
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
}
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
+ /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
return OPERATOR_FINISHED;
}
@@ -686,7 +709,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
ot->idname = "CURVES_OT_snap_curves_to_surface";
ot->description = "Move curves so that the first point is exactly on the surface mesh";
- ot->poll = snap_curves_to_surface_poll;
+ ot->poll = editable_curves_with_surface_poll;
ot->exec = snap_curves_to_surface_exec;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -715,21 +738,6 @@ 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)
@@ -745,21 +753,20 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
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);
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
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");
+ attributes.remove(".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");
+ attributes.remove(".selection_curve_float");
}
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
@@ -784,7 +791,7 @@ static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
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->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -820,13 +827,11 @@ static void CURVES_OT_disable_selection(wmOperatorType *ot)
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->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-namespace select_all {
-
static bool varray_contains_nonzero(const VArray<float> &data)
{
bool contains_nonzero = false;
@@ -841,6 +846,19 @@ static bool varray_contains_nonzero(const VArray<float> &data)
return contains_nonzero;
}
+bool has_anything_selected(const Curves &curves_id)
+{
+ const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ switch (curves_id.selection_domain) {
+ case ATTR_DOMAIN_POINT:
+ return varray_contains_nonzero(curves.selection_point_float());
+ case ATTR_DOMAIN_CURVE:
+ return varray_contains_nonzero(curves.selection_curve_float());
+ }
+ BLI_assert_unreachable();
+ return false;
+}
+
static bool any_point_selected(const CurvesGeometry &curves)
{
return varray_contains_nonzero(curves.selection_point_float());
@@ -856,6 +874,8 @@ static bool any_point_selected(const Span<Curves *> curves_ids)
return false;
}
+namespace select_all {
+
static void invert_selection(MutableSpan<float> selection)
{
threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
@@ -876,25 +896,14 @@ static int select_all_exec(bContext *C, wmOperator *op)
}
for (Curves *curves_id : unique_curves) {
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
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
+ /* As an optimization, just remove the selection attributes when everything is selected. */
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+ attributes.remove(".selection_point_float");
+ attributes.remove(".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();
@@ -924,13 +933,95 @@ static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot)
ot->description = "(De)select all control points";
ot->exec = select_all::select_all_exec;
- ot->poll = selection_poll;
+ ot->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
WM_operator_properties_select_all(ot);
}
+namespace surface_set {
+
+static bool surface_set_poll(bContext *C)
+{
+ const Object *object = CTX_data_active_object(C);
+ if (object == nullptr) {
+ return false;
+ }
+ if (object->type != OB_MESH) {
+ return false;
+ }
+ return true;
+}
+
+static int surface_set_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+
+ Object &new_surface_ob = *CTX_data_active_object(C);
+
+ Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
+ const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.ldata,
+ CD_MLOOPUV);
+
+ CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
+ if (selected_ob->type != OB_CURVES) {
+ continue;
+ }
+ Object &curves_ob = *selected_ob;
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+
+ MEM_SAFE_FREE(curves_id.surface_uv_map);
+ if (new_uv_map_name != nullptr) {
+ curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
+ }
+
+ bool missing_uvs;
+ bool invalid_uvs;
+ snap_curves_to_surface::snap_curves_to_surface_exec_object(
+ curves_ob,
+ new_surface_ob,
+ snap_curves_to_surface::AttachMode::Nearest,
+ &invalid_uvs,
+ &missing_uvs);
+
+ /* Add deformation modifier if necessary. */
+ blender::ed::curves::ensure_surface_deformation_node_exists(*C, curves_ob);
+
+ curves_id.surface = &new_surface_ob;
+ ED_object_parent_set(
+ op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr);
+
+ DEG_id_tag_update(&curves_ob.id, ID_RECALC_TRANSFORM);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
+
+ /* Required for deformation. */
+ new_surface_ob.modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION;
+ DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
+ }
+ CTX_DATA_END;
+
+ DEG_relations_tag_update(bmain);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace surface_set
+
+static void CURVES_OT_surface_set(wmOperatorType *ot)
+{
+ ot->name = "Set Curves Surface Object";
+ ot->idname = __func__;
+ ot->description =
+ "Use the active object as surface for selected curves objects and set it as the parent";
+
+ ot->exec = surface_set::surface_set_exec;
+ ot->poll = surface_set::surface_set_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
} // namespace blender::ed::curves
void ED_operatortypes_curves()
@@ -942,4 +1033,5 @@ void ED_operatortypes_curves()
WM_operatortype_append(CURVES_OT_set_selection_domain);
WM_operatortype_append(SCULPT_CURVES_OT_select_all);
WM_operatortype_append(CURVES_OT_disable_selection);
+ WM_operatortype_append(CURVES_OT_surface_set);
}
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index c7e782b7b89..a6a9b2fcd26 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -282,8 +282,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
RNA_enum_get(op->ptr, "mode"));
Mesh *mesh = reinterpret_cast<Mesh *>(ob_data);
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
/* General conversion steps are always the same:
* 1. Convert old data to right domain and data type.
@@ -301,33 +300,33 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- GVArray src_varray = mesh_component.attribute_get_for_read(name, dst_domain, dst_type);
+ GVArray src_varray = attributes.lookup_or_default(name, dst_domain, dst_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, dst_domain, dst_type, AttributeInitMove(new_data));
+ attributes.remove(name);
+ attributes.add(name, dst_domain, dst_type, blender::bke::AttributeInitMove(new_data));
break;
}
case ConvertAttributeMode::UVMap: {
MLoopUV *dst_uvs = static_cast<MLoopUV *>(
MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopUV), __func__));
- VArray<float2> src_varray = mesh_component.attribute_get_for_read<float2>(
+ VArray<float2> src_varray = attributes.lookup_or_default<float2>(
name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f});
for (const int i : IndexRange(mesh->totloop)) {
copy_v2_v2(dst_uvs[i].uv, src_varray[i]);
}
- mesh_component.attribute_try_delete(name);
+ attributes.remove(name);
CustomData_add_layer_named(
&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str());
break;
}
case ConvertAttributeMode::VertexGroup: {
Array<float> src_weights(mesh->totvert);
- VArray<float> src_varray = mesh_component.attribute_get_for_read<float>(
+ VArray<float> src_varray = attributes.lookup_or_default<float>(
name, ATTR_DOMAIN_POINT, 0.0f);
src_varray.materialize(src_weights);
- mesh_component.attribute_try_delete(name);
+ attributes.remove(name);
bDeformGroup *defgroup = BKE_object_defgroup_new(ob, name.c_str());
const int defgroup_index = BLI_findindex(BKE_id_defgroup_list_get(&mesh->id), defgroup);
@@ -406,7 +405,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot)
prop = RNA_def_float_color(
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_subtype(prop, PROP_COLOR);
RNA_def_property_float_array_default(prop, default_color);
}
@@ -652,15 +651,16 @@ bool ED_geometry_attribute_convert(Mesh *mesh,
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);
+ blender::bke::MutableAttributeAccessor attributes = blender::bke::mesh_attributes_for_write(
+ *mesh);
+
+ GVArray src_varray = attributes.lookup_or_default(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));
+ attributes.remove(name);
+ attributes.add(name, new_domain, new_type, blender::bke::AttributeInitMove(new_data));
int *active_index = BKE_id_attributes_active_index_p(&mesh->id);
if (*active_index > 0) {
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 1ce67185c1e..af1f09d7e25 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -278,12 +278,13 @@ static int gizmo_move_modal(bContext *C,
CTX_data_ensure_evaluated_depsgraph(C),
region,
CTX_wm_view3d(C),
- (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
+ (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST),
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
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 d468906f127..c5a542c0bf3 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -345,7 +345,7 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
prop = RNA_def_enum_flag(gzt->srna,
"snap_elements_force",
rna_enum_snap_element_items,
- SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST,
"Snap Elements",
"");
RNA_def_property_enum_funcs_runtime(prop,
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index f720f261ad5..ae09aea28d3 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -841,7 +841,8 @@ void ED_annotation_draw_2dimage(const bContext *C)
}
/* draw it! */
- annotation_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, area->spacetype);
+ annotation_draw_data_all(
+ scene, gpd, offsx, offsy, sizex, sizey, scene->r.cfra, dflag, area->spacetype);
}
void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d)
@@ -877,7 +878,7 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d)
}
annotation_draw_data_all(
- scene, gpd, 0, 0, region->winx, region->winy, CFRA, dflag, area->spacetype);
+ scene, gpd, 0, 0, region->winx, region->winy, scene->r.cfra, dflag, area->spacetype);
}
void ED_annotation_draw_view3d(
@@ -928,7 +929,8 @@ void ED_annotation_draw_view3d(
}
/* draw it! */
- annotation_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype);
+ annotation_draw_data_all(
+ scene, gpd, offsx, offsy, winx, winy, scene->r.cfra, dflag, v3d->spacetype);
}
void ED_annotation_draw_ex(
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 8c393cc4f3f..d08d56a354a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1568,7 +1568,7 @@ static void annotation_paint_initstroke(tGPsdata *p,
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode);
+ p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, add_frame_mode);
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
@@ -2062,11 +2062,18 @@ static void annotation_draw_apply_event(
PointerRNA itemptr;
float mousef[2];
- /* convert from window-space to area-space mouse coordinates
- * add any x,y override position for fake events
- */
- p->mval[0] = (float)event->mval[0] - x;
- p->mval[1] = (float)event->mval[1] - y;
+ /* Convert from window-space to area-space mouse coordinates
+ * add any x,y override position for fake events. */
+ if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
+ /* The first run may be a drag event, see: T99368. */
+ WM_event_drag_start_mval_fl(event, p->region, p->mval);
+ p->mval[0] -= x;
+ p->mval[1] -= y;
+ }
+ else {
+ p->mval[0] = (float)event->mval[0] - x;
+ p->mval[1] = (float)event->mval[1] - y;
+ }
/* Key to toggle stabilization. */
if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) {
@@ -2634,7 +2641,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
annotation_add_missing_events(C, op, event, p);
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index 949043e4be1..8a98dcb57fc 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -214,6 +214,18 @@ void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked,
}
}
+void ED_gpencil_set_active_channel(bGPdata *gpd, bGPDlayer *gpl)
+{
+ gpl->flag |= GP_LAYER_SELECT;
+
+ /* Update other layer status. */
+ if (BKE_gpencil_layer_active_get(gpd) != gpl) {
+ BKE_gpencil_layer_active_set(gpd, gpl);
+ BKE_gpencil_layer_autolock_set(gpd, false);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+}
+
/* ***************************************** */
/* Frame Editing Tools */
@@ -316,8 +328,13 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- /* assume that each of these is a GP layer */
for (ale = anim_data.first; ale; ale = ale->next) {
+ /* This function only deals with grease pencil layer frames.
+ * This check is needed in the case of a call from the main dopesheet. */
+ if (ale->type != ANIMTYPE_GPLAYER) {
+ continue;
+ }
+
ListBase copied_frames = {NULL, NULL};
bGPDlayer *gpl = (bGPDlayer *)ale->data;
@@ -354,19 +371,13 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
}
/* in case 'relative' paste method is used */
- gpencil_anim_copy_cfra = CFRA;
+ gpencil_anim_copy_cfra = scene->r.cfra;
/* clean up */
ANIM_animdata_freelist(&anim_data);
- /* check if anything ended up in the buffer */
- if (ELEM(NULL, gpencil_anim_copybuf.first, gpencil_anim_copybuf.last)) {
- BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
- return false;
- }
-
/* report success */
- return true;
+ return !BLI_listbase_is_empty(&gpencil_anim_copybuf);
}
bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
@@ -381,7 +392,6 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
/* check if buffer is empty */
if (BLI_listbase_is_empty(&gpencil_anim_copybuf)) {
- BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste");
return false;
}
@@ -393,13 +403,13 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
/* methods of offset (eKeyPasteOffset) */
switch (offset_mode) {
case KEYFRAME_PASTE_OFFSET_CFRA_START:
- offset = (CFRA - gpencil_anim_copy_firstframe);
+ offset = (scene->r.cfra - gpencil_anim_copy_firstframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_END:
- offset = (CFRA - gpencil_anim_copy_lastframe);
+ offset = (scene->r.cfra - gpencil_anim_copy_lastframe);
break;
case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
- offset = (CFRA - gpencil_anim_copy_cfra);
+ offset = (scene->r.cfra - gpencil_anim_copy_cfra);
break;
case KEYFRAME_PASTE_OFFSET_NONE:
offset = 0;
@@ -414,6 +424,11 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
/* from selected channels */
for (ale = anim_data.first; ale; ale = ale->next) {
+ /* only deal with GPlayers (case of calls from general dopesheet) */
+ if (ale->type != ANIMTYPE_GPLAYER) {
+ continue;
+ }
+
bGPDlayer *gpld = (bGPDlayer *)ale->data;
bGPDlayer *gpls = NULL;
bGPDframe *gpfs, *gpf;
@@ -503,7 +518,7 @@ static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene)
static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
{
if (gpf->flag & GP_FRAME_SELECT) {
- gpf->framenum = (int)CFRA;
+ gpf->framenum = (int)scene->r.cfra;
}
return false;
}
@@ -545,8 +560,8 @@ static bool gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene)
int diff;
if (gpf->flag & GP_FRAME_SELECT) {
- diff = CFRA - gpf->framenum;
- gpf->framenum = CFRA + diff;
+ diff = scene->r.cfra - gpf->framenum;
+ gpf->framenum = scene->r.cfra + diff;
}
return false;
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
index 2f22fad53e7..e8e6d328804 100644
--- a/source/blender/editors/gpencil/gpencil_add_blank.c
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -76,7 +76,7 @@ void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4]))
bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
/* frames */
- BKE_gpencil_frame_addnew(layer, CFRA);
+ BKE_gpencil_frame_addnew(layer, scene->r.cfra);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 65ee732f6a9..bc046e89d21 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -844,8 +844,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* frames */
/* NOTE: No need to check for existing, as this will take care of it for us */
- bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, CFRA);
- bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, CFRA);
+ bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, scene->r.cfra);
+ bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, scene->r.cfra);
/* generate strokes */
gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false);
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index bc5fe9b5cfb..e24964c4832 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -211,8 +211,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
- bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA);
- bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, CFRA);
+ bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, scene->r.cfra);
+ bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, scene->r.cfra);
UNUSED_VARS(frame_color);
/* generate stroke */
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc
index 66f53bea326..e480852a9bb 100644
--- a/source/blender/editors/gpencil/gpencil_bake_animation.cc
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc
@@ -265,7 +265,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
}
/* Move scene to new frame. */
- CFRA = i;
+ scene->r.cfra = i;
BKE_scene_graph_update_for_newframe(depsgraph);
/* Loop all objects in the list. */
@@ -285,7 +285,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
/* Apply time modifier. */
int remap_cfra = BKE_gpencil_time_modifier_cfra(
- depsgraph, scene, elem->ob, gpl_src, CFRA, false);
+ depsgraph, scene, elem->ob, gpl_src, scene->r.cfra, false);
/* Duplicate frame. */
bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(
gpl_src, remap_cfra, GP_GETFRAME_USE_PREV);
@@ -293,7 +293,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
continue;
}
bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true);
- gpf_dst->framenum = CFRA + frame_offset;
+ gpf_dst->framenum = scene->r.cfra + frame_offset;
gpf_dst->flag &= ~GP_FRAME_SELECT;
BLI_addtail(&gpl_dst->frames, gpf_dst);
@@ -337,7 +337,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
}
}
/* Return scene frame state and DB to original state. */
- CFRA = oldframe;
+ scene->r.cfra = oldframe;
BKE_scene_graph_update_for_newframe(depsgraph);
/* Free memory. */
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 0601d009bf7..e02a82f4555 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -588,7 +588,7 @@ static void gpencil_stroke_path_animation(bContext *C,
}
/* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -1271,7 +1271,7 @@ static void gpencil_layer_to_curve(bContext *C,
Collection *collection = CTX_data_collection(C);
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
bGPDstroke *prev_gps = NULL;
Object *ob;
Curve *cu;
@@ -1414,7 +1414,7 @@ static bool gpencil_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl,
int i;
bool valid = true;
- if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) ||
+ if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV)) ||
!(gps = gpf->strokes.first)) {
return false;
}
@@ -1481,7 +1481,7 @@ static bool gpencil_convert_poll(bContext *C)
* and if we are not in edit mode!
*/
return ((area && area->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_active_get(gpd)) &&
- (gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) &&
+ (gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV)) &&
(gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd)));
}
@@ -1811,7 +1811,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
/* Add layer and frame. */
bGPdata *gpd = (bGPdata *)ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false);
- bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
+ bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, scene->r.cfra);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
if (done) {
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 6843c42d2d0..b7ac73b9692 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -226,7 +226,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
/* Add a new frame to make it visible in Dopesheet. */
if (gpl != NULL) {
- gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ gpl->actframe = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
}
}
}
@@ -646,12 +646,12 @@ static int gpencil_frame_duplicate_exec(bContext *C, wmOperator *op)
}
if (mode == 0) {
- BKE_gpencil_frame_addcopy(gpl_active, CFRA);
+ BKE_gpencil_frame_addcopy(gpl_active, scene->r.cfra);
}
else {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->flag & GP_LAYER_LOCKED) == 0) {
- BKE_gpencil_frame_addcopy(gpl, CFRA);
+ BKE_gpencil_frame_addcopy(gpl, scene->r.cfra);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 5028baf1589..71cf9b1fafd 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1718,7 +1718,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
* we are obliged to add a new frame if one
* doesn't exist already
*/
- gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
if (gpf) {
/* Create new stroke */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
@@ -1971,7 +1971,7 @@ static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd);
@@ -2075,7 +2075,7 @@ static int gpencil_actframe_delete_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd == NULL) {
@@ -2150,7 +2150,7 @@ static int gpencil_actframe_delete_all_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* try to get the "active" frame - but only if it actually occurs on this frame */
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
if (gpf == NULL) {
continue;
@@ -3709,35 +3709,44 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
bool changed = false;
- /* read all selected strokes */
+ /* Read all selected strokes. */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL) {
- continue;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
continue;
}
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Flip stroke. */
- BKE_gpencil_stroke_flip(gps);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* Flip stroke. */
+ BKE_gpencil_stroke_flip(gps);
+ changed = true;
+ }
+ }
}
-
- changed = true;
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
}
@@ -3818,7 +3827,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
/* update frame to get the new location of objects */
if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
cfra_prv = gpf->framenum;
- CFRA = gpf->framenum;
+ scene->r.cfra = gpf->framenum;
BKE_scene_graph_update_for_newframe(depsgraph);
}
@@ -3846,7 +3855,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
CTX_DATA_END;
/* return frame state and DB to original state */
- CFRA = oldframe;
+ scene->r.cfra = oldframe;
BKE_scene_graph_update_for_newframe(depsgraph);
if (sctx != NULL) {
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 3f06dbfdbb3..5305c764b3a 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1748,7 +1748,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->v3d = tgpf->area->spacedata.first;
tgpf->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpf->win = CTX_wm_window(C);
- tgpf->active_cfra = CFRA;
+ tgpf->active_cfra = scene->r.cfra;
tgpf->reports = op->reports;
/* Setup space conversions. */
@@ -2222,8 +2222,8 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Hash of selected frames. */
GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64);
- /* If not multi-frame and there is no frame in CFRA for the active layer, create
- * a new frame. */
+ /* If not multi-frame and there is no frame in scene->r.cfra for the active layer,
+ * create a new frame. */
if (!is_multiedit) {
tgpf->gpf = BKE_gpencil_layer_frame_get(
tgpf->gpl,
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 0039dbae674..e7a4f2fe2dc 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -483,10 +483,10 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
tgpil->gpl = gpl;
- bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, CFRA);
+ bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, scene->r.cfra);
tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpf, true);
- gpf = gpencil_get_next_keyframe(gpl, CFRA);
+ gpf = gpencil_get_next_keyframe(gpl, scene->r.cfra);
tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpf, true);
BLI_addtail(&tgpi->ilayers, tgpil);
@@ -750,7 +750,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
tGPDinterpolate *tgpi = NULL;
/* Cannot interpolate if not between 2 frames. */
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
bGPDframe *gpf_prv = gpencil_get_previous_keyframe(gpl, cfra);
bGPDframe *gpf_next = gpencil_get_next_keyframe(gpl, cfra);
if (ELEM(NULL, gpf_prv, gpf_next)) {
@@ -1221,7 +1221,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
GP_SpaceConversion gsc;
gpencil_point_conversion_init(C, &gsc);
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate;
const int step = RNA_int_get(op->ptr, "step");
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 06343dcad43..8ff3f20cef3 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -113,7 +113,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
else {
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, add_frame_mode);
/* stroke */
bGPDstroke *gps = BKE_gpencil_stroke_new(MAX2(ob->actcol - 1, 0), totpoints, brush->size);
diff --git a/source/blender/editors/gpencil/gpencil_mesh.cc b/source/blender/editors/gpencil/gpencil_mesh.cc
index aee00d4ede3..b27e1c75746 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.cc
+++ b/source/blender/editors/gpencil/gpencil_mesh.cc
@@ -283,7 +283,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
}
/* Move scene to new frame. */
- CFRA = i;
+ scene->r.cfra = i;
BKE_scene_graph_update_for_newframe(depsgraph);
/* Loop all objects in the list. */
@@ -325,7 +325,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
}
/* Return scene frame state and DB to original state. */
- CFRA = oldframe;
+ scene->r.cfra = oldframe;
BKE_scene_graph_update_for_newframe(depsgraph);
/* Remove unused materials. */
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 7c7f532f087..13ea5179b23 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -2166,7 +2166,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
if (gpl->actframe && gpl->actframe->strokes.first) {
if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
short frame_mode = IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_COPY : GP_GETFRAME_USE_PREV;
- gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, frame_mode);
+ gpl->actframe = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, frame_mode);
}
has_layer_to_erase = true;
break;
@@ -2204,7 +2204,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
bool need_tag = p->gpl->actframe == NULL;
bGPDframe *actframe = p->gpl->actframe;
- p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode);
+ p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, add_frame_mode);
/* Only if there wasn't an active frame, need update. */
if (need_tag) {
DEG_id_tag_update(&p->gpd->id, ID_RECALC_GEOMETRY);
@@ -3755,7 +3755,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
bool is_speed_guide = ((guide->use_guide) &&
(p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index b57b8145749..befff611d58 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -292,7 +292,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
Brush *brush = tgpi->brush;
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
@@ -1195,7 +1195,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tgpi->orign_type = RNA_enum_get(op->ptr, "type");
/* set current frame number */
- tgpi->cframe = CFRA;
+ tgpi->cframe = scene->r.cfra;
/* set GP datablock */
tgpi->gpd = gpd;
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index f25d5937ac4..e27cd255217 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -1013,7 +1013,7 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
gpl = CTX_data_active_gpencil_layer(C);
}
bGPDframe *gpf = BKE_gpencil_layer_frame_get(
- gpl, CFRA, IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV);
+ gpl, scene->r.cfra, IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV);
if (gpf == NULL) {
continue;
}
@@ -1336,7 +1336,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso
bGPdata *gpd = gso->gpd;
Scene *scene = gso->scene;
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
/* only try to add a new frame if this is the first stroke, or the frame has changed */
if ((gpd == NULL) || (cfra == gso->cfra)) {
@@ -1518,7 +1518,8 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* To each point individually... */
pt = &gps->points[i];
- if ((pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) {
+ if ((i != gps->totpoints - 2) && (pt->runtime.pt_orig == NULL) &&
+ (tool != GPSCULPT_TOOL_GRAB)) {
continue;
}
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index e903d11a1e0..a19265720e8 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -759,7 +759,7 @@ static bool gpencil_select_same_layer(bContext *C)
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
bGPDstroke *gps;
bool found = false;
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index 24a0dab24c9..f6e88e05d46 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -295,7 +295,7 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op)
job->base_active = CTX_data_active_base(C);
job->ob_active = job->base_active->object;
job->image = (Image *)job->ob_active->data;
- job->frame_target = CFRA;
+ job->frame_target = scene->r.cfra;
job->use_current_frame = RNA_boolean_get(op->ptr, "use_current_frame");
/* Create a new grease pencil object or reuse selected. */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index da40eef87fd..cc3c68abc55 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -324,11 +324,15 @@ typedef enum eAnimFilter_Flags {
/** duplicate entries for animation data attached to multi-user blocks must not occur */
ANIMFILTER_NODUPLIS = (1 << 11),
+ /** avoid channel that does not have any F-curve data */
+ ANIMFILTER_FCURVESONLY = (1 << 12),
+
/** for checking if we should keep some collapsed channel around (internal use only!) */
ANIMFILTER_TMP_PEEK = (1 << 30),
/** Ignore ONLYSEL flag from #bDopeSheet.filterflag (internal use only!) */
ANIMFILTER_TMP_IGNORE_ONLYSEL = (1u << 31),
+
} eAnimFilter_Flags;
/** \} */
@@ -1042,6 +1046,8 @@ void ED_keymap_anim(struct wmKeyConfig *keyconf);
void ED_operatormacros_graph(void);
/* space_action */
void ED_operatormacros_action(void);
+/* space_nla*/
+void ED_operatormacros_nla(void);
/** \} */
diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h
index 9233b65b2ce..00831ff7cc3 100644
--- a/source/blender/editors/include/ED_curves.h
+++ b/source/blender/editors/include/ED_curves.h
@@ -6,6 +6,8 @@
#pragma once
+struct bContext;
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -19,10 +21,19 @@ void ED_operatortypes_curves(void);
#ifdef __cplusplus
# include "BKE_curves.hh"
+# include "BLI_vector_set.hh"
namespace blender::ed::curves {
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
+bool has_anything_selected(const Curves &curves_id);
+VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
+void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob);
-}
+bool editable_curves_with_surface_poll(bContext *C);
+bool curves_with_surface_poll(bContext *C);
+bool editable_curves_poll(bContext *C);
+bool curves_poll(bContext *C);
+
+} // namespace blender::ed::curves
#endif
diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h
index 8aab1533e25..b1c0b649d2b 100644
--- a/source/blender/editors/include/ED_curves_sculpt.h
+++ b/source/blender/editors/include/ED_curves_sculpt.h
@@ -10,8 +10,33 @@
extern "C" {
#endif
+struct Curves;
+
void ED_operatortypes_sculpt_curves(void);
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BLI_index_mask.hh"
+# include "BLI_vector.hh"
+
+namespace blender::ed::sculpt_paint {
+
+/**
+ * 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);
+
+/**
+ * Find points that are selected (a selection factor greater than zero),
+ * or points in curves with a selection factor greater than zero).
+ */
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+} // namespace blender::ed::sculpt_paint
+
+#endif
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 0943636a452..b6488d6da56 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -239,7 +239,7 @@ void ED_annotation_draw_ex(
/* ----------- Grease-Pencil AnimEdit API ------------------ */
/**
- * Loops over the gp-frames for a gp-layer, and applies the given callback.
+ * Loops over the GP-frames for a GP-layer, and applies the given callback.
*/
bool ED_gpencil_layer_frames_looper(struct bGPDlayer *gpl,
struct Scene *scene,
@@ -281,6 +281,11 @@ void ED_gpencil_select_frames(struct bGPDlayer *gpl, short select_mode);
void ED_gpencil_select_frame(struct bGPDlayer *gpl, int selx, short select_mode);
/**
+ * Set the layer's channel as active
+ */
+void ED_gpencil_set_active_channel(struct bGPdata *gpd, struct bGPDlayer *gpl);
+
+/**
* Delete selected frames.
*/
bool ED_gpencil_layer_frames_delete(struct bGPDlayer *gpl);
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index 163797d395d..1d63e01c84b 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -229,6 +229,16 @@ typedef enum eKeyMergeMode {
KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL,
} eKeyMergeMode;
+/* Possible errors occurring while pasting keys. */
+typedef enum eKeyPasteError {
+ /* No errors occurred */
+ KEYFRAME_PASTE_OK,
+ /* Nothing was copied */
+ KEYFRAME_PASTE_NOTHING_TO_PASTE,
+ /* No F-curves was selected to paste into*/
+ KEYFRAME_PASTE_NOWHERE_TO_PASTE
+} eKeyPasteError;
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -378,9 +388,6 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con
/* ************************************************ */
/* Destructive Editing API (keyframes_general.c) */
-void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc);
-bool delete_fcurve_keys(struct FCurve *fcu);
-void clear_fcurve_keys(struct FCurve *fcu);
bool duplicate_fcurve_keys(struct FCurve *fcu);
float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr);
@@ -416,11 +423,11 @@ void sample_fcurve(struct FCurve *fcu);
void ANIM_fcurves_copybuf_free(void);
short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data);
-short paste_animedit_keys(struct bAnimContext *ac,
- ListBase *anim_data,
- eKeyPasteOffset offset_mode,
- eKeyMergeMode merge_mode,
- bool flip);
+eKeyPasteError paste_animedit_keys(struct bAnimContext *ac,
+ ListBase *anim_data,
+ eKeyPasteOffset offset_mode,
+ eKeyMergeMode merge_mode,
+ bool flip);
/* ************************************************ */
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 30a98129ee6..b73b62d5a9d 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -137,7 +137,6 @@ void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructi
*/
struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
const struct Scene *scene,
- bool face_selected,
bool uv_selected,
bool use_winding,
bool do_islands);
@@ -425,6 +424,9 @@ void paintvert_select_ungrouped(struct Object *ob, bool extend, bool flush_flags
void paintvert_flush_flags(struct Object *ob);
void paintvert_tag_select_update(struct bContext *C, struct Object *ob);
+void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected);
+void paintvert_reveal(struct bContext *C, struct Object *ob, bool select);
+
/* mirrtopo */
typedef struct MirrTopoStore_t {
intptr_t *index_lookup;
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index ba5834fd508..048424cdee1 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -22,6 +22,7 @@ struct UndoType;
struct bContext;
struct wmKeyConfig;
struct wmOperator;
+typedef struct PaintTileMap PaintTileMap;
/* paint_ops.c */
@@ -76,7 +77,7 @@ void ED_image_undo_restore(struct UndoStep *us);
/** Export for ED_undo_sys. */
void ED_image_undosys_type(struct UndoType *ut);
-void *ED_image_paint_tile_find(struct ListBase *paint_tiles,
+void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map,
struct Image *image,
struct ImBuf *ibuf,
struct ImageUser *iuser,
@@ -84,7 +85,7 @@ void *ED_image_paint_tile_find(struct ListBase *paint_tiles,
int y_tile,
unsigned short **r_mask,
bool validate);
-void *ED_image_paint_tile_push(struct ListBase *paint_tiles,
+void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map,
struct Image *image,
struct ImBuf *ibuf,
struct ImBuf **tmpibuf,
@@ -98,7 +99,7 @@ void *ED_image_paint_tile_push(struct ListBase *paint_tiles,
void ED_image_paint_tile_lock_init(void);
void ED_image_paint_tile_lock_end(void);
-struct ListBase *ED_image_paint_tile_list_get(void);
+struct PaintTileMap *ED_image_paint_tile_map_get(void);
#define ED_IMAGE_UNDO_TILE_BITS 6
#define ED_IMAGE_UNDO_TILE_SIZE (1 << ED_IMAGE_UNDO_TILE_BITS)
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 20353c21f93..db44d9af706 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -60,6 +60,10 @@ struct SnapObjectParams {
bool use_occlusion_test : true;
/* exclude back facing geometry from snapping */
bool use_backface_culling : true;
+ /* Break nearest face snapping into steps to improve transformations across U-shaped targets. */
+ short face_nearest_steps;
+ /* Enable to force nearest face snapping to snap to target the source was initially near. */
+ bool keep_on_same_target;
};
typedef struct SnapObjectContext SnapObjectContext;
@@ -114,12 +118,33 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx,
bool sort,
struct ListBase *r_hit_list);
+/**
+ * Perform snapping.
+ *
+ * Given a 2D region value, snap to vert/edge/face/grid.
+ *
+ * \param sctx: Snap context.
+ * \param snap_to: Target elements to snap source to.
+ * \param params: Addition snapping options.
+ * \param init_co: Initial world-space coordinate of source (optional).
+ * \param mval: Current transformed screen-space coordinate or mouse position (optional).
+ * \param prev_co: Current transformed world-space coordinate of source (optional).
+ * \param dist_px: Maximum distance to snap (in pixels).
+ * \param r_loc: Snapped world-space coordinate.
+ * \param r_no: Snapped world-space normal (optional).
+ * \param r_index: Index of snapped-to target element (optional).
+ * \param r_ob: Snapped-to target object (optional).
+ * \param r_obmat: Matrix of snapped-to target object (optional).
+ * \param r_face_nor: World-space normal of snapped-to target face (optional).
+ * \return Snapped-to element, #eSnapMode.
+ */
eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
- eSnapMode snap_to,
+ const eSnapMode snap_to,
const struct SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -135,19 +160,23 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *s
* Given a 2D region value, snap to vert/edge/face.
*
* \param sctx: Snap context.
- * \param mval: Screenspace coordinate.
- * \param prev_co: Coordinate for perpendicular point calculation (optional).
+ * \param snap_to: Target elements to snap source to.
+ * \param params: Addition snapping options.
+ * \param init_co: Initial world-space coordinate of source (optional).
+ * \param mval: Current transformed screen-space coordinate or mouse position (optional).
+ * \param prev_co: Current transformed world-space coordinate of source (optional).
* \param dist_px: Maximum distance to snap (in pixels).
- * \param r_loc: hit location.
- * \param r_no: hit normal (optional).
- * \return Snap success.
+ * \param r_loc: Snapped world-space coordinate.
+ * \param r_no: Snapped world-space normal (optional).
+ * \return Snapped-to element, #eSnapMode.
*/
eSnapMode ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx,
struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
- eSnapMode snap_to,
+ const eSnapMode snap_to,
const struct SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh
new file mode 100644
index 00000000000..dfddace8899
--- /dev/null
+++ b/source/blender/editors/include/UI_abstract_view.hh
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup editorui
+ *
+ * Base class for all views (UIs to display data sets) and view items, supporting common features.
+ * https://wiki.blender.org/wiki/Source/Interface/Views
+ *
+ * One of the most important responsibilities of the base class is managing reconstruction,
+ * enabling state that is persistent over reconstructions/redraws. Other features:
+ * - Renaming
+ * - Custom context menus
+ * - Notifier listening
+ * - Drag controllers (dragging view items)
+ * - Drop controllers (dropping onto/into view items)
+ */
+
+#pragma once
+
+#include <array>
+#include <memory>
+
+#include "DNA_defs.h"
+
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+
+struct bContext;
+struct uiBlock;
+struct uiBut;
+struct uiLayout;
+struct uiViewItemHandle;
+struct wmDrag;
+struct wmNotifier;
+
+namespace blender::ui {
+
+class AbstractViewItem;
+class AbstractViewItemDropController;
+class AbstractViewItemDragController;
+
+class AbstractView {
+ friend class AbstractViewItem;
+
+ bool is_reconstructed_ = false;
+ /**
+ * Only one item can be renamed at a time. So rather than giving each item an own rename buffer
+ * (which just adds unused memory in most cases), have one here that is managed by the view.
+ *
+ * This fixed-size buffer is needed because that's what the rename button requires. In future we
+ * may be able to bind the button to a `std::string` or similar.
+ */
+ std::unique_ptr<std::array<char, MAX_NAME>> rename_buffer_;
+
+ public:
+ virtual ~AbstractView() = default;
+
+ /** Listen to a notifier, returning true if a redraw is needed. */
+ virtual bool listen(const wmNotifier &) const;
+
+ /**
+ * Makes \a item valid for display in this view. Behavior is undefined for items not registered
+ * with this.
+ */
+ void register_item(AbstractViewItem &item);
+
+ /** Only one item can be renamed at a time. */
+ bool is_renaming() const;
+ /** \return If renaming was started successfully. */
+ bool begin_renaming();
+ void end_renaming();
+ Span<char> get_rename_buffer() const;
+ MutableSpan<char> get_rename_buffer();
+
+ protected:
+ AbstractView() = default;
+
+ virtual void update_children_from_old(const AbstractView &old_view) = 0;
+
+ /**
+ * Match the view and its items against an earlier version of itself (if any) and copy the old UI
+ * state (e.g. collapsed, active, selected, renaming, etc.) to the new one. See
+ * #AbstractViewItem.update_from_old().
+ * After this, reconstruction is complete (see #is_reconstructed()).
+ */
+ void update_from_old(uiBlock &new_block);
+ /**
+ * Check if the view is fully (re-)constructed. That means, both the build function and
+ * #update_from_old() have finished.
+ */
+ bool is_reconstructed() const;
+};
+
+class AbstractViewItem {
+ friend class AbstractView;
+ friend class ViewItemAPIWrapper;
+
+ protected:
+ /**
+ * The view this item is a part of, and was registered for using #AbstractView::register_item().
+ * If this wasn't done, the behavior of items is undefined.
+ */
+ AbstractView *view_ = nullptr;
+ bool is_active_ = false;
+ bool is_renaming_ = false;
+
+ public:
+ virtual ~AbstractViewItem() = default;
+
+ virtual void build_context_menu(bContext &C, uiLayout &column) const;
+
+ /**
+ * Queries if the view item supports renaming in principle. Renaming may still fail, e.g. if
+ * another item is already being renamed.
+ */
+ virtual bool supports_renaming() const;
+ /**
+ * Try renaming the item, or the data it represents. Can assume
+ * #AbstractViewItem::supports_renaming() returned true. Sub-classes that override this should
+ * usually call this, unless they have a custom #AbstractViewItem.matches() implementation.
+ *
+ * \return True if the renaming was successful.
+ */
+ virtual bool rename(StringRefNull new_name);
+ /**
+ * Get the string that should be used for renaming, typically the item's label. This string will
+ * not be modified, but if the renaming is canceled, the value will be reset to this.
+ */
+ virtual StringRef get_rename_string() const;
+
+ /**
+ * If an item wants to support being dragged, it has to return a drag controller here.
+ * That is an object implementing #AbstractViewItemDragController.
+ */
+ virtual std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const;
+ /**
+ * If an item wants to support dropping data into it, it has to return a drop controller here.
+ * That is an object implementing #AbstractViewItemDropController.
+ *
+ * \note This drop controller may be requested for each event. The view doesn't keep a drop
+ * controller around currently. So it can not contain persistent state.
+ */
+ virtual std::unique_ptr<AbstractViewItemDropController> create_drop_controller() const;
+
+ /** Get the view this item is registered for using #AbstractView::register_item(). */
+ AbstractView &get_view() const;
+
+ /**
+ * Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
+ * can't be sure about the item state.
+ */
+ bool is_active() const;
+
+ bool is_renaming() const;
+ void begin_renaming();
+ void end_renaming();
+ void rename_apply();
+
+ template<typename ToType = AbstractViewItem>
+ static ToType *from_item_handle(uiViewItemHandle *handle);
+
+ protected:
+ AbstractViewItem() = default;
+
+ /**
+ * Compare this item's identity to \a other to check if they represent the same data.
+ * Implementations can assume that the types match already (caller must check).
+ *
+ * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active,
+ * renaming, etc.).
+ */
+ virtual bool matches(const AbstractViewItem &other) const = 0;
+
+ /**
+ * 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.
+ *
+ * \note Always call the base class implementation when overriding this!
+ */
+ virtual void update_from_old(const AbstractViewItem &old);
+
+ /**
+ * Add a text button for renaming the item to \a block. This must be used for the built-in
+ * renaming to work. This button is meant to appear temporarily. It is removed when renaming is
+ * done.
+ */
+ void add_rename_button(uiBlock &block);
+};
+
+template<typename ToType> ToType *AbstractViewItem::from_item_handle(uiViewItemHandle *handle)
+{
+ static_assert(std::is_base_of<AbstractViewItem, ToType>::value,
+ "Type must derive from and implement the AbstractViewItem interface");
+
+ return dynamic_cast<ToType *>(reinterpret_cast<AbstractViewItem *>(handle));
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Drag 'n Drop
+ * \{ */
+
+/**
+ * Class to enable dragging a view item. An item can return a drop controller for itself by
+ * implementing #AbstractViewItem::create_drag_controller().
+ */
+class AbstractViewItemDragController {
+ protected:
+ AbstractView &view_;
+
+ public:
+ AbstractViewItemDragController(AbstractView &view);
+ virtual ~AbstractViewItemDragController() = default;
+
+ virtual int get_drag_type() const = 0;
+ virtual void *create_drag_data() const = 0;
+ virtual void on_drag_start();
+
+ /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
+ * exception if the view is not of the requested type. */
+ template<class ViewType> inline ViewType &get_view() const;
+};
+
+/**
+ * Class to define the behavior when dropping something onto/into a view item, plus the behavior
+ * when dragging over this item. An item can return a drop controller for itself via a custom
+ * implementation of #AbstractViewItem::create_drop_controller().
+ */
+class AbstractViewItemDropController {
+ protected:
+ AbstractView &view_;
+
+ public:
+ AbstractViewItemDropController(AbstractView &view);
+ virtual ~AbstractViewItemDropController() = default;
+
+ /**
+ * Check if the data dragged with \a drag can be dropped on the item this controller is for.
+ * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping
+ * isn't possible on this item. Shouldn't be done too aggressively, e.g.
+ * don't set this if the drag-type can't be dropped here; only if it can
+ * but there's another reason it can't be dropped.
+ * Can assume this is a non-null pointer.
+ */
+ virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
+ /**
+ * Custom text to display when dragging over a view item. Should explain what happens when
+ * dropping the data onto this item. Will only be used if #AbstractViewItem::can_drop()
+ * returns true, so the implementing override doesn't have to check that again.
+ * The returned value must be a translated string.
+ */
+ virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
+ /**
+ * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this
+ * controller is for.
+ */
+ virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0;
+
+ /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
+ * exception if the view is not of the requested type. */
+ template<class ViewType> inline ViewType &get_view() const;
+};
+
+template<class ViewType> ViewType &AbstractViewItemDragController::get_view() const
+{
+ static_assert(std::is_base_of<AbstractView, ViewType>::value,
+ "Type must derive from and implement the ui::AbstractView interface");
+ return dynamic_cast<ViewType &>(view_);
+}
+
+template<class ViewType> ViewType &AbstractViewItemDropController::get_view() const
+{
+ static_assert(std::is_base_of<AbstractView, ViewType>::value,
+ "Type must derive from and implement the ui::AbstractView interface");
+ return dynamic_cast<ViewType &>(view_);
+}
+
+/** \} */
+
+} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh
index 6f553f4fad1..402c0c8512f 100644
--- a/source/blender/editors/include/UI_grid_view.hh
+++ b/source/blender/editors/include/UI_grid_view.hh
@@ -13,12 +13,13 @@
#include "BLI_map.hh"
#include "BLI_vector.hh"
+#include "UI_abstract_view.hh"
#include "UI_resources.h"
struct bContext;
struct PreviewImage;
struct uiBlock;
-struct uiButGridTile;
+struct uiButViewItem;
struct uiLayout;
struct View2D;
struct wmNotifier;
@@ -31,43 +32,29 @@ class AbstractGridView;
/** \name Grid-View Item Type
* \{ */
-class AbstractGridViewItem {
+class AbstractGridViewItem : public AbstractViewItem {
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;
+ /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */
+ uiButViewItem *view_item_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);
+ /** See AbstractViewItem::matches(). */
+ virtual bool matches(const AbstractViewItem &other) const override;
+
/** Called when the item's state changes from inactive to active. */
virtual void on_activate();
/**
@@ -77,13 +64,6 @@ class AbstractGridViewItem {
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
@@ -111,7 +91,7 @@ struct GridViewStyle {
int tile_height = 0;
};
-class AbstractGridView {
+class AbstractGridView : public AbstractView {
friend class AbstractGridViewItem;
friend class GridViewBuilder;
friend class GridViewLayoutBuilder;
@@ -122,7 +102,6 @@ class AbstractGridView {
* #update_from_old(). */
Map<StringRef, AbstractGridViewItem *> item_map_;
GridViewStyle style_;
- bool is_reconstructed_ = false;
public:
AbstractGridView();
@@ -131,9 +110,6 @@ class AbstractGridView {
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).
@@ -154,19 +130,8 @@ class AbstractGridView {
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);
+ void update_children_from_old(const AbstractView &old_view) override;
AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &item_to_match,
const AbstractGridView &view_to_search_in) const;
/**
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index f1c0acf43f7..09057fd846e 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -652,7 +652,7 @@ DEF_ICON(PARTICLE_TIP)
DEF_ICON(PARTICLE_PATH)
/* EDITING */
-DEF_ICON_BLANK(669)
+DEF_ICON(SNAP_FACE_NEAREST)
DEF_ICON(SNAP_FACE_CENTER)
DEF_ICON(SNAP_PERPENDICULAR)
DEF_ICON(SNAP_MIDPOINT)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index b2ec2102ddd..a8d25b75036 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -72,14 +72,10 @@ typedef struct uiBut uiBut;
typedef struct uiButExtraOpIcon uiButExtraOpIcon;
typedef struct uiLayout uiLayout;
typedef struct uiPopupBlockHandle uiPopupBlockHandle;
-/* C handle for C++ #ui::AbstractTreeView type. */
-typedef struct uiTreeViewHandle uiTreeViewHandle;
-/* C handle for C++ #ui::AbstractTreeViewItem type. */
-typedef struct uiTreeViewItemHandle uiTreeViewItemHandle;
-/* C handle for C++ #ui::AbstractGridView type. */
-typedef struct uiGridViewHandle uiGridViewHandle;
-/* C handle for C++ #ui::AbstractGridViewItem type. */
-typedef struct uiGridViewItemHandle uiGridViewItemHandle;
+/* C handle for C++ #ui::AbstractView type. */
+typedef struct uiViewHandle uiViewHandle;
+/* C handle for C++ #ui::AbstractViewItem type. */
+typedef struct uiViewItemHandle uiViewItemHandle;
/* Defines */
@@ -393,10 +389,8 @@ typedef enum {
/** Resize handle (resize uilist). */
UI_BTYPE_GRIP = 57 << 9,
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,
+ /* An item a view (see #ui::AbstractViewItem). */
+ UI_BTYPE_VIEW_ITEM = 59 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@@ -1687,8 +1681,6 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name);
*/
void UI_but_hint_drawstr_set(uiBut *but, const char *string);
-void UI_but_treerow_indentation_set(uiBut *but, int indentation);
-
void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]);
void UI_but_number_step_size_set(uiBut *but, float step_size);
@@ -3203,54 +3195,44 @@ void UI_interface_tag_script_reload(void);
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);
+bool UI_view_item_is_active(const uiViewItemHandle *item_handle);
+bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle);
/**
- * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't
- * support dragging, i.e. it won't create a drag-controller upon request.
- * \return True if dragging started successfully, otherwise false.
- */
-bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_);
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
- const struct wmDrag *drag,
- const char **r_disabled_hint);
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag);
-/**
- * Let a tree-view item handle a drop event.
- * \return True if the drop was handled by the tree-view item.
- */
-bool UI_tree_view_item_drop_handle(struct bContext *C,
- const uiTreeViewItemHandle *item_,
- const struct ListBase *drags);
-/**
- * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around
- * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed,
+ * Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around
+ * #AbstractViewItem::supports_renaming(). This also checks if there is another item being renamed,
* and returns false if so.
*/
-bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle);
-void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle);
+bool UI_view_item_can_rename(const uiViewItemHandle *item_handle);
+void UI_view_item_begin_rename(uiViewItemHandle *item_handle);
-void UI_tree_view_item_context_menu_build(struct bContext *C,
- const uiTreeViewItemHandle *item,
- uiLayout *column);
+void UI_view_item_context_menu_build(struct bContext *C,
+ const uiViewItemHandle *item_handle,
+ uiLayout *column);
/**
- * \param xy: Coordinate to find a tree-row item at, in window space.
+ * Attempt to start dragging \a item_. This will not work if the view item doesn't
+ * support dragging, i.e. if it won't create a drag-controller upon request.
+ * \return True if dragging started successfully, otherwise false.
*/
-uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region,
- const int xy[2]) ATTR_NONNULL(1, 2);
-uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region);
-
+bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_);
+bool UI_view_item_can_drop(const uiViewItemHandle *item_,
+ const struct wmDrag *drag,
+ const char **r_disabled_hint);
+char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag);
/**
- * Listen to \a notifier, returning true if the region should redraw.
+ * Let a view item handle a drop event.
+ * \return True if the drop was handled by the view item.
*/
-bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier);
+bool UI_view_item_drop_handle(struct bContext *C,
+ const uiViewItemHandle *item_,
+ const struct ListBase *drags);
+
/**
- * Listen to \a notifier, returning true if the region should redraw.
+ * \param xy: Coordinate to find a view item at, in window space.
*/
-bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view, const wmNotifier *notifier);
+uiViewItemHandle *UI_region_views_find_item_at(const struct ARegion *region, const int xy[2])
+ ATTR_NONNULL();
+uiViewItemHandle *UI_region_views_find_active_item(const struct ARegion *region);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
index 3dc56b01993..82bfdd7e212 100644
--- a/source/blender/editors/include/UI_interface.hh
+++ b/source/blender/editors/include/UI_interface.hh
@@ -46,7 +46,7 @@ void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path);
void attribute_search_add_items(
StringRefNull str,
- bool is_output,
+ bool can_create_attribute,
Span<const nodes::geometry_nodes_eval_log::GeometryAttributeInfo *> infos,
uiSearchItems *items,
bool is_first);
@@ -54,12 +54,12 @@ void attribute_search_add_items(
} // namespace blender::ui
/**
- * Override this for all available tree types.
+ * Override this for all available view types.
*/
blender::ui::AbstractGridView *UI_block_add_view(
uiBlock &block,
blender::StringRef idname,
- std::unique_ptr<blender::ui::AbstractGridView> tree_view);
+ std::unique_ptr<blender::ui::AbstractGridView> grid_view);
blender::ui::AbstractTreeView *UI_block_add_view(
uiBlock &block,
blender::StringRef idname,
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index 1aeb13ca5cc..872a6085060 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -9,7 +9,6 @@
#pragma once
-#include <array>
#include <functional>
#include <memory>
#include <string>
@@ -19,23 +18,19 @@
#include "BLI_function_ref.hh"
#include "BLI_vector.hh"
+#include "UI_abstract_view.hh"
#include "UI_resources.h"
struct bContext;
struct uiBlock;
struct uiBut;
-struct uiButTreeRow;
+struct uiButViewItem;
struct uiLayout;
-struct wmDrag;
-struct wmEvent;
-struct wmNotifier;
namespace blender::ui {
class AbstractTreeView;
class AbstractTreeViewItem;
-class AbstractTreeViewItemDropController;
-class AbstractTreeViewItemDragController;
/* ---------------------------------------------------------------------- */
/** \name Tree-View Item Container
@@ -112,45 +107,20 @@ using TreeViewOrItem = TreeViewItemContainer;
/** \name Tree-View Base Class
* \{ */
-class AbstractTreeView : public TreeViewItemContainer {
+class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
friend class AbstractTreeViewItem;
friend class TreeViewBuilder;
- /**
- * Only one item can be renamed at a time. So the tree is informed about the renaming state to
- * enforce that.
- */
- std::unique_ptr<std::array<char, MAX_NAME>> rename_buffer_;
-
- bool is_reconstructed_ = false;
-
public:
virtual ~AbstractTreeView() = default;
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;
-
protected:
virtual void build_tree() = 0;
- /**
- * Check if the tree is fully (re-)constructed. That means, both #build_tree() and
- * #update_from_old() have finished.
- */
- bool is_reconstructed() const;
-
private:
- /**
- * Match the tree-view against an earlier version of itself (if any) and copy the old UI state
- * (e.g. collapsed, active, selected, renaming, etc.) to the new one. See
- * #AbstractTreeViewItem.update_from_old().
- */
- void update_from_old(uiBlock &new_block);
+ void update_children_from_old(const AbstractView &old_view) override;
static void update_children_from_old_recursive(const TreeViewOrItem &new_items,
const TreeViewOrItem &old_items);
static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item,
@@ -177,7 +147,7 @@ class AbstractTreeView : public TreeViewItemContainer {
* It also stores state information that needs to be persistent over redraws, like the collapsed
* state.
*/
-class AbstractTreeViewItem : public TreeViewItemContainer {
+class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContainer {
friend class AbstractTreeView;
friend class TreeViewLayoutBuilder;
/* Higher-level API. */
@@ -185,20 +155,17 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
private:
bool is_open_ = false;
- bool is_active_ = false;
- bool is_renaming_ = false;
protected:
/** 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;
+ /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */
+ uiButViewItem *view_item_but_ = nullptr;
public:
virtual ~AbstractTreeViewItem() = default;
virtual void build_row(uiLayout &row) = 0;
- virtual void build_context_menu(bContext &C, uiLayout &column) const;
AbstractTreeView &get_tree_view() const;
@@ -210,11 +177,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* can't be sure about the item state.
*/
bool is_collapsed() 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:
/**
@@ -227,31 +189,21 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
virtual std::optional<bool> should_be_active() const;
- /**
- * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if
- * another item is already being renamed.
- */
- virtual bool supports_renaming() const;
- /**
- * Try renaming the item, or the data it represents. Can assume
- * #AbstractTreeViewItem::supports_renaming() returned true. Sub-classes that override this
- * should usually call this, unless they have a custom #AbstractTreeViewItem.matches().
- *
- * \return True if the renaming was successful.
- */
- virtual bool rename(StringRefNull new_name);
+ /** See AbstractViewItem::get_rename_string(). */
+ virtual StringRef get_rename_string() const override;
+ /** See AbstractViewItem::rename(). */
+ virtual bool rename(StringRefNull new_name) override;
/**
* Return whether the item can be collapsed. Used to disable collapsing for items with children.
*/
virtual bool supports_collapsing() const;
- /**
- * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of
- * the last redraw to this item. If sub-classes introduce more advanced state they should
- * override this and make it update their state accordingly.
- */
- virtual void update_from_old(const AbstractTreeViewItem &old);
+ /** See #AbstractViewItem::matches(). */
+ virtual bool matches(const AbstractViewItem &other) const override;
+
+ /** See #AbstractViewItem::update_from_old(). */
+ virtual void update_from_old(const AbstractViewItem &old) override;
/**
* Compare this item to \a other to check if they represent the same data.
@@ -259,22 +211,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* open/closed, active, etc.). Items are only matched if their parents also match.
* By default this just matches the item's label (if the parents match!). If that isn't
* good enough for a sub-class, that can override it.
- */
- virtual bool matches(const AbstractTreeViewItem &other) const;
-
- /**
- * If an item wants to support being dragged, it has to return a drag controller here.
- * That is an object implementing #AbstractTreeViewItemDragController.
- */
- virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const;
- /**
- * If an item wants to support dropping data into it, it has to return a drop controller here.
- * That is an object implementing #AbstractTreeViewItemDropController.
*
- * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop
- * controller around currently. So it can not contain persistent state.
+ * TODO #matches_single() is a rather temporary name, used to indicate that this only compares
+ * the item itself, not the parents. Item matching is expected to change quite a bit anyway.
*/
- virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const;
+ virtual bool matches_single(const AbstractTreeViewItem &other) const;
/**
* Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate()
@@ -292,29 +233,24 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
bool is_hovered() const;
bool is_collapsible() const;
- bool is_renaming() const;
void ensure_parents_uncollapsed();
- uiButTreeRow *tree_row_button();
+ uiButViewItem *view_item_button();
private:
- static void rename_button_fn(bContext *, void *, char *);
- static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but);
static void tree_row_click_fn(struct bContext *, void *, void *);
static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *);
static bool is_collapse_chevron_but(const uiBut *but);
/** See #AbstractTreeView::change_state_delayed() */
void change_state_delayed();
- void end_renaming();
void add_treerow_button(uiBlock &block);
void add_indent(uiLayout &row) const;
void add_collapse_chevron(uiBlock &block) const;
void add_rename_button(uiLayout &row);
- bool matches_including_parents(const AbstractTreeViewItem &other) const;
bool has_active_child() const;
int count_parents() const;
};
@@ -322,69 +258,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Drag 'n Drop
- * \{ */
-
-/**
- * Class to enable dragging a tree-item. An item can return a drop controller for itself via a
- * custom implementation of #AbstractTreeViewItem::create_drag_controller().
- */
-class AbstractTreeViewItemDragController {
- protected:
- AbstractTreeView &tree_view_;
-
- public:
- AbstractTreeViewItemDragController(AbstractTreeView &tree_view);
- virtual ~AbstractTreeViewItemDragController() = default;
-
- virtual int get_drag_type() const = 0;
- virtual void *create_drag_data() const = 0;
- virtual void on_drag_start();
-
- template<class TreeViewType> inline TreeViewType &tree_view() const;
-};
-
-/**
- * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this
- * item. An item can return a drop controller for itself via a custom implementation of
- * #AbstractTreeViewItem::create_drop_controller().
- */
-class AbstractTreeViewItemDropController {
- protected:
- AbstractTreeView &tree_view_;
-
- public:
- AbstractTreeViewItemDropController(AbstractTreeView &tree_view);
- virtual ~AbstractTreeViewItemDropController() = default;
-
- /**
- * Check if the data dragged with \a drag can be dropped on the item this controller is for.
- * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping
- * isn't possible on this item. Shouldn't be done too aggressively, e.g.
- * don't set this if the drag-type can't be dropped here; only if it can
- * but there's another reason it can't be dropped.
- * Can assume this is a non-null pointer.
- */
- virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
- /**
- * Custom text to display when dragging over a tree item. Should explain what happens when
- * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop()
- * returns true, so the implementing override doesn't have to check that again.
- * The returned value must be a translated string.
- */
- virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
- /**
- * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this
- * controller is for.
- */
- virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0;
-
- template<class TreeViewType> inline TreeViewType &tree_view() const;
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
/** \name Predefined Tree-View Item Types
*
* Common, Basic Tree-View Item Types.
@@ -455,18 +328,4 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args)
add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
}
-template<class TreeViewType> TreeViewType &AbstractTreeViewItemDragController::tree_view() const
-{
- static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value,
- "Type must derive from and implement the AbstractTreeView interface");
- return static_cast<TreeViewType &>(tree_view_);
-}
-
-template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const
-{
- static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value,
- "Type must derive from and implement the AbstractTreeView interface");
- return static_cast<TreeViewType &>(tree_view_);
-}
-
} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 5c4eb254462..e508c96b4f1 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y);
bool UI_view2d_view_to_region_clip(
const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL();
+bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
+ const float xy_a[2],
+ const float xy_b[2],
+ int r_region_a[2],
+ int r_region_b[2]) ATTR_NONNULL();
+
/**
* Convert from 2d-view space to screen/region space
*
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 2a1852bd6e7..07c3bfdafbf 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
+ .
../include
../../blenfont
../../blenkernel
@@ -25,7 +26,13 @@ set(INC
)
set(SRC
- grid_view.cc
+ eyedroppers/interface_eyedropper.c
+ eyedroppers/eyedropper_color.c
+ eyedroppers/eyedropper_colorband.c
+ eyedroppers/eyedropper_datablock.c
+ eyedroppers/eyedropper_depth.c
+ eyedroppers/eyedropper_driver.c
+ eyedroppers/eyedropper_gpencil_color.c
interface.cc
interface_align.c
interface_anim.c
@@ -35,13 +42,6 @@ set(SRC
interface_drag.cc
interface_draw.c
interface_dropboxes.cc
- interface_eyedropper.c
- interface_eyedropper_color.c
- interface_eyedropper_colorband.c
- interface_eyedropper_datablock.c
- interface_eyedropper_depth.c
- interface_eyedropper_driver.c
- interface_eyedropper_gpencil_color.c
interface_handlers.c
interface_icons.c
interface_icons_event.c
@@ -67,17 +67,20 @@ set(SRC
interface_templates.c
interface_undo.c
interface_utils.cc
- interface_view.cc
interface_widgets.c
resources.c
- tree_view.cc
view2d.cc
view2d_draw.cc
view2d_edge_pan.cc
view2d_gizmo_navigate.cc
view2d_ops.cc
+ views/abstract_view.cc
+ views/abstract_view_item.cc
+ views/grid_view.cc
+ views/interface_view.cc
+ views/tree_view.cc
- interface_eyedropper_intern.h
+ eyedroppers/eyedropper_intern.h
interface_intern.h
interface_regions_intern.h
)
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
index c015a60de89..9c430afd5f0 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
@@ -50,7 +50,7 @@
#include "RE_pipeline.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
typedef struct Eyedropper {
struct ColorManagedDisplay *display;
@@ -479,7 +479,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
eyedropper_color_sample(C, eye, event->xy);
diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
index a69c36fefbd..3f63a8020ed 100644
--- a/source/blender/editors/interface/interface_eyedropper_colorband.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
@@ -35,7 +35,7 @@
#include "interface_intern.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
typedef struct Colorband_RNAUpdateCb {
PointerRNA ptr;
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
index 01b958576b6..1710d0faa4d 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
@@ -38,7 +38,7 @@
#include "ED_space_api.h"
#include "ED_view3d.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
index 3c6f127582a..3fb5a74944b 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
@@ -38,7 +38,7 @@
#include "ED_space_api.h"
#include "ED_view3d.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
index 0cacb55c60c..a9314df44a5 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
@@ -32,7 +32,7 @@
#include "ED_keyframing.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
typedef struct DriverDropper {
@@ -83,14 +83,14 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
if (but == NULL) {
return;
}
- /* Get paths for src... */
+ /* Get paths for the source. */
PointerRNA *target_ptr = &but->rnapoin;
PropertyRNA *target_prop = but->rnaprop;
const int target_index = but->rnaindex;
char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
- /* ... and destination */
+ /* Get paths for the destination. */
char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop);
/* Now create driver(s) */
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
index f3c70e6a96a..c3879fe8bbd 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
@@ -46,7 +46,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
typedef struct EyedropperGPencil {
diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
index 76316a85807..946f2145d1d 100644
--- a/source/blender/editors/interface/interface_eyedropper_intern.h
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
@@ -3,7 +3,7 @@
/** \file
* \ingroup edinterface
*
- * Share between interface_eyedropper_*.c files.
+ * Share between `interface/eyedropper/` files.
*/
#pragma once
@@ -35,7 +35,7 @@ void datadropper_win_area_find(const struct bContext *C,
*
* Special check for image or nodes where we MAY have HDR pixels which don't display.
*
- * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking.
+ * \note Exposed by 'eyedropper_intern.h' for use with color band picking.
*/
void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]);
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
index eaec1e249b7..c6fb8f0ab68 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
@@ -21,7 +21,7 @@
#include "interface_intern.h"
-#include "interface_eyedropper_intern.h" /* own include */
+#include "eyedropper_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/* Keymap
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index 3f623566807..2f9e69137ed 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -769,20 +769,11 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
return false;
}
- if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) {
- uiButTreeRow *but_treerow = (uiButTreeRow *)but;
- uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut;
- if (!but_treerow->tree_item || !oldbut_treerow->tree_item ||
- !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) {
- return false;
- }
- }
-
- 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)) {
+ if ((but->type == UI_BTYPE_VIEW_ITEM) && (oldbut->type == UI_BTYPE_VIEW_ITEM)) {
+ uiButViewItem *but_item = (uiButViewItem *)but;
+ uiButViewItem *oldbut_item = (uiButViewItem *)oldbut;
+ if (!but_item->view_item || !oldbut_item->view_item ||
+ !UI_view_item_matches(but_item->view_item, oldbut_item->view_item)) {
return false;
}
}
@@ -907,16 +898,10 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
progress_oldbut->progress = progress_but->progress;
break;
}
- case UI_BTYPE_TREEROW: {
- uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut;
- uiButTreeRow *treerow_newbut = (uiButTreeRow *)but;
- SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item);
- break;
- }
- 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);
+ case UI_BTYPE_VIEW_ITEM: {
+ uiButViewItem *view_item_oldbut = (uiButViewItem *)oldbut;
+ uiButViewItem *view_item_newbut = (uiButViewItem *)but;
+ SWAP(uiViewItemHandle *, view_item_newbut->view_item, view_item_oldbut->view_item);
break;
}
default:
@@ -1013,7 +998,7 @@ static bool ui_but_update_from_old_block(const bContext *C,
/* 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 (ui_but_is_view_item(but)) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
flag_copy |= UI_ACTIVE;
}
@@ -2245,21 +2230,12 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
}
}
break;
- case UI_BTYPE_TREEROW: {
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
-
- is_push = -1;
- if (tree_row_but->tree_item) {
- is_push = UI_tree_view_item_is_active(tree_row_but->tree_item);
- }
- break;
- }
- case UI_BTYPE_GRID_TILE: {
- uiButGridTile *grid_tile_but = (uiButGridTile *)but;
+ case UI_BTYPE_VIEW_ITEM: {
+ const uiButViewItem *view_item_but = (const uiButViewItem *)but;
is_push = -1;
- if (grid_tile_but->view_item) {
- is_push = UI_grid_view_item_is_active(grid_tile_but->view_item);
+ if (view_item_but->view_item) {
+ is_push = UI_view_item_is_active(view_item_but->view_item);
}
break;
}
@@ -4011,17 +3987,13 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButCurveProfile);
alloc_str = "uiButCurveProfile";
break;
- case UI_BTYPE_TREEROW:
- 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";
+ case UI_BTYPE_VIEW_ITEM:
+ alloc_size = sizeof(uiButViewItem);
+ alloc_str = "uiButViewItem";
break;
default:
alloc_size = sizeof(uiBut);
@@ -4214,7 +4186,6 @@ static uiBut *ui_def_but(uiBlock *block,
UI_BTYPE_BLOCK,
UI_BTYPE_BUT_MENU,
UI_BTYPE_SEARCH_MENU,
- UI_BTYPE_TREEROW,
UI_BTYPE_POPOVER)) {
but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
}
@@ -6469,15 +6440,6 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
return but;
}
-void UI_but_treerow_indentation_set(uiBut *but, int indentation)
-{
- uiButTreeRow *but_row = (uiButTreeRow *)but;
- BLI_assert(but->type == UI_BTYPE_TREEROW);
-
- but_row->indentation = indentation;
- BLI_assert(indentation >= 0);
-}
-
void UI_but_hint_drawstr_set(uiBut *but, const char *string)
{
ui_but_add_shortcut(but, string, false);
@@ -6623,7 +6585,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
MenuType *mt = UI_but_menutype_get(but);
if (mt) {
if (type == BUT_GET_RNA_LABEL) {
- tmp = BLI_strdup(mt->label);
+ tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label));
}
else {
/* Not all menus are from Python. */
@@ -6653,7 +6615,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
PanelType *pt = UI_but_paneltype_get(but);
if (pt) {
if (type == BUT_GET_RNA_LABEL) {
- tmp = BLI_strdup(pt->label);
+ tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label));
}
else {
/* Not all panels are from Python. */
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index e58298cdaee..518fe65ee09 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -927,11 +927,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
{
const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C);
- uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy);
- if (treerow_but) {
- BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW);
- UI_tree_view_item_context_menu_build(
- C, treerow_but->tree_item, uiLayoutColumn(layout, false));
+ uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region,
+ event->xy);
+ if (view_item_but) {
+ BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM);
+ UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false));
uiItemS(layout);
}
}
diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc
index 4c68870b2c7..1db3db32411 100644
--- a/source/blender/editors/interface/interface_drag.cc
+++ b/source/blender/editors/interface/interface_drag.cc
@@ -122,7 +122,7 @@ bool ui_but_drag_is_draggable(const uiBut *but)
void ui_but_drag_start(bContext *C, uiBut *but)
{
- wmDrag *drag = WM_event_start_drag(C,
+ wmDrag *drag = WM_drag_data_create(C,
but->icon,
but->dragtype,
but->dragpoin,
@@ -136,6 +136,8 @@ void ui_but_drag_start(bContext *C, uiBut *but)
WM_event_drag_image(drag, but->imb, but->imb_scale);
}
+ WM_event_start_prepared_drag(C, drag);
+
/* 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)) {
diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc
index 9d3c1372b15..b72d8d2c238 100644
--- a/source/blender/editors/interface/interface_dropboxes.cc
+++ b/source/blender/editors/interface/interface_dropboxes.cc
@@ -22,15 +22,14 @@
#include "UI_interface.h"
/* -------------------------------------------------------------------- */
-/** \name Tree View Drag/Drop Callbacks
+/** \name View Drag/Drop Callbacks
* \{ */
-static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
+static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region,
- event->xy);
- if (!hovered_tree_item) {
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy);
+ if (!hovered_item) {
return false;
}
@@ -39,21 +38,21 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve
}
drag->drop_state.free_disabled_info = false;
- return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->drop_state.disabled_info);
+ return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info);
}
-static char *ui_tree_view_drop_tooltip(bContext *C,
- wmDrag *drag,
- const int xy[2],
- wmDropBox *UNUSED(drop))
+static char *ui_view_drop_tooltip(bContext *C,
+ wmDrag *drag,
+ const int xy[2],
+ wmDropBox *UNUSED(drop))
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy);
- if (!hovered_tree_item) {
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy);
+ if (!hovered_item) {
return nullptr;
}
- return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag);
+ return UI_view_item_drop_tooltip(hovered_item, drag);
}
/** \} */
@@ -140,12 +139,7 @@ void ED_dropboxes_ui()
{
ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0);
- WM_dropbox_add(lb,
- "UI_OT_tree_view_drop",
- ui_tree_view_drop_poll,
- nullptr,
- nullptr,
- ui_tree_view_drop_tooltip);
+ WM_dropbox_add(lb, "UI_OT_view_drop", ui_view_drop_poll, nullptr, nullptr, ui_view_drop_tooltip);
WM_dropbox_add(lb,
"UI_OT_drop_name",
ui_drop_name_poll,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 7c00c4f1875..80fd0cbe16e 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -1151,7 +1151,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
data->applied = true;
}
-static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+static void ui_apply_but_VIEW_ITEM(bContext *C,
+ uiBlock *block,
+ uiBut *but,
+ uiHandleButtonData *data)
{
if (data->apply_through_extra_icon) {
/* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons.
@@ -2128,10 +2131,10 @@ static bool ui_but_drag_init(bContext *C,
return false;
}
}
- else if (but->type == UI_BTYPE_TREEROW) {
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- if (tree_row_but->tree_item) {
- UI_tree_view_item_drag_start(C, tree_row_but->tree_item);
+ else if (but->type == UI_BTYPE_VIEW_ITEM) {
+ const uiButViewItem *view_item_but = (uiButViewItem *)but;
+ if (view_item_but->view_item) {
+ UI_view_item_drag_start(C, view_item_but->view_item);
}
}
else {
@@ -2289,11 +2292,8 @@ 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);
+ case UI_BTYPE_VIEW_ITEM:
+ ui_apply_but_VIEW_ITEM(C, block, but, data);
break;
case UI_BTYPE_LISTROW:
ui_apply_but_LISTROW(C, block, but, data);
@@ -3172,19 +3172,11 @@ static bool ui_textedit_insert_buf(uiBut *but,
return changed;
}
-static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
+static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
{
+ BLI_assert(isascii(ascii));
const char buf[2] = {ascii, '\0'};
-
- if (UI_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) {
- printf(
- "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii);
-
- return false;
- }
-
- /* in some cases we want to allow invalid utf8 chars */
- return ui_textedit_insert_buf(but, data, buf, 1);
+ return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1);
}
static void ui_textedit_move(uiBut *but,
@@ -3897,30 +3889,27 @@ static void ui_do_but_textedit(
}
}
- if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
+ if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
#ifdef WITH_INPUT_IME
&& !is_ime_composing && !WM_event_is_ime_switch(event)
#endif
) {
- char ascii = event->ascii;
+ char utf8_buf_override[2] = {'\0', '\0'};
const char *utf8_buf = event->utf8_buf;
/* Exception that's useful for number buttons, some keyboard
* numpads have a comma instead of a period. */
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */
- if (event->type == EVT_PADPERIOD && ascii == ',') {
- ascii = '.';
- utf8_buf = NULL; /* force ascii fallback */
+ if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) {
+ utf8_buf_override[0] = '.';
+ utf8_buf = utf8_buf_override;
}
}
- if (utf8_buf && utf8_buf[0]) {
+ if (utf8_buf[0]) {
const int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
BLI_assert(utf8_buf_len != -1);
- changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len);
- }
- else {
- changed = ui_textedit_insert_ascii(but, data, ascii);
+ changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len);
}
retval = WM_UI_HANDLER_BREAK;
@@ -3952,6 +3941,9 @@ static void ui_do_but_textedit(
else if (event->type == WM_IME_COMPOSITE_END) {
changed = true;
}
+#else
+ /* Prevent the function from being unused. */
+ (void)ui_textedit_insert_ascii;
#endif
if (changed) {
@@ -4517,7 +4509,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
if (event->type == EVT_UNKNOWNKEY) {
@@ -4583,7 +4575,7 @@ static int ui_do_but_KEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
@@ -4772,53 +4764,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_TREEROW(bContext *C,
- uiBut *but,
- uiHandleButtonData *data,
- const wmEvent *event)
-{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW);
-
- 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;
- UI_tree_view_item_begin_rename(tree_row_but->tree_item);
- ED_region_tag_redraw(CTX_wm_region(C));
- return WM_UI_HANDLER_BREAK;
- }
- }
- }
- else if (data->state == BUTTON_STATE_WAIT_DRAG) {
- /* Let "default" button handling take care of the drag logic. */
- return ui_do_but_EXIT(C, but, data, event);
- }
-
- return WM_UI_HANDLER_CONTINUE;
-}
-
-static int ui_do_but_GRIDTILE(bContext *C,
- uiBut *but,
- uiHandleButtonData *data,
- const wmEvent *event)
+static int ui_do_but_VIEW_ITEM(bContext *C,
+ uiBut *but,
+ uiHandleButtonData *data,
+ const wmEvent *event)
{
- BLI_assert(but->type == UI_BTYPE_GRID_TILE);
+ uiButViewItem *view_item_but = (uiButViewItem *)but;
+ BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE) {
@@ -4839,8 +4791,7 @@ static int ui_do_but_GRIDTILE(bContext *C,
case KM_DBL_CLICK:
data->cancel = true;
- // uiButGridTile *grid_tile_but = (uiButGridTile *)but;
- // UI_tree_view_item_begin_rename(grid_tile_but->tree_item);
+ UI_view_item_begin_rename(view_item_but->view_item);
ED_region_tag_redraw(CTX_wm_region(C));
return WM_UI_HANDLER_BREAK;
}
@@ -7988,7 +7939,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
* to spawn the context menu should also activate the item. This makes it clear which item
* will be operated on.
* Apply the button immediately, so context menu polls get the right active item. */
- if (ELEM(but->type, UI_BTYPE_TREEROW)) {
+ if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) {
ui_apply_but(C, but->block, but, but->active, true);
}
@@ -8053,11 +8004,8 @@ 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);
+ case UI_BTYPE_VIEW_ITEM:
+ retval = ui_do_but_VIEW_ITEM(C, but, data, event);
break;
case UI_BTYPE_SCROLL:
retval = ui_do_but_SCROLL(C, block, but, data, event);
@@ -8149,7 +8097,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
#ifdef USE_DRAG_MULTINUM
data = but->active;
if (data) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) ||
+ if (ISMOUSE_MOTION(event->type) ||
/* if we started dragging, progress on any event */
(data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) {
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
@@ -9487,7 +9435,7 @@ static int ui_list_activate_hovered_row(bContext *C,
}
/* Simulate click on listrow button itself (which may be overlapped by another button). Also
- * calls the custom activate operator (ui_list->custom_activate_opname). */
+ * calls the custom activate operator (#uiListDyn::custom_activate_optype). */
UI_but_execute(C, region, listrow);
((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype;
@@ -9558,13 +9506,13 @@ static void ui_list_activate_row_from_index(
uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox);
if (new_active_row) {
/* Preferred way to update the active item, also calls the custom activate operator
- * (#uiList.custom_activate_opname). */
+ * (#uiListDyn::custom_activate_optype). */
UI_but_execute(C, region, new_active_row);
}
else {
/* A bit ugly, set the active index in RNA directly. That's because a button that's
* scrolled away in the list box isn't created at all.
- * The custom activate operator (#uiList.custom_activate_opname) is not called in this case
+ * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case
* (which may need the row button context). */
RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index);
RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
@@ -9733,7 +9681,7 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio
}
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
- if (ui_but_is_view_item(but)) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
but->flag &= ~UI_ACTIVE;
has_view_item = true;
}
@@ -9760,7 +9708,7 @@ static int ui_handle_view_item_event(bContext *C,
ARegion *region,
uiBut *view_but)
{
- BLI_assert(ui_but_is_view_item(view_but));
+ BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM);
if (event->type == LEFTMOUSE) {
/* Will free active button if there already is one. */
ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER);
@@ -10148,8 +10096,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
/* Pass, needed to click-exit outside of non-floating menus. */
ui_region_auto_open_clear(but->active->region);
}
- else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) &&
- ISMOUSE(event->type)) {
+ else if (ISMOUSE_BUTTON(event->type)) {
if (!ui_but_contains_point_px(but, but->active->region, event->xy)) {
but = NULL;
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 791e51b81a6..03b9d03a6e3 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -343,20 +343,12 @@ typedef struct uiButProgressbar {
float progress;
} uiButProgressbar;
-/** Derived struct for #UI_BTYPE_TREEROW. */
-typedef struct uiButTreeRow {
+typedef struct uiButViewItem {
uiBut but;
- uiTreeViewItemHandle *tree_item;
- int indentation;
-} uiButTreeRow;
-
-/** Derived struct for #UI_BTYPE_GRID_TILE. */
-typedef struct uiButGridTile {
- uiBut but;
-
- uiGridViewItemHandle *view_item;
-} uiButGridTile;
+ /* C-Handle to the view item this button was created for. */
+ uiViewItemHandle *view_item;
+} uiButViewItem;
/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
@@ -1372,7 +1364,6 @@ 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,
@@ -1406,9 +1397,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region,
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);
+uiBut *ui_view_item_find_active(const struct ARegion *region);
typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
/**
@@ -1543,12 +1532,11 @@ void ui_interface_tag_script_reload_queries(void);
/* interface_view.cc */
void ui_block_free_views(struct uiBlock *block);
-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);
+uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
+ const uiViewHandle *new_view);
+
+uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block(
+ const uiBlock *new_block, const uiViewItemHandle *new_item_handle);
/* interface_templates.c */
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index aafb56119ae..ddd805f6010 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -2054,40 +2054,39 @@ static void UI_OT_list_start_filter(wmOperatorType *ot)
/** \name UI Tree-View Drop Operator
* \{ */
-static bool ui_tree_view_drop_poll(bContext *C)
+static bool ui_view_drop_poll(bContext *C)
{
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
- region, win->eventstate->xy);
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy);
- return hovered_tree_item != NULL;
+ return hovered_item != NULL;
}
-static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
if (event->custom != EVT_DATA_DRAGDROP) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
const ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy);
+ uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy);
- if (!UI_tree_view_item_drop_handle(C, hovered_tree_item, event->customdata)) {
+ if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_FINISHED;
}
-static void UI_OT_tree_view_drop(wmOperatorType *ot)
+static void UI_OT_view_drop(wmOperatorType *ot)
{
- ot->name = "Tree View drop";
- ot->idname = "UI_OT_tree_view_drop";
- ot->description = "Drag and drop items onto a tree item";
+ ot->name = "View drop";
+ ot->idname = "UI_OT_view_drop";
+ ot->description = "Drag and drop items onto a data-set item";
- ot->invoke = ui_tree_view_drop_invoke;
- ot->poll = ui_tree_view_drop_poll;
+ ot->invoke = ui_view_drop_invoke;
+ ot->poll = ui_view_drop_poll;
ot->flag = OPTYPE_INTERNAL;
}
@@ -2095,43 +2094,42 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name UI Tree-View Item Rename Operator
+/** \name UI View Item Rename Operator
*
- * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to
- * context menus for example, tree-view API users don't have to implement their own renaming
- * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename()
- * override.
+ * General purpose renaming operator for views. Thanks to this, to add a rename button to context
+ * menus for example, view API users don't have to implement their own renaming operators with the
+ * same logic as they already have for their #ui::AbstractViewItem::rename() override.
*
* \{ */
-static bool ui_tree_view_item_rename_poll(bContext *C)
+static bool ui_view_item_rename_poll(bContext *C)
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
- return active_item != NULL && UI_tree_view_item_can_rename(active_item);
+ const uiViewItemHandle *active_item = UI_region_views_find_active_item(region);
+ return active_item != NULL && UI_view_item_can_rename(active_item);
}
-static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op))
+static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op))
{
ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
+ uiViewItemHandle *active_item = UI_region_views_find_active_item(region);
- UI_tree_view_item_begin_rename(active_item);
+ UI_view_item_begin_rename(active_item);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}
-static void UI_OT_tree_view_item_rename(wmOperatorType *ot)
+static void UI_OT_view_item_rename(wmOperatorType *ot)
{
- ot->name = "Rename Tree-View Item";
- ot->idname = "UI_OT_tree_view_item_rename";
- ot->description = "Rename the active item in the tree";
+ ot->name = "Rename View Item";
+ ot->idname = "UI_OT_view_item_rename";
+ ot->description = "Rename the active item in the data-set view";
- ot->exec = ui_tree_view_item_rename_exec;
- ot->poll = ui_tree_view_item_rename_poll;
+ ot->exec = ui_view_item_rename_exec;
+ ot->poll = ui_view_item_rename_poll;
/* Could get a custom tooltip via the `get_description()` callback and another overridable
- * function of the tree-view. */
+ * function of the view. */
ot->flag = OPTYPE_INTERNAL;
}
@@ -2235,8 +2233,8 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_list_start_filter);
- WM_operatortype_append(UI_OT_tree_view_drop);
- WM_operatortype_append(UI_OT_tree_view_item_rename);
+ WM_operatortype_append(UI_OT_view_drop);
+ WM_operatortype_append(UI_OT_view_item_rename);
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 87bfb7ca0f7..d4a9a4ca4cd 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -2318,7 +2318,7 @@ int ui_handler_panel_region(bContext *C,
const uiBut *active_but)
{
/* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc
index 71cf60985df..f084f3e06cb 100644
--- a/source/blender/editors/interface/interface_query.cc
+++ b/source/blender/editors/interface/interface_query.cc
@@ -54,13 +54,7 @@ bool ui_but_is_toggle(const uiBut *but)
UI_BTYPE_TOGGLE_N,
UI_BTYPE_CHECKBOX,
UI_BTYPE_CHECKBOX_N,
- UI_BTYPE_ROW,
- UI_BTYPE_TREEROW);
-}
-
-bool ui_but_is_view_item(const uiBut *but)
-{
- return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE);
+ UI_BTYPE_ROW);
}
bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip)
@@ -462,14 +456,9 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut
return ui_but_find(region, ui_but_is_listrow_at_index, &data);
}
-static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata))
-{
- return but->type == UI_BTYPE_TREEROW;
-}
-
static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata))
{
- return ui_but_is_view_item(but);
+ return but->type == UI_BTYPE_VIEW_ITEM;
}
uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2])
@@ -477,24 +466,19 @@ 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, false, ui_but_is_treerow, nullptr);
-}
-
-static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata)
+static bool ui_but_is_active_view_item(const uiBut *but, const void *UNUSED(customdata))
{
- if (!ui_but_is_treerow(but, customdata)) {
+ if (but->type != UI_BTYPE_VIEW_ITEM) {
return false;
}
- const uiButTreeRow *treerow_but = (const uiButTreeRow *)but;
- return UI_tree_view_item_is_active(treerow_but->tree_item);
+ const uiButViewItem *view_item_but = (const uiButViewItem *)but;
+ return UI_view_item_is_active(view_item_but->view_item);
}
-uiBut *ui_tree_row_find_active(const ARegion *region)
+uiBut *ui_view_item_find_active(const ARegion *region)
{
- return ui_but_find(region, ui_but_is_active_treerow, nullptr);
+ return ui_but_find(region, ui_but_is_active_view_item, nullptr);
}
/** \} */
diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc
index b11564f09c5..09902dd6c35 100644
--- a/source/blender/editors/interface/interface_region_menu_pie.cc
+++ b/source/blender/editors/interface/interface_region_menu_pie.cc
@@ -371,7 +371,7 @@ void ui_pie_menu_level_create(uiBlock *block,
EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>(
MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array"));
memcpy(remaining, items + totitem_parent, array_size);
- /* A nullptr terminating sentinel element is required. */
+ /* A null terminating sentinel element is required. */
memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));
/* yuk, static... issue is we can't reliably free this without doing dangerous changes */
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 82d4405e1b5..88fe866f717 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -1005,7 +1005,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
/* this could get its own 'BUT_GET_...' type */
/* never fails */
- /* move ownership (no need for re-alloc) */
+ /* Move ownership (no need for re-allocation). */
if (rnaprop) {
field->text = RNA_path_full_property_py_ex(
CTX_data_main(C), &but->rnapoin, rnaprop, but->rnaindex, true);
diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc
index 4e587bd5338..0a684903f0f 100644
--- a/source/blender/editors/interface/interface_template_attribute_search.cc
+++ b/source/blender/editors/interface/interface_template_attribute_search.cc
@@ -50,7 +50,7 @@ static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttrib
}
void attribute_search_add_items(StringRefNull str,
- const bool is_output,
+ const bool can_create_attribute,
Span<const GeometryAttributeInfo *> infos,
uiSearchItems *seach_items,
const bool is_first)
@@ -68,8 +68,12 @@ void attribute_search_add_items(StringRefNull str,
}
if (!contained) {
dummy_info.name = str;
- UI_search_item_add(
- seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0);
+ UI_search_item_add(seach_items,
+ str.c_str(),
+ &dummy_info,
+ can_create_attribute ? ICON_ADD : ICON_NONE,
+ 0,
+ 0);
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 05ae5299e58..14da5a7cd62 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -741,8 +741,15 @@ 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, false);
+ BKE_lib_override_library_create(bmain,
+ scene,
+ view_layer,
+ NULL,
+ id,
+ &collection_active->id,
+ NULL,
+ &id_override,
+ U.experimental.use_override_new_fully_editable);
}
else if (object_active != NULL && !ID_IS_LINKED(object_active) &&
&object_active->instance_collection->id == id) {
@@ -755,7 +762,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
&object_active->id,
&object_active->id,
&id_override,
- false);
+ U.experimental.use_override_new_fully_editable);
}
break;
case ID_OB:
@@ -765,8 +772,15 @@ 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, false);
+ BKE_lib_override_library_create(bmain,
+ scene,
+ view_layer,
+ NULL,
+ id,
+ &collection_active->id,
+ NULL,
+ &id_override,
+ U.experimental.use_override_new_fully_editable);
}
break;
case ID_ME:
@@ -796,12 +810,19 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
&collection_active->id,
NULL,
&id_override,
- false);
+ U.experimental.use_override_new_fully_editable);
}
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, false);
+ BKE_lib_override_library_create(bmain,
+ scene,
+ view_layer,
+ NULL,
+ id,
+ &object_active->id,
+ NULL,
+ &id_override,
+ U.experimental.use_override_new_fully_editable);
}
}
break;
@@ -1024,6 +1045,26 @@ static const char *template_id_browse_tip(const StructRNA *type)
}
/**
+ * Add a superimposed extra icon to \a but, for workspace pinning.
+ * Rather ugly special handling, but this is really a special case at this point, nothing worth
+ * generalizing.
+ */
+static void template_id_workspace_pin_extra_icon(const TemplateID *template_ui, uiBut *but)
+{
+ if ((template_ui->idcode != ID_SCE) || (template_ui->ptr.type != &RNA_Window)) {
+ return;
+ }
+
+ const wmWindow *win = template_ui->ptr.data;
+ const WorkSpace *workspace = WM_window_get_active_workspace(win);
+ UI_but_extra_operator_icon_add(but,
+ "WORKSPACE_OT_scene_pin_toggle",
+ WM_OP_INVOKE_DEFAULT,
+ (workspace->flags & WORKSPACE_USE_PIN_SCENE) ? ICON_PINNED :
+ ICON_UNPINNED);
+}
+
+/**
* \return a type-based i18n context, needed e.g. by "New" button.
* In most languages, this adjective takes different form based on gender of type name...
*/
@@ -1220,6 +1261,8 @@ static void template_ID(const bContext *C,
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
+ template_id_workspace_pin_extra_icon(template_ui, but);
+
if (ID_IS_LINKED(id)) {
const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name));
if (id->tag & LIB_TAG_INDIRECT) {
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
deleted file mode 100644
index 699ac0c2b53..00000000000
--- a/source/blender/editors/interface/interface_view.cc
+++ /dev/null
@@ -1,229 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/** \file
- * \ingroup edinterface
- *
- * This part of the UI-View API is mostly needed to support persistent state of items within the
- * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
- * can compare the old view items with the new view items and keep state persistent for matching
- * ones.
- */
-
-#include <memory>
-#include <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;
-using namespace blender::ui;
-
-/**
- * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a
- * #std::variant.
- */
-struct ViewLink : public Link {
- using TreeViewPtr = std::unique_ptr<AbstractTreeView>;
- 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, 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;
-}
-
-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(view);
- view_link->idname = idname;
-
- 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)
-{
- LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
- MEM_delete(link);
- }
-}
-
-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);
- if (!tree_row_but) {
- return nullptr;
- }
-
- return tree_row_but->tree_item;
-}
-
-uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
-{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region);
- if (!tree_row_but) {
- return nullptr;
- }
-
- return tree_row_but->tree_item;
-}
-
-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<T>(*view_link) == &view) {
- return view_link->idname;
- }
- }
-
- return {};
-}
-
-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;
- }
-
- StringRef idname = ui_block_view_find_idname(new_block, new_view);
- if (idname.is_empty()) {
- return nullptr;
- }
-
- LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
- if (old_view_link->idname == idname) {
- return get_view_from_link<T>(*old_view_link);
- }
- }
-
- return nullptr;
-}
-
-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);
-
- AbstractTreeView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view);
- 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)
-{
- uiBlock *old_block = new_block->oldblock;
- if (!old_block) {
- return nullptr;
- }
-
- const AbstractTreeViewItem &new_item = *reinterpret_cast<const AbstractTreeViewItem *>(
- new_item_handle);
- const AbstractTreeView *old_tree_view = ui_block_view_find_matching_in_old_block(
- *new_block, new_item.get_tree_view());
- if (!old_tree_view) {
- return nullptr;
- }
-
- LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) {
- if (old_but->type != UI_BTYPE_TREEROW) {
- continue;
- }
- uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but;
- if (!old_treerow_but->tree_item) {
- continue;
- }
- AbstractTreeViewItem &old_item = *reinterpret_cast<AbstractTreeViewItem *>(
- old_treerow_but->tree_item);
- /* Check if the row is from the expected tree-view. */
- if (&old_item.get_tree_view() != old_tree_view) {
- continue;
- }
-
- if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) {
- return old_treerow_but;
- }
- }
-
- return nullptr;
-}
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index e2df2d77817..855e72788d2 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -104,8 +104,7 @@ typedef enum {
UI_WTYPE_LISTITEM,
UI_WTYPE_PROGRESSBAR,
UI_WTYPE_NODESOCKET,
- UI_WTYPE_TREEROW,
- UI_WTYPE_GRID_TILE,
+ UI_WTYPE_VIEW_ITEM,
} uiWidgetTypeEnum;
/**
@@ -3672,12 +3671,11 @@ static void widget_progressbar(uiBut *but,
widgetbase_draw(&wtb_bar, wcol);
}
-static void widget_treerow_exec(uiWidgetColors *wcol,
- rcti *rect,
- const uiWidgetStateInfo *state,
- int UNUSED(roundboxalign),
- int indentation,
- const float zoom)
+static void widget_view_item(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int UNUSED(roundboxalign),
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -3690,31 +3688,6 @@ static void widget_treerow_exec(uiWidgetColors *wcol,
if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) {
widgetbase_draw(&wtb, wcol);
}
-
- BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect));
- BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0);
-}
-
-static void widget_treerow(uiBut *but,
- uiWidgetColors *wcol,
- rcti *rect,
- 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,
@@ -4608,14 +4581,9 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_progressbar;
break;
- case UI_WTYPE_TREEROW:
- wt.wcol_theme = &btheme->tui.wcol_view_item;
- wt.custom = widget_treerow;
- break;
-
- case UI_WTYPE_GRID_TILE:
+ case UI_WTYPE_VIEW_ITEM:
wt.wcol_theme = &btheme->tui.wcol_view_item;
- wt.draw = widget_gridtile;
+ wt.draw = widget_view_item;
break;
case UI_WTYPE_NODESOCKET:
@@ -4949,13 +4917,8 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
fstyle = &style->widgetlabel;
break;
- case UI_BTYPE_TREEROW:
- wt = widget_type(UI_WTYPE_TREEROW);
- fstyle = &style->widgetlabel;
- break;
-
- case UI_BTYPE_GRID_TILE:
- wt = widget_type(UI_WTYPE_GRID_TILE);
+ case UI_BTYPE_VIEW_ITEM:
+ wt = widget_type(UI_WTYPE_VIEW_ITEM);
fstyle = &style->widgetlabel;
break;
diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc
index ee4bfd351ae..1bf7e25b154 100644
--- a/source/blender/editors/interface/view2d.cc
+++ b/source/blender/editors/interface/view2d.cc
@@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl(
*r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask));
}
+bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
+ const float xy_a[2],
+ const float xy_b[2],
+ int r_region_a[2],
+ int r_region_b[2])
+{
+ rctf rect_unit;
+ rect_unit.xmin = rect_unit.ymin = 0.0f;
+ rect_unit.xmax = rect_unit.ymax = 1.0f;
+
+ /* Express given coordinates as proportional values. */
+ const float s_a[2] = {
+ (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
+ (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
+ };
+ const float s_b[2] = {
+ (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
+ (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
+ };
+
+ /* Set initial value in case coordinates lie outside bounds. */
+ r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED;
+
+ if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) {
+ r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask)));
+ r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask)));
+ r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask)));
+ r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask)));
+
+ return true;
+ }
+
+ return false;
+}
+
void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
{
const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};
diff --git a/source/blender/editors/interface/views/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc
new file mode 100644
index 00000000000..077c76a08f1
--- /dev/null
+++ b/source/blender/editors/interface/views/abstract_view.cc
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "interface_intern.h"
+
+#include "UI_abstract_view.hh"
+
+namespace blender::ui {
+
+void AbstractView::register_item(AbstractViewItem &item)
+{
+ /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to
+ * have the view base class register the items, rather than setting the view on the item. */
+ item.view_ = this;
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name View Reconstruction
+ * \{ */
+
+bool AbstractView::is_reconstructed() const
+{
+ return is_reconstructed_;
+}
+
+void AbstractView::update_from_old(uiBlock &new_block)
+{
+ uiBlock *old_block = new_block.oldblock;
+ if (!old_block) {
+ is_reconstructed_ = true;
+ return;
+ }
+
+ uiViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block(
+ &new_block, reinterpret_cast<uiViewHandle *>(this));
+ if (old_view_handle == nullptr) {
+ /* Initial construction, nothing to update. */
+ is_reconstructed_ = true;
+ return;
+ }
+
+ AbstractView &old_view = reinterpret_cast<AbstractView &>(*old_view_handle);
+
+ /* Update own persistent data. */
+ /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's
+ * pointer to identify itself over redraws. */
+ rename_buffer_ = std::move(old_view.rename_buffer_);
+ old_view.rename_buffer_ = nullptr;
+
+ update_children_from_old(old_view);
+
+ /* Finished (re-)constructing the tree. */
+ is_reconstructed_ = true;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Default implementations of virtual functions
+ * \{ */
+
+bool AbstractView::listen(const wmNotifier & /*notifier*/) const
+{
+ /* Nothing by default. */
+ return false;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Renaming
+ * \{ */
+
+bool AbstractView::is_renaming() const
+{
+ return rename_buffer_ != nullptr;
+}
+
+bool AbstractView::begin_renaming()
+{
+ if (is_renaming()) {
+ return false;
+ }
+
+ rename_buffer_ = std::make_unique<decltype(rename_buffer_)::element_type>();
+ return true;
+}
+
+void AbstractView::end_renaming()
+{
+ BLI_assert(is_renaming());
+ rename_buffer_ = nullptr;
+}
+
+Span<char> AbstractView::get_rename_buffer() const
+{
+ return *rename_buffer_;
+}
+MutableSpan<char> AbstractView::get_rename_buffer()
+{
+ return *rename_buffer_;
+}
+
+/** \} */
+
+} // namespace blender::ui
diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc
new file mode 100644
index 00000000000..f73183d07e9
--- /dev/null
+++ b/source/blender/editors/interface/views/abstract_view_item.cc
@@ -0,0 +1,373 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "BKE_context.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+
+#include "UI_interface.h"
+#include "interface_intern.h"
+
+#include "UI_abstract_view.hh"
+
+namespace blender::ui {
+
+/* ---------------------------------------------------------------------- */
+/** \name View Reconstruction
+ * \{ */
+
+void AbstractViewItem::update_from_old(const AbstractViewItem &old)
+{
+ is_active_ = old.is_active_;
+ is_renaming_ = old.is_renaming_;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Renaming
+ * \{ */
+
+bool AbstractViewItem::supports_renaming() const
+{
+ /* No renaming by default. */
+ return false;
+}
+bool AbstractViewItem::rename(StringRefNull /*new_name*/)
+{
+ /* No renaming by default. */
+ return false;
+}
+
+StringRef AbstractViewItem::get_rename_string() const
+{
+ /* No rename string by default. */
+ return {};
+}
+
+bool AbstractViewItem::is_renaming() const
+{
+ return is_renaming_;
+}
+
+void AbstractViewItem::begin_renaming()
+{
+ AbstractView &view = get_view();
+ if (view.is_renaming() || !supports_renaming()) {
+ return;
+ }
+
+ if (view.begin_renaming()) {
+ is_renaming_ = true;
+ }
+
+ StringRef initial_str = get_rename_string();
+ std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer()));
+}
+
+void AbstractViewItem::rename_apply()
+{
+ const AbstractView &view = get_view();
+ rename(view.get_rename_buffer().data());
+ end_renaming();
+}
+
+void AbstractViewItem::end_renaming()
+{
+ if (!is_renaming()) {
+ return;
+ }
+
+ is_renaming_ = false;
+
+ AbstractView &view = get_view();
+ view.end_renaming();
+}
+
+static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but)
+{
+ /* A minimal sanity check, can't do much more here. */
+ BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin);
+
+ LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) {
+ if (but->type != UI_BTYPE_VIEW_ITEM) {
+ continue;
+ }
+
+ uiButViewItem *view_item_but = (uiButViewItem *)but;
+ AbstractViewItem *item = reinterpret_cast<AbstractViewItem *>(view_item_but->view_item);
+ const AbstractView &view = item->get_view();
+
+ if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) {
+ return item;
+ }
+ }
+
+ return nullptr;
+}
+
+static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr))
+{
+ const uiBut *rename_but = static_cast<uiBut *>(arg);
+ AbstractViewItem *item = find_item_from_rename_button(*rename_but);
+ BLI_assert(item);
+ item->rename_apply();
+}
+
+void AbstractViewItem::add_rename_button(uiBlock &block)
+{
+ AbstractView &view = get_view();
+ uiBut *rename_but = uiDefBut(&block,
+ UI_BTYPE_TEXT,
+ 1,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ view.get_rename_buffer().data(),
+ 1.0f,
+ view.get_rename_buffer().size(),
+ 0,
+ 0,
+ "");
+
+ /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the
+ * callback is executed. */
+ UI_but_func_rename_set(rename_but, rename_button_fn, rename_but);
+ UI_but_flag_disable(rename_but, UI_BUT_UNDO);
+
+ const bContext *evil_C = reinterpret_cast<bContext *>(block.evil_C);
+ ARegion *region = CTX_wm_region(evil_C);
+ /* Returns false if the button was removed. */
+ if (UI_but_active_only(evil_C, region, &block, rename_but) == false) {
+ end_renaming();
+ }
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Context Menu
+ * \{ */
+
+void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const
+{
+ /* No context menu by default. */
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Drag 'n Drop
+ * \{ */
+
+std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_controller() const
+{
+ /* There's no drag controller (and hence no drag support) by default. */
+ return nullptr;
+}
+
+std::unique_ptr<AbstractViewItemDropController> AbstractViewItem::create_drop_controller() const
+{
+ /* There's no drop controller (and hence no drop support) by default. */
+ return nullptr;
+}
+
+AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view)
+{
+}
+
+void AbstractViewItemDragController::on_drag_start()
+{
+ /* Do nothing by default. */
+}
+
+AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view)
+{
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name General Getters & Setters
+ * \{ */
+
+AbstractView &AbstractViewItem::get_view() const
+{
+ if (UNLIKELY(!view_)) {
+ throw std::runtime_error(
+ "Invalid state, item must be registered through AbstractView::register_item()");
+ }
+ return *view_;
+}
+
+bool AbstractViewItem::is_active() const
+{
+ BLI_assert_msg(get_view().is_reconstructed(),
+ "State can't be queried until reconstruction is completed");
+ return is_active_;
+}
+
+/** \} */
+
+} // namespace blender::ui
+
+/* ---------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
+
+namespace blender::ui {
+
+/**
+ * Helper class to provide a higher level public (C-)API. Has access to private/protected view item
+ * members and ensures some invariants that way.
+ */
+class ViewItemAPIWrapper {
+ public:
+ static bool matches(const AbstractViewItem &a, const AbstractViewItem &b)
+ {
+ if (typeid(a) != typeid(b)) {
+ return false;
+ }
+ /* TODO should match the view as well. */
+ return a.matches(b);
+ }
+
+ static bool can_rename(const AbstractViewItem &item)
+ {
+ const AbstractView &view = item.get_view();
+ return !view.is_renaming() && item.supports_renaming();
+ }
+
+ static bool drag_start(bContext &C, const AbstractViewItem &item)
+ {
+ const std::unique_ptr<AbstractViewItemDragController> drag_controller =
+ item.create_drag_controller();
+ if (!drag_controller) {
+ return false;
+ }
+
+ WM_event_start_drag(&C,
+ ICON_NONE,
+ drag_controller->get_drag_type(),
+ drag_controller->create_drag_data(),
+ 0,
+ WM_DRAG_FREE_DATA);
+ drag_controller->on_drag_start();
+
+ return true;
+ }
+
+ static bool can_drop(const AbstractViewItem &item,
+ const wmDrag &drag,
+ const char **r_disabled_hint)
+ {
+ const std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return false;
+ }
+
+ return drop_controller->can_drop(drag, r_disabled_hint);
+ }
+
+ static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag)
+ {
+ const std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return {};
+ }
+
+ return drop_controller->drop_tooltip(drag);
+ }
+
+ static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags)
+ {
+ std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+
+ const char *disabled_hint_dummy = nullptr;
+ LISTBASE_FOREACH (const wmDrag *, drag, &drags) {
+ if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) {
+ return drop_controller->on_drop(&C, *drag);
+ }
+ }
+
+ return false;
+ }
+};
+
+} // namespace blender::ui
+
+using namespace blender::ui;
+
+bool UI_view_item_is_active(const uiViewItemHandle *item_handle)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ return item.is_active();
+}
+
+bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle)
+{
+ const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle);
+ const AbstractViewItem &b = reinterpret_cast<const AbstractViewItem &>(*b_handle);
+ return ViewItemAPIWrapper::matches(a, b);
+}
+
+bool UI_view_item_can_rename(const uiViewItemHandle *item_handle)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ return ViewItemAPIWrapper::can_rename(item);
+}
+
+void UI_view_item_begin_rename(uiViewItemHandle *item_handle)
+{
+ AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle);
+ item.begin_renaming();
+}
+
+void UI_view_item_context_menu_build(bContext *C,
+ const uiViewItemHandle *item_handle,
+ uiLayout *column)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ item.build_context_menu(*C, *column);
+}
+
+bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::drag_start(*C, item);
+}
+
+bool UI_view_item_can_drop(const uiViewItemHandle *item_,
+ const wmDrag *drag,
+ const char **r_disabled_hint)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint);
+}
+
+char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+
+ const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag);
+ return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str());
+}
+
+bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::drop_handle(*C, item, *drags);
+}
+
+/** \} */
diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc
index 194052862cf..52ff1460cbd 100644
--- a/source/blender/editors/interface/grid_view.cc
+++ b/source/blender/editors/interface/views/grid_view.cc
@@ -29,9 +29,8 @@ AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridVie
items_.append(std::move(item));
AbstractGridViewItem &added_item = *items_.last();
- added_item.view_ = this;
-
item_map_.add(added_item.identifier_, &added_item);
+ register_item(added_item);
return added_item;
}
@@ -43,12 +42,6 @@ void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
}
}
-bool AbstractGridView::listen(const wmNotifier & /*notifier*/) const
-{
- /* Nothing by default. */
- return false;
-}
-
AbstractGridViewItem *AbstractGridView::find_matching_item(
const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const
{
@@ -67,34 +60,18 @@ void AbstractGridView::change_state_delayed()
foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); });
}
-void AbstractGridView::update_from_old(uiBlock &new_block)
+void AbstractGridView::update_children_from_old(const AbstractView &old_view)
{
- 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;
- }
+ const AbstractGridView &old_grid_view = dynamic_cast<const AbstractGridView &>(old_view);
- 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);
+ foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) {
+ const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_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
@@ -117,18 +94,19 @@ AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(i
{
}
-bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const
+bool AbstractGridViewItem::matches(const AbstractViewItem &other) const
{
- return identifier_ == other.identifier_;
+ const AbstractGridViewItem &other_grid_item = dynamic_cast<const AbstractGridViewItem &>(other);
+ return identifier_ == other_grid_item.identifier_;
}
void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
void *but_arg1,
void * /*arg2*/)
{
- uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1;
+ uiButViewItem *view_item_but = (uiButViewItem *)but_arg1;
AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
- *grid_tile_but->view_item);
+ *view_item_but->view_item);
grid_item.activate();
}
@@ -136,8 +114,8 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
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,
+ view_item_but_ = (uiButViewItem *)uiDefBut(&block,
+ UI_BTYPE_VIEW_ITEM,
0,
"",
0,
@@ -151,15 +129,8 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
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_;
+ view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
+ UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr);
}
void AbstractGridViewItem::on_activate()
@@ -180,11 +151,6 @@ void AbstractGridViewItem::change_state_delayed()
}
}
-void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old)
-{
- is_active_ = old.is_active_;
-}
-
void AbstractGridViewItem::activate()
{
BLI_assert_msg(get_view().is_reconstructed(),
@@ -213,7 +179,7 @@ const AbstractGridView &AbstractGridViewItem::get_view() const
throw std::runtime_error(
"Invalid state, item must be added through AbstractGridView::add_item()");
}
- return *view_;
+ return dynamic_cast<AbstractGridView &>(*view_);
}
/* ---------------------------------------------------------------------- */
@@ -495,31 +461,3 @@ std::optional<bool> PreviewGridItem::should_be_active() const
}
} // 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/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc
new file mode 100644
index 00000000000..c568a8cab74
--- /dev/null
+++ b/source/blender/editors/interface/views/interface_view.cc
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ *
+ * Code to manage views as part of the regular screen hierarchy. E.g. managing ownership of views
+ * inside blocks (#uiBlock.views), looking up items in the region, passing WM notifiers to views,
+ * etc.
+ *
+ * Blocks and their contained views are reconstructed on every redraw. This file also contains
+ * functions related to this recreation of views inside blocks. For example to query state
+ * information before the view is done reconstructing (#AbstractView.is_reconstructed() returns
+ * false), it may be enough to query the previous version of the block/view/view-item. Since such
+ * queries rely on the details of the UI reconstruction process, they should remain internal to
+ * `interface/` code.
+ */
+
+#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_abstract_view.hh"
+#include "UI_grid_view.hh"
+#include "UI_tree_view.hh"
+
+using namespace blender;
+using namespace blender::ui;
+
+/**
+ * Wrapper to store views in a #ListBase, addressable via an identifier.
+ */
+struct ViewLink : public Link {
+ std::string idname;
+ std::unique_ptr<AbstractView> view;
+};
+
+template<class T>
+static T *ui_block_add_view_impl(uiBlock &block,
+ StringRef idname,
+ std::unique_ptr<AbstractView> view)
+{
+ ViewLink *view_link = MEM_new<ViewLink>(__func__);
+ BLI_addtail(&block.views, view_link);
+
+ view_link->view = std::move(view);
+ view_link->idname = idname;
+
+ return dynamic_cast<T *>(view_link->view.get());
+}
+
+AbstractGridView *UI_block_add_view(uiBlock &block,
+ StringRef idname,
+ std::unique_ptr<AbstractGridView> grid_view)
+{
+ return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_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)
+{
+ LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
+ MEM_delete(link);
+ }
+}
+
+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 (view_link->view->listen(*listener_params->notifier)) {
+ ED_region_tag_redraw(region);
+ }
+ }
+}
+
+uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2])
+{
+ uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy);
+ if (!item_but) {
+ return nullptr;
+ }
+
+ return item_but->view_item;
+}
+
+uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region)
+{
+ uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region);
+ if (!item_but) {
+ return nullptr;
+ }
+
+ return item_but->view_item;
+}
+
+static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view)
+{
+ /* First get the idname the of the view we're looking for. */
+ LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
+ if (view_link->view.get() == &view) {
+ return view_link->idname;
+ }
+ }
+
+ return {};
+}
+
+template<class T>
+static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block,
+ const T &new_view)
+{
+ uiBlock *old_block = new_block.oldblock;
+ if (!old_block) {
+ return nullptr;
+ }
+
+ StringRef idname = ui_block_view_find_idname(new_block, new_view);
+ if (idname.is_empty()) {
+ return nullptr;
+ }
+
+ LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
+ if (old_view_link->idname == idname) {
+ return dynamic_cast<T *>(old_view_link->view.get());
+ }
+ }
+
+ return nullptr;
+}
+
+uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
+ const uiViewHandle *new_view_handle)
+{
+ BLI_assert(new_block && new_view_handle);
+ const AbstractView &new_view = reinterpret_cast<const AbstractView &>(*new_view_handle);
+
+ AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view);
+ return reinterpret_cast<uiViewHandle *>(old_view);
+}
+
+uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block(
+ const uiBlock *new_block, const uiViewItemHandle *new_item_handle)
+{
+ uiBlock *old_block = new_block->oldblock;
+ if (!old_block) {
+ return nullptr;
+ }
+
+ const AbstractViewItem &new_item = *reinterpret_cast<const AbstractViewItem *>(new_item_handle);
+ const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(
+ *new_block, new_item.get_view());
+ if (!old_view) {
+ return nullptr;
+ }
+
+ LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) {
+ if (old_but->type != UI_BTYPE_VIEW_ITEM) {
+ continue;
+ }
+ uiButViewItem *old_item_but = (uiButViewItem *)old_but;
+ if (!old_item_but->view_item) {
+ continue;
+ }
+ AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item);
+ /* Check if the item is from the expected view. */
+ if (&old_item.get_view() != old_view) {
+ continue;
+ }
+
+ if (UI_view_item_matches(reinterpret_cast<const uiViewItemHandle *>(&new_item),
+ reinterpret_cast<const uiViewItemHandle *>(&old_item))) {
+ return old_item_but;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc
index 96158ee48f6..43fdf741ac5 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/views/tree_view.cc
@@ -37,9 +37,11 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
if (root_ == nullptr) {
root_ = this;
}
-
+ AbstractTreeView &tree_view = static_cast<AbstractTreeView &>(*root_);
AbstractTreeViewItem &added_item = *children_.last();
added_item.root_ = root_;
+ tree_view.register_item(added_item);
+
if (root_ != this) {
/* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely
* nice to static_cast this, but well... */
@@ -68,45 +70,11 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
-bool AbstractTreeView::listen(const wmNotifier & /*notifier*/) const
+void AbstractTreeView::update_children_from_old(const AbstractView &old_view)
{
- /* Nothing by default. */
- return false;
-}
+ const AbstractTreeView &old_tree_view = dynamic_cast<const AbstractTreeView &>(old_view);
-bool AbstractTreeView::is_renaming() const
-{
- return rename_buffer_ != nullptr;
-}
-
-void AbstractTreeView::update_from_old(uiBlock &new_block)
-{
- uiBlock *old_block = new_block.oldblock;
- if (!old_block) {
- /* Initial construction, nothing to update. */
- is_reconstructed_ = true;
- return;
- }
-
- 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;
- return;
- }
-
- AbstractTreeView &old_view = reinterpret_cast<AbstractTreeView &>(*old_view_handle);
-
- /* Update own persistent data. */
- /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's
- * pointer to identify itself over redraws. */
- rename_buffer_ = std::move(old_view.rename_buffer_);
- old_view.rename_buffer_ = nullptr;
-
- update_children_from_old_recursive(*this, old_view);
-
- /* Finished (re-)constructing the tree. */
- is_reconstructed_ = true;
+ update_children_from_old_recursive(*this, old_tree_view);
}
void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items,
@@ -129,7 +97,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child(
const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items)
{
for (const auto &iter_item : items.children_) {
- if (lookup_item.matches(*iter_item)) {
+ if (lookup_item.matches_single(*iter_item)) {
/* We have a matching item! */
return iter_item.get();
}
@@ -138,11 +106,6 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child(
return nullptr;
}
-bool AbstractTreeView::is_reconstructed() const
-{
- return is_reconstructed_;
-}
-
void AbstractTreeView::change_state_delayed()
{
BLI_assert_msg(
@@ -157,9 +120,8 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/,
void *but_arg1,
void * /*arg2*/)
{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1;
- AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(
- *tree_row_but->tree_item);
+ uiButViewItem *item_but = (uiButViewItem *)but_arg1;
+ AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item);
tree_item.activate();
/* Not only activate the item, also show its children. Maybe this should be optional, or
@@ -170,11 +132,11 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/,
void AbstractTreeViewItem::add_treerow_button(uiBlock &block)
{
/* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */
- tree_row_but_ = (uiButTreeRow *)uiDefBut(
- &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
+ view_item_but_ = (uiButViewItem *)uiDefBut(
+ &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
- tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this);
- UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr);
+ view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
+ UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr);
}
void AbstractTreeViewItem::add_indent(uiLayout &row) const
@@ -206,10 +168,10 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C,
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region,
- win->eventstate->xy);
- AbstractTreeViewItem *hovered_item = reinterpret_cast<AbstractTreeViewItem *>(
- hovered_item_handle);
+ uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region,
+ win->eventstate->xy);
+
+ AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle);
BLI_assert(hovered_item != nullptr);
hovered_item->toggle_collapsed();
@@ -243,40 +205,6 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const
BLI_assert(is_collapse_chevron_but(but));
}
-AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button(
- const uiBut &rename_but)
-{
- /* A minimal sanity check, can't do much more here. */
- BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin);
-
- LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) {
- if (but->type != UI_BTYPE_TREEROW) {
- continue;
- }
-
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- AbstractTreeViewItem *item = reinterpret_cast<AbstractTreeViewItem *>(tree_row_but->tree_item);
- const AbstractTreeView &tree_view = item->get_tree_view();
-
- if (item->is_renaming() && (tree_view.rename_buffer_->data() == rename_but.poin)) {
- return item;
- }
- }
-
- return nullptr;
-}
-
-void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr))
-{
- const uiBut *rename_but = static_cast<uiBut *>(arg);
- AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but);
- BLI_assert(item);
-
- const AbstractTreeView &tree_view = item->get_tree_view();
- item->rename(tree_view.rename_buffer_->data());
- item->end_renaming();
-}
-
void AbstractTreeViewItem::add_rename_button(uiLayout &row)
{
uiBlock *block = uiLayoutGetBlock(&row);
@@ -286,33 +214,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row)
/* Enable emboss for the text button. */
UI_block_emboss_set(block, UI_EMBOSS);
- AbstractTreeView &tree_view = get_tree_view();
- uiBut *rename_but = uiDefBut(block,
- UI_BTYPE_TEXT,
- 1,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- tree_view.rename_buffer_->data(),
- 1.0f,
- tree_view.rename_buffer_->max_size(),
- 0,
- 0,
- "");
-
- /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the
- * callback is executed. */
- UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but);
- UI_but_flag_disable(rename_but, UI_BUT_UNDO);
-
- const bContext *evil_C = static_cast<bContext *>(block->evil_C);
- ARegion *region = CTX_wm_region(evil_C);
- /* Returns false if the button was removed. */
- if (UI_but_active_only(evil_C, region, block, rename_but) == false) {
- end_renaming();
- }
+ AbstractViewItem::add_rename_button(*block);
UI_block_emboss_set(block, previous_emboss);
UI_block_layout_set_current(block, &row);
@@ -345,79 +247,35 @@ bool AbstractTreeViewItem::supports_collapsing() const
return true;
}
-std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller()
- const
+StringRef AbstractTreeViewItem::get_rename_string() const
{
- /* There's no drag controller (and hence no drag support) by default. */
- return nullptr;
-}
-
-std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller()
- const
-{
- /* There's no drop controller (and hence no drop support) by default. */
- return nullptr;
-}
-
-bool AbstractTreeViewItem::supports_renaming() const
-{
- /* No renaming by default. */
- return false;
+ return label_;
}
bool AbstractTreeViewItem::rename(StringRefNull new_name)
{
- /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches()
+ /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single()
* recognizes the item. (It only compares labels by default.) */
label_ = new_name;
return true;
}
-void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const
+void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old)
{
- /* No context menu by default. */
-}
+ AbstractViewItem::update_from_old(old);
-void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
-{
- is_open_ = old.is_open_;
- is_active_ = old.is_active_;
- is_renaming_ = old.is_renaming_;
+ const AbstractTreeViewItem &old_tree_item = dynamic_cast<const AbstractTreeViewItem &>(old);
+ is_open_ = old_tree_item.is_open_;
}
-bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
+bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const
{
return label_ == other.label_;
}
-void AbstractTreeViewItem::begin_renaming()
-{
- AbstractTreeView &tree_view = get_tree_view();
- if (tree_view.is_renaming() || !supports_renaming()) {
- return;
- }
-
- is_renaming_ = true;
-
- tree_view.rename_buffer_ = std::make_unique<decltype(tree_view.rename_buffer_)::element_type>();
- std::copy(std::begin(label_), std::end(label_), std::begin(*tree_view.rename_buffer_));
-}
-
-void AbstractTreeViewItem::end_renaming()
-{
- if (!is_renaming()) {
- return;
- }
-
- is_renaming_ = false;
-
- AbstractTreeView &tree_view = get_tree_view();
- tree_view.rename_buffer_ = nullptr;
-}
-
AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
{
- return static_cast<AbstractTreeView &>(*root_);
+ return dynamic_cast<AbstractTreeView &>(get_view());
}
int AbstractTreeViewItem::count_parents() const
@@ -453,26 +311,19 @@ void AbstractTreeViewItem::deactivate()
is_active_ = false;
}
-bool AbstractTreeViewItem::is_active() const
-{
- BLI_assert_msg(get_tree_view().is_reconstructed(),
- "State can't be queried until reconstruction is completed");
- return is_active_;
-}
-
bool AbstractTreeViewItem::is_hovered() const
{
BLI_assert_msg(get_tree_view().is_reconstructed(),
"State can't be queried until reconstruction is completed");
- BLI_assert_msg(tree_row_but_ != nullptr,
+ BLI_assert_msg(view_item_but_ != nullptr,
"Hovered state can't be queried before the tree row is being built");
- const uiTreeViewItemHandle *this_handle = reinterpret_cast<const uiTreeViewItemHandle *>(this);
+ const uiViewItemHandle *this_item_handle = reinterpret_cast<const uiViewItemHandle *>(this);
/* The new layout hasn't finished construction yet, so the final state of the button is unknown.
* Get the matching button from the previous redraw instead. */
- uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block,
- this_handle);
- return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE);
+ uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block(
+ view_item_but_->but.block, this_item_handle);
+ return old_item_but && (old_item_but->but.flag & UI_ACTIVE);
}
bool AbstractTreeViewItem::is_collapsed() const
@@ -500,11 +351,6 @@ bool AbstractTreeViewItem::is_collapsible() const
return this->supports_collapsing();
}
-bool AbstractTreeViewItem::is_renaming() const
-{
- return is_renaming_;
-}
-
void AbstractTreeViewItem::ensure_parents_uncollapsed()
{
for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) {
@@ -512,19 +358,21 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed()
}
}
-bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const
+bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const
{
- if (!matches(other)) {
+ const AbstractTreeViewItem &other_tree_item = dynamic_cast<const AbstractTreeViewItem &>(other);
+
+ if (!matches_single(other_tree_item)) {
return false;
}
- if (count_parents() != other.count_parents()) {
+ if (count_parents() != other_tree_item.count_parents()) {
return false;
}
- for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_;
+ for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_;
parent && other_parent;
parent = parent->parent_, other_parent = other_parent->parent_) {
- if (!parent->matches(*other_parent)) {
+ if (!parent->matches_single(*other_parent)) {
return false;
}
}
@@ -532,9 +380,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem
return true;
}
-uiButTreeRow *AbstractTreeViewItem::tree_row_button()
+uiButViewItem *AbstractTreeViewItem::view_item_button()
{
- return tree_row_but_;
+ return view_item_but_;
}
void AbstractTreeViewItem::change_state_delayed()
@@ -547,25 +395,6 @@ void AbstractTreeViewItem::change_state_delayed()
/* ---------------------------------------------------------------------- */
-AbstractTreeViewItemDragController::AbstractTreeViewItemDragController(AbstractTreeView &tree_view)
- : tree_view_(tree_view)
-{
-}
-
-void AbstractTreeViewItemDragController::on_drag_start()
-{
- /* Do nothing by default. */
-}
-
-/* ---------------------------------------------------------------------- */
-
-AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view)
- : tree_view_(tree_view)
-{
-}
-
-/* ---------------------------------------------------------------------- */
-
class TreeViewLayoutBuilder {
uiBlock &block_;
@@ -611,7 +440,7 @@ void TreeViewLayoutBuilder::polish_layout(const uiBlock &block)
UI_but_drawflag_enable(static_cast<uiBut *>(but->next), UI_BUT_NO_TEXT_PADDING);
}
- if (but->type == UI_BTYPE_TREEROW) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
break;
}
}
@@ -723,161 +552,4 @@ std::optional<bool> BasicTreeViewItem::should_be_active() const
return std::nullopt;
}
-/* ---------------------------------------------------------------------- */
-
-/**
- * Helper for a public (C-)API, presenting higher level functionality. Has access to internal
- * data/functionality (friend of #AbstractTreeViewItem), which is sometimes needed when
- * functionality of the API needs to be constructed from multiple internal conditions and/or
- * functions that on their own shouldn't be part of the API.
- */
-class TreeViewItemAPIWrapper {
- public:
- static bool matches(const AbstractTreeViewItem &a, const AbstractTreeViewItem &b)
- {
- /* TODO should match the tree-view as well. */
- return a.matches_including_parents(b);
- }
-
- static bool drag_start(bContext &C, const AbstractTreeViewItem &item)
- {
- const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller =
- item.create_drag_controller();
- if (!drag_controller) {
- return false;
- }
-
- WM_event_start_drag(&C,
- ICON_NONE,
- drag_controller->get_drag_type(),
- drag_controller->create_drag_data(),
- 0,
- WM_DRAG_FREE_DATA);
- drag_controller->on_drag_start();
-
- return true;
- }
-
- static bool can_drop(const AbstractTreeViewItem &item,
- const wmDrag &drag,
- const char **r_disabled_hint)
- {
- const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
- if (!drop_controller) {
- return false;
- }
-
- return drop_controller->can_drop(drag, r_disabled_hint);
- }
-
- static std::string drop_tooltip(const AbstractTreeViewItem &item, const wmDrag &drag)
- {
- const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
- if (!drop_controller) {
- return {};
- }
-
- return drop_controller->drop_tooltip(drag);
- }
-
- static bool drop_handle(bContext &C, const AbstractTreeViewItem &item, const ListBase &drags)
- {
- std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
-
- const char *disabled_hint_dummy = nullptr;
- LISTBASE_FOREACH (const wmDrag *, drag, &drags) {
- if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) {
- return drop_controller->on_drop(&C, *drag);
- }
- }
-
- return false;
- }
-
- static bool can_rename(const AbstractTreeViewItem &item)
- {
- const AbstractTreeView &tree_view = item.get_tree_view();
- return !tree_view.is_renaming() && item.supports_renaming();
- }
-};
-
} // namespace blender::ui
-
-/* ---------------------------------------------------------------------- */
-/* C-API */
-
-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);
- return item.is_active();
-}
-
-bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
- const uiTreeViewItemHandle *b_handle)
-{
- const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle);
- const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle);
- return TreeViewItemAPIWrapper::matches(a, b);
-}
-
-bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::drag_start(*C, item);
-}
-
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
- const wmDrag *drag,
- const char **r_disabled_hint)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint);
-}
-
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
-
- const std::string tooltip = TreeViewItemAPIWrapper::drop_tooltip(item, *drag);
- return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str());
-}
-
-bool UI_tree_view_item_drop_handle(bContext *C,
- const uiTreeViewItemHandle *item_,
- const ListBase *drags)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::drop_handle(*C, item, *drags);
-}
-
-bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
- return TreeViewItemAPIWrapper::can_rename(item);
-}
-
-void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle)
-{
- AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle);
- item.begin_renaming();
-}
-
-void UI_tree_view_item_context_menu_build(bContext *C,
- const uiTreeViewItemHandle *item_handle,
- uiLayout *column)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
- item.build_context_menu(*C, *column);
-}
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 0e8e0f83597..0068586730f 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -144,10 +144,10 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
/* Take some defaults from the scene, if not specified explicitly. */
Scene *scene = CTX_data_scene(C);
if (params.frame_start == INT_MIN) {
- params.frame_start = SFRA;
+ params.frame_start = scene->r.sfra;
}
if (params.frame_end == INT_MIN) {
- params.frame_end = EFRA;
+ params.frame_end = scene->r.efra;
}
const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
@@ -248,8 +248,8 @@ static void wm_alembic_export_draw(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) {
- RNA_int_set(op->ptr, "start", SFRA);
- RNA_int_set(op->ptr, "end", EFRA);
+ RNA_int_set(op->ptr, "start", scene->r.sfra);
+ RNA_int_set(op->ptr, "end", scene->r.efra);
RNA_boolean_set(op->ptr, "init_scene_frame_range", false);
}
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 6e5ae9f3cba..3f905dd7de0 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -153,9 +153,9 @@ static int wm_gpencil_export_svg_exec(bContext *C, wmOperator *op)
.v3d = v3d,
.ob = ob,
.mode = GP_EXPORT_TO_SVG,
- .frame_start = CFRA,
- .frame_end = CFRA,
- .frame_cur = CFRA,
+ .frame_start = scene->r.cfra,
+ .frame_end = scene->r.cfra,
+ .frame_cur = scene->r.cfra,
.flag = flag,
.scale = 1.0f,
.select_mode = select_mode,
@@ -306,9 +306,9 @@ static int wm_gpencil_export_pdf_exec(bContext *C, wmOperator *op)
.v3d = v3d,
.ob = ob,
.mode = GP_EXPORT_TO_PDF,
- .frame_start = SFRA,
- .frame_end = EFRA,
- .frame_cur = CFRA,
+ .frame_start = scene->r.sfra,
+ .frame_end = scene->r.efra,
+ .frame_cur = scene->r.cfra,
.flag = flag,
.scale = 1.0f,
.select_mode = select_mode,
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index 45f5441616f..9ac64407dcf 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -90,9 +90,9 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op)
.v3d = v3d,
.ob = NULL,
.mode = GP_IMPORT_FROM_SVG,
- .frame_start = CFRA,
- .frame_end = CFRA,
- .frame_cur = CFRA,
+ .frame_start = scene->r.cfra,
+ .frame_end = scene->r.cfra,
+ .frame_cur = scene->r.cfra,
.flag = flag,
.scale = scale,
.select_mode = 0,
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 53fa4788d0e..79ec7ebf2a5 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -208,11 +208,11 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op)
int end = RNA_int_get(op->ptr, "end_frame");
/* Set the defaults. */
if (start == INT_MIN) {
- start = SFRA;
+ start = scene->r.sfra;
changed = true;
}
if (end == INT_MAX) {
- end = EFRA;
+ end = scene->r.efra;
changed = true;
}
/* Fix user errors. */
@@ -266,7 +266,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
"Export multiple frames instead of the current frame only");
RNA_def_int(ot->srna,
"start_frame",
- INT_MIN, /* wm_obj_export_check uses this to set SFRA. */
+ INT_MIN, /* wm_obj_export_check uses this to set scene->r.sfra. */
INT_MIN,
INT_MAX,
"Start Frame",
@@ -275,7 +275,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
INT_MAX);
RNA_def_int(ot->srna,
"end_frame",
- INT_MAX, /* wm_obj_export_check uses this to set EFRA. */
+ INT_MAX, /* wm_obj_export_check uses this to set scene->r.efra. */
INT_MIN,
INT_MAX,
"End Frame",
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 37c1815fca3..df30870007f 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -258,7 +258,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2
&u,
NULL)) {
Scene *scene = CTX_data_scene(C);
- const float ctime = CFRA;
+ const float ctime = scene->r.cfra;
MaskSplinePoint *new_point;
int point_index = point - spline->points;
@@ -295,7 +295,7 @@ static bool add_vertex_extrude(const bContext *C,
const float co[2])
{
Scene *scene = CTX_data_scene(C);
- const float ctime = CFRA;
+ const float ctime = scene->r.cfra;
MaskSpline *spline;
MaskSplinePoint *point;
@@ -394,7 +394,7 @@ static bool add_vertex_extrude(const bContext *C,
static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, const float co[2])
{
Scene *scene = CTX_data_scene(C);
- const float ctime = CFRA;
+ const float ctime = scene->r.cfra;
MaskSpline *spline;
MaskSplinePoint *new_point = NULL, *ref_point = NULL;
@@ -874,7 +874,7 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Primitive Add Suqare Operator
+/** \name Primitive Add Square Operator
* \{ */
static int primitive_square_add_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index 4662fe9d1a8..4c01154ba49 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -802,7 +802,7 @@ void ED_mask_draw_frames(
mask_layer_shape = mask_layer_shape->next) {
int frame = mask_layer_shape->frame;
- // draw_keyframe(i, CFRA, sfra, framelen, 1);
+ // draw_keyframe(i, scene->r.cfra, sfra, framelen, 1);
int height = (frame == cfra) ? 22 : 10;
int x = (frame - sfra) * framelen;
immVertex2i(pos, x, region_bottom);
diff --git a/source/blender/editors/mask/mask_editaction.c b/source/blender/editors/mask/mask_editaction.c
index 8a23a53a5d2..9819532224e 100644
--- a/source/blender/editors/mask/mask_editaction.c
+++ b/source/blender/editors/mask/mask_editaction.c
@@ -304,7 +304,7 @@ static bool snap_mask_layer_nearestsec(MaskLayerShape *mask_layer_shape, Scene *
static bool snap_mask_layer_cframe(MaskLayerShape *mask_layer_shape, Scene *scene)
{
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
- mask_layer_shape->frame = (int)CFRA;
+ mask_layer_shape->frame = (int)scene->r.cfra;
}
return false;
}
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 14976d860f6..d34b274c111 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -856,7 +856,7 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Don't key sliding feather UW's. */
if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == false) {
if (IS_AUTOKEY_ON(scene)) {
- ED_mask_layer_shape_auto_key(data->mask_layer, CFRA);
+ ED_mask_layer_shape_auto_key(data->mask_layer, scene->r.cfra);
}
}
@@ -1262,7 +1262,7 @@ static int slide_spline_curvature_modal(bContext *C, wmOperator *op, const wmEve
if (event->type == slide_data->event_invoke_type && event->val == KM_RELEASE) {
/* Don't key sliding feather UW's. */
if (IS_AUTOKEY_ON(scene)) {
- ED_mask_layer_shape_auto_key(slide_data->mask_layer, CFRA);
+ ED_mask_layer_shape_auto_key(slide_data->mask_layer, scene->r.cfra);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
@@ -1525,7 +1525,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
if (changed_layer) {
if (IS_AUTOKEY_ON(scene)) {
- ED_mask_layer_shape_auto_key(mask_layer, CFRA);
+ ED_mask_layer_shape_auto_key(mask_layer, scene->r.cfra);
}
}
}
@@ -1587,7 +1587,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op)
if (changed_layer) {
if (IS_AUTOKEY_ON(scene)) {
- ED_mask_layer_shape_auto_key(mask_layer, CFRA);
+ ED_mask_layer_shape_auto_key(mask_layer, scene->r.cfra);
}
}
}
diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c
index 02e1524e23e..bb865e925d7 100644
--- a/source/blender/editors/mask/mask_query.c
+++ b/source/blender/editors/mask/mask_query.c
@@ -682,8 +682,7 @@ void ED_mask_get_size(ScrArea *area, int *width, int *height)
}
case SPACE_SEQ: {
// Scene *scene = CTX_data_scene(C);
- // *width = (scene->r.size * scene->r.xsch) / 100;
- // *height = (scene->r.size * scene->r.ysch) / 100;
+ // BKE_render_resolution(&scene->r, false, width, height);
break;
}
case SPACE_IMAGE: {
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index 4bd80208b9e..95cad3f54ca 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -31,8 +31,6 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "DEG_depsgraph.h"
-
#include "mask_intern.h" /* own include */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index 55145f27012..48944c081a8 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -33,7 +33,7 @@
static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
- const int frame = CFRA;
+ const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
@@ -76,7 +76,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot)
static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
- const int frame = CFRA;
+ const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
@@ -122,7 +122,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot)
static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
- const int frame = CFRA;
+ const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
@@ -214,7 +214,7 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- const int frame = CFRA;
+ const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc
index 69fe69fe117..b69cd8b8606 100644
--- a/source/blender/editors/mesh/editface.cc
+++ b/source/blender/editors/mesh/editface.cc
@@ -551,3 +551,54 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
paintvert_flush_flags(ob);
}
}
+
+void paintvert_hide(bContext *C, Object *ob, const bool unselected)
+{
+ Mesh *const me = BKE_mesh_from_object(ob);
+
+ if (me == NULL || me->totvert == 0) {
+ return;
+ }
+
+ for (int i = 0; i < me->totvert; i++) {
+ MVert *const mvert = &me->mvert[i];
+
+ if ((mvert->flag & ME_HIDE) == 0) {
+ if (((mvert->flag & SELECT) == 0) == unselected) {
+ mvert->flag |= ME_HIDE;
+ }
+ }
+
+ if (mvert->flag & ME_HIDE) {
+ mvert->flag &= ~SELECT;
+ }
+ }
+
+ BKE_mesh_flush_hidden_from_verts(me);
+
+ paintvert_flush_flags(ob);
+ paintvert_tag_select_update(C, ob);
+}
+
+void paintvert_reveal(bContext *C, Object *ob, const bool select)
+{
+ Mesh *const me = BKE_mesh_from_object(ob);
+
+ if (me == NULL || me->totvert == 0) {
+ return;
+ }
+
+ for (int i = 0; i < me->totvert; i++) {
+ MVert *const mvert = &me->mvert[i];
+
+ if (mvert->flag & ME_HIDE) {
+ SET_FLAG_FROM_TEST(mvert->flag, select, SELECT);
+ mvert->flag &= ~ME_HIDE;
+ }
+ }
+
+ BKE_mesh_flush_hidden_from_verts(me);
+
+ paintvert_flush_flags(ob);
+ paintvert_tag_select_update(C, ob);
+}
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index 8d63b1ea020..330008d92d1 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -702,7 +702,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
- (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
+ (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST));
/* First calculate the center of transformation. */
zero_v3(center);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 1febc429edc..c5add97fb00 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -8692,7 +8692,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
* Free the data here, then use #point_normals_ensure to add it back on demand. */
if (ret == OPERATOR_PASS_THROUGH) {
/* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
- if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (!ISMOUSE_MOTION(event->type)) {
point_normals_free(op);
}
}
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 04030583f5c..ac5530c8ea9 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -593,13 +593,143 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
return vmap->vert[v];
}
+#define INVALID_ISLAND ((unsigned int)-1)
+
+static void bm_uv_assign_island(UvElementMap *element_map,
+ UvElement *element,
+ int nisland,
+ uint *map,
+ UvElement *islandbuf,
+ int islandbufsize)
+{
+ element->island = nisland;
+ map[element - element_map->buf] = islandbufsize;
+
+ /* Copy *element to islandbuf[islandbufsize]. */
+ islandbuf[islandbufsize].l = element->l;
+ islandbuf[islandbufsize].separate = element->separate;
+ islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index;
+ islandbuf[islandbufsize].island = element->island;
+ islandbuf[islandbufsize].flag = element->flag;
+}
+
+static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
+ const Scene *scene,
+ UvElement *islandbuf,
+ uint *map,
+ bool uv_selected,
+ int cd_loop_uv_offset)
+{
+ int totuv = element_map->totalUVs;
+
+ /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
+ UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table");
+ for (int i = 0; i < totuv; i++) {
+ UvElement *head = element_map->buf + i;
+ if (head->separate) {
+ UvElement *element = head;
+ while (element) {
+ head_table[element - element_map->buf] = head;
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* Depth first search the graph, building islands as we go. */
+ int nislands = 0;
+ int islandbufsize = 0;
+ int stack_upper_bound = totuv;
+ UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound,
+ "uv_island_element_stack");
+ int stacksize_uv = 0;
+ for (int i = 0; i < totuv; i++) {
+ UvElement *element = element_map->buf + i;
+ if (element->island != INVALID_ISLAND) {
+ /* Unique UV (element and all it's children) are already part of an island. */
+ continue;
+ }
+
+ /* Create a new island, i.e. nislands++. */
+
+ BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */
+
+ /* Seed the graph search. */
+ stack_uv[stacksize_uv++] = element;
+ while (element) {
+ bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+
+ /* Traverse the graph. */
+ while (stacksize_uv) {
+ BLI_assert(stacksize_uv < stack_upper_bound);
+ element = stack_uv[--stacksize_uv];
+ while (element) {
+
+ /* Scan forwards around the BMFace that contains element->l. */
+ if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) {
+ UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next);
+ if (next->island == INVALID_ISLAND) {
+ UvElement *tail = head_table[next - element_map->buf];
+ stack_uv[stacksize_uv++] = tail;
+ while (tail) {
+ bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
+ tail = tail->next;
+ if (tail && tail->separate) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* Scan backwards around the BMFace that contains element->l. */
+ if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) {
+ UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev);
+ if (prev->island == INVALID_ISLAND) {
+ UvElement *tail = head_table[prev - element_map->buf];
+ stack_uv[stacksize_uv++] = tail;
+ while (tail) {
+ bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
+ tail = tail->next;
+ if (tail && tail->separate) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* The same for all the UvElements in this unique UV. */
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+ }
+ nislands++;
+ }
+ BLI_assert(islandbufsize == totuv);
+
+ MEM_SAFE_FREE(stack_uv);
+ MEM_SAFE_FREE(head_table);
+
+ return nislands;
+}
+
UvElementMap *BM_uv_element_map_create(BMesh *bm,
const Scene *scene,
- const bool face_selected,
const bool uv_selected,
const bool use_winding,
const bool do_islands)
{
+ /* In uv sync selection, all UVs are visible. */
+ const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
+
BMVert *ev;
BMFace *efa;
BMLoop *l;
@@ -623,15 +753,21 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
/* generate UvElement array */
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- if (!uv_selected) {
- totuv += efa->len;
- }
- else {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- totuv++;
- }
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
+ if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
+ }
+
+ if (!uv_selected) {
+ totuv += efa->len;
+ }
+ else {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ totuv++;
}
}
}
@@ -649,46 +785,48 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
"UvElement");
if (use_winding) {
- winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding");
+ winding = MEM_callocN(sizeof(*winding) * totfaces, "winding");
}
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
- if (use_winding) {
- winding[j] = false;
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ continue;
}
- if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- float(*tf_uv)[2] = NULL;
+ if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
+ }
- if (use_winding) {
- tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
- }
+ float(*tf_uv)[2] = NULL;
- BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
- if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- continue;
- }
-
- buf->l = l;
- buf->separate = 0;
- buf->island = INVALID_ISLAND;
- buf->loop_of_poly_index = i;
+ if (use_winding) {
+ tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
+ }
- buf->next = element_map->vert[BM_elem_index_get(l->v)];
- element_map->vert[BM_elem_index_get(l->v)] = buf;
+ BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
+ if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
- if (use_winding) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- copy_v2_v2(tf_uv[i], luv->uv);
- }
+ buf->l = l;
+ buf->separate = 0;
+ buf->island = INVALID_ISLAND;
+ buf->loop_of_poly_index = i;
- buf++;
- }
+ buf->next = element_map->vert[BM_elem_index_get(l->v)];
+ element_map->vert[BM_elem_index_get(l->v)] = buf;
if (use_winding) {
- winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ copy_v2_v2(tf_uv[i], luv->uv);
}
+
+ buf++;
+ }
+
+ if (winding) {
+ winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
}
}
@@ -771,6 +909,15 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face");
copy_vn_i(island_number, totfaces, INVALID_ISLAND);
+ const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
+ scene->toolsettings->selectmode & SCE_SELECT_EDGE :
+ scene->toolsettings->uv_selectmode & UV_SELECT_EDGE;
+ if (use_uv_edge_connectivity) {
+ nislands = bm_uv_edge_select_build_islands(
+ element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset);
+ islandbufsize = totuv;
+ }
+
/* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
* Now we should sort uv's in islands. */
for (i = 0; i < totuv; i++) {
@@ -798,13 +945,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
if (element->l->f == efa) {
/* found the uv corresponding to our face and vertex.
* Now fill it to the buffer */
- element->island = nislands;
- map[element - element_map->buf] = islandbufsize;
- islandbuf[islandbufsize].l = element->l;
- islandbuf[islandbufsize].separate = element->separate;
- islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index;
- islandbuf[islandbufsize].island = nislands;
- islandbufsize++;
+ bm_uv_assign_island(
+ element_map, element, nislands, map, islandbuf, islandbufsize++);
for (element = initelement; element; element = element->next) {
if (element->separate && element != initelement) {
@@ -1646,12 +1788,13 @@ void EDBM_project_snap_verts(
depsgraph,
region,
CTX_wm_view3d(C),
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_NOT_ACTIVE,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
+ NULL,
mval,
NULL,
NULL,
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 97376a495c1..a2f993c92b9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -76,7 +76,6 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc
index 041a1383b28..e9abf8c1441 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -24,6 +24,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_fluidsim_types.h"
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
@@ -36,6 +37,7 @@
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -71,6 +73,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_nla.h"
+#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_pointcloud.h"
@@ -113,6 +116,10 @@
#include "object_intern.h"
+using blender::float3;
+using blender::float4x4;
+using blender::Vector;
+
/* -------------------------------------------------------------------- */
/** \name Local Enum Declarations
* \{ */
@@ -1328,21 +1335,21 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
const char *ob_name = nullptr;
switch (type) {
case GP_EMPTY: {
- ob_name = "GPencil";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil");
break;
}
case GP_MONKEY: {
- ob_name = "Suzanne";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne");
break;
}
case GP_STROKE: {
- ob_name = "Stroke";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke");
break;
}
case GP_LRT_OBJECT:
case GP_LRT_SCENE:
case GP_LRT_COLLECTION: {
- ob_name = "Line Art";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt");
break;
}
default: {
@@ -1984,7 +1991,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op)
AnimData *adt = BKE_animdata_ensure_id(&ob->id);
NlaTrack *nlt = BKE_nlatrack_add(adt, nullptr, is_liboverride);
NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, static_cast<Speaker *>(ob->data));
- strip->start = CFRA;
+ strip->start = scene->r.cfra;
strip->end += strip->start;
/* hook them up */
@@ -2023,14 +2030,6 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot)
/** \name Add Curves Operator
* \{ */
-static bool object_curves_add_poll(bContext *C)
-{
- if (!U.experimental.use_new_curves_type) {
- return false;
- }
- return ED_operator_objectmode(C);
-}
-
static int object_curves_random_add_exec(bContext *C, wmOperator *op)
{
using namespace blender;
@@ -2043,7 +2042,6 @@ static int object_curves_random_add_exec(bContext *C, wmOperator *op)
}
Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits);
- object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
Curves *curves_id = static_cast<Curves *>(object->data);
bke::CurvesGeometry::wrap(curves_id->geometry) = ed::curves::primitive_random_sphere(500, 8);
@@ -2060,7 +2058,7 @@ void OBJECT_OT_curves_random_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = object_curves_random_add_exec;
- ot->poll = object_curves_add_poll;
+ ot->poll = ED_operator_objectmode;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2070,38 +2068,48 @@ void OBJECT_OT_curves_random_add(wmOperatorType *ot)
static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
{
+ Scene *scene = CTX_data_scene(C);
+
ushort local_view_bits;
- float loc[3], rot[3];
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
+ C, op, 'Z', nullptr, nullptr, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
Object *surface_ob = CTX_data_active_object(C);
+ BLI_assert(surface_ob != nullptr);
- Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits);
- object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
+ Object *curves_ob = ED_object_add_type(
+ C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
+ BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false);
- if (surface_ob != nullptr && surface_ob->type == OB_MESH) {
- Curves *curves_id = static_cast<Curves *>(object->data);
- curves_id->surface = surface_ob;
- id_us_plus(&surface_ob->id);
+ /* Set surface object. */
+ Curves *curves_id = static_cast<Curves *>(curves_ob->data);
+ curves_id->surface = surface_ob;
- 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);
- }
+ /* Parent to surface object. */
+ ED_object_parent_set(
+ op->reports, C, scene, curves_ob, surface_ob, PAR_OBJECT, false, true, nullptr);
+
+ /* Decide which UV map to use for attachment. */
+ 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);
}
+ /* Add deformation modifier. */
+ blender::ed::curves::ensure_surface_deformation_node_exists(*C, *curves_ob);
+
+ /* Make sure the surface object has a rest position attribute which is necessary for
+ * deformations. */
+ surface_ob->modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION;
+
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;
}
@@ -2763,28 +2771,6 @@ static const EnumPropertyItem convert_target_items[] = {
{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) {
@@ -3192,9 +3178,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
/* Anonymous attributes shouldn't be available on the applied geometry. */
- MeshComponent component;
- component.replace(new_mesh, GeometryOwnershipType::Editable);
- component.attributes_remove_anonymous();
+ blender::bke::mesh_attributes_for_write(*new_mesh).remove_anonymous();
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
}
@@ -3544,7 +3528,6 @@ 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,
@@ -3600,7 +3583,8 @@ static Base *object_add_duplicate_internal(Main *bmain,
ViewLayer *view_layer,
Object *ob,
const eDupli_ID_Flags dupflag,
- const eLibIDDuplicateFlags duplicate_options)
+ const eLibIDDuplicateFlags duplicate_options,
+ Object **r_ob_new)
{
Base *base, *basen = nullptr;
Object *obn;
@@ -3611,6 +3595,9 @@ static Base *object_add_duplicate_internal(Main *bmain,
else {
obn = static_cast<Object *>(
ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)));
+ if (r_ob_new) {
+ *r_ob_new = obn;
+ }
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
base = BKE_view_layer_base_find(view_layer, ob);
@@ -3623,7 +3610,7 @@ static Base *object_add_duplicate_internal(Main *bmain,
}
basen = BKE_view_layer_base_find(view_layer, obn);
- if (base != nullptr) {
+ if (base != nullptr && basen != nullptr) {
basen->local_view_bits = base->local_view_bits;
}
@@ -3654,7 +3641,8 @@ Base *ED_object_add_duplicate(
base->object,
dupflag,
LIB_ID_DUPLICATE_IS_SUBPROCESS |
- LIB_ID_DUPLICATE_IS_ROOT_ID);
+ LIB_ID_DUPLICATE_IS_ROOT_ID,
+ nullptr);
if (basen == nullptr) {
return nullptr;
}
@@ -3687,45 +3675,73 @@ static int duplicate_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool linked = RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag;
- bool changed = false;
/* We need to handle that here ourselves, because we may duplicate several objects, in which case
* we also want to remap pointers between those... */
BKE_main_id_newptr_and_tag_clear(bmain);
+ /* Do not do collection re-syncs for each object; will do it once afterwards.
+ * However this means we can't get to new duplicated Base's immediately, will
+ * have to process them after the sync. */
+ BKE_layer_collection_resync_forbid();
+
+ /* Duplicate the selected objects, remember data needed to process
+ * after the sync (the base of the original object, and the copy of the
+ * original object). */
+ blender::Vector<std::pair<Base *, Object *>> source_bases_new_objects;
+ Object *ob_new_active = nullptr;
+
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
- Base *basen = object_add_duplicate_internal(bmain,
- scene,
- view_layer,
- base->object,
- dupflag,
- LIB_ID_DUPLICATE_IS_SUBPROCESS |
- LIB_ID_DUPLICATE_IS_ROOT_ID);
+ Object *ob_new = NULL;
+ object_add_duplicate_internal(bmain,
+ scene,
+ view_layer,
+ base->object,
+ dupflag,
+ LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID,
+ &ob_new);
+ if (ob_new == nullptr) {
+ continue;
+ }
+ source_bases_new_objects.append({base, ob_new});
/* note that this is safe to do with this context iterator,
* the list is made in advance */
ED_object_base_select(base, BA_DESELECT);
- ED_object_base_select(basen, BA_SELECT);
- changed = true;
- if (basen == nullptr) {
- continue;
- }
-
- /* new object becomes active */
+ /* new object will become active */
if (BASACT(view_layer) == base) {
- ED_object_base_activate(C, basen);
- }
-
- if (basen->object->data) {
- DEG_id_tag_update(static_cast<ID *>(basen->object->data), 0);
+ ob_new_active = ob_new;
}
}
CTX_DATA_END;
- if (!changed) {
+ if (source_bases_new_objects.is_empty()) {
return OPERATOR_CANCELLED;
}
+ /* Sync the collection now, after everything is duplicated. */
+ BKE_layer_collection_resync_allow();
+ BKE_main_collection_sync(bmain);
+
+ /* After sync we can get to the new Base data, process it here. */
+ for (const auto &item : source_bases_new_objects) {
+ Object *ob_new = item.second;
+ Base *base_source = item.first;
+ Base *base_new = BKE_view_layer_base_find(view_layer, ob_new);
+ if (base_new == nullptr) {
+ continue;
+ }
+ ED_object_base_select(base_new, BA_SELECT);
+ if (ob_new == ob_new_active) {
+ ED_object_base_activate(C, base_new);
+ }
+ if (base_new->object->data) {
+ DEG_id_tag_update(static_cast<ID *>(base_new->object->data), 0);
+ }
+ /* #object_add_duplicate_internal will not have done this, since
+ * before the collection sync it would not have found the new base yet. */
+ base_new->local_view_bits = base_source->local_view_bits;
+ }
/* Note that this will also clear newid pointers and tags. */
copy_object_set_idnew(C);
@@ -3808,7 +3824,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
* the case here. So we have to do the new-ID relinking ourselves
* (#copy_object_set_idnew()).
*/
- LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID);
+ LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID,
+ nullptr);
if (basen == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
@@ -3976,7 +3993,7 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot)
/* api callbacks */
ot->invoke = object_add_drop_xy_generic_invoke;
ot->exec = object_transform_to_mouse_exec;
- ot->poll = ED_operator_objectmode;
+ ot->poll = ED_operator_objectmode_poll_msg;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 58daf753679..a664d93bb2e 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -324,7 +324,7 @@ static bool write_external_bake_pixels(const char *filepath,
const int height,
const int margin,
const int margin_type,
- ImageFormatData *im_format,
+ ImageFormatData const *im_format,
const bool is_noncolor,
Mesh const *mesh_eval,
char const *uv_layer,
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index e65b5a61299..4896ddb5258 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1469,8 +1469,6 @@ 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;
@@ -1518,7 +1516,11 @@ 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);
+ if (use_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");
+ 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;
}
diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc
index 202c6d96a47..e7cfcf48fd3 100644
--- a/source/blender/editors/object/object_modifier.cc
+++ b/source/blender/editors/object/object_modifier.cc
@@ -757,9 +757,7 @@ static bool modifier_apply_obdata(
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
/* Anonymous attributes shouldn't be available on the applied geometry. */
- MeshComponent component;
- component.replace(me, GeometryOwnershipType::Editable);
- component.attributes_remove_anonymous();
+ blender::bke::mesh_attributes_for_write(*me).remove_anonymous();
if (md_eval->type == eModifierType_Multires) {
multires_customdata_delete(me);
@@ -820,7 +818,7 @@ static bool modifier_apply_obdata(
/* Create a temporary geometry set and component. */
GeometrySet geometry_set;
geometry_set.get_component_for_write<CurveComponent>().replace(
- &curves, GeometryOwnershipType::Editable);
+ &curves, GeometryOwnershipType::ReadOnly);
ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0};
mti->modifyGeometrySet(md_eval, &mectx, &geometry_set);
@@ -828,20 +826,18 @@ static bool modifier_apply_obdata(
BKE_report(reports, RPT_ERROR, "Evaluated geometry from modifier does not contain curves");
return false;
}
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
Curves &curves_eval = *geometry_set.get_curves_for_write();
/* Anonymous attributes shouldn't be available on the applied geometry. */
- component.attributes_remove_anonymous();
+ blender::bke::CurvesGeometry::wrap(curves_eval.geometry)
+ .attributes_for_write()
+ .remove_anonymous();
- /* If the modifier's output is a different curves data-block, copy the relevant information to
- * the original. */
- if (&curves_eval != &curves) {
- blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
- blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
- Main *bmain = DEG_get_bmain(depsgraph);
- BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
- }
+ /* Copy the relevant information to the original. */
+ blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
+ blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
}
else {
/* TODO: implement for point clouds and volumes. */
@@ -1440,7 +1436,6 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
Scene *scene = CTX_data_scene(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_report = RNA_boolean_get(op->ptr, "report");
const bool do_single_user = RNA_boolean_get(op->ptr, "single_user");
const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata");
@@ -1449,6 +1444,8 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
return OPERATOR_CANCELLED;
}
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
+
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
ED_object_single_obdata_user(bmain, scene, ob);
BKE_main_id_newptr_and_tag_clear(bmain);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index f55ffdf0fcd..01042824aac 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -951,7 +951,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
1);
struct {
- bool mesh, gpencil;
+ bool mesh, gpencil, curves;
} has_children_of_type = {0};
CTX_DATA_BEGIN (C, Object *, child, selected_editable_objects) {
@@ -964,6 +964,9 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
if (child->type == OB_GPENCIL) {
has_children_of_type.gpencil = true;
}
+ if (child->type == OB_CURVES) {
+ has_children_of_type.curves = true;
+ }
}
CTX_DATA_END;
@@ -987,6 +990,11 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
else if (parent->type == OB_LATTICE) {
uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_LATTICE);
}
+ else if (parent->type == OB_MESH) {
+ if (has_children_of_type.curves) {
+ uiItemO(layout, "Object (Attach Curves to Surface)", ICON_NONE, "CURVES_OT_surface_set");
+ }
+ }
/* vertex parenting */
if (OB_TYPE_SUPPORT_PARVERT(parent->type)) {
@@ -2267,7 +2275,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
ID *id_root = NULL;
bool is_override_instancing_object = false;
- const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable");
+ const bool do_fully_editable = U.experimental.use_override_new_fully_editable;
GSet *user_overrides_objects_uids = do_fully_editable ? NULL :
BLI_gset_new(BLI_ghashutil_inthash_p,
@@ -2495,13 +2503,6 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
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);
}
/** \} */
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index ebcf8573ccd..0e3945bff15 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
@@ -299,6 +301,10 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op)
bool changed = false;
if (RNA_boolean_get(op->ptr, "all")) {
+ if (RNA_boolean_get(op->ptr, "apply_mix")) {
+ float *arr = BKE_key_evaluate_object_ex(ob, NULL, NULL, 0, ob->data);
+ MEM_freeN(arr);
+ }
changed = BKE_object_shapekey_free(bmain, ob);
}
else {
@@ -315,6 +321,34 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+static bool shape_key_remove_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+ const bool do_all = RNA_enum_get(op->ptr, "all");
+
+ /* Only show seed for randomize action! */
+ if (STREQ(prop_id, "apply_mix") && !do_all) {
+ return false;
+ }
+ return true;
+}
+
+static char *shape_key_remove_get_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(ot),
+ PointerRNA *ptr)
+{
+ const bool do_apply_mix = RNA_boolean_get(ptr, "apply_mix");
+
+ if (do_apply_mix) {
+ return BLI_strdup(
+ TIP_("Apply current visible shape to the object data, and delete all shape keys"));
+ }
+
+ return NULL;
+}
+
void OBJECT_OT_shape_key_remove(wmOperatorType *ot)
{
/* identifiers */
@@ -325,12 +359,19 @@ void OBJECT_OT_shape_key_remove(wmOperatorType *ot)
/* api callbacks */
ot->poll = shape_key_mode_exists_poll;
ot->exec = shape_key_remove_exec;
+ ot->poll_property = shape_key_remove_poll_property;
+ ot->get_description = shape_key_remove_get_description;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys");
+ RNA_def_boolean(ot->srna, "all", false, "All", "Remove all shape keys");
+ RNA_def_boolean(ot->srna,
+ "apply_mix",
+ false,
+ "Apply Mix",
+ "Apply current mix of shape keys to the geometry before removing them");
}
/** \} */
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 70c3eed3768..c9c96900af3 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -2222,7 +2222,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
bool is_finished = false;
- if (ISMOUSE(xfd->init_event)) {
+ if (ISMOUSE_BUTTON(xfd->init_event)) {
if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
is_finished = true;
}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 9ad36cacc7d..17b7fe7fe5e 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -693,7 +693,7 @@ static const EnumPropertyItem WT_vertex_group_select_item[] = {
const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *C,
PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
+ PropertyRNA *prop,
bool *r_free,
const uint selection_mask)
{
@@ -731,6 +731,12 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *
RNA_enum_items_add_value(&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_ALL);
}
+ /* Set `Deform Bone` as default selection if armature is present. */
+ if (ob) {
+ RNA_def_property_enum_default(
+ prop, BKE_modifiers_is_deformed_by_armature(ob) ? WT_VGROUP_BONE_DEFORM : WT_VGROUP_ALL);
+ }
+
RNA_enum_item_end(&item, &totitem);
*r_free = true;
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 0e03feba340..03f9b4eb867 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -32,6 +32,7 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_legacy_convert.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
@@ -1512,7 +1513,7 @@ static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob,
}
}
- psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering);
+ psys_cache_edit_paths(depsgraph, scene, ob, edit, scene->r.cfra, G.is_rendering);
/* disable update flag */
LOOP_POINTS {
@@ -1647,11 +1648,11 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla
* and flagging with PEK_HIDE will prevent selection. This might get restored once this is
* supported in drawing (but doesn't make much sense for hair anyways). */
if (edit->psys && edit->psys->part->type == PART_EMITTER) {
- PE_hide_keys_time(scene, edit, CFRA);
+ PE_hide_keys_time(scene, edit, scene->r.cfra);
}
/* regenerate path caches */
- psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering);
+ psys_cache_edit_paths(depsgraph, scene, ob, edit, scene->r.cfra, G.is_rendering);
/* disable update flag */
LOOP_POINTS {
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 6bea6e2c19e..96aea0ededf 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -26,6 +26,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_legacy_convert.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index 96195bdcc2e..80de8fae072 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -256,8 +256,8 @@ static void fluid_bake_sequence(FluidJob *job)
frame = is_first_frame ? fds->cache_frame_start : (*pause_frame);
/* Save orig frame and update scene frame. */
- orig_frame = CFRA;
- CFRA = frame;
+ orig_frame = scene->r.cfra;
+ scene->r.cfra = frame;
/* Loop through selected frames. */
for (; frame <= fds->cache_frame_end; frame++) {
@@ -280,7 +280,7 @@ static void fluid_bake_sequence(FluidJob *job)
*(job->progress) = progress;
}
- CFRA = frame;
+ scene->r.cfra = frame;
/* Update animation system. */
ED_update_for_newframe(job->bmain, job->depsgraph);
@@ -293,7 +293,7 @@ static void fluid_bake_sequence(FluidJob *job)
}
/* Restore frame position that we were on before bake. */
- CFRA = orig_frame;
+ scene->r.cfra = orig_frame;
}
static void fluid_bake_endjob(void *customdata)
diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc
index d907a52543c..77ad23f1e3f 100644
--- a/source/blender/editors/render/render_opengl.cc
+++ b/source/blender/editors/render/render_opengl.cc
@@ -494,7 +494,8 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender)
for (view_id = 0; view_id < oglrender->views_len; view_id++) {
context.view_id = view_id;
context.gpu_offscreen = oglrender->ofs;
- oglrender->seq_data.ibufs_arr[view_id] = SEQ_render_give_ibuf(&context, CFRA, chanshown);
+ oglrender->seq_data.ibufs_arr[view_id] = SEQ_render_give_ibuf(
+ &context, scene->r.cfra, chanshown);
}
}
@@ -757,8 +758,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
WM_jobs_kill_all_except(wm, CTX_wm_screen(C));
/* create offscreen buffer */
- sizex = (scene->r.size * scene->r.xsch) / 100;
- sizey = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &sizex, &sizey);
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */
@@ -1136,12 +1136,12 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
RenderResult *rr;
/* go to next frame */
- if (CFRA < oglrender->nfra) {
- CFRA++;
+ if (scene->r.cfra < oglrender->nfra) {
+ scene->r.cfra++;
}
- while (CFRA < oglrender->nfra) {
+ while (scene->r.cfra < oglrender->nfra) {
BKE_scene_graph_update_for_newframe(depsgraph);
- CFRA++;
+ scene->r.cfra++;
}
is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
@@ -1184,7 +1184,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
}
if (oglrender->render_frames == nullptr ||
- BLI_BITMAP_TEST_BOOL(oglrender->render_frames, CFRA - PSFRA)) {
+ BLI_BITMAP_TEST_BOOL(oglrender->render_frames, scene->r.cfra - PSFRA)) {
/* render into offscreen buffer */
screen_opengl_render_apply(C, oglrender);
}
@@ -1204,7 +1204,7 @@ finally: /* Step the frame and bail early if needed */
oglrender->nfra += scene->r.frame_step;
/* stop at the end or on error */
- if (CFRA >= PEFRA || !ok) {
+ if (scene->r.cfra >= PEFRA || !ok) {
screen_opengl_render_end(C, static_cast<OGLRender *>(op->customdata));
return false;
}
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index addcedc4481..97bbcaa102f 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -805,7 +805,7 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
/* Preview need to be in the current frame to get a thumbnail similar of what
* viewport displays. */
- CFRA = preview_data->cfra;
+ scene->r.cfra = preview_data->cfra;
ViewLayer *view_layer = static_cast<ViewLayer *>(scene->view_layers.first);
Depsgraph *depsgraph = DEG_graph_new(
diff --git a/source/blender/editors/render/render_view.cc b/source/blender/editors/render/render_view.cc
index a7ff2aad05a..9a16c910205 100644
--- a/source/blender/editors/render/render_view.cc
+++ b/source/blender/editors/render/render_view.cc
@@ -19,6 +19,7 @@
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_report.h"
+#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BLT_translation.h"
@@ -130,8 +131,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
}
if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) {
- int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100;
- int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100;
+ int sizex, sizey;
+ BKE_render_resolution(&scene->r, false, &sizex, &sizey);
+
+ sizex += 30 * UI_DPI_FAC;
+ sizey += 60 * UI_DPI_FAC;
/* arbitrary... miniature image window views don't make much sense */
if (sizex < 320) {
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index fe69759481b..0d6b6ee1d78 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -100,6 +100,7 @@ const char *screen_context_dir[] = {
"active_gpencil_frame",
"active_annotation_layer",
"active_operator",
+ "active_action",
"selected_visible_actions",
"selected_editable_actions",
"visible_fcurves",
@@ -969,6 +970,7 @@ static eContextResult screen_ctx_active_operator(const bContext *C, bContextData
}
static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
bContextDataResult *result,
+ bool active_only,
bool editable)
{
bAnimContext ac;
@@ -978,11 +980,17 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
SpaceAction *saction = (SpaceAction *)ac.sl;
if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) {
- if (saction->action && !(editable && ID_IS_LINKED(saction->action))) {
- CTX_data_id_list_add(result, &saction->action->id);
+ if (active_only) {
+ CTX_data_id_pointer_set(result, (ID *)saction->action);
+ }
+ else {
+ if (saction->action && !(editable && ID_IS_LINKED(saction->action))) {
+ CTX_data_id_list_add(result, &saction->action->id);
+ }
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
}
- CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return CTX_RESULT_OK;
}
}
@@ -995,7 +1003,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
switch (ac.spacetype) {
case SPACE_GRAPH:
- filter |= ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL;
+ filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE |
+ (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL);
break;
case SPACE_ACTION:
@@ -1006,7 +1015,7 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
- GSet *seen_set = BLI_gset_ptr_new("seen actions");
+ GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions");
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
/* In dopesheet check selection status of individual items, skipping
@@ -1019,36 +1028,48 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
bAction *action = ANIM_channel_action_get(ale);
if (action) {
- if (editable && ID_IS_LINKED(action)) {
- continue;
+ if (active_only) {
+ CTX_data_id_pointer_set(result, (ID *)action);
+ break;
}
+ else {
+ if (editable && ID_IS_LINKED(action)) {
+ continue;
+ }
- /* Add the action to the output list if not already added. */
- if (BLI_gset_add(seen_set, action)) {
- CTX_data_id_list_add(result, &action->id);
+ /* Add the action to the output list if not already added. */
+ if (BLI_gset_add(seen_set, action)) {
+ CTX_data_id_list_add(result, &action->id);
+ }
}
}
}
- BLI_gset_free(seen_set, NULL);
-
ANIM_animdata_freelist(&anim_data);
- CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ if (!active_only) {
+ BLI_gset_free(seen_set, NULL);
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ }
+
return CTX_RESULT_OK;
}
return CTX_RESULT_NO_DATA;
}
-
+static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result)
+{
+ return screen_ctx_sel_actions_impl(C, result, true, false);
+}
static eContextResult screen_ctx_selected_visible_actions(const bContext *C,
bContextDataResult *result)
{
- return screen_ctx_sel_actions_impl(C, result, false);
+ return screen_ctx_sel_actions_impl(C, result, false, false);
}
static eContextResult screen_ctx_selected_editable_actions(const bContext *C,
bContextDataResult *result)
{
- return screen_ctx_sel_actions_impl(C, result, true);
+ return screen_ctx_sel_actions_impl(C, result, false, true);
}
static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C,
bContextDataResult *result,
@@ -1059,8 +1080,9 @@ static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C,
ListBase anim_data = {NULL, NULL};
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS) |
- (ac.spacetype == SPACE_GRAPH ? ANIMFILTER_CURVE_VISIBLE :
- ANIMFILTER_LIST_VISIBLE) |
+ (ac.spacetype == SPACE_GRAPH ?
+ (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) :
+ ANIMFILTER_LIST_VISIBLE) |
extra_filter;
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1104,7 +1126,7 @@ static eContextResult screen_ctx_active_editable_fcurve(const bContext *C,
ListBase anim_data = {NULL, NULL};
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
- ANIMFILTER_CURVE_VISIBLE);
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1130,8 +1152,9 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C,
/* Use keyframes from editable selected FCurves. */
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FOREDIT |
ANIMFILTER_SEL) |
- (ac.spacetype == SPACE_GRAPH ? ANIMFILTER_CURVE_VISIBLE :
- ANIMFILTER_LIST_VISIBLE);
+ (ac.spacetype == SPACE_GRAPH ?
+ (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) :
+ ANIMFILTER_LIST_VISIBLE);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1260,6 +1283,7 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers);
register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes);
register_context_function("active_operator", screen_ctx_active_operator);
+ register_context_function("active_action", screen_ctx_active_action);
register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions);
register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions);
register_context_function("editable_fcurves", screen_ctx_editable_fcurves);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index d35bd5ab2fe..3618b933443 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -2951,9 +2951,9 @@ static int frame_offset_exec(bContext *C, wmOperator *op)
int delta = RNA_int_get(op->ptr, "delta");
- CFRA += delta;
- FRAMENUMBER_MIN_CLAMP(CFRA);
- SUBFRA = 0.0f;
+ scene->r.cfra += delta;
+ FRAMENUMBER_MIN_CLAMP(scene->r.cfra);
+ scene->r.subframe = 0.0f;
areas_do_frame_follow(C, false);
@@ -2992,7 +2992,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
- /* Don't change CFRA directly if animtimer is running as this can cause
+ /* Don't change scene->r.cfra directly if animtimer is running as this can cause
* first/last frame not to be actually shown (bad since for example physics
* simulations aren't reset properly).
*/
@@ -3010,10 +3010,10 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
}
else {
if (RNA_boolean_get(op->ptr, "end")) {
- CFRA = PEFRA;
+ scene->r.cfra = PEFRA;
}
else {
- CFRA = PSFRA;
+ scene->r.cfra = PSFRA;
}
areas_do_frame_follow(C, true);
@@ -3062,7 +3062,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- float cfra = (float)(CFRA);
+ float cfra = (float)(scene->r.cfra);
/* Initialize binary-tree-list for getting keyframes. */
struct AnimKeylist *keylist = ED_keylist_create();
@@ -3104,9 +3104,9 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
}
while ((ak != NULL) && (done == false)) {
- if (CFRA != (int)ak->cfra) {
+ if (scene->r.cfra != (int)ak->cfra) {
/* this changes the frame, so set the frame and we're done */
- CFRA = (int)ak->cfra;
+ scene->r.cfra = (int)ak->cfra;
done = true;
}
else {
@@ -3165,20 +3165,20 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
static int marker_jump_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- int closest = CFRA;
+ int closest = scene->r.cfra;
const bool next = RNA_boolean_get(op->ptr, "next");
bool found = false;
/* find matching marker in the right direction */
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
if (next) {
- if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
+ if ((marker->frame > scene->r.cfra) && (!found || closest > marker->frame)) {
closest = marker->frame;
found = true;
}
}
else {
- if ((marker->frame < CFRA) && (!found || closest < marker->frame)) {
+ if ((marker->frame < scene->r.cfra) && (!found || closest < marker->frame)) {
closest = marker->frame;
found = true;
}
@@ -3192,7 +3192,7 @@ static int marker_jump_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- CFRA = closest;
+ scene->r.cfra = closest;
areas_do_frame_follow(C, true);
@@ -4714,11 +4714,11 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con
if (sad->flag & ANIMPLAY_FLAG_JUMPED) {
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
#ifdef PROFILE_AUDIO_SYNCH
- old_frame = CFRA;
+ old_frame = scene->r.cfra;
#endif
}
- /* since we follow drawflags, we can't send notifier but tag regions ourselves */
+ /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */
if (depsgraph != NULL) {
ED_update_for_newframe(bmain, depsgraph);
}
diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c
index 1156452310c..9d66debda6f 100644
--- a/source/blender/editors/screen/screen_user_menu.c
+++ b/source/blender/editors/screen/screen_user_menu.c
@@ -214,7 +214,14 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu)
wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
if (ot != NULL) {
IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
- uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL);
+ uiItemFullO_ptr(menu->layout,
+ ot,
+ CTX_IFACE_(ot->translation_context, ui_name),
+ ICON_NONE,
+ prop,
+ umi_op->opcontext,
+ 0,
+ NULL);
is_empty = false;
}
else {
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index 01417650b3d..cb29f15420c 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -54,17 +54,87 @@ WorkSpace *ED_workspace_add(Main *bmain, const char *name)
return BKE_workspace_add(bmain, name);
}
+static void workspace_exit(WorkSpace *workspace, wmWindow *win)
+{
+ /* Scene pinning: Store whatever scene was active when leaving the workspace. It's reactivated
+ * when the workspace gets reactivated as well. */
+ if (workspace->flags & WORKSPACE_USE_PIN_SCENE) {
+ workspace->pin_scene = WM_window_get_active_scene(win);
+ }
+ else {
+ /* The active scene may have been changed. So also always update the unpinned scene to the
+ * latest when leaving a workspace that has no scene pinning. */
+ win->unpinned_scene = WM_window_get_active_scene(win);
+ }
+}
+
+/**
+ * State changes (old workspace to new workspace):
+ * 1) unpinned -> pinned
+ * * Store current scene as the unpinned one (done in #workspace_exit()).
+ * * Change the current scene to the pinned one.
+ * 2) pinned -> pinned
+ * * Change the current scene to the new pinned one.
+ * 3) pinned -> unpinned
+ * * Change current scene back to the unpinned one
+ * 4) unpinned -> unpinned
+ * * Make sure the unpinned scene is active.
+ *
+ * Note that the pin scene must also be updated when leaving a workspace with a pinned scene.
+ * That's done separately via workspace_exit() above.
+ */
+static void workspace_scene_pinning_update(WorkSpace *workspace_new,
+ const WorkSpace *workspace_old,
+ bContext *C)
+{
+ wmWindow *win = CTX_wm_window(C);
+ Main *bmain = CTX_data_main(C);
+ Scene *active_scene = WM_window_get_active_scene(win);
+
+ const bool is_new_pinned = (workspace_new->flags & WORKSPACE_USE_PIN_SCENE);
+ const bool is_old_pinned = (workspace_old->flags & WORKSPACE_USE_PIN_SCENE);
+
+ /* State changes 1 and 2. */
+ if (is_new_pinned) {
+ if (workspace_new->pin_scene && (workspace_new->pin_scene != active_scene)) {
+ WM_window_set_active_scene(bmain, C, win, workspace_new->pin_scene);
+ workspace_new->pin_scene = NULL;
+ }
+ }
+ /* State change 3 - Changing from workspace with pinned scene to unpinned scene. */
+ else if (is_old_pinned) {
+ if (win->unpinned_scene) {
+ WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
+ }
+ else {
+ /* When leaving a workspace where the pinning was just enabled, the unpinned scene wasn't set
+ * yet. */
+ win->unpinned_scene = active_scene;
+ }
+ }
+ else {
+ /* When leaving a workspace where the pinning was just disabled, we still want to restore the
+ * unpinned scene. */
+ if (win->unpinned_scene) {
+ WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
+ }
+ }
+
+ BLI_assert(WM_window_get_active_scene(win));
+}
+
/**
* Changes the object mode (if needed) to the one set in \a workspace_new.
* Object mode is still stored on object level. In future it should all be workspace level instead.
*/
static void workspace_change_update(WorkSpace *workspace_new,
- const WorkSpace *workspace_old,
+ WorkSpace *workspace_old,
bContext *C,
wmWindowManager *wm)
{
+ workspace_scene_pinning_update(workspace_new, workspace_old, C);
/* needs to be done before changing mode! (to ensure right context) */
- UNUSED_VARS(workspace_old, workspace_new, C, wm);
+ UNUSED_VARS(wm);
#if 0
Object *ob_act = CTX_data_active_object(C) eObjectMode mode_old = workspace_old->object_mode;
eObjectMode mode_new = workspace_new->object_mode;
@@ -113,6 +183,8 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager
return false;
}
+ workspace_exit(workspace_old, win);
+
screen_change_prepare(screen_old, screen_new, bmain, C, win);
if (screen_new == NULL) {
@@ -143,6 +215,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo
WorkSpace *workspace_new = ED_workspace_add(bmain, workspace_old->id.name + 2);
workspace_new->flags = workspace_old->flags;
+ workspace_new->pin_scene = workspace_old->pin_scene;
workspace_new->object_mode = workspace_old->object_mode;
workspace_new->order = workspace_old->order;
BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids);
@@ -512,6 +585,35 @@ static void WORKSPACE_OT_reorder_to_front(wmOperatorType *ot)
ot->exec = workspace_reorder_to_front_exec;
}
+static int workspace_scene_pin_toggle(bContext *C, wmOperator *UNUSED(op))
+{
+ WorkSpace *workspace = workspace_context_get(C);
+
+ /* Trivial. The operator is only needed to display a superimposed extra icon, which
+ * requires an operator. */
+ workspace->flags ^= WORKSPACE_USE_PIN_SCENE;
+
+ WM_event_add_notifier(C, NC_WORKSPACE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void WORKSPACE_OT_scene_pin_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Pin Scene to Workspace";
+ ot->description =
+ "Remember the last used scene for the current workspace and switch to it whenever this "
+ "workspace is activated again";
+ ot->idname = "WORKSPACE_OT_scene_pin_toggle";
+
+ /* api callbacks */
+ ot->poll = workspace_context_poll;
+ ot->exec = workspace_scene_pin_toggle;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
void ED_operatortypes_workspace(void)
{
WM_operatortype_append(WORKSPACE_OT_duplicate);
@@ -520,6 +622,7 @@ void ED_operatortypes_workspace(void)
WM_operatortype_append(WORKSPACE_OT_append_activate);
WM_operatortype_append(WORKSPACE_OT_reorder_to_back);
WM_operatortype_append(WORKSPACE_OT_reorder_to_front);
+ WM_operatortype_append(WORKSPACE_OT_scene_pin_toggle);
}
/** \} Workspace Operators */
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 34247b4ef75..edb0f1cda4d 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -32,10 +32,15 @@ set(SRC
curves_sculpt_brush.cc
curves_sculpt_comb.cc
curves_sculpt_delete.cc
+ curves_sculpt_density.cc
curves_sculpt_grow_shrink.cc
curves_sculpt_ops.cc
- curves_sculpt_selection.cc
+ curves_sculpt_pinch.cc
+ curves_sculpt_puff.cc
curves_sculpt_selection_paint.cc
+ curves_sculpt_selection.cc
+ curves_sculpt_slide.cc
+ curves_sculpt_smooth.cc
curves_sculpt_snake_hook.cc
paint_canvas.cc
paint_cursor.c
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
index bac03de77e7..6c693376ad3 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
@@ -23,6 +23,8 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_report.h"
@@ -42,6 +44,8 @@
#include "WM_api.h"
+#include "DEG_depsgraph_query.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.
@@ -80,13 +84,15 @@ struct AddOperationExecutor {
AddOperation *self_ = nullptr;
CurvesSculptCommonContext ctx_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
- Object *surface_ob_ = nullptr;
- Mesh *surface_ = nullptr;
- Span<MLoopTri> surface_looptris_;
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
const CurvesSculpt *curves_sculpt_ = nullptr;
const Brush *brush_ = nullptr;
@@ -97,15 +103,7 @@ struct AddOperationExecutor {
float brush_radius_re_;
float2 brush_pos_re_;
- CurvesSculptTransforms transforms_;
-
- BVHTreeFromMesh surface_bvh_;
-
- struct AddedPoints {
- Vector<float3> positions_cu;
- Vector<float3> bary_coords;
- Vector<int> looptri_indices;
- };
+ CurvesSurfaceTransforms transforms_;
AddOperationExecutor(const bContext &C) : ctx_(C)
{
@@ -114,19 +112,26 @@ struct AddOperationExecutor {
void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
self_ = &self;
- object_ = CTX_data_active_object(&C);
+ curves_ob_orig_ = CTX_data_active_object(&C);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
- if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
return;
}
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
+
+ Object &surface_ob_orig = *curves_id_orig_->surface;
+ Mesh &surface_orig = *static_cast<Mesh *>(surface_ob_orig.data);
- surface_ob_ = curves_id_->surface;
- surface_ = static_cast<Mesh *>(surface_ob_->data);
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, &surface_ob_orig);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -143,59 +148,70 @@ struct AddOperationExecutor {
return;
}
+ /* Find UV map. */
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id_orig_->surface_uv_map != nullptr) {
+ surface_uv_map = bke::mesh_attributes(surface_orig)
+ .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER);
+ surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_)
+ .lookup<float2>(curves_id_orig_->surface_uv_map,
+ ATTR_DOMAIN_CORNER);
+ }
+
+ if (surface_uv_map.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+
const double time = PIL_check_seconds_timer() * 1000000.0;
/* Use a pointer cast to avoid overflow warnings. */
RandomNumberGenerator rng{*(uint32_t *)(&time)};
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
- surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
- BKE_mesh_runtime_looptri_len(surface_)};
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
/* Sample points on the surface using one of multiple strategies. */
- AddedPoints added_points;
+ Vector<float2> sampled_uvs;
if (add_amount_ == 1) {
- this->sample_in_center_with_symmetry(added_points);
+ this->sample_in_center_with_symmetry(sampled_uvs);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
- this->sample_projected_with_symmetry(rng, added_points);
+ this->sample_projected_with_symmetry(rng, sampled_uvs);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
- this->sample_spherical_with_symmetry(rng, added_points);
+ this->sample_spherical_with_symmetry(rng, sampled_uvs);
}
else {
BLI_assert_unreachable();
}
- if (added_points.bary_coords.is_empty()) {
+ if (sampled_uvs.is_empty()) {
/* No new points have been added. */
return;
}
- /* 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 Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig),
+ BKE_mesh_runtime_looptri_len(&surface_orig)};
/* Find normals. */
- if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
- BKE_mesh_calc_normals_split(surface_);
+ if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(&surface_orig);
}
const Span<float3> corner_normals_su = {
- reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
- surface_->totloop};
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.ldata, CD_NORMAL)),
+ surface_orig.totloop};
+
+ const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};
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.uvs = sampled_uvs;
add_inputs.interpolate_length = brush_settings_->flag &
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
add_inputs.interpolate_shape = brush_settings_->flag &
@@ -204,13 +220,10 @@ struct AddOperationExecutor {
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.transforms = &transforms_;
+ add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
+ add_inputs.surface = &surface_orig;
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) {
@@ -218,17 +231,22 @@ struct AddOperationExecutor {
add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
}
- geometry::add_curves_on_mesh(*curves_, add_inputs);
+ const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
+ *curves_orig_, add_inputs);
+
+ if (add_outputs.uv_error) {
+ report_invalid_uv_map(stroke_extension.reports);
+ }
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
/**
* Sample a single point exactly at the mouse position.
*/
- void sample_in_center_with_symmetry(AddedPoints &r_added_points)
+ void sample_in_center_with_symmetry(Vector<float2> &r_sampled_uvs)
{
float3 ray_start_wo, ray_end_wo;
ED_view3d_win_to_segment_clipped(
@@ -237,15 +255,15 @@ struct AddOperationExecutor {
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));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float4x4 transform = transforms_.curves_to_surface * brush_transform;
- this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu);
+ this->sample_in_center(r_sampled_uvs, transform * ray_start_cu, transform * ray_end_cu);
}
}
- void sample_in_center(AddedPoints &r_added_points,
+ void sample_in_center(Vector<float2> &r_sampled_uvs,
const float3 &ray_start_su,
const float3 &ray_end_su)
{
@@ -254,58 +272,61 @@ struct AddOperationExecutor {
BVHTreeRayHit ray_hit;
ray_hit.dist = FLT_MAX;
ray_hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh_.tree,
+ BLI_bvhtree_ray_cast(surface_bvh_eval_.tree,
ray_start_su,
ray_direction_su,
0.0f,
&ray_hit,
- surface_bvh_.raycast_callback,
- &surface_bvh_);
+ surface_bvh_eval_.raycast_callback,
+ &surface_bvh_eval_);
if (ray_hit.index == -1) {
return;
}
const int looptri_index = ray_hit.index;
+ const MLoopTri &looptri = surface_looptris_eval_[looptri_index];
const float3 brush_pos_su = ray_hit.co;
const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
- *surface_, surface_looptris_[looptri_index], brush_pos_su);
+ *surface_eval_, looptri, 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);
- r_added_points.looptri_indices.append(looptri_index);
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords, looptri, surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
/**
* Sample points by shooting rays within the brush radius in the 3D view.
*/
- void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs)
{
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->sample_projected(rng, r_added_points, brush_transform);
+ this->sample_projected(rng, r_sampled_uvs, brush_transform);
}
}
void sample_projected(RandomNumberGenerator &rng,
- AddedPoints &r_added_points,
+ Vector<float2> &r_sampled_uvs,
const float4x4 &brush_transform)
{
- const int old_amount = r_added_points.bary_coords.size();
+ const int old_amount = r_sampled_uvs.size();
const int max_iterations = 100;
int current_iteration = 0;
- while (r_added_points.bary_coords.size() < old_amount + add_amount_) {
+ while (r_sampled_uvs.size() < old_amount + add_amount_) {
if (current_iteration++ >= max_iterations) {
break;
}
- const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size();
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ Vector<float3> positions_su;
+
+ const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size();
const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
rng,
- *surface_,
- surface_bvh_,
+ *surface_eval_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_,
[&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
@@ -320,11 +341,14 @@ struct AddOperationExecutor {
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;
+ bary_coords,
+ looptri_indices,
+ positions_su);
+
+ for (const int i : IndexRange(new_points)) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
}
}
@@ -332,13 +356,13 @@ struct AddOperationExecutor {
/**
* Sample points in a 3D sphere around the surface position that the mouse hovers over.
*/
- void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs)
{
const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
*ctx_.region,
*ctx_.v3d,
transforms_,
- surface_bvh_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_);
if (!brush_3d.has_value()) {
@@ -353,24 +377,27 @@ struct AddOperationExecutor {
view_ray_start_wo,
view_ray_end_wo,
true);
- const float3 view_direction_su = math::normalize(
- transforms_.world_to_surface * view_ray_end_wo -
- transforms_.world_to_surface * view_ray_start_wo);
+
+ const float3 view_ray_start_cu = transforms_.world_to_curves * view_ray_start_wo;
+ const float3 view_ray_end_cu = transforms_.world_to_curves * view_ray_end_wo;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float4x4 transform = transforms_.curves_to_surface * brush_transform;
+
const float3 brush_pos_su = transform * brush_3d->position_cu;
+ const float3 view_direction_su = math::normalize(transform * view_ray_end_cu -
+ transform * view_ray_start_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);
+
+ this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su);
}
}
void sample_spherical(RandomNumberGenerator &rng,
- AddedPoints &r_added_points,
+ Vector<float2> &r_sampled_uvs,
const float3 &brush_pos_su,
const float brush_radius_su,
const float3 &view_direction_su)
@@ -378,32 +405,32 @@ struct AddOperationExecutor {
const float brush_radius_sq_su = pow2f(brush_radius_su);
/* Find surface triangles within brush radius. */
- Vector<int> looptri_indices;
+ Vector<int> selected_looptri_indices;
if (use_front_face_) {
BLI_bvhtree_range_query_cpp(
- *surface_bvh_.tree,
+ *surface_bvh_eval_.tree,
brush_pos_su,
brush_radius_su,
[&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
- const MLoopTri &looptri = surface_looptris_[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 MLoopTri &looptri = surface_looptris_eval_[index];
+ const float3 v0_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[0]].v].co;
+ const float3 v1_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[1]].v].co;
+ const float3 v2_su = surface_eval_->mvert[surface_eval_->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, view_direction_su) >= 0.0f) {
return;
}
- looptri_indices.append(index);
+ selected_looptri_indices.append(index);
});
}
else {
BLI_bvhtree_range_query_cpp(
- *surface_bvh_.tree,
+ *surface_bvh_eval_.tree,
brush_pos_su,
brush_radius_su,
[&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
- looptri_indices.append(index);
+ selected_looptri_indices.append(index);
});
}
@@ -417,42 +444,45 @@ struct AddOperationExecutor {
const int max_iterations = 5;
int current_iteration = 0;
- const int old_amount = r_added_points.bary_coords.size();
- while (r_added_points.bary_coords.size() < old_amount + add_amount_) {
+ const int old_amount = r_sampled_uvs.size();
+ while (r_sampled_uvs.size() < old_amount + add_amount_) {
if (current_iteration++ >= max_iterations) {
break;
}
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ Vector<float3> positions_su;
const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
rng,
- *surface_,
- looptri_indices,
+ *surface_eval_,
+ selected_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;
+ bary_coords,
+ looptri_indices,
+ positions_su);
+ for (const int i : IndexRange(new_points)) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
}
/* Remove samples when there are too many. */
- while (r_added_points.bary_coords.size() > old_amount + add_amount_) {
+ while (r_sampled_uvs.size() > old_amount + add_amount_) {
const int index_to_remove = rng.get_int32(add_amount_) + old_amount;
- r_added_points.bary_coords.remove_and_reorder(index_to_remove);
- r_added_points.looptri_indices.remove_and_reorder(index_to_remove);
- r_added_points.positions_cu.remove_and_reorder(index_to_remove);
+ r_sampled_uvs.remove_and_reorder(index_to_remove);
}
}
void ensure_curve_roots_kdtree()
{
if (self_->curve_roots_kdtree_ == nullptr) {
- self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num());
- for (const int curve_i : curves_->curves_range()) {
- const int root_point_i = curves_->offsets()[curve_i];
- const float3 &root_pos_cu = curves_->positions()[root_point_i];
+ self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
+ for (const int curve_i : curves_orig_->curves_range()) {
+ const int root_point_i = curves_orig_->offsets()[curve_i];
+ const float3 &root_pos_cu = curves_orig_->positions()[root_point_i];
BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu);
}
BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
@@ -466,17 +496,8 @@ void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &
executor.execute(*this, C, stroke_extension);
}
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
- ReportList *reports)
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
{
- const Object &ob_active = *CTX_data_active_object(&C);
- BLI_assert(ob_active.type == OB_CURVES);
- 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 {};
- }
-
return std::make_unique<AddOperation>();
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
index 1c785fa6452..a180a232189 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
@@ -8,6 +8,9 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
#include "ED_view3d.h"
@@ -20,6 +23,10 @@
#include "BLI_length_parameterize.hh"
#include "BLI_task.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "BLT_translation.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.
@@ -48,7 +55,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
const float brush_radius_re,
const ARegion &region,
const RegionView3D &rv3d,
- const Object &object)
+ const Object &object,
+ const Span<float3> positions)
{
/* 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);
@@ -88,8 +96,6 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
}
};
- const Span<float3> positions = curves.positions();
-
BrushPositionCandidate best_candidate = threading::parallel_reduce(
curves.curves_range(),
128,
@@ -175,20 +181,21 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
{
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;
+ Object *surface_object = curves_id.surface;
+ Object *surface_object_eval = DEG_get_evaluated_object(&depsgraph, surface_object);
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);
/* Shorten ray when the surface object is hit. */
- if (surface_object != nullptr) {
+ if (surface_object_eval != nullptr) {
const float4x4 surface_to_world_mat = surface_object->obmat;
const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- Mesh &surface = *static_cast<Mesh *>(surface_object->data);
+ Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval);
BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2);
+ BKE_bvhtree_from_mesh_get(&surface_bvh, surface_eval, BVHTREE_FROM_LOOPTRI, 2);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo;
@@ -222,6 +229,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo;
const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo;
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_object);
+
const std::optional<float3> brush_position_optional_cu = find_curves_brush_position(
curves,
center_ray_start_cu,
@@ -229,7 +239,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
brush_radius_re,
region,
rv3d,
- curves_object);
+ curves_object,
+ deformation.positions);
if (!brush_position_optional_cu.has_value()) {
/* Nothing found. */
return std::nullopt;
@@ -258,7 +269,7 @@ std::optional<CurvesBrush3D> sample_curves_surface_3d_brush(
const Depsgraph &depsgraph,
const ARegion &region,
const View3D &v3d,
- const CurvesSculptTransforms &transforms,
+ const CurvesSurfaceTransforms &transforms,
const BVHTreeFromMesh &surface_bvh,
const float2 &brush_pos_re,
const float brush_radius_re)
@@ -345,7 +356,7 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n
{
/* 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));
+ Array<float> orig_lengths(length_parameterize::segments_num(positions.size(), false));
length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths);
const float orig_total_length = orig_lengths.last();
@@ -363,11 +374,10 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n
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);
+ length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors);
Array<float3> new_positions(positions.size() - 1);
- length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
+ length_parameterize::interpolate<float3>(positions, indices, factors, new_positions);
positions.drop_back(1).copy_from(new_positions);
positions.last() = new_last_position;
}
@@ -381,18 +391,26 @@ CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C)
this->rv3d = CTX_wm_region_view3d(&C);
}
-CurvesSculptTransforms::CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob)
+void report_missing_surface(ReportList *reports)
{
- 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();
- }
+ BKE_report(reports, RPT_WARNING, TIP_("Missing surface mesh"));
+}
+
+void report_missing_uv_map_on_original_surface(ReportList *reports)
+{
+ BKE_report(
+ reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on original surface"));
+}
+
+void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
+{
+ BKE_report(
+ reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on evaluated surface"));
+}
+
+void report_invalid_uv_map(ReportList *reports)
+{
+ BKE_report(reports, RPT_WARNING, TIP_("Invalid UV map: UV islands must not overlap"));
}
} // 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 541bf9d8253..52f2ddc6550 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
@@ -13,12 +13,15 @@
#include "PIL_time.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "BKE_attribute_math.hh"
#include "BKE_brush.h"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
@@ -88,9 +91,9 @@ struct CombOperationExecutor {
eBrushFalloffShape falloff_shape_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
VArray<float> point_factors_;
Vector<int64_t> selected_curve_indices_;
@@ -100,7 +103,7 @@ struct CombOperationExecutor {
float2 brush_pos_re_;
float2 brush_pos_diff_re_;
- CurvesSculptTransforms transforms_;
+ CurvesSurfaceTransforms transforms_;
CombOperationExecutor(const bContext &C) : ctx_(C)
{
@@ -112,7 +115,12 @@ struct CombOperationExecutor {
BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; });
- object_ = CTX_data_active_object(&C);
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
+ if (curves_orig_->curves_num() == 0) {
+ return;
+ }
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -122,16 +130,10 @@ struct CombOperationExecutor {
falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
- if (curves_->curves_num() == 0) {
- return;
- }
-
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
- point_factors_ = get_point_selection(*curves_id_);
- curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+ point_factors_ = get_point_selection(*curves_id_orig_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
brush_pos_prev_re_ = self_->brush_pos_last_re_;
brush_pos_re_ = stroke_extension.mouse_position;
@@ -160,9 +162,9 @@ struct CombOperationExecutor {
this->restore_segment_lengths(changed_curves);
- curves_->tag_positions_changed();
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ curves_orig_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
@@ -172,7 +174,7 @@ struct CombOperationExecutor {
void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
{
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
this->comb_projected(r_changed_curves, brush_transform);
}
@@ -183,10 +185,12 @@ struct CombOperationExecutor {
{
const float4x4 brush_transform_inv = brush_transform.inverted();
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
@@ -195,16 +199,18 @@ struct CombOperationExecutor {
Vector<int> &local_changed_curves = r_changed_curves.local();
for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i];
+ const float3 old_pos_cu = deformation.positions[point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
/* Find the position of the point in screen space. */
- float2 old_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_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_);
+ old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_);
if (distance_to_brush_sq_re > brush_radius_sq_re) {
/* Ignore the point because it's too far away. */
continue;
@@ -219,16 +225,20 @@ struct CombOperationExecutor {
/* 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;
+ const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_symm_pos_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 *
- (transforms_.world_to_curves * new_position_wo);
- positions_cu[point_i] = new_position_cu;
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+ const float3 new_pos_cu = brush_transform *
+ (transforms_.world_to_curves * new_symm_pos_wo);
+
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu_orig[point_i] += translation_orig;
curve_changed = true;
}
@@ -245,7 +255,7 @@ struct CombOperationExecutor {
void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
float3 brush_start_wo, brush_end_wo;
ED_view3d_win_to_3d(ctx_.v3d,
@@ -264,7 +274,7 @@ struct CombOperationExecutor {
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));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
this->comb_spherical(r_changed_curves,
brush_transform * brush_start_cu,
@@ -278,17 +288,20 @@ struct CombOperationExecutor {
const float3 &brush_end_cu,
const float brush_radius_cu)
{
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+
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 : curve_selection_.slice(range)) {
bool curve_changed = false;
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 pos_old_cu = positions_cu[point_i];
+ const float3 pos_old_cu = deformation.positions[point_i];
/* Compute distance to the brush. */
const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
@@ -306,8 +319,12 @@ struct CombOperationExecutor {
/* Combine the falloff and brush strength. */
const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
+ const float3 translation_eval_cu = weight * brush_diff_cu;
+ const float3 translation_orig_cu = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval_cu);
+
/* Update the point position. */
- positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu;
+ positions_cu[point_i] += translation_orig_cu;
curve_changed = true;
}
if (curve_changed) {
@@ -326,7 +343,7 @@ struct CombOperationExecutor {
*ctx_.region,
*ctx_.v3d,
*ctx_.rv3d,
- *object_,
+ *curves_ob_orig_,
brush_pos_re_,
brush_radius_base_re_);
if (brush_3d.has_value()) {
@@ -340,11 +357,11 @@ struct CombOperationExecutor {
*/
void initialize_segment_lengths()
{
- const Span<float3> positions_cu = curves_->positions();
- self_->segment_lengths_cu_.reinitialize(curves_->points_num());
- threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
+ const Span<float3> positions_cu = curves_orig_->positions();
+ self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num());
+ threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[point_i];
const float3 &p2_cu = positions_cu[point_i + 1];
@@ -361,12 +378,12 @@ struct CombOperationExecutor {
void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves)
{
const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) {
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);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
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];
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
index eab7dabcd22..a44499ce133 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
@@ -51,6 +51,12 @@ using blender::bke::CurvesGeometry;
class DeleteOperation : public CurvesSculptStrokeOperation {
private:
CurvesBrush3D brush_3d_;
+ /**
+ * Need to store those in case the brush is evaluated more than once before the curves are
+ * evaluated again. This can happen when the mouse is moved quickly and the brush spacing is
+ * small.
+ */
+ Vector<float3> deformed_positions_;
friend struct DeleteOperationExecutor;
@@ -76,7 +82,7 @@ struct DeleteOperationExecutor {
float2 brush_pos_re_;
- CurvesSculptTransforms transforms_;
+ CurvesSurfaceTransforms transforms_;
DeleteOperationExecutor(const bContext &C) : ctx_(C)
{
@@ -100,7 +106,7 @@ struct DeleteOperationExecutor {
brush_pos_re_ = stroke_extension.mouse_position;
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
@@ -109,6 +115,9 @@ struct DeleteOperationExecutor {
if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
this->initialize_spherical_brush_reference_point();
}
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+ self_->deformed_positions_ = deformation.positions;
}
Array<bool> curves_to_delete(curves_->curves_num(), false);
@@ -123,12 +132,22 @@ struct DeleteOperationExecutor {
}
Vector<int64_t> indices;
- const IndexMask mask = index_mask_ops::find_indices_based_on_predicate(
+ const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate(
curves_->curves_range(), 4096, indices, [&](const int curve_i) {
return curves_to_delete[curve_i];
});
- curves_->remove_curves(mask);
+ /* Remove deleted curves from the stored deformed positions. */
+ const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert(
+ curves_->curves_range());
+ Vector<float3> new_deformed_positions;
+ for (const IndexRange curves_range : ranges_to_keep) {
+ new_deformed_positions.extend(
+ self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range)));
+ }
+ self_->deformed_positions_ = std::move(new_deformed_positions);
+
+ curves_->remove_curves(mask_to_delete);
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
@@ -151,8 +170,6 @@ struct DeleteOperationExecutor {
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);
@@ -160,7 +177,7 @@ struct DeleteOperationExecutor {
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()];
+ const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()];
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
@@ -171,8 +188,8 @@ struct DeleteOperationExecutor {
}
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];
+ const float3 pos1_cu = brush_transform_inv * self_->deformed_positions_[segment_i];
+ const float3 pos2_cu = brush_transform_inv * self_->deformed_positions_[segment_i + 1];
float2 pos1_re, pos2_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values);
@@ -212,8 +229,6 @@ struct DeleteOperationExecutor {
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 * brush_radius_factor_;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@@ -222,7 +237,7 @@ struct DeleteOperationExecutor {
const IndexRange points = curves_->points_for_curve(curve_i);
if (points.size() == 1) {
- const float3 &pos_cu = positions_cu[points.first()];
+ const float3 &pos_cu = self_->deformed_positions_[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;
@@ -231,8 +246,8 @@ struct DeleteOperationExecutor {
}
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 float3 &pos1_cu = self_->deformed_positions_[segment_i];
+ const float3 &pos2_cu = self_->deformed_positions_[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) {
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc
new file mode 100644
index 00000000000..6ad3d0e4aad
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc
@@ -0,0 +1,912 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <numeric>
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_index_mask_ops.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_task.hh"
+
+#include "PIL_time.h"
+
+#include "GEO_add_curves_on_mesh.hh"
+
+#include "DNA_brush_types.h"
+#include "DNA_mesh_types.h"
+
+#include "WM_api.h"
+
+#include "curves_sculpt_intern.hh"
+
+namespace blender::ed::sculpt_paint {
+
+class DensityAddOperation : public CurvesSculptStrokeOperation {
+ private:
+ /** Used when some data should be interpolated from existing curves. */
+ KDTree_3d *original_curve_roots_kdtree_ = nullptr;
+ /** Contains curve roots of all curves that existed before the brush started. */
+ KDTree_3d *deformed_curve_roots_kdtree_ = nullptr;
+ /** Root positions of curves that have been added in the current brush stroke. */
+ Vector<float3> new_deformed_root_positions_;
+ int original_curve_num_ = 0;
+
+ friend struct DensityAddOperationExecutor;
+
+ public:
+ ~DensityAddOperation() override
+ {
+ if (original_curve_roots_kdtree_ != nullptr) {
+ BLI_kdtree_3d_free(original_curve_roots_kdtree_);
+ }
+ if (deformed_curve_roots_kdtree_ != nullptr) {
+ BLI_kdtree_3d_free(deformed_curve_roots_kdtree_);
+ }
+ }
+
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+struct DensityAddOperationExecutor {
+ DensityAddOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
+
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
+
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
+
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ const BrushCurvesSculptSettings *brush_settings_ = nullptr;
+
+ float brush_strength_;
+ float brush_radius_re_;
+ float2 brush_pos_re_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ DensityAddOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(DensityAddOperation &self,
+ const bContext &C,
+ const StrokeExtension &stroke_extension)
+ {
+ self_ = &self;
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
+
+ if (stroke_extension.is_first) {
+ self_->original_curve_num_ = curves_orig_->curves_num();
+ }
+
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
+ return;
+ }
+
+ surface_ob_orig_ = curves_id_orig_->surface;
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
+ /* Find UV map. */
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id_orig_->surface_uv_map != nullptr) {
+ surface_uv_map = bke::mesh_attributes(*surface_orig_)
+ .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER);
+ surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_)
+ .lookup<float2>(curves_id_orig_->surface_uv_map,
+ ATTR_DOMAIN_CORNER);
+ }
+ if (surface_uv_map.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
+
+ curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
+ brush_settings_ = brush_->curves_sculpt_settings;
+ brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
+ brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension);
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+
+ Vector<float3> new_positions_cu;
+ Vector<float2> new_uvs;
+ const double time = PIL_check_seconds_timer() * 1000000.0;
+ RandomNumberGenerator rng{*(uint32_t *)(&time)};
+
+ /* Find potential new curve root points. */
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+ for (float3 &pos : new_positions_cu) {
+ pos = transforms_.surface_to_curves * pos;
+ }
+
+ if (stroke_extension.is_first) {
+ this->prepare_curve_roots_kdtrees();
+ }
+
+ const int already_added_curves = self_->new_deformed_root_positions_.size();
+ KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves +
+ new_positions_cu.size());
+ BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); });
+
+ /* Used to tag all curves that are too close to existing curves or too close to other new
+ * curves. */
+ Array<bool> new_curve_skipped(new_positions_cu.size(), false);
+ threading::parallel_invoke(
+ /* Build kdtree from root points created by the current stroke. */
+ [&]() {
+ for (const int i : IndexRange(already_added_curves)) {
+ BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]);
+ }
+ for (const int new_i : new_positions_cu.index_range()) {
+ const float3 &root_pos_cu = new_positions_cu[new_i];
+ BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu);
+ }
+ BLI_kdtree_3d_balance(new_roots_kdtree);
+ },
+ /* Check which new root points are close to roots that existed before the current stroke
+ * started. */
+ [&]() {
+ threading::parallel_for(
+ new_positions_cu.index_range(), 128, [&](const IndexRange range) {
+ for (const int new_i : range) {
+ const float3 &new_root_pos_cu = new_positions_cu[new_i];
+ KDTreeNearest_3d nearest;
+ nearest.dist = FLT_MAX;
+ BLI_kdtree_3d_find_nearest(
+ self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest);
+ if (nearest.dist < brush_settings_->minimum_distance) {
+ new_curve_skipped[new_i] = true;
+ }
+ }
+ });
+ });
+
+ /* Find new points that are too close too other new points. */
+ for (const int new_i : new_positions_cu.index_range()) {
+ if (new_curve_skipped[new_i]) {
+ continue;
+ }
+ const float3 &root_pos_cu = new_positions_cu[new_i];
+ BLI_kdtree_3d_range_search_cb_cpp(
+ new_roots_kdtree,
+ root_pos_cu,
+ brush_settings_->minimum_distance,
+ [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ if (other_new_i == -1) {
+ new_curve_skipped[new_i] = true;
+ return false;
+ }
+ if (new_i == other_new_i) {
+ return true;
+ }
+ new_curve_skipped[other_new_i] = true;
+ return true;
+ });
+ }
+
+ /* Remove points that are too close to others. */
+ for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) {
+ if (new_curve_skipped[i]) {
+ new_positions_cu.remove_and_reorder(i);
+ new_uvs.remove_and_reorder(i);
+ }
+ }
+ self_->new_deformed_root_positions_.extend(new_positions_cu);
+
+ /* Find normals. */
+ if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_orig_);
+ }
+ const Span<float3> corner_normals_su = {
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)),
+ surface_orig_->totloop};
+
+ const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(surface_orig_),
+ BKE_mesh_runtime_looptri_len(surface_orig_)};
+ const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};
+
+ geometry::AddCurvesOnMeshInputs add_inputs;
+ add_inputs.uvs = new_uvs;
+ 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.transforms = &transforms_;
+ add_inputs.surface = surface_orig_;
+ add_inputs.corner_normals_su = corner_normals_su;
+ add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
+ add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_;
+
+ const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
+ *curves_orig_, add_inputs);
+
+ if (add_outputs.uv_error) {
+ report_invalid_uv_map(stroke_extension.reports);
+ }
+
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
+ ED_region_tag_redraw(ctx_.region);
+ }
+
+ void prepare_curve_roots_kdtrees()
+ {
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+ const Span<int> curve_offsets = curves_orig_->offsets();
+ const Span<float3> original_positions = curves_orig_->positions();
+ const Span<float3> deformed_positions = deformation.positions;
+ BLI_assert(original_positions.size() == deformed_positions.size());
+
+ auto roots_kdtree_from_positions = [&](const Span<float3> positions) {
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num());
+ for (const int curve_i : curves_orig_->curves_range()) {
+ const int root_point_i = curve_offsets[curve_i];
+ BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]);
+ }
+ BLI_kdtree_3d_balance(kdtree);
+ return kdtree;
+ };
+
+ threading::parallel_invoke(
+ [&]() {
+ self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions);
+ },
+ [&]() {
+ self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions);
+ });
+ }
+
+ void sample_projected_with_symmetry(RandomNumberGenerator &rng,
+ Vector<float2> &r_uvs,
+ Vector<float3> &r_positions_su)
+ {
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+ const float4x4 transform = transforms_.curves_to_surface * brush_transform *
+ transforms_.world_to_curves;
+ Vector<float3> positions_su;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
+ rng,
+ *surface_eval_,
+ surface_bvh_eval_,
+ 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);
+ r_start_su = transform * start_wo;
+ r_end_su = transform * end_wo;
+ },
+ true,
+ brush_settings_->density_add_attempts,
+ brush_settings_->density_add_attempts,
+ bary_coords,
+ looptri_indices,
+ positions_su);
+
+ /* Remove some sampled points randomly based on the brush falloff and strength. */
+ for (int i = new_points - 1; i >= 0; i--) {
+ const float3 pos_su = positions_su[i];
+ const float3 pos_cu = brush_transform_inv * transforms_.surface_to_curves * pos_su;
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+ const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re);
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, dist_to_brush_re, brush_radius_re_);
+ const float weight = brush_strength_ * radius_falloff;
+ if (rng.get_float() > weight) {
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ positions_su.remove_and_reorder(i);
+ }
+ }
+
+ for (const int i : bary_coords.index_range()) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_uvs.append(uv);
+ }
+ r_positions_su.extend(positions_su);
+ }
+ }
+
+ void sample_spherical_with_symmetry(RandomNumberGenerator &rng,
+ Vector<float2> &r_uvs,
+ Vector<float3> &r_positions_su)
+ {
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ transforms_,
+ surface_bvh_eval_,
+ brush_pos_re_,
+ brush_radius_re_);
+ if (!brush_3d.has_value()) {
+ return;
+ }
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ const float3 brush_pos_cu = brush_transform * brush_3d->position_cu;
+ const float3 brush_pos_su = transforms_.curves_to_surface * brush_pos_cu;
+ const float brush_radius_su = transform_brush_radius(
+ transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu);
+ const float brush_radius_sq_su = pow2f(brush_radius_su);
+
+ Vector<int> selected_looptri_indices;
+ BLI_bvhtree_range_query_cpp(
+ *surface_bvh_eval_.tree,
+ brush_pos_su,
+ brush_radius_su,
+ [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
+ selected_looptri_indices.append(index);
+ });
+
+ const float brush_plane_area_su = M_PI * brush_radius_sq_su;
+ const float approximate_density_su = brush_settings_->density_add_attempts /
+ brush_plane_area_su;
+
+ Vector<float3> positions_su;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
+ rng,
+ *surface_eval_,
+ selected_looptri_indices,
+ brush_pos_su,
+ brush_radius_su,
+ approximate_density_su,
+ bary_coords,
+ looptri_indices,
+ positions_su);
+
+ /* Remove some sampled points randomly based on the brush falloff and strength. */
+ for (int i = new_points - 1; i >= 0; i--) {
+ const float3 pos_su = positions_su[i];
+ const float3 pos_cu = transforms_.surface_to_curves * pos_su;
+ const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu);
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, dist_to_brush_cu, brush_3d->radius_cu);
+ const float weight = brush_strength_ * radius_falloff;
+ if (rng.get_float() > weight) {
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ positions_su.remove_and_reorder(i);
+ }
+ }
+
+ for (const int i : bary_coords.index_range()) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_uvs.append(uv);
+ }
+ r_positions_su.extend(positions_su);
+ }
+ }
+};
+
+void DensityAddOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
+{
+ DensityAddOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+class DensitySubtractOperation : public CurvesSculptStrokeOperation {
+ private:
+ friend struct DensitySubtractOperationExecutor;
+
+ /**
+ * Deformed root positions of curves that still exist. This has to be stored in case the brush is
+ * executed more than once before the curves are evaluated again. This can happen when the mouse
+ * is moved quickly and the brush spacing is small.
+ */
+ Vector<float3> deformed_root_positions_;
+
+ public:
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * 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 DensitySubtractOperationExecutor {
+ DensitySubtractOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *object_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
+
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ BVHTreeFromMesh surface_bvh_eval_;
+
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
+ float brush_strength_;
+ float2 brush_pos_re_;
+
+ float minimum_distance_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ KDTree_3d *root_points_kdtree_;
+
+ DensitySubtractOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(DensitySubtractOperation &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);
+ if (curves_->curves_num() == 0) {
+ return;
+ }
+
+ surface_ob_orig_ = curves_id_->surface;
+ if (surface_ob_orig_ == nullptr) {
+ return;
+ }
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
+
+ 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);
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance;
+
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+
+ if (stroke_extension.is_first) {
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+ for (const int curve_i : curves_->curves_range()) {
+ const int first_point_i = curves_->offsets()[curve_i];
+ self_->deformed_root_positions_.append(deformation.positions[first_point_i]);
+ }
+ }
+
+ root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size());
+ BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); });
+ for (const int curve_i : curve_selection_) {
+ const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
+ BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu);
+ }
+ BLI_kdtree_3d_balance(root_points_kdtree_);
+
+ /* Find all curves that should be deleted. */
+ Array<bool> curves_to_delete(curves_->curves_num(), false);
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->reduce_density_projected_with_symmetry(curves_to_delete);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->reduce_density_spherical_with_symmetry(curves_to_delete);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ Vector<int64_t> indices;
+ const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate(
+ curves_->curves_range(), 4096, indices, [&](const int curve_i) {
+ return curves_to_delete[curve_i];
+ });
+
+ /* Remove deleted curves fromt he stored deformed root positions. */
+ const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert(
+ curves_->curves_range());
+ BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
+ Vector<float3> new_deformed_positions;
+ for (const IndexRange range : ranges_to_keep) {
+ new_deformed_positions.extend(self_->deformed_root_positions_.as_span().slice(range));
+ }
+ self_->deformed_root_positions_ = std::move(new_deformed_positions);
+
+ curves_->remove_curves(mask_to_delete);
+ BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
+
+ 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 reduce_density_projected_with_symmetry(MutableSpan<bool> curves_to_delete)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->reduce_density_projected(brush_transform, curves_to_delete);
+ }
+ }
+
+ void reduce_density_projected(const float4x4 &brush_transform,
+ MutableSpan<bool> curves_to_delete)
+ {
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const float brush_radius_sq_re = pow2f(brush_radius_re);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ /* Randomly select the curves that are allowed to be removed, based on the brush radius and
+ * strength. */
+ Array<bool> allow_remove_curve(curves_->curves_num(), false);
+ threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
+ RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0));
+
+ for (const int curve_i : range) {
+ if (curves_to_delete[curve_i]) {
+ allow_remove_curve[curve_i] = true;
+ continue;
+ }
+ const float3 pos_cu = brush_transform * self_->deformed_root_positions_[curve_i];
+
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+ const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_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;
+ if (rng.get_float() < weight) {
+ allow_remove_curve[curve_i] = true;
+ }
+ }
+ });
+
+ /* Detect curves that are too close to other existing curves. */
+ for (const int curve_i : curve_selection_) {
+ if (curves_to_delete[curve_i]) {
+ continue;
+ }
+ if (!allow_remove_curve[curve_i]) {
+ continue;
+ }
+ const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i];
+ const float3 pos_cu = brush_transform * orig_pos_cu;
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+ const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
+ if (dist_to_brush_sq_re > brush_radius_sq_re) {
+ continue;
+ }
+ BLI_kdtree_3d_range_search_cb_cpp(
+ root_points_kdtree_,
+ orig_pos_cu,
+ minimum_distance_,
+ [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ if (other_curve_i == curve_i) {
+ return true;
+ }
+ if (allow_remove_curve[other_curve_i]) {
+ curves_to_delete[other_curve_i] = true;
+ }
+ return true;
+ });
+ }
+ }
+
+ void reduce_density_spherical_with_symmetry(MutableSpan<bool> curves_to_delete)
+ {
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ transforms_,
+ surface_bvh_eval_,
+ brush_pos_re_,
+ brush_radius_re);
+ if (!brush_3d.has_value()) {
+ return;
+ }
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ const float3 brush_pos_cu = brush_transform * brush_3d->position_cu;
+ this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_delete);
+ }
+ }
+
+ void reduce_density_spherical(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ MutableSpan<bool> curves_to_delete)
+ {
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ /* Randomly select the curves that are allowed to be removed, based on the brush radius and
+ * strength. */
+ Array<bool> allow_remove_curve(curves_->curves_num(), false);
+ threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
+ RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0));
+
+ for (const int curve_i : range) {
+ if (curves_to_delete[curve_i]) {
+ allow_remove_curve[curve_i] = true;
+ continue;
+ }
+ const float3 pos_cu = self_->deformed_root_positions_[curve_i];
+
+ const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+ 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;
+ if (rng.get_float() < weight) {
+ allow_remove_curve[curve_i] = true;
+ }
+ }
+ });
+
+ /* Detect curves that are too close to other existing curves. */
+ for (const int curve_i : curve_selection_) {
+ if (curves_to_delete[curve_i]) {
+ continue;
+ }
+ if (!allow_remove_curve[curve_i]) {
+ continue;
+ }
+ const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
+ const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ BLI_kdtree_3d_range_search_cb_cpp(
+ root_points_kdtree_,
+ pos_cu,
+ minimum_distance_,
+ [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ if (other_curve_i == curve_i) {
+ return true;
+ }
+ if (allow_remove_curve[other_curve_i]) {
+ curves_to_delete[other_curve_i] = true;
+ }
+ return true;
+ });
+ }
+ }
+};
+
+void DensitySubtractOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
+{
+ DensitySubtractOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+/**
+ * Detects whether the brush should be in Add or Subtract mode.
+ */
+static bool use_add_density_mode(const BrushStrokeMode brush_mode,
+ const bContext &C,
+ const StrokeExtension &stroke_start)
+{
+ const Scene &scene = *CTX_data_scene(&C);
+ const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint);
+ const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C);
+ const ARegion &region = *CTX_wm_region(&C);
+ const View3D &v3d = *CTX_wm_view3d(&C);
+
+ const eBrushCurvesSculptDensityMode density_mode = static_cast<eBrushCurvesSculptDensityMode>(
+ brush.curves_sculpt_settings->density_mode);
+ const bool use_invert = brush_mode == BRUSH_STROKE_INVERT;
+
+ if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD) {
+ return !use_invert;
+ }
+ if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE) {
+ return use_invert;
+ }
+
+ const Object &curves_ob_orig = *CTX_data_active_object(&C);
+ const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
+ Object *surface_ob_orig = curves_id_orig.surface;
+ if (surface_ob_orig == nullptr) {
+ return true;
+ }
+ Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig);
+ if (surface_ob_eval == nullptr) {
+ return true;
+ }
+ const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id_orig.geometry);
+ if (curves.curves_num() <= 1) {
+ return true;
+ }
+ const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
+ if (surface_mesh_eval == nullptr) {
+ return true;
+ }
+
+ const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface);
+ BVHTreeFromMesh surface_bvh_eval;
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
+
+ const float2 brush_pos_re = stroke_start.mouse_position;
+ /* Reduce radius so that only an inner circle is used to determine the existing density. */
+ const float brush_radius_re = BKE_brush_size_get(&scene, &brush) * 0.5f;
+
+ /* Find the surface point under the brush. */
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(
+ depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re);
+ if (!brush_3d.has_value()) {
+ return true;
+ }
+
+ const float3 brush_pos_cu = brush_3d->position_cu;
+ const float brush_radius_cu = brush_3d->radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig);
+ const Span<int> offsets = curves.offsets();
+
+ /* Compute distance from brush to curve roots. */
+ Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num());
+ threading::EnumerableThreadSpecific<int> valid_curve_count_by_thread;
+ threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
+ int &valid_curve_count = valid_curve_count_by_thread.local();
+ for (const int curve_i : range) {
+ const int root_point_i = offsets[curve_i];
+ const float3 &root_pos_cu = deformation.positions[root_point_i];
+ const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu);
+ if (dist_sq_cu < brush_radius_sq_cu) {
+ distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu),
+ curve_i};
+ valid_curve_count++;
+ }
+ else {
+ distances_sq_to_brush[curve_i] = {FLT_MAX, -1};
+ }
+ }
+ });
+ const int valid_curve_count = std::accumulate(
+ valid_curve_count_by_thread.begin(), valid_curve_count_by_thread.end(), 0);
+
+ /* Find a couple of curves that are closest to the brush center. */
+ const int check_curve_count = std::min<int>(8, valid_curve_count);
+ std::partial_sort(distances_sq_to_brush.begin(),
+ distances_sq_to_brush.begin() + check_curve_count,
+ distances_sq_to_brush.end());
+
+ /* Compute the minimum pair-wise distance between the curve roots that are close to the brush
+ * center. */
+ float min_dist_sq_cu = FLT_MAX;
+ for (const int i : IndexRange(check_curve_count)) {
+ const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]];
+ for (int j = i + 1; j < check_curve_count; j++) {
+ const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]];
+ const float dist_sq_cu = math::distance_squared(pos_i, pos_j);
+ math::min_inplace(min_dist_sq_cu, dist_sq_cu);
+ }
+ }
+
+ const float min_dist_cu = std::sqrt(min_dist_sq_cu);
+ if (min_dist_cu > brush.curves_sculpt_settings->minimum_distance) {
+ return true;
+ }
+
+ return false;
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation(
+ const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
+{
+ if (use_add_density_mode(brush_mode, C, stroke_start)) {
+ return std::make_unique<DensityAddOperation>();
+ }
+ return std::make_unique<DensitySubtractOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
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 cf893f09fc6..1ee43d98e6f 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
@@ -4,8 +4,7 @@
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_float4x4.hh"
-#include "BLI_kdtree.h"
-#include "BLI_rand.hh"
+#include "BLI_length_parameterize.hh"
#include "BLI_vector.hh"
#include "PIL_time.h"
@@ -14,19 +13,13 @@
#include "BKE_attribute_math.hh"
#include "BKE_brush.h"
-#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
-#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"
#include "DNA_curves_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect {
private:
const Brush &brush_;
+ /** Storage of per-curve parameterization data to avoid reallocation. */
+ struct ParameterizationBuffers {
+ Array<float3> old_positions;
+ Array<float> old_lengths;
+ Array<float> sample_lengths;
+ Array<int> indices;
+ Array<float> factors;
+
+ void reinitialize(const int points_num)
+ {
+ this->old_positions.reinitialize(points_num);
+ this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false));
+ this->sample_lengths.reinitialize(points_num);
+ this->indices.reinitialize(points_num);
+ this->factors.reinitialize(points_num);
+ }
+ };
+
public:
ShrinkCurvesEffect(const Brush &brush) : brush_(brush)
{
@@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect {
{
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ ParameterizationBuffers data;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
- const IndexRange curve_points = curves.points_for_curve(curve_i);
- this->shrink_curve(positions_cu, curve_points, move_distance_cu);
+ const IndexRange points = curves.points_for_curve(curve_i);
+ this->shrink_curve(positions_cu.slice(points), move_distance_cu, data);
}
});
}
+ private:
void shrink_curve(MutableSpan<float3> positions,
- const IndexRange curve_points,
- const float shrink_length) const
+ const float shrink_length,
+ ParameterizationBuffers &data) const
{
- PolySpline spline;
- spline.resize(curve_points.size());
- MutableSpan<float3> spline_positions = spline.positions();
- spline_positions.copy_from(positions.slice(curve_points));
- spline.mark_cache_invalid();
+ namespace lp = length_parameterize;
+ data.reinitialize(positions.size());
+
+ /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */
+ data.old_positions.as_mutable_span().copy_from(positions);
+
+ lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths);
+
const float min_length = brush_.curves_sculpt_settings->minimum_length;
- const float old_length = spline.length();
+ const float old_length = data.old_lengths.last();
const float new_length = std::max(min_length, old_length - shrink_length);
const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f);
- Vector<float> old_point_lengths;
- old_point_lengths.append(0.0f);
- for (const int i : spline_positions.index_range().drop_back(1)) {
- const float3 &p1 = spline_positions[i];
- const float3 &p2 = spline_positions[i + 1];
- const float length = math::distance(p1, p2);
- old_point_lengths.append(old_point_lengths.last() + length);
+ data.sample_lengths.first() = 0.0f;
+ for (const int i : data.old_lengths.index_range()) {
+ data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor;
}
- for (const int i : spline_positions.index_range()) {
- const float eval_length = old_point_lengths[i] * length_factor;
- const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1});
- positions[curve_points[i]] = p;
- }
+ lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors);
+
+ lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions);
}
};
@@ -247,7 +254,7 @@ struct CurvesEffectOperationExecutor {
eBrushFalloffShape falloff_shape_;
- CurvesSculptTransforms transforms_;
+ CurvesSurfaceTransforms transforms_;
float2 brush_pos_start_re_;
float2 brush_pos_end_re_;
@@ -289,7 +296,7 @@ struct CurvesEffectOperationExecutor {
falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape);
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
brush_pos_start_re_ = self.last_mouse_position_;
brush_pos_end_re_ = stroke_extension.mouse_position;
@@ -335,7 +342,8 @@ struct CurvesEffectOperationExecutor {
void gather_influences_projected(
threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@@ -361,8 +369,8 @@ struct CurvesEffectOperationExecutor {
float max_move_distance_cu = 0.0f;
for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) {
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];
+ const float3 p1_cu = brush_transform_inv * deformation.positions[segment_i];
+ const float3 p2_cu = brush_transform_inv * deformation.positions[segment_i + 1];
float2 p1_re, p2_re;
ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values);
@@ -423,7 +431,8 @@ struct CurvesEffectOperationExecutor {
void gather_influences_spherical(
threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
float3 brush_pos_start_wo, brush_pos_end_wo;
ED_view3d_win_to_3d(ctx_.v3d,
@@ -461,8 +470,8 @@ struct CurvesEffectOperationExecutor {
const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu;
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];
+ const float3 &p1_cu = deformation.positions[segment_i];
+ const float3 &p2_cu = deformation.positions[segment_i + 1];
float3 closest_on_segment_cu;
float3 closest_on_brush_cu;
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index ad3871bee45..7d40ed80a65 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -12,8 +12,11 @@
#include "BLI_virtual_array.hh"
#include "BKE_attribute.h"
+#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
+#include "ED_curves_sculpt.h"
+
struct ARegion;
struct RegionView3D;
struct Depsgraph;
@@ -22,15 +25,18 @@ struct Object;
struct Brush;
struct Scene;
struct BVHTreeFromMesh;
+struct ReportList;
namespace blender::ed::sculpt_paint {
using bke::CurvesGeometry;
+using bke::CurvesSurfaceTransforms;
struct StrokeExtension {
bool is_first;
float2 mouse_position;
float pressure;
+ ReportList *reports = nullptr;
};
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension);
@@ -52,8 +58,7 @@ class CurvesSculptStrokeOperation {
virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0;
};
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
- ReportList *reports);
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
@@ -66,7 +71,7 @@ std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStro
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);
+ const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start);
std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation();
struct CurvesBrush3D {
@@ -97,12 +102,6 @@ VArray<float> get_curves_selection(const Curves &curves_id);
*/
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 {
@@ -111,29 +110,16 @@ class CurvesSculptCommonContext {
const Scene *scene = nullptr;
ARegion *region = nullptr;
const View3D *v3d = nullptr;
- const RegionView3D *rv3d = nullptr;
+ 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 CurvesSurfaceTransforms &transforms,
const BVHTreeFromMesh &surface_bvh,
const float2 &brush_pos_re,
const float brush_radius_re);
@@ -142,4 +128,9 @@ float transform_brush_radius(const float4x4 &transform,
const float3 &brush_position,
const float old_radius);
+void report_missing_surface(ReportList *reports);
+void report_missing_uv_map_on_original_surface(ReportList *reports);
+void report_missing_uv_map_on_evaluated_surface(ReportList *reports);
+void report_invalid_uv_map(ReportList *reports);
+
} // 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 e6da2039433..02ebd622469 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -1,34 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector_set.hh"
#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
+#include "ED_curves.h"
#include "ED_curves_sculpt.h"
#include "ED_image.h"
#include "ED_object.h"
#include "ED_screen.h"
+#include "ED_space_api.h"
#include "ED_view3d.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "DNA_brush_types.h"
#include "DNA_curves_types.h"
#include "DNA_screen_types.h"
#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "curves_sculpt_intern.h"
#include "curves_sculpt_intern.hh"
#include "paint_intern.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
+
/* -------------------------------------------------------------------- */
/** \name Poll Functions
* \{ */
@@ -90,8 +109,8 @@ float brush_strength_get(const Scene &scene,
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)
+static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(
+ bContext &C, wmOperator &op, const StrokeExtension &stroke_start)
{
const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode"));
@@ -106,11 +125,21 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
case CURVES_SCULPT_TOOL_SNAKE_HOOK:
return new_snake_hook_operation();
case CURVES_SCULPT_TOOL_ADD:
- return new_add_operation(C, op.reports);
+ return new_add_operation();
case CURVES_SCULPT_TOOL_GROW_SHRINK:
return new_grow_shrink_operation(mode, C);
case CURVES_SCULPT_TOOL_SELECTION_PAINT:
return new_selection_paint_operation(mode, C);
+ case CURVES_SCULPT_TOOL_PINCH:
+ return new_pinch_operation(mode, C);
+ case CURVES_SCULPT_TOOL_SMOOTH:
+ return new_smooth_operation();
+ case CURVES_SCULPT_TOOL_PUFF:
+ return new_puff_operation();
+ case CURVES_SCULPT_TOOL_DENSITY:
+ return new_density_operation(mode, C, stroke_start);
+ case CURVES_SCULPT_TOOL_SLIDE:
+ return new_slide_operation();
}
BLI_assert_unreachable();
return {};
@@ -121,7 +150,10 @@ struct SculptCurvesBrushStrokeData {
PaintStroke *stroke;
};
-static bool stroke_get_location(bContext *C, float out[3], const float mouse[2])
+static bool stroke_get_location(bContext *C,
+ float out[3],
+ const float mouse[2],
+ bool UNUSED(force_original))
{
out[0] = mouse[0];
out[1] = mouse[1];
@@ -147,10 +179,11 @@ 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");
+ stroke_extension.reports = op->reports;
if (!op_data->operation) {
stroke_extension.is_first = true;
- op_data->operation = start_brush_operation(*C, *op);
+ op_data->operation = start_brush_operation(*C, *op, stroke_extension);
}
else {
stroke_extension.is_first = false;
@@ -236,18 +269,6 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
/** \name * CURVES_OT_sculptmode_toggle
* \{ */
-static bool curves_sculptmode_toggle_poll(bContext *C)
-{
- const Object *ob = CTX_data_active_object(C);
- if (ob == nullptr) {
- return false;
- }
- if (ob->type != OB_CURVES) {
- return false;
- }
- return true;
-}
-
static void curves_sculptmode_enter(bContext *C)
{
Scene *scene = CTX_data_scene(C);
@@ -309,13 +330,935 @@ static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
ot->description = "Enter/Exit sculpt mode for curves";
ot->exec = curves_sculptmode_toggle_exec;
- ot->poll = curves_sculptmode_toggle_poll;
+ ot->poll = curves::curves_poll;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
}
/** \} */
+namespace select_random {
+
+static int select_random_exec(bContext *C, wmOperator *op)
+{
+ VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
+
+ const int seed = RNA_int_get(op->ptr, "seed");
+ RandomNumberGenerator rng{static_cast<uint32_t>(seed)};
+
+ const bool partial = RNA_boolean_get(op->ptr, "partial");
+ const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve");
+ const float probability = RNA_float_get(op->ptr, "probability");
+ const float min_value = RNA_float_get(op->ptr, "min");
+ const auto next_partial_random_value = [&]() {
+ return rng.get_float() * (1.0f - min_value) + min_value;
+ };
+ const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
+
+ for (Curves *curves_id : unique_curves) {
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ const bool was_anything_selected = curves::has_anything_selected(*curves_id);
+ switch (curves_id->selection_domain) {
+ case ATTR_DOMAIN_POINT: {
+ MutableSpan<float> selection = curves.selection_point_float_for_write();
+ if (!was_anything_selected) {
+ selection.fill(1.0f);
+ }
+ if (partial) {
+ if (constant_per_curve) {
+ for (const int curve_i : curves.curves_range()) {
+ const float random_value = next_partial_random_value();
+ const IndexRange points = curves.points_for_curve(curve_i);
+ for (const int point_i : points) {
+ selection[point_i] *= random_value;
+ }
+ }
+ }
+ else {
+ for (const int point_i : selection.index_range()) {
+ const float random_value = next_partial_random_value();
+ selection[point_i] *= random_value;
+ }
+ }
+ }
+ else {
+ if (constant_per_curve) {
+ for (const int curve_i : curves.curves_range()) {
+ const bool random_value = next_bool_random_value();
+ const IndexRange points = curves.points_for_curve(curve_i);
+ if (!random_value) {
+ selection.slice(points).fill(0.0f);
+ }
+ }
+ }
+ else {
+ for (const int point_i : selection.index_range()) {
+ const bool random_value = next_bool_random_value();
+ if (!random_value) {
+ selection[point_i] = 0.0f;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CURVE: {
+ MutableSpan<float> selection = curves.selection_curve_float_for_write();
+ if (!was_anything_selected) {
+ selection.fill(1.0f);
+ }
+ if (partial) {
+ for (const int curve_i : curves.curves_range()) {
+ const float random_value = next_partial_random_value();
+ selection[curve_i] *= random_value;
+ }
+ }
+ else {
+ for (const int curve_i : curves.curves_range()) {
+ const bool random_value = next_bool_random_value();
+ if (!random_value) {
+ selection[curve_i] = 0.0f;
+ }
+ }
+ }
+ break;
+ }
+ }
+ MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
+ curves.selection_point_float_for_write() :
+ curves.selection_curve_float_for_write();
+ const bool was_any_selected = std::any_of(
+ selection.begin(), selection.end(), [](const float v) { return v > 0.0f; });
+ if (was_any_selected) {
+ for (float &v : selection) {
+ v *= rng.get_float();
+ }
+ }
+ else {
+ for (float &v : selection) {
+ v = rng.get_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);
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void select_random_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+
+ uiItemR(layout, op->ptr, "seed", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "constant_per_curve", 0, nullptr, ICON_NONE);
+ uiItemR(layout, op->ptr, "partial", 0, nullptr, ICON_NONE);
+
+ if (RNA_boolean_get(op->ptr, "partial")) {
+ uiItemR(layout, op->ptr, "min", UI_ITEM_R_SLIDER, "Min", ICON_NONE);
+ }
+ else {
+ uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, "Probability", ICON_NONE);
+ }
+}
+
+} // namespace select_random
+
+static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot)
+{
+ ot->name = "Select Random";
+ ot->idname = __func__;
+ ot->description = "Randomizes existing selection or create new random selection";
+
+ ot->exec = select_random::select_random_exec;
+ ot->poll = curves::editable_curves_poll;
+ ot->ui = select_random::select_random_ui;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna,
+ "seed",
+ 0,
+ INT32_MIN,
+ INT32_MAX,
+ "Seed",
+ "Source of randomness",
+ INT32_MIN,
+ INT32_MAX);
+ RNA_def_boolean(
+ ot->srna, "partial", false, "Partial", "Allow points or curves to be selected partially");
+ RNA_def_float(ot->srna,
+ "probability",
+ 0.5f,
+ 0.0f,
+ 1.0f,
+ "Probability",
+ "Chance of every point or curve being included in the selection",
+ 0.0f,
+ 1.0f);
+ RNA_def_float(ot->srna,
+ "min",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Min",
+ "Minimum value for the random selection",
+ 0.0f,
+ 1.0f);
+ RNA_def_boolean(ot->srna,
+ "constant_per_curve",
+ true,
+ "Constant per Curve",
+ "The generated random number is the same for every control point of a curve");
+}
+
+namespace select_end {
+static bool select_end_poll(bContext *C)
+{
+ if (!curves::editable_curves_poll(C)) {
+ return false;
+ }
+ const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data);
+ if (curves_id->selection_domain != ATTR_DOMAIN_POINT) {
+ CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode");
+ return false;
+ }
+ return true;
+}
+
+static int select_end_exec(bContext *C, wmOperator *op)
+{
+ VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
+ const bool end_points = RNA_boolean_get(op->ptr, "end_points");
+ const int amount = RNA_int_get(op->ptr, "amount");
+
+ for (Curves *curves_id : unique_curves) {
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ const bool was_anything_selected = curves::has_anything_selected(*curves_id);
+ MutableSpan<float> selection = curves.selection_point_float_for_write();
+ if (!was_anything_selected) {
+ selection.fill(1.0f);
+ }
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ if (end_points) {
+ selection.slice(points.drop_back(amount)).fill(0.0f);
+ }
+ else {
+ selection.slice(points.drop_front(amount)).fill(0.0f);
+ }
+ }
+ });
+
+ /* 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_end
+
+static void SCULPT_CURVES_OT_select_end(wmOperatorType *ot)
+{
+ ot->name = "Select End";
+ ot->idname = __func__;
+ ot->description = "Select end points of curves";
+
+ ot->exec = select_end::select_end_exec;
+ ot->poll = select_end::select_end_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna,
+ "end_points",
+ true,
+ "End Points",
+ "Select points at the end of the curve as opposed to the beginning");
+ RNA_def_int(
+ ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX);
+}
+
+namespace select_grow {
+
+struct GrowOperatorDataPerCurve : NonCopyable, NonMovable {
+ Curves *curves_id;
+ Vector<int> selected_point_indices;
+ Vector<int> unselected_point_indices;
+ Array<float> distances_to_selected;
+ Array<float> distances_to_unselected;
+
+ Array<float> original_selection;
+ float pixel_to_distance_factor;
+};
+
+struct GrowOperatorData {
+ int initial_mouse_x;
+ Vector<std::unique_ptr<GrowOperatorDataPerCurve>> per_curve;
+};
+
+static void update_points_selection(const GrowOperatorDataPerCurve &data,
+ const float distance,
+ MutableSpan<float> points_selection)
+{
+ if (distance > 0.0f) {
+ threading::parallel_for(
+ data.unselected_point_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int point_i = data.unselected_point_indices[i];
+ const float distance_to_selected = data.distances_to_selected[i];
+ const float selection = distance_to_selected <= distance ? 1.0f : 0.0f;
+ points_selection[point_i] = selection;
+ }
+ });
+ threading::parallel_for(
+ data.selected_point_indices.index_range(), 512, [&](const IndexRange range) {
+ for (const int point_i : data.selected_point_indices.as_span().slice(range)) {
+ points_selection[point_i] = 1.0f;
+ }
+ });
+ }
+ else {
+ threading::parallel_for(
+ data.selected_point_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int point_i = data.selected_point_indices[i];
+ const float distance_to_unselected = data.distances_to_unselected[i];
+ const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f;
+ points_selection[point_i] = selection;
+ }
+ });
+ threading::parallel_for(
+ data.unselected_point_indices.index_range(), 512, [&](const IndexRange range) {
+ for (const int point_i : data.unselected_point_indices.as_span().slice(range)) {
+ points_selection[point_i] = 0.0f;
+ }
+ });
+ }
+}
+
+static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x)
+{
+ GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
+
+ for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
+ Curves &curves_id = *curve_op_data->curves_id;
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x;
+
+ /* Grow or shrink selection based on precomputed distances. */
+ switch (curves_id.selection_domain) {
+ case ATTR_DOMAIN_POINT: {
+ MutableSpan<float> points_selection = curves.selection_point_float_for_write();
+ update_points_selection(*curve_op_data, distance, points_selection);
+ break;
+ }
+ case ATTR_DOMAIN_CURVE: {
+ Array<float> new_points_selection(curves.points_num());
+ update_points_selection(*curve_op_data, distance, new_points_selection);
+ /* Propagate grown point selection to the curve selection. */
+ MutableSpan<float> curves_selection = curves.selection_curve_float_for_write();
+ for (const int curve_i : curves.curves_range()) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ const Span<float> points_selection = new_points_selection.as_span().slice(points);
+ const float max_selection = *std::max_element(points_selection.begin(),
+ points_selection.end());
+ curves_selection[curve_i] = max_selection;
+ }
+ break;
+ }
+ }
+
+ /* 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;
+}
+
+static void select_grow_invoke_per_curve(Curves &curves_id,
+ Object &curves_ob,
+ const ARegion &region,
+ const View3D &v3d,
+ const RegionView3D &rv3d,
+ GrowOperatorDataPerCurve &curve_op_data)
+{
+ curve_op_data.curves_id = &curves_id;
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ const Span<float3> positions = curves.positions();
+
+ /* Find indices of selected and unselected points. */
+ switch (curves_id.selection_domain) {
+ case ATTR_DOMAIN_POINT: {
+ const VArray<float> points_selection = curves.selection_point_float();
+ curve_op_data.original_selection.reinitialize(points_selection.size());
+ points_selection.materialize(curve_op_data.original_selection);
+ for (const int point_i : points_selection.index_range()) {
+ const float point_selection = points_selection[point_i];
+ if (point_selection > 0.0f) {
+ curve_op_data.selected_point_indices.append(point_i);
+ }
+ else {
+ curve_op_data.unselected_point_indices.append(point_i);
+ }
+ }
+
+ break;
+ }
+ case ATTR_DOMAIN_CURVE: {
+ const VArray<float> curves_selection = curves.selection_curve_float();
+ curve_op_data.original_selection.reinitialize(curves_selection.size());
+ curves_selection.materialize(curve_op_data.original_selection);
+ for (const int curve_i : curves_selection.index_range()) {
+ const float curve_selection = curves_selection[curve_i];
+ const IndexRange points = curves.points_for_curve(curve_i);
+ if (curve_selection > 0.0f) {
+ for (const int point_i : points) {
+ curve_op_data.selected_point_indices.append(point_i);
+ }
+ }
+ else {
+ for (const int point_i : points) {
+ curve_op_data.unselected_point_indices.append(point_i);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ threading::parallel_invoke(
+ [&]() {
+ /* Build KD-tree for the selected points. */
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_point_indices.size());
+ BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
+ for (const int point_i : curve_op_data.selected_point_indices) {
+ const float3 &position = positions[point_i];
+ BLI_kdtree_3d_insert(kdtree, point_i, position);
+ }
+ BLI_kdtree_3d_balance(kdtree);
+
+ /* For each unselected point, compute the distance to the closest selected point. */
+ curve_op_data.distances_to_selected.reinitialize(
+ curve_op_data.unselected_point_indices.size());
+ threading::parallel_for(curve_op_data.unselected_point_indices.index_range(),
+ 256,
+ [&](const IndexRange range) {
+ for (const int i : range) {
+ const int point_i = curve_op_data.unselected_point_indices[i];
+ const float3 &position = positions[point_i];
+ KDTreeNearest_3d nearest;
+ BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
+ curve_op_data.distances_to_selected[i] = nearest.dist;
+ }
+ });
+ },
+ [&]() {
+ /* Build KD-tree for the unselected points. */
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.unselected_point_indices.size());
+ BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
+ for (const int point_i : curve_op_data.unselected_point_indices) {
+ const float3 &position = positions[point_i];
+ BLI_kdtree_3d_insert(kdtree, point_i, position);
+ }
+ BLI_kdtree_3d_balance(kdtree);
+
+ /* For each selected point, compute the distance to the closest unselected point. */
+ curve_op_data.distances_to_unselected.reinitialize(
+ curve_op_data.selected_point_indices.size());
+ threading::parallel_for(
+ curve_op_data.selected_point_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int point_i = curve_op_data.selected_point_indices[i];
+ const float3 &position = positions[point_i];
+ KDTreeNearest_3d nearest;
+ BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
+ curve_op_data.distances_to_unselected[i] = nearest.dist;
+ }
+ });
+ });
+
+ float4x4 curves_to_world_mat = curves_ob.obmat;
+ float4x4 world_to_curves_mat = curves_to_world_mat.inverted();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(&rv3d, &curves_ob, projection.values);
+
+ /* Compute how mouse movements in screen space are converted into grow/shrink distances in
+ * object space. */
+ curve_op_data.pixel_to_distance_factor = threading::parallel_reduce(
+ curve_op_data.selected_point_indices.index_range(),
+ 256,
+ FLT_MAX,
+ [&](const IndexRange range, float pixel_to_distance_factor) {
+ for (const int i : range) {
+ const int point_i = curve_op_data.selected_point_indices[i];
+ const float3 &pos_cu = positions[point_i];
+
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(&region, pos_cu, pos_re, projection.values);
+ if (pos_re.x < 0 || pos_re.y < 0 || pos_re.x > region.winx || pos_re.y > region.winy) {
+ continue;
+ }
+ /* Compute how far this point moves in curve space when it moves one unit in screen
+ * space. */
+ const float2 pos_offset_re = pos_re + float2(1, 0);
+ float3 pos_offset_wo;
+ ED_view3d_win_to_3d(
+ &v3d, &region, curves_to_world_mat * pos_cu, pos_offset_re, pos_offset_wo);
+ const float3 pos_offset_cu = world_to_curves_mat * pos_offset_wo;
+ const float dist_cu = math::distance(pos_cu, pos_offset_cu);
+ const float dist_re = math::distance(pos_re, pos_offset_re);
+ const float factor = dist_cu / dist_re;
+ math::min_inplace(pixel_to_distance_factor, factor);
+ }
+ return pixel_to_distance_factor;
+ },
+ [](const float a, const float b) { return std::min(a, b); });
+}
+
+static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *active_ob = CTX_data_active_object(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+
+ GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__);
+ op->customdata = op_data;
+
+ op_data->initial_mouse_x = event->xy[0];
+
+ Curves &curves_id = *static_cast<Curves *>(active_ob->data);
+ auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>();
+ select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data);
+ op_data->per_curve.append(std::move(curve_op_data));
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
+ const int mouse_x = event->xy[0];
+ const int mouse_diff_x = mouse_x - op_data.initial_mouse_x;
+ switch (event->type) {
+ case MOUSEMOVE: {
+ select_grow_update(C, op, mouse_diff_x);
+ break;
+ }
+ case LEFTMOUSE: {
+ MEM_delete(&op_data);
+ return OPERATOR_FINISHED;
+ }
+ case EVT_ESCKEY:
+ case RIGHTMOUSE: {
+ /* Undo operator by resetting the selection to the original value. */
+ for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
+ Curves &curves_id = *curve_op_data->curves_id;
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ switch (curves_id.selection_domain) {
+ case ATTR_DOMAIN_POINT: {
+ MutableSpan<float> points_selection = curves.selection_point_float_for_write();
+ points_selection.copy_from(curve_op_data->original_selection);
+ break;
+ }
+ case ATTR_DOMAIN_CURVE: {
+ MutableSpan<float> curves_seletion = curves.selection_curve_float_for_write();
+ curves_seletion.copy_from(curve_op_data->original_selection);
+ break;
+ }
+ }
+
+ /* 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);
+ }
+ MEM_delete(&op_data);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ return OPERATOR_RUNNING_MODAL;
+}
+
+} // namespace select_grow
+
+static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot)
+{
+ ot->name = "Select Grow";
+ ot->idname = __func__;
+ ot->description = "Select curves which are close to curves that are selected already";
+
+ ot->invoke = select_grow::select_grow_invoke;
+ ot->modal = select_grow::select_grow_modal;
+ ot->poll = curves::editable_curves_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ PropertyRNA *prop;
+ prop = RNA_def_float(ot->srna,
+ "distance",
+ 0.1f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Distance",
+ "By how much to grow the selection",
+ -10.0f,
+ 10.f);
+ RNA_def_property_subtype(prop, PROP_DISTANCE);
+}
+
+namespace min_distance_edit {
+
+static bool min_distance_edit_poll(bContext *C)
+{
+ if (!curves::curves_with_surface_poll(C)) {
+ return false;
+ }
+ Scene *scene = CTX_data_scene(C);
+ const Brush *brush = BKE_paint_brush_for_read(&scene->toolsettings->curves_sculpt->paint);
+ if (brush == nullptr) {
+ return false;
+ }
+ if (brush->curves_sculpt_tool != CURVES_SCULPT_TOOL_DENSITY) {
+ return false;
+ }
+ return true;
+}
+
+struct MinDistanceEditData {
+ /** Brush whose minimum distance is modified. */
+ Brush *brush;
+ float4x4 curves_to_world_mat;
+
+ /** Where the preview is drawn. */
+ float3 pos_cu;
+ float3 normal_cu;
+
+ int2 initial_mouse;
+ float initial_minimum_distance;
+
+ /** The operator uses a new cursor, but the existing cursors should be restored afterwards. */
+ ListBase orig_paintcursors;
+ void *cursor;
+
+ /** Store the viewport region in case the operator was called from the header. */
+ ARegion *region;
+ RegionView3D *rv3d;
+};
+
+static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data)
+{
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = op_data.region;
+
+ const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
+ const float brush_radius = BKE_brush_size_get(scene, op_data.brush);
+
+ float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
+ if (math::is_zero(tangent_x_cu)) {
+ tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
+ }
+ tangent_x_cu = math::normalize(tangent_x_cu);
+ const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
+
+ /* Sample a few points to get a good estimate of how large the grid has to be. */
+ Vector<float3> points_wo;
+ points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu);
+ points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu);
+ points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu);
+ points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu);
+
+ Vector<float2> points_re;
+ for (const float3 &pos_wo : points_wo) {
+ float2 pos_re;
+ ED_view3d_project_v2(region, pos_wo, pos_re);
+ points_re.append(pos_re);
+ }
+
+ float2 origin_re;
+ ED_view3d_project_v2(region, op_data.pos_cu, origin_re);
+
+ int needed_points = 0;
+ for (const float2 &pos_re : points_re) {
+ const float distance = math::length(pos_re - origin_re);
+ const int needed_points_iter = (brush_radius * 2.0f) / distance;
+
+ if (needed_points_iter > needed_points) {
+ needed_points = needed_points_iter;
+ }
+ }
+
+ /* Limit to a hard-coded number since it only adds noise at some point. */
+ return std::min(300, needed_points);
+}
+
+static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), void *customdata)
+{
+ Scene *scene = CTX_data_scene(C);
+ MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata);
+
+ const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
+
+ float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
+ if (math::is_zero(tangent_x_cu)) {
+ tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
+ }
+ tangent_x_cu = math::normalize(tangent_x_cu);
+ const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
+
+ const int points_per_side = calculate_points_per_side(C, op_data);
+ const int points_per_axis_num = 2 * points_per_side + 1;
+
+ Vector<float3> points_wo;
+ for (const int x_i : IndexRange(points_per_axis_num)) {
+ for (const int y_i : IndexRange(points_per_axis_num)) {
+ const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f);
+ const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f);
+
+ const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f +
+ x_iter * tangent_x_cu + y_iter * tangent_y_cu;
+ const float3 point_pos_wo = op_data.curves_to_world_mat * point_pos_cu;
+ points_wo.append(point_pos_wo);
+ }
+ }
+
+ float4 circle_col = float4(op_data.brush->add_col);
+ float circle_alpha = op_data.brush->cursor_overlay_alpha;
+ float brush_radius_re = BKE_brush_size_get(scene, op_data.brush);
+
+ /* Draw the grid. */
+ GPU_matrix_push();
+ GPU_matrix_push_projection();
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ ARegion *region = op_data.region;
+ RegionView3D *rv3d = op_data.rv3d;
+ wmWindow *win = CTX_wm_window(C);
+
+ /* It does the same as: `view3d_operator_needs_opengl(C);`. */
+ wmViewport(&region->winrct);
+ GPU_matrix_projection_set(rv3d->winmat);
+ GPU_matrix_set(rv3d->viewmat);
+
+ GPUVertFormat *format3d = immVertexFormat();
+
+ const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR);
+
+ GPU_point_size(3.0f);
+ immBegin(GPU_PRIM_POINTS, points_wo.size());
+
+ float3 brush_origin_wo = op_data.curves_to_world_mat * op_data.pos_cu;
+ float2 brush_origin_re;
+ ED_view3d_project_v2(region, brush_origin_wo, brush_origin_re);
+
+ /* Smooth alpha transition until the brush edge. */
+ const int alpha_border_re = 20;
+ const float dist_to_inner_border_re = brush_radius_re - alpha_border_re;
+
+ for (const float3 &pos_wo : points_wo) {
+ float2 pos_re;
+ ED_view3d_project_v2(region, pos_wo, pos_re);
+
+ const float dist_to_point_re = math::distance(pos_re, brush_origin_re);
+ const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re);
+
+ immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha);
+ immVertex3fv(pos3d, pos_wo);
+ }
+ immEnd();
+ immUnbindProgram();
+
+ /* Reset the drawing settings. */
+ GPU_point_size(1.0f);
+ GPU_matrix_pop_projection();
+ GPU_matrix_pop();
+
+ int4 scissor;
+ GPU_scissor_get(scissor);
+ wmWindowViewport(win);
+ GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
+
+ /* Draw the brush circle. */
+ GPU_matrix_translate_2f((float)op_data.initial_mouse.x, (float)op_data.initial_mouse.y);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ immUniformColor3fvAlpha(circle_col, circle_alpha);
+ imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80);
+
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+}
+
+static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ Scene *scene = CTX_data_scene(C);
+
+ Object &curves_ob_orig = *CTX_data_active_object(C);
+ Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
+ Object &surface_ob_orig = *curves_id_orig.surface;
+ Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig);
+ if (surface_ob_eval == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+ Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
+ if (surface_me_eval == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BVHTreeFromMesh surface_bvh_eval;
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
+
+ const int2 mouse_pos_int_re{event->mval};
+ const float2 mouse_pos_re{mouse_pos_int_re};
+
+ float3 ray_start_wo, ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true);
+
+ const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig};
+
+ const float3 ray_start_su = transforms.world_to_surface * ray_start_wo;
+ const float3 ray_end_su = transforms.world_to_surface * 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_eval.tree,
+ ray_start_su,
+ ray_direction_su,
+ 0.0f,
+ &ray_hit,
+ surface_bvh_eval.raycast_callback,
+ &surface_bvh_eval);
+ if (ray_hit.index == -1) {
+ WM_report(RPT_ERROR, "Cursor must be over the surface mesh");
+ return OPERATOR_CANCELLED;
+ }
+
+ const float3 hit_pos_su = ray_hit.co;
+ const float3 hit_normal_su = ray_hit.no;
+
+ const float3 hit_pos_cu = transforms.surface_to_curves * hit_pos_su;
+ const float3 hit_normal_cu = math::normalize(transforms.surface_to_curves_normal *
+ hit_normal_su);
+
+ MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__);
+ op_data->curves_to_world_mat = transforms.curves_to_world;
+ op_data->normal_cu = hit_normal_cu;
+ op_data->pos_cu = hit_pos_cu;
+ op_data->initial_mouse = event->xy;
+ op_data->brush = BKE_paint_brush(&scene->toolsettings->curves_sculpt->paint);
+ op_data->initial_minimum_distance = op_data->brush->curves_sculpt_settings->minimum_distance;
+
+ if (op_data->initial_minimum_distance <= 0.0f) {
+ op_data->initial_minimum_distance = 0.01f;
+ }
+
+ op->customdata = op_data;
+
+ /* Temporarily disable other paint cursors. */
+ wmWindowManager *wm = CTX_wm_manager(C);
+ op_data->orig_paintcursors = wm->paintcursors;
+ BLI_listbase_clear(&wm->paintcursors);
+
+ /* Add minimum distance paint cursor. */
+ op_data->cursor = WM_paint_cursor_activate(
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, op->type->poll, min_distance_edit_draw, op_data);
+
+ op_data->region = CTX_wm_region(C);
+ op_data->rv3d = CTX_wm_region_view3d(C);
+
+ WM_event_add_modal_handler(C, op);
+ ED_region_tag_redraw(region);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(op->customdata);
+
+ auto finish = [&]() {
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ /* Remove own cursor. */
+ WM_paint_cursor_end(static_cast<wmPaintCursor *>(op_data.cursor));
+ /* Restore original paint cursors. */
+ wm->paintcursors = op_data.orig_paintcursors;
+
+ ED_region_tag_redraw(region);
+ MEM_freeN(&op_data);
+ };
+
+ switch (event->type) {
+ case MOUSEMOVE: {
+ const int2 mouse_pos_int_re{event->xy};
+ const float2 mouse_pos_re{mouse_pos_int_re};
+
+ const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x;
+ const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f);
+ op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance *
+ factor;
+
+ ED_region_tag_redraw(region);
+ WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
+ break;
+ }
+ case LEFTMOUSE: {
+ if (event->val == KM_PRESS) {
+ finish();
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+ case RIGHTMOUSE:
+ case EVT_ESCKEY: {
+ op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance;
+ finish();
+ WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+} // namespace min_distance_edit
+
+static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot)
+{
+ ot->name = "Edit Minimum Distance";
+ ot->idname = __func__;
+ ot->description = "Change the minimum distance used by the density brush";
+
+ ot->poll = min_distance_edit::min_distance_edit_poll;
+ ot->invoke = min_distance_edit::min_distance_edit_invoke;
+ ot->modal = min_distance_edit::min_distance_edit_modal;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
+}
+
} // namespace blender::ed::sculpt_paint
/* -------------------------------------------------------------------- */
@@ -327,6 +1270,10 @@ void ED_operatortypes_sculpt_curves()
using namespace blender::ed::sculpt_paint;
WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke);
WM_operatortype_append(CURVES_OT_sculptmode_toggle);
+ WM_operatortype_append(SCULPT_CURVES_OT_select_random);
+ WM_operatortype_append(SCULPT_CURVES_OT_select_end);
+ WM_operatortype_append(SCULPT_CURVES_OT_select_grow);
+ WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit);
}
/** \} */
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
new file mode 100644
index 00000000000..3e43b1a6361
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_paint.h"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#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.
+ * `su`: Local space of the surface object.
+ * `wo`: World space.
+ * `re`: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+class PinchOperation : public CurvesSculptStrokeOperation {
+ private:
+ bool invert_pinch_;
+ Array<float> segment_lengths_cu_;
+
+ /** Only used when a 3D brush is used. */
+ CurvesBrush3D brush_3d_;
+
+ friend struct PinchOperationExecutor;
+
+ public:
+ PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch)
+ {
+ }
+
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+struct PinchOperationExecutor {
+ PinchOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *object_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ VArray<float> point_factors_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
+ float brush_strength_;
+
+ float invert_factor_;
+
+ float2 brush_pos_re_;
+
+ PinchOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(PinchOperation &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);
+ if (curves_->curves_num() == 0) {
+ return;
+ }
+
+ 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_ = BKE_brush_alpha_get(ctx_.scene, brush_);
+
+ invert_factor_ = self_->invert_pinch_ ? -1.0f : 1.0f;
+
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+
+ point_factors_ = get_point_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
+ brush_pos_re_ = stroke_extension.mouse_position;
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+
+ if (stroke_extension.is_first) {
+ this->initialize_segment_lengths();
+
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
+ }
+ }
+
+ Array<bool> changed_curves(curves_->curves_num(), false);
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->pinch_projected_with_symmetry(changed_curves);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->pinch_spherical_with_symmetry(changed_curves);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ this->restore_segment_lengths(changed_curves);
+ curves_->tag_positions_changed();
+ 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 pinch_projected_with_symmetry(MutableSpan<bool> r_changed_curves)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->pinch_projected(brush_transform, r_changed_curves);
+ }
+ }
+
+ void pinch_projected(const float4x4 &brush_transform, MutableSpan<bool> r_changed_curves)
+ {
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ 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(), 256, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 old_pos_cu = deformation.positions[point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
+
+ const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_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 t = safe_divide(dist_to_brush_re, brush_radius_base_re_);
+ const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
+ const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
+ point_factors_[point_i];
+
+ const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight);
+
+ float3 new_symm_pos_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+
+ const float3 new_pos_cu = brush_transform * transforms_.world_to_curves *
+ new_symm_pos_wo;
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu[point_i] += translation_orig;
+ r_changed_curves[curve_i] = true;
+ }
+ }
+ });
+ }
+
+ void pinch_spherical_with_symmetry(MutableSpan<bool> r_changed_curves)
+ {
+ float3 brush_pos_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_pos_wo);
+ const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo;
+ 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));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->pinch_spherical(brush_transform * brush_pos_cu, brush_radius_cu, r_changed_curves);
+ }
+ }
+
+ void pinch_spherical(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ MutableSpan<bool> r_changed_curves)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 old_pos_cu = deformation.positions[point_i];
+
+ const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
+ const float t = safe_divide(dist_to_brush_cu, brush_radius_cu);
+ const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
+ const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
+ point_factors_[point_i];
+
+ const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight);
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu[point_i] += translation_orig;
+
+ r_changed_curves[curve_i] = true;
+ }
+ }
+ });
+ }
+
+ void initialize_segment_lengths()
+ {
+ const Span<float3> positions_cu = curves_->positions();
+ self_->segment_lengths_cu_.reinitialize(curves_->points_num());
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions_cu[point_i];
+ const float3 &p2_cu = positions_cu[point_i + 1];
+ const float length_cu = math::distance(p1_cu, p2_cu);
+ self_->segment_lengths_cu_[point_i] = length_cu;
+ }
+ }
+ });
+ }
+
+ void restore_segment_lengths(const Span<bool> changed_curves)
+ {
+ const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+
+ threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ if (!changed_curves[curve_i]) {
+ continue;
+ }
+ 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];
+ const float3 direction = math::normalize(p2_cu - p1_cu);
+ const float expected_length_cu = expected_lengths_cu[points[segment_i]];
+ p2_cu = p1_cu + direction * expected_length_cu;
+ }
+ }
+ });
+ }
+};
+
+void PinchOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
+{
+ PinchOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode,
+ const bContext &C)
+{
+ const Scene &scene = *CTX_data_scene(&C);
+ const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint);
+
+ const bool invert_pinch = (brush_mode == BRUSH_STROKE_INVERT) !=
+ ((brush.flag & BRUSH_DIR_IN) != 0);
+ return std::make_unique<PinchOperation>(invert_pinch);
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc
new file mode 100644
index 00000000000..139e0d67e89
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc
@@ -0,0 +1,382 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "WM_api.h"
+
+#include "BLI_length_parameterize.hh"
+
+#include "GEO_add_curves_on_mesh.hh"
+
+#include "curves_sculpt_intern.hh"
+
+namespace blender::ed::sculpt_paint {
+
+class PuffOperation : public CurvesSculptStrokeOperation {
+ private:
+ /** Only used when a 3D brush is used. */
+ CurvesBrush3D brush_3d_;
+
+ /** Length of each segment indexed by the index of the first point in the segment. */
+ Array<float> segment_lengths_cu_;
+
+ friend struct PuffOperationExecutor;
+
+ public:
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * 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 PuffOperationExecutor {
+ PuffOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *object_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ VArray<float> point_factors_;
+ 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_;
+ float brush_strength_;
+ float2 brush_pos_re_;
+
+ eBrushFalloffShape falloff_shape_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ Object *surface_ob_ = nullptr;
+ Mesh *surface_ = nullptr;
+ Span<MLoopTri> surface_looptris_;
+ Span<float3> corner_normals_su_;
+ BVHTreeFromMesh surface_bvh_;
+
+ PuffOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(PuffOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
+ {
+ UNUSED_VARS(C, stroke_extension);
+ self_ = &self;
+
+ object_ = CTX_data_active_object(&C);
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ if (curves_->curves_num() == 0) {
+ return;
+ }
+ if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ return;
+ }
+
+ 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);
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ point_factors_ = get_point_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+
+ falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
+
+ surface_ob_ = curves_id_->surface;
+ surface_ = static_cast<Mesh *>(surface_ob_->data);
+
+ transforms_ = CurvesSurfaceTransforms(*object_, surface_ob_);
+
+ 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};
+
+ BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
+
+ surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
+ BKE_mesh_runtime_looptri_len(surface_)};
+
+ if (stroke_extension.is_first) {
+ this->initialize_segment_lengths();
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
+ }
+ }
+
+ Array<float> curve_weights(curve_selection_.size(), 0.0f);
+
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->find_curve_weights_projected_with_symmetry(curve_weights);
+ }
+ else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->find_curves_weights_spherical_with_symmetry(curve_weights);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ this->puff(curve_weights);
+ this->restore_segment_lengths();
+
+ curves_->tag_positions_changed();
+ 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 find_curve_weights_projected_with_symmetry(MutableSpan<float> r_curve_weights)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->find_curve_weights_projected(brush_transform, r_curve_weights);
+ }
+ }
+
+ void find_curve_weights_projected(const float4x4 &brush_transform,
+ MutableSpan<float> r_curve_weights)
+ {
+ 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);
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_selection_i : range) {
+ const int curve_i = curve_selection_[curve_selection_i];
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]];
+ float2 prev_pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+ BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; });
+
+ const float dist_to_brush_sq_re = dist_squared_to_line_segment_v2(
+ brush_pos_re_, prev_pos_re, pos_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 = radius_falloff;
+ math::max_inplace(r_curve_weights[curve_selection_i], weight);
+ }
+ }
+ });
+ }
+
+ void find_curves_weights_spherical_with_symmetry(MutableSpan<float> r_curve_weights)
+ {
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ float3 brush_pos_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_pos_wo);
+ const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo;
+ 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));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->find_curves_weights_spherical(
+ brush_transform * brush_pos_cu, brush_radius_cu, r_curve_weights);
+ }
+ }
+
+ void find_curves_weights_spherical(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ MutableSpan<float> r_curve_weights)
+ {
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_selection_i : range) {
+ const int curve_i = curve_selection_[curve_selection_i];
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 &prev_pos_cu = deformation.positions[point_i - 1];
+ const float3 &pos_cu = deformation.positions[point_i];
+ const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3(
+ brush_pos_cu, prev_pos_cu, pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ 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 = radius_falloff;
+ math::max_inplace(r_curve_weights[curve_selection_i], weight);
+ }
+ }
+ });
+ }
+
+ void puff(const Span<float> curve_weights)
+ {
+ BLI_assert(curve_weights.size() == curve_selection_.size());
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ Vector<float> accumulated_lengths_cu;
+ for (const int curve_selection_i : range) {
+ const int curve_i = curve_selection_[curve_selection_i];
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const int first_point_i = points[0];
+ const float3 first_pos_cu = positions_cu[first_point_i];
+ const float3 first_pos_su = transforms_.curves_to_surface * first_pos_cu;
+
+ /* Find the nearest position on the surface. The curve will be aligned to the normal of
+ * that point. */
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(surface_bvh_.tree,
+ first_pos_su,
+ &nearest,
+ surface_bvh_.nearest_callback,
+ &surface_bvh_);
+
+ const MLoopTri &looptri = surface_looptris_[nearest.index];
+ const float3 closest_pos_su = nearest.co;
+ 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;
+ float3 bary_coords;
+ interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su);
+ const float3 normal_su = geometry::compute_surface_point_normal(
+ looptri, bary_coords, corner_normals_su_);
+ const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su);
+
+ accumulated_lengths_cu.reinitialize(points.size() - 1);
+ length_parameterize::accumulate_lengths<float3>(
+ positions_cu.slice(points), false, accumulated_lengths_cu);
+
+ /* Align curve to the surface normal while making sure that the curve does not fold up much
+ * in the process (e.g. when the curve was pointing in the opposite direction before). */
+ for (const int i : IndexRange(points.size()).drop_front(1)) {
+ const int point_i = points[i];
+ const float3 old_pos_cu = positions_cu[point_i];
+
+ /* Compute final position of the point. */
+ const float length_param_cu = accumulated_lengths_cu[i - 1];
+ const float3 goal_pos_cu = first_pos_cu + length_param_cu * normal_cu;
+
+ const float weight = 0.01f * brush_strength_ * point_factors_[point_i] *
+ curve_weights[curve_selection_i];
+ float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight);
+
+ /* Make sure the point does not move closer to the root point than it was initially. This
+ * makes the curve kind of "rotate up". */
+ const float old_dist_to_root_cu = math::distance(old_pos_cu, first_pos_cu);
+ const float new_dist_to_root_cu = math::distance(new_pos_cu, first_pos_cu);
+ if (new_dist_to_root_cu < old_dist_to_root_cu) {
+ const float3 offset = math::normalize(new_pos_cu - first_pos_cu);
+ new_pos_cu += (old_dist_to_root_cu - new_dist_to_root_cu) * offset;
+ }
+
+ positions_cu[point_i] = new_pos_cu;
+ }
+ }
+ });
+ }
+
+ void initialize_segment_lengths()
+ {
+ const Span<float3> positions_cu = curves_->positions();
+ self_->segment_lengths_cu_.reinitialize(curves_->points_num());
+ threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions_cu[point_i];
+ const float3 &p2_cu = positions_cu[point_i + 1];
+ const float length_cu = math::distance(p1_cu, p2_cu);
+ self_->segment_lengths_cu_[point_i] = length_cu;
+ }
+ }
+ });
+ }
+
+ void restore_segment_lengths()
+ {
+ const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ 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[segment_i];
+ p2_cu = p1_cu + direction * expected_length_cu;
+ }
+ }
+ });
+ }
+};
+
+void PuffOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
+{
+ PuffOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation()
+{
+ return std::make_unique<PuffOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
index f620fed5761..5bfc8ccc667 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
@@ -6,6 +6,8 @@
#include "curves_sculpt_intern.hh"
+#include "ED_curves_sculpt.h"
+
namespace blender::ed::sculpt_paint {
static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
@@ -62,7 +64,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
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) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@@ -78,7 +80,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
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) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@@ -102,4 +104,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i
r_indices);
}
+static IndexMask retrieve_selected_points(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.points_num());
+ }
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return selection[i] > 0.0f;
+ });
+ }
+ 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.points_num());
+ }
+ const VArray<float> point_selection = curves.adapt_domain(
+ selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return point_selection[i] > 0.0f;
+ });
+ }
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return CurvesGeometry::wrap(curves_id.geometry).points_range();
+ }
+ return retrieve_selected_points(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
index b40aebcaaf1..cc5a5e7ae8a 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
@@ -66,7 +66,7 @@ struct SelectionPaintOperationExecutor {
float2 brush_pos_re_;
- CurvesSculptTransforms transforms_;
+ CurvesSurfaceTransforms transforms_;
SelectionPaintOperationExecutor(const bContext &C) : ctx_(C)
{
@@ -104,7 +104,7 @@ struct SelectionPaintOperationExecutor {
}
}
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
@@ -140,6 +140,7 @@ struct SelectionPaintOperationExecutor {
* 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);
+ ctx_.rv3d->rflag &= ~RV3D_PAINTING;
ED_region_tag_redraw(ctx_.region);
}
@@ -160,14 +161,15 @@ struct SelectionPaintOperationExecutor {
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
- Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
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];
+ const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
/* Find the position of the point in screen space. */
float2 pos_re;
@@ -214,14 +216,15 @@ struct SelectionPaintOperationExecutor {
void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
{
- Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
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];
+ const float3 pos_old_cu = deformation.positions[i];
/* Compute distance to the brush. */
const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu);
@@ -255,9 +258,11 @@ struct SelectionPaintOperationExecutor {
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();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@@ -273,8 +278,8 @@ struct SelectionPaintOperationExecutor {
[&](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];
+ const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i];
+ const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1];
float2 pos1_re;
float2 pos2_re;
@@ -322,7 +327,8 @@ struct SelectionPaintOperationExecutor {
void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const float brush_radius_cu = self_->brush_3d_.radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@@ -336,8 +342,8 @@ struct SelectionPaintOperationExecutor {
[&](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 float3 &pos1_cu = deformation.positions[segment_i];
+ const float3 &pos2_cu = deformation.positions[segment_i + 1];
const float distance_sq_cu = dist_squared_to_line_segment_v3(
brush_cu, pos1_cu, pos2_cu);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
new file mode 100644
index 00000000000..443fbcb883c
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
@@ -0,0 +1,480 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float3x3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_report.h"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "GEO_add_curves_on_mesh.hh"
+#include "GEO_reverse_uv_sampler.hh"
+
+#include "BLT_translation.h"
+
+namespace blender::ed::sculpt_paint {
+
+using geometry::ReverseUVSampler;
+
+struct SlideCurveInfo {
+ /** Index of the curve to slide. */
+ int curve_i;
+ /** A weight based on the initial distance to the brush. */
+ float radius_falloff;
+ /**
+ * Normal of the surface where the curve was attached. This is used to rotate the curve if it is
+ * moved to a place with a different normal.
+ */
+ float3 initial_normal_cu;
+};
+
+struct SlideInfo {
+ /** The transform used for the curves below (e.g. for symmetry). */
+ float4x4 brush_transform;
+ Vector<SlideCurveInfo> curves_to_slide;
+};
+
+class SlideOperation : public CurvesSculptStrokeOperation {
+ private:
+ float2 initial_brush_pos_re_;
+ /** Information about which curves to slide. This is initialized when the brush starts. */
+ Vector<SlideInfo> slide_info_;
+ /** Positions of all curve points at the start of sliding. */
+ Array<float3> initial_positions_cu_;
+ /** Deformed positions of all curve points at the start of sliding. */
+ Array<float3> initial_deformed_positions_cu_;
+
+ friend struct SlideOperationExecutor;
+
+ public:
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * 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 SlideOperationExecutor {
+ SlideOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ const CurvesSculpt *curves_sculpt_ = nullptr;
+ const Brush *brush_ = nullptr;
+ float brush_radius_base_re_;
+ float brush_radius_factor_;
+ float brush_strength_;
+
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
+
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
+ Span<MLoopTri> surface_looptris_orig_;
+ VArraySpan<float2> surface_uv_map_orig_;
+ Span<float3> corner_normals_orig_su_;
+
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
+
+ VArray<float> curve_factors_;
+ Vector<int64_t> selected_curve_indices_;
+ IndexMask curve_selection_;
+
+ float2 brush_pos_re_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ std::atomic<bool> found_invalid_uv_mapping_{false};
+
+ SlideOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(SlideOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
+ {
+ UNUSED_VARS(C, stroke_extension);
+ self_ = &self;
+
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
+ return;
+ }
+ if (curves_orig_->curves_num() == 0) {
+ return;
+ }
+ if (curves_id_orig_->surface_uv_map == nullptr) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map;
+
+ 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);
+
+ curve_factors_ = get_curves_selection(*curves_id_orig_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
+
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
+
+ surface_ob_orig_ = curves_id_orig_->surface;
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+ surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_),
+ BKE_mesh_runtime_looptri_len(surface_orig_)};
+ surface_uv_map_orig_ =
+ bke::mesh_attributes(*surface_orig_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER);
+ if (surface_uv_map_orig_.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_orig_);
+ }
+ corner_normals_orig_su_ = {
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)),
+ surface_orig_->totloop};
+
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+ if (surface_eval_ == nullptr) {
+ return;
+ }
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
+ surface_uv_map_eval_ =
+ bke::mesh_attributes(*surface_eval_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER);
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
+
+ if (stroke_extension.is_first) {
+ self_->initial_brush_pos_re_ = brush_pos_re_;
+ /* Remember original and deformed positions of all points. Otherwise this information is lost
+ * when sliding starts, but it's still used. */
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+ self_->initial_positions_cu_ = curves_orig_->positions();
+ self_->initial_deformed_positions_cu_ = deformation.positions;
+
+ /* First find all curves to slide. When the mouse moves, only those curves will be moved. */
+ this->find_curves_to_slide_with_symmetry();
+ return;
+ }
+ this->slide_with_symmetry();
+
+ if (found_invalid_uv_mapping_) {
+ BKE_report(
+ stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid"));
+ }
+
+ curves_orig_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
+ ED_region_tag_redraw(ctx_.region);
+ }
+
+ void find_curves_to_slide_with_symmetry()
+ {
+ const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
+ const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ transforms_,
+ surface_bvh_eval_,
+ brush_pos_re_,
+ brush_radius_re);
+ if (!brush_3d.has_value()) {
+ return;
+ }
+ const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
+ for (const float4x4 &brush_transform : brush_transforms) {
+ self_->slide_info_.append_as();
+ SlideInfo &slide_info = self_->slide_info_.last();
+ slide_info.brush_transform = brush_transform;
+ this->find_curves_to_slide(brush_transform * brush_3d->position_cu,
+ brush_3d->radius_cu,
+ reverse_uv_sampler_orig,
+ slide_info.curves_to_slide);
+ }
+ }
+
+ void find_curves_to_slide(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ const ReverseUVSampler &reverse_uv_sampler_orig,
+ Vector<SlideCurveInfo> &r_curves_to_slide)
+ {
+ const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords();
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ const Span<int> offsets = curves_orig_->offsets();
+ for (const int curve_i : curve_selection_) {
+ const int first_point_i = offsets[curve_i];
+ const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i];
+ const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ /* Root point is too far away from curve center. */
+ continue;
+ }
+ 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 float2 uv = surface_uv_coords[curve_i];
+ ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ /* The curve does not have a valid surface attachment. */
+ found_invalid_uv_mapping_.store(true);
+ continue;
+ }
+ /* Compute the normal at the initial surface position. */
+ const float3 normal_cu = math::normalize(
+ transforms_.surface_to_curves_normal *
+ geometry::compute_surface_point_normal(
+ *result.looptri, result.bary_weights, corner_normals_orig_su_));
+
+ r_curves_to_slide.append({curve_i, radius_falloff, normal_cu});
+ }
+ }
+
+ void slide_with_symmetry()
+ {
+ const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
+ for (const SlideInfo &slide_info : self_->slide_info_) {
+ this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform);
+ }
+ }
+
+ void slide(const Span<SlideCurveInfo> slide_curves,
+ const ReverseUVSampler &reverse_uv_sampler_orig,
+ const float4x4 &brush_transform)
+ {
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+
+ const Span<MVert> verts_orig_su{surface_orig_->mvert, surface_orig_->totvert};
+ const Span<MLoop> loops_orig{surface_orig_->mloop, surface_orig_->totloop};
+
+ MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write();
+ MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
+
+ const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_;
+
+ /* The brush transformation has to be applied in curves space. */
+ const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface *
+ brush_transform *
+ transforms_.world_to_curves;
+
+ threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) {
+ for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) {
+ const int curve_i = slide_curve_info.curve_i;
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
+ const int first_point_i = points[0];
+
+ const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i];
+ const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu;
+ const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu;
+
+ float2 old_first_symm_pos_eval_re;
+ ED_view3d_project_float_v2_m4(ctx_.region,
+ old_first_symm_pos_eval_cu,
+ old_first_symm_pos_eval_re,
+ projection.values);
+
+ const float radius_falloff = slide_curve_info.radius_falloff;
+ const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
+ const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re +
+ curve_weight * brush_pos_diff_re;
+
+ /* Compute the ray that will be used to find the new position on the surface. */
+ float3 ray_start_wo, ray_end_wo;
+ ED_view3d_win_to_segment_clipped(ctx_.depsgraph,
+ ctx_.region,
+ ctx_.v3d,
+ new_first_symm_pos_eval_re,
+ ray_start_wo,
+ ray_end_wo,
+ true);
+ const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo;
+ const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo;
+ const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
+
+ /* Find the ray hit that is closest to the initial curve root position. */
+ int looptri_index_eval;
+ float3 hit_pos_eval_su;
+ if (!this->find_closest_ray_hit(ray_start_su,
+ ray_direction_su,
+ old_first_pos_eval_su,
+ looptri_index_eval,
+ hit_pos_eval_su)) {
+ continue;
+ }
+
+ /* Compute the uv of the new surface position on the evaluated mesh. */
+ const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval];
+ const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
+ *surface_eval_, looptri_eval, hit_pos_eval_su);
+ const float2 uv = attribute_math::mix3(bary_weights_eval,
+ surface_uv_map_eval_[looptri_eval.tri[0]],
+ surface_uv_map_eval_[looptri_eval.tri[1]],
+ surface_uv_map_eval_[looptri_eval.tri[2]]);
+
+ /* Try to find the same uv on the original surface. */
+ const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ found_invalid_uv_mapping_.store(true);
+ continue;
+ }
+ const MLoopTri &looptri_orig = *result.looptri;
+ const float3 &bary_weights_orig = result.bary_weights;
+
+ /* Gather old and new surface normal. */
+ const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu;
+ const float3 new_normal_cu = math::normalize(
+ transforms_.surface_to_curves_normal *
+ geometry::compute_surface_point_normal(
+ *result.looptri, result.bary_weights, corner_normals_orig_su_));
+
+ /* Gather old and new surface position. */
+ const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i];
+ const float3 new_first_pos_orig_cu =
+ transforms_.surface_to_curves *
+ attribute_math::mix3<float3>(bary_weights_orig,
+ verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co,
+ verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co,
+ verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co);
+
+ /* Actually transform curve points. */
+ const float4x4 slide_transform = this->get_slide_transform(
+ old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu);
+ for (const int point_i : points) {
+ positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i];
+ }
+ surface_uv_coords[curve_i] = uv;
+ }
+ });
+ }
+
+ bool find_closest_ray_hit(const float3 &ray_start_su,
+ const float3 &ray_direction_su,
+ const float3 &point_su,
+ int &r_looptri_index,
+ float3 &r_hit_pos)
+ {
+ float best_dist_sq_su = FLT_MAX;
+ int best_looptri_index_eval;
+ float3 best_hit_pos_su;
+ BLI_bvhtree_ray_cast_all_cpp(
+ *surface_bvh_eval_.tree,
+ ray_start_su,
+ ray_direction_su,
+ 0.0f,
+ FLT_MAX,
+ [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) {
+ surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit);
+ if (hit.index < 0) {
+ return;
+ }
+ const float3 &hit_pos_su = hit.co;
+ const float dist_sq_su = math::distance_squared(hit_pos_su, point_su);
+ if (dist_sq_su < best_dist_sq_su) {
+ best_dist_sq_su = dist_sq_su;
+ best_hit_pos_su = hit_pos_su;
+ best_looptri_index_eval = hit.index;
+ }
+ });
+
+ if (best_dist_sq_su == FLT_MAX) {
+ return false;
+ }
+ r_looptri_index = best_looptri_index_eval;
+ r_hit_pos = best_hit_pos_su;
+ return true;
+ }
+
+ float4x4 get_slide_transform(const float3 &old_root_pos,
+ const float3 &new_root_pos,
+ const float3 &old_normal,
+ const float3 &new_normal)
+ {
+ float3x3 rotation_3x3;
+ rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal);
+ float4x4 rotation_4x4;
+ copy_m4_m3(rotation_4x4.values, rotation_3x3.values);
+
+ float4x4 transform = float4x4::identity();
+ sub_v3_v3(transform.values[3], old_root_pos);
+ mul_m4_m4_pre(transform.values, rotation_4x4.values);
+ add_v3_v3(transform.values[3], new_root_pos);
+ return transform;
+ }
+};
+
+void SlideOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
+{
+ SlideOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation()
+{
+ return std::make_unique<SlideOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc
new file mode 100644
index 00000000000..37a7f1c83ed
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+
+#include "DNA_brush_types.h"
+
+#include "WM_api.h"
+
+#include "BLI_enumerable_thread_specific.hh"
+
+#include "curves_sculpt_intern.hh"
+
+namespace blender::ed::sculpt_paint {
+
+class SmoothOperation : public CurvesSculptStrokeOperation {
+ private:
+ /** Only used when a 3D brush is used. */
+ CurvesBrush3D brush_3d_;
+
+ friend struct SmoothOperationExecutor;
+
+ public:
+ void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * 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 SmoothOperationExecutor {
+ SmoothOperation *self_ = nullptr;
+ CurvesSculptCommonContext ctx_;
+
+ Object *object_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ VArray<float> point_factors_;
+ 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_;
+ float brush_strength_;
+ float2 brush_pos_re_;
+
+ CurvesSurfaceTransforms transforms_;
+
+ SmoothOperationExecutor(const bContext &C) : ctx_(C)
+ {
+ }
+
+ void execute(SmoothOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
+ {
+ UNUSED_VARS(C, stroke_extension);
+ self_ = &self;
+
+ object_ = CTX_data_active_object(&C);
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ if (curves_->curves_num() == 0) {
+ return;
+ }
+
+ 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);
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ point_factors_ = get_point_selection(*curves_id_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+ if (stroke_extension.is_first) {
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ *ctx_.rv3d,
+ *object_,
+ brush_pos_re_,
+ brush_radius_base_re_);
+ }
+ }
+
+ Array<float> point_smooth_factors(curves_->points_num(), 0.0f);
+
+ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->find_projected_smooth_factors_with_symmetry(point_smooth_factors);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ this->smooth(point_smooth_factors);
+ curves_->tag_positions_changed();
+ 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 find_projected_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors)
+ {
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors);
+ }
+ }
+
+ void find_projected_smooth_factors(const float4x4 &brush_transform,
+ MutableSpan<float> r_point_smooth_factors)
+ {
+ const float4x4 brush_transform_inv = 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);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points) {
+ const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i];
+ float2 pos_re;
+ ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
+ const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_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);
+ /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too
+ * large. */
+ const float weight_factor = 0.1f;
+ const float weight = weight_factor * brush_strength_ * radius_falloff *
+ point_factors_[point_i];
+ math::max_inplace(r_point_smooth_factors[point_i], weight);
+ }
+ }
+ });
+ }
+
+ void find_spherical_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors)
+ {
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+
+ float3 brush_pos_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_pos_wo);
+ const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo;
+ 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));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->find_spherical_smooth_factors(
+ brush_transform * brush_pos_cu, brush_radius_cu, r_point_smooth_factors);
+ }
+ }
+
+ void find_spherical_smooth_factors(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ MutableSpan<float> r_point_smooth_factors)
+ {
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points) {
+ const float3 &pos_cu = deformation.positions[point_i];
+ const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ 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);
+ /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too
+ * large. */
+ const float weight_factor = 0.1f;
+ const float weight = weight_factor * brush_strength_ * radius_falloff *
+ point_factors_[point_i];
+ math::max_inplace(r_point_smooth_factors[point_i], weight);
+ }
+ }
+ });
+ }
+
+ void smooth(const Span<float> point_smooth_factors)
+ {
+ MutableSpan<float3> positions = curves_->positions_for_write();
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ Vector<float3> old_positions;
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ old_positions.clear();
+ old_positions.extend(positions.slice(points));
+ for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {
+ const int point_i = points[i];
+ const float smooth_factor = point_smooth_factors[point_i];
+ if (smooth_factor == 0.0f) {
+ continue;
+ }
+ /* Move towards the middle of the neighboring points. */
+ const float3 old_pos = old_positions[i];
+ const float3 &prev_pos = old_positions[i - 1];
+ const float3 &next_pos = old_positions[i + 1];
+ const float3 goal_pos = math::midpoint(prev_pos, next_pos);
+ const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor);
+ positions[point_i] = new_pos;
+ }
+ }
+ });
+ }
+};
+
+void SmoothOperation::on_stroke_extended(const bContext &C,
+ const StrokeExtension &stroke_extension)
+{
+ SmoothOperationExecutor executor{C};
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation()
+{
+ return std::make_unique<SmoothOperation>();
+}
+
+} // 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 b63e5a7756b..54b81fa221d 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -89,7 +89,7 @@ struct SnakeHookOperatorExecutor {
Vector<int64_t> selected_curve_indices_;
IndexMask curve_selection_;
- CurvesSculptTransforms transforms_;
+ CurvesSurfaceTransforms transforms_;
float2 brush_pos_prev_re_;
float2 brush_pos_re_;
@@ -123,7 +123,7 @@ struct SnakeHookOperatorExecutor {
return;
}
- transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
curve_factors_ = get_curves_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
@@ -176,6 +176,8 @@ struct SnakeHookOperatorExecutor {
void projected_snake_hook(const float4x4 &brush_transform)
{
const float4x4 brush_transform_inv = brush_transform.inverted();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
MutableSpan<float3> positions_cu = curves_->positions_for_write();
@@ -189,12 +191,14 @@ struct SnakeHookOperatorExecutor {
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
- const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i];
+ const float3 old_pos_cu = deformation.positions[last_point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
- float2 old_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
- const float distance_to_brush_sq_re = math::distance_squared(old_pos_re,
+ const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re,
brush_pos_prev_re_);
if (distance_to_brush_sq_re > brush_radius_sq_re) {
continue;
@@ -204,17 +208,21 @@ struct SnakeHookOperatorExecutor {
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;
+ const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_symm_pos_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 *
- (transforms_.world_to_curves * new_position_wo);
-
- move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+ const float3 new_pos_cu = brush_transform *
+ (transforms_.world_to_curves * new_symm_pos_wo);
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ last_point_i, translation_eval);
+
+ move_last_point_and_resample(positions_cu.slice(points),
+ positions_cu[last_point_i] + translation_orig);
}
});
}
@@ -252,6 +260,9 @@ struct SnakeHookOperatorExecutor {
const float3 &brush_end_cu,
const float brush_radius_cu)
{
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@@ -260,7 +271,7 @@ struct SnakeHookOperatorExecutor {
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
- const float3 old_pos_cu = positions_cu[last_point_i];
+ const float3 old_pos_cu = deformation.positions[last_point_i];
const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
old_pos_cu, brush_start_cu, brush_end_cu);
@@ -274,9 +285,12 @@ struct SnakeHookOperatorExecutor {
brush_, distance_to_brush_cu, brush_radius_cu);
const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
- const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu;
+ const float3 translation_eval = weight * brush_diff_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ last_point_i, translation_eval);
- move_last_point_and_resample(positions_cu.slice(points), new_pos_cu);
+ move_last_point_and_resample(positions_cu.slice(points),
+ positions_cu[last_point_i] + translation_orig);
}
});
}
diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc
index 56cd4751881..24290fed323 100644
--- a/source/blender/editors/sculpt_paint/paint_image.cc
+++ b/source/blender/editors/sculpt_paint/paint_image.cc
@@ -125,7 +125,7 @@ void ED_imapaint_dirty_region(
imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
@@ -276,10 +276,11 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
(ID_IS_LINKED(sima->image) || ID_IS_OVERRIDE_LIBRARY(sima->image))) {
return false;
}
- ARegion *region = CTX_wm_region(C);
-
- if ((sima->mode == SI_MODE_PAINT) && region->regiontype == RGN_TYPE_WINDOW) {
- return true;
+ if (sima->mode == SI_MODE_PAINT) {
+ const ARegion *region = CTX_wm_region(C);
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ return true;
+ }
}
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index fae2e6863fa..5df65e596b9 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1196,7 +1196,7 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
ImBuf tmpbuf;
IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0);
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
for (int ty = tiley; ty <= tileh; ty++) {
for (int tx = tilex; tx <= tilew; tx++) {
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 50480b8aef0..9449cc6eb8d 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1813,7 +1813,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
}
if (generate_tile) {
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ struct PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
volatile void *undorect;
if (tinf->masked) {
undorect = ED_image_paint_tile_push(undo_tiles,
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index ea17114efa5..99c25953d50 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -46,7 +46,10 @@ typedef struct CoNo {
/* paint_stroke.c */
-typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
+typedef bool (*StrokeGetLocation)(struct bContext *C,
+ float location[3],
+ const float mouse[2],
+ bool force_original);
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
typedef void (*StrokeUpdateStep)(struct bContext *C,
struct wmOperator *op,
@@ -370,10 +373,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot);
void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot);
void PAINT_OT_face_select_all(struct wmOperatorType *ot);
void PAINT_OT_face_select_hide(struct wmOperatorType *ot);
-void PAINT_OT_face_select_reveal(struct wmOperatorType *ot);
+
+void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot);
void PAINT_OT_vert_select_all(struct wmOperatorType *ot);
void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot);
+void PAINT_OT_vert_select_hide(struct wmOperatorType *ot);
bool vert_paint_poll(struct bContext *C);
bool mask_paint_poll(struct bContext *C);
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index ce6b397af15..994ae4011b4 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -1454,6 +1454,7 @@ void ED_operatortypes_paint(void)
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);
WM_operatortype_append(PAINT_OT_vert_select_ungrouped);
+ WM_operatortype_append(PAINT_OT_vert_select_hide);
/* vertex */
WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
@@ -1472,7 +1473,8 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
WM_operatortype_append(PAINT_OT_face_select_all);
WM_operatortype_append(PAINT_OT_face_select_hide);
- WM_operatortype_append(PAINT_OT_face_select_reveal);
+
+ WM_operatortype_append(PAINT_OT_face_vert_reveal);
/* partial visibility */
WM_operatortype_append(PAINT_OT_hide_show);
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 63e6dc7e965..97fa2b936e0 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -79,6 +79,8 @@ typedef struct PaintStroke {
float last_mouse_position[2];
float last_world_space_position[3];
+ float last_scene_spacing_delta[3];
+
bool stroke_over_mesh;
/* space distance covered so far */
float stroke_distance;
@@ -120,6 +122,8 @@ typedef struct PaintStroke {
StrokeUpdateStep update_step;
StrokeRedraw redraw;
StrokeDone done;
+
+ bool original; /* Ray-cast original mesh at start of stroke. */
} PaintStroke;
/*** Cursors ***/
@@ -243,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode)
return false;
}
+static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode))
+{
+ return brush->flag & BRUSH_ANCHORED;
+}
+
static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode)
{
if (brush->flag & BRUSH_ANCHORED) {
@@ -392,7 +401,7 @@ static bool paint_brush_update(bContext *C,
halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
if (stroke->get_location) {
- if (stroke->get_location(C, r_location, halfway)) {
+ if (stroke->get_location(C, r_location, halfway, stroke->original)) {
hit = true;
location_sampled = true;
location_success = true;
@@ -466,7 +475,7 @@ static bool paint_brush_update(bContext *C,
if (!location_sampled) {
if (stroke->get_location) {
- if (stroke->get_location(C, r_location, mouse)) {
+ if (stroke->get_location(C, r_location, mouse, stroke->original)) {
location_success = true;
*r_location_is_set = true;
}
@@ -550,8 +559,16 @@ static void paint_brush_stroke_add_step(
stroke->last_pressure = pressure;
if (paint_stroke_use_scene_spacing(brush, mode)) {
- SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position);
- mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ float world_space_position[3];
+
+ if (SCULPT_stroke_get_location(
+ C, world_space_position, stroke->last_mouse_position, stroke->original)) {
+ copy_v3_v3(stroke->last_world_space_position, world_space_position);
+ mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ }
+ else {
+ add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta);
+ }
}
if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) {
@@ -698,7 +715,7 @@ static float paint_space_stroke_spacing(bContext *C,
spacing *= stroke->zoom_2d;
if (paint_stroke_use_scene_spacing(brush, mode)) {
- return max_ff(0.001f, size_clamp * spacing / 50.0f);
+ return size_clamp * spacing / 50.0f;
}
return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
}
@@ -807,7 +824,7 @@ static int paint_space_stroke(bContext *C,
if (use_scene_spacing) {
float world_space_position[3];
- bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse);
+ bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, world_space_position);
if (hit && stroke->stroke_over_mesh) {
sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position);
@@ -838,6 +855,8 @@ static int paint_space_stroke(bContext *C,
stroke->last_world_space_position,
final_world_space_position);
ED_view3d_project_v2(region, final_world_space_position, mouse);
+
+ mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing);
}
else {
mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
@@ -896,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C,
stroke->ups = ups;
stroke->stroke_mode = RNA_enum_get(op->ptr, "mode");
+ stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C));
+
get_imapaint_zoom(C, &zoomx, &zoomy);
stroke->zoom_2d = max_ff(zoomx, zoomy);
@@ -995,7 +1016,7 @@ static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptTool tool)
{
- return ELEM(tool, CURVES_SCULPT_TOOL_ADD);
+ return ELEM(tool, CURVES_SCULPT_TOOL_ADD, CURVES_SCULPT_TOOL_DENSITY);
}
bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
@@ -1191,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C,
copy_v2_v2(stroke->last_mouse_position, old_pos);
if (use_scene_spacing) {
- bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos);
- bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos);
+ bool hit_old = SCULPT_stroke_get_location(
+ C, world_space_position_old, old_pos, stroke->original);
+ bool hit_new = SCULPT_stroke_get_location(
+ C, world_space_position_new, new_pos, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old);
mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new);
if (hit_old && hit_new && stroke->stroke_over_mesh) {
@@ -1336,7 +1359,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
- C, stroke->last_world_space_position, data + 2 * j);
+ C, stroke->last_world_space_position, data + 2 * j, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
}
@@ -1468,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
if (paint_stroke_use_scene_spacing(br, mode)) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
- C, stroke->last_world_space_position, sample_average.mouse);
+ C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
}
stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
@@ -1527,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
paint_stroke_line_constrain(stroke, mouse);
- if (stroke->stroke_started &&
- (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) {
+ if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
(br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
@@ -1538,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
}
else if (first_modal ||
/* regular dabs */
- (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
+ (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 3f26f590b70..1f272882100 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -749,25 +749,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot)
ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects");
}
-static int face_select_reveal_exec(bContext *C, wmOperator *op)
+static int vert_select_hide_exec(bContext *C, wmOperator *op)
+{
+ const bool unselected = RNA_boolean_get(op->ptr, "unselected");
+ Object *ob = CTX_data_active_object(C);
+ paintvert_hide(C, ob, unselected);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_vert_select_hide(wmOperatorType *ot)
+{
+ ot->name = "Vertex Select Hide";
+ ot->description = "Hide selected vertices";
+ ot->idname = "PAINT_OT_vert_select_hide";
+
+ ot->exec = vert_select_hide_exec;
+ ot->poll = vert_paint_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(
+ ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices");
+}
+
+static int face_vert_reveal_exec(bContext *C, wmOperator *op)
{
const bool select = RNA_boolean_get(op->ptr, "select");
Object *ob = CTX_data_active_object(C);
- paintface_reveal(C, ob, select);
+
+ if (BKE_paint_select_vert_test(ob)) {
+ paintvert_reveal(C, ob, select);
+ }
+ else {
+ paintface_reveal(C, ob, select);
+ }
+
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
-void PAINT_OT_face_select_reveal(wmOperatorType *ot)
+static bool face_vert_reveal_poll(bContext *C)
{
- ot->name = "Face Select Reveal";
- ot->description = "Reveal hidden faces";
- ot->idname = "PAINT_OT_face_select_reveal";
+ Object *ob = CTX_data_active_object(C);
- ot->exec = face_select_reveal_exec;
- ot->poll = facemask_paint_poll;
+ /* Allow using this operator when no selection is enabled but hiding is applied. */
+ return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob);
+}
+
+void PAINT_OT_face_vert_reveal(wmOperatorType *ot)
+{
+ ot->name = "Reveal Faces/Vertices";
+ ot->description = "Reveal hidden faces and vertices";
+ ot->idname = "PAINT_OT_face_vert_reveal";
+
+ ot->exec = face_vert_reveal_exec;
+ ot->poll = face_vert_reveal_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "select", true, "Select", "");
+ RNA_def_boolean(ot->srna,
+ "select",
+ true,
+ "Select",
+ "Specifies whether the newly revealed geometry should be selected");
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc
index c93b0fc3aa8..6dc8375bb0d 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.cc
+++ b/source/blender/editors/sculpt_paint/paint_vertex.cc
@@ -1176,12 +1176,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
BLI_assert(ob->sculpt == nullptr);
ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = object_mode;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true);
}
static void vwpaint_init_stroke(Depsgraph *depsgraph, Object *ob)
{
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true);
SculptSession *ss = ob->sculpt;
/* Ensure ss->cache is allocated. It will mostly be initialized in
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
index 9a3f2b9b708..8b726c7b942 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
@@ -92,11 +92,9 @@ static bool vertex_paint_from_weight(Object *ob)
return false;
}
- MeshComponent component;
- component.replace(me, GeometryOwnershipType::Editable);
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
- bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write(
- active_color_layer->name);
+ bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
if (!color_attribute) {
BLI_assert_unreachable();
return false;
@@ -104,7 +102,7 @@ static bool vertex_paint_from_weight(Object *ob)
/* Retrieve the vertex group with the domain and type of the existing color
* attribute, in order to let the attribute API handle both conversions. */
- const GVArray vertex_group = component.attribute_get_for_read(
+ const GVArray vertex_group = attributes.lookup(
deform_group->name,
ATTR_DOMAIN_POINT,
bke::cpp_type_to_custom_data_type(color_attribute.varray.type()));
@@ -113,14 +111,11 @@ static bool vertex_paint_from_weight(Object *ob)
return false;
}
- GVArray_GSpan interpolated{component.attribute_try_adapt_domain(
- vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)};
+ GVArraySpan interpolated{
+ attributes.adapt_domain(vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)};
color_attribute.varray.set_all(interpolated.data());
-
- if (color_attribute.tag_modified_fn) {
- color_attribute.tag_modified_fn();
- }
+ color_attribute.finish();
tag_object_after_update(ob);
return true;
@@ -167,29 +162,28 @@ static IndexMask get_selected_indices(const Mesh &mesh,
Span<MVert> verts(mesh.mvert, mesh.totvert);
Span<MPoly> faces(mesh.mpoly, mesh.totpoly);
- MeshComponent component;
- component.replace(&const_cast<Mesh &>(mesh), GeometryOwnershipType::ReadOnly);
+ bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) {
- const VArray<bool> selection = component.attribute_try_adapt_domain(
+ const VArray<bool> selection = attributes.adapt_domain(
VArray<bool>::ForFunc(faces.size(),
[&](const int i) { return faces[i].flag & ME_FACE_SEL; }),
ATTR_DOMAIN_FACE,
domain);
return index_mask_ops::find_indices_from_virtual_array(
- IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices);
+ IndexMask(attributes.domain_size(domain)), selection, 4096, indices);
}
if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) {
- const VArray<bool> selection = component.attribute_try_adapt_domain(
+ const VArray<bool> selection = attributes.adapt_domain(
VArray<bool>::ForFunc(verts.size(), [&](const int i) { return verts[i].flag & SELECT; }),
ATTR_DOMAIN_POINT,
domain);
return index_mask_ops::find_indices_from_virtual_array(
- IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices);
+ IndexMask(attributes.domain_size(domain)), selection, 4096, indices);
}
- return IndexMask(component.attribute_domain_num(domain));
+ return IndexMask(attributes.domain_size(domain));
}
static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection)
@@ -202,17 +196,15 @@ static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask sele
return;
}
- MeshComponent component;
- component.replace(&mesh, GeometryOwnershipType::Editable);
+ bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
- if (component.attribute_get_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) {
+ if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) {
return;
}
- GVArray color_attribute_point = component.attribute_try_get_for_read(active_color_layer->name,
- ATTR_DOMAIN_POINT);
+ GVArray color_attribute_point = attributes.lookup(active_color_layer->name, ATTR_DOMAIN_POINT);
- GVArray color_attribute_corner = component.attribute_try_adapt_domain(
+ GVArray color_attribute_corner = attributes.adapt_domain(
color_attribute_point, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER);
color_attribute_corner.materialize(selection, active_color_layer->data);
@@ -278,11 +270,9 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn)
return false;
}
- MeshComponent component;
- component.replace(&mesh, GeometryOwnershipType::Editable);
+ bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh);
- bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write(
- active_color_layer->name);
+ bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
if (!color_attribute) {
BLI_assert_unreachable();
return false;
@@ -310,6 +300,8 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn)
});
});
+ color_attribute.finish();
+
DEG_id_tag_update(&mesh.id, 0);
return true;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index cbf7701efe9..7cb370efb94 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -3219,7 +3219,7 @@ static void do_brush_action_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
- else if (SCULPT_TOOL_NEEDS_COLOR(data->brush->sculpt_tool)) {
+ else if (SCULPT_tool_is_paint(data->brush->sculpt_tool)) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
@@ -3246,7 +3246,7 @@ static void do_brush_action(Sculpt *sd,
/* Check for unsupported features. */
PBVHType type = BKE_pbvh_type(ss->pbvh);
- if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) {
+ if (SCULPT_tool_is_paint(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) {
if (type != PBVH_FACES) {
return;
}
@@ -3362,7 +3362,7 @@ static void do_brush_action(Sculpt *sd,
if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
if (!ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss, 1.0f, 0.0f, 0.0f, false, true);
+ ob, 1.0f, 0.0f, 0.0f, false, true);
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
@@ -4693,8 +4693,8 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
(brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) ||
(brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
- SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) ||
- (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
+ SCULPT_tool_is_paint(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) ||
+ (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) ||
(brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_PAINT));
@@ -4710,7 +4710,8 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b
if (ss->shapekey_active || ss->deform_modifiers_active ||
(!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false);
+ BKE_sculpt_update_object_for_edit(
+ depsgraph, ob, need_pmap, false, SCULPT_tool_is_paint(brush->sculpt_tool));
}
}
@@ -4951,7 +4952,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
return true;
}
-bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2])
+bool SCULPT_stroke_get_location(bContext *C,
+ float out[3],
+ const float mval[2],
+ bool force_original)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob;
@@ -4967,7 +4971,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2])
ss = ob->sculpt;
cache = ss->cache;
- original = (cache) ? cache->original : false;
+ original = force_original || ((cache) ? cache->original : false);
const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
@@ -5049,7 +5053,8 @@ static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss)
static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ ToolSettings *tool_settings = CTX_data_tool_settings(C);
+ Sculpt *sd = tool_settings->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
int mode = RNA_enum_get(op->ptr, "mode");
@@ -5069,7 +5074,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
sculpt_brush_init_tex(sd, ss);
need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, mode);
- needs_colors = SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool);
+ needs_colors = SCULPT_tool_is_paint(brush->sculpt_tool) &&
+ !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob);
if (needs_colors) {
BKE_sculpt_color_layer_create_if_needed(ob);
@@ -5078,7 +5084,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
/* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
* earlier steps modifying the data. */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors);
+ BKE_sculpt_update_object_for_edit(
+ depsgraph, ob, need_pmap, need_mask, SCULPT_tool_is_paint(brush->sculpt_tool));
ED_paint_tool_update_sticky_shading_color(C, ob);
}
@@ -5280,7 +5287,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2])
{
float co_dummy[3];
- return SCULPT_stroke_get_location(C, co_dummy, mval);
+ return SCULPT_stroke_get_location(C, co_dummy, mval, false);
}
bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports)
@@ -5316,7 +5323,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
/* 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)) {
+ if (brush && SCULPT_tool_is_paint(brush->sculpt_tool) &&
+ !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
View3D *v3d = CTX_wm_view3d(C);
if (v3d->shading.type == OB_SOLID) {
v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR;
@@ -5408,7 +5416,7 @@ static void sculpt_stroke_update_step(bContext *C,
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
- else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ else if (SCULPT_tool_is_paint(brush->sculpt_tool)) {
if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_IMAGE);
}
@@ -5494,14 +5502,27 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
struct PaintStroke *stroke;
int ignore_background_click;
int retval;
+ Object *ob = CTX_data_active_object(C);
+
+ /* Test that ob is visible; otherwise we won't be able to get evaluated data
+ * from the depsgraph. We do this here instead of SCULPT_mode_poll
+ * to avoid falling through to the translate operator in the
+ * global view3d keymap.
+ *
+ * Note: BKE_object_is_visible_in_viewport is not working here (it returns false
+ * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly.
+ */
+
+ if (ob->visibility_flag & OB_HIDE_VIEWPORT) {
+ return OPERATOR_CANCELLED;
+ }
sculpt_brush_stroke_init(C, op);
- Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) &&
+ if (SCULPT_tool_is_paint(brush->sculpt_tool) &&
!SCULPT_handles_colors_report(ob->sculpt, op->reports)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 9d231f2ccd2..bf3e8a24fdf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -611,13 +611,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
-static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph)
+static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph)
{
ListBase *cache = NULL;
DEG_OBJECT_ITER_BEGIN (depsgraph,
ob,
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
DEG_ITER_OBJECT_FLAG_DUPLI) {
+ if (STREQ(object->id.name, ob->id.name)) {
+ continue;
+ }
+
CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Collision);
if (!cmd) {
@@ -1029,13 +1033,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss,
MEM_SAFE_FREE(nodes);
}
-SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
+SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob,
const float cloth_mass,
const float cloth_damping,
const float cloth_softbody_strength,
const bool use_collisions,
const bool needs_deform_coords)
{
+ SculptSession *ss = ob->sculpt;
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@@ -1073,7 +1078,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->softbody_strength = cloth_softbody_strength;
if (use_collisions) {
- cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
+ cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph);
}
cloth_sim_initialize_default_node_state(ss, cloth_sim);
@@ -1184,7 +1189,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
brush->cloth_mass,
brush->cloth_damping,
brush->cloth_constraint_softbody_strength,
@@ -1571,7 +1576,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
cloth_mass,
cloth_damping,
0.0f,
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index a5d9f5306e2..a9186010a9f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata,
const int mode = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 2c4f2f7370a..179e07cc7fa 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -845,7 +845,10 @@ void SCULPT_tag_update_overlays(bContext *C);
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise.
*/
-bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]);
+bool SCULPT_stroke_get_location(struct bContext *C,
+ float out[3],
+ const float mouse[2],
+ bool force_original);
/**
* Gets the normal, location and active vertex location of the geometry under the cursor. This also
* updates the active vertex and cursor related data of the SculptSession using the mouse position
@@ -1341,7 +1344,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim);
/* Public functions. */
-struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss,
+struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob,
float cloth_mass,
float cloth_damping,
float cloth_softbody_strength,
@@ -1805,7 +1808,10 @@ void SCULPT_OT_brush_stroke(struct wmOperatorType *ot);
/* end sculpt_ops.c */
-#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)
+BLI_INLINE bool SCULPT_tool_is_paint(int tool)
+{
+ return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
+}
#ifdef __cplusplus
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index 2ac0dbdb9d4..7e813590e21 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -427,7 +427,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
madd_v3_v3fl(current_disp, no, -dot_v3v3(current_disp, no));
normalize_v3_v3(current_disp_norm, current_disp);
- mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+ mul_v3_v3fl(current_disp, current_disp_norm, bstrength);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float totw = 0.0f;
@@ -457,12 +457,12 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
/* Weight by how close we are to our target distance from vd.co. */
- float w = (1.0f + fabsf(len_v3(vertex_disp) / ss->cache->bstrength - 1.0f));
+ float w = (1.0f + fabsf(len_v3(vertex_disp) / bstrength - 1.0f));
/* TODO: use cotangents (or at least face areas) here. */
float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco);
if (len > 0.0f) {
- len = ss->cache->bstrength / len;
+ len = bstrength / len;
}
else { /* Coincident point. */
len = 1.0f;
@@ -532,7 +532,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
- if (!SCULPT_has_colors(ss)) {
+ if (!SCULPT_has_colors(ss) || ss->cache->bstrength == 0.0f) {
return;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
index 975a8f21aaf..f51a603ee5d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -383,7 +383,7 @@ static void push_undo(const NodeData &node_data,
continue;
}
int tilex, tiley, tilew, tileh;
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
undo_region_tiles(&image_buffer,
tile_undo.region.xmin,
tile_undo.region.ymin,
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 365000ab163..8856e3bf3db 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -426,7 +426,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
RNA_float_get(op->ptr, "mouse_x"),
RNA_float_get(op->ptr, "mouse_y"),
};
- if (SCULPT_stroke_get_location(C, stroke_location, mval)) {
+ if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) {
copy_v3_v3(ss->pivot_pos, stroke_location);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index 8e1f4f4d495..dfa85e8e56d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -211,7 +211,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
}
}
- MEM_freeN(tmp_uvdata);
+ MEM_SAFE_FREE(tmp_uvdata);
}
static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
@@ -240,7 +240,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
}
- /* Original Lacplacian algorithm included removal of normal component of translation.
+ /* Original Laplacian algorithm included removal of normal component of translation.
* here it is not needed since we translate along the UV plane always. */
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co);
@@ -283,7 +283,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
}
}
- MEM_freeN(tmp_uvdata);
+ MEM_SAFE_FREE(tmp_uvdata);
}
static void uv_sculpt_stroke_apply(bContext *C,
@@ -417,20 +417,14 @@ static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op)
if (data->elementMap) {
BM_uv_element_map_free(data->elementMap);
}
- if (data->uv) {
- MEM_freeN(data->uv);
- }
- if (data->uvedges) {
- MEM_freeN(data->uvedges);
- }
+ MEM_SAFE_FREE(data->uv);
+ MEM_SAFE_FREE(data->uvedges);
if (data->initial_stroke) {
- if (data->initial_stroke->initialSelection) {
- MEM_freeN(data->initial_stroke->initialSelection);
- }
- MEM_freeN(data->initial_stroke);
+ MEM_SAFE_FREE(data->initial_stroke->initialSelection);
+ MEM_SAFE_FREE(data->initial_stroke);
}
- MEM_freeN(data);
+ MEM_SAFE_FREE(data);
op->customdata = NULL;
}
@@ -489,7 +483,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
int island_index = 0;
- /* Holds, for each UvElement in elementMap, a pointer to its unique UV. */
+ /* Holds, for each UvElement in elementMap, an index of its unique UV. */
int *uniqueUv;
data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ?
UV_SCULPT_TOOL_RELAX :
@@ -500,20 +494,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
if (do_island_optimization) {
/* We will need island information */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true);
- }
- else {
- data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true);
- }
+ data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true);
}
else {
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false);
- }
- else {
- data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false);
- }
+ data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false);
}
if (!data->elementMap) {
@@ -550,12 +534,8 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
/* we have at most totalUVs edges */
edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges");
if (!data->uv || !uniqueUv || !edgeHash || !edges) {
- if (edges) {
- MEM_freeN(edges);
- }
- if (uniqueUv) {
- MEM_freeN(uniqueUv);
- }
+ MEM_SAFE_FREE(edges);
+ MEM_SAFE_FREE(uniqueUv);
if (edgeHash) {
BLI_ghash_free(edgeHash, NULL, NULL);
}
@@ -635,14 +615,14 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
}
}
- MEM_freeN(uniqueUv);
+ MEM_SAFE_FREE(uniqueUv);
/* Allocate connectivity data, we allocate edges once */
data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
"uv_brush_edge_connectivity_data");
if (!data->uvedges) {
BLI_ghash_free(edgeHash, NULL, NULL);
- MEM_freeN(edges);
+ MEM_SAFE_FREE(edges);
uv_sculpt_stroke_exit(C, op);
return NULL;
}
@@ -656,7 +636,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
/* cleanup temporary stuff */
BLI_ghash_free(edgeHash, NULL, NULL);
- MEM_freeN(edges);
+ MEM_SAFE_FREE(edges);
/* transfer boundary edge property to UV's */
if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) {
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 2a8a2be8b65..08b795db0c3 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -340,7 +340,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
AUD_DeviceSpecs specs;
AUD_Container container;
AUD_Codec codec;
- const char *result;
+ int result;
+ char error_message[1024] = {'\0'};
sound_bake_animation_exec(C, op);
@@ -372,7 +373,9 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
codec,
bitrate,
NULL,
- NULL);
+ NULL,
+ error_message,
+ sizeof(error_message));
}
else {
result = AUD_mixdown(scene_eval->sound_scene,
@@ -385,13 +388,15 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
codec,
bitrate,
NULL,
- NULL);
+ NULL,
+ error_message,
+ sizeof(error_message));
}
BKE_sound_reset_scene_specs(scene_eval);
- if (result) {
- BKE_report(op->reports, RPT_ERROR, result);
+ if (!result) {
+ BKE_report(op->reports, RPT_ERROR, error_message);
return OPERATOR_CANCELLED;
}
#else /* WITH_AUDASPACE */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 87a271543d1..23c92cbdaa0 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -158,8 +158,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
/* get data to filter, from Action or Dopesheet */
/* XXX: what is sel doing here?!
* Commented it, was breaking things (eg. the "auto preview range" tool). */
- filter = (ANIMFILTER_DATA_VISIBLE |
- ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL */ /*| ANIMFILTER_CURVESONLY*/ |
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL */ |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -178,10 +177,12 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
/* Find gp-frame which is less than or equal to current-frame. */
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- const float framenum = (float)gpf->framenum;
- *min = min_ff(*min, framenum);
- *max = max_ff(*max, framenum);
- found = true;
+ if (!onlySel || (gpf->flag & GP_FRAME_SELECT)) {
+ const float framenum = (float)gpf->framenum;
+ *min = min_ff(*min, framenum);
+ *max = max_ff(*max, framenum);
+ found = true;
+ }
}
}
else if (ale->datatype == ALE_MASKLAY) {
@@ -498,7 +499,7 @@ static short copy_action_keys(bAnimContext *ac)
ANIM_fcurves_copybuf_free();
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -511,13 +512,13 @@ static short copy_action_keys(bAnimContext *ac)
return ok;
}
-static short paste_action_keys(bAnimContext *ac,
- const eKeyPasteOffset offset_mode,
- const eKeyMergeMode merge_mode,
- bool flip)
+static eKeyPasteError paste_action_keys(bAnimContext *ac,
+ const eKeyPasteOffset offset_mode,
+ const eKeyMergeMode merge_mode,
+ bool flip)
{
ListBase anim_data = {NULL, NULL};
- int filter, ok = 0;
+ int filter;
/* filter data
* - First time we try to filter more strictly, allowing only selected channels
@@ -525,15 +526,15 @@ static short paste_action_keys(bAnimContext *ac,
* - Second time, we loosen things up if nothing was found the first time, allowing
* users to just paste keyframes back into the original curve again T31670.
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) {
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
}
/* paste keyframes */
- ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
+ const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
/* clean up */
ANIM_animdata_freelist(&anim_data);
@@ -555,7 +556,8 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op)
/* copy keyframes */
if (ac.datatype == ANIMCONT_GPENCIL) {
if (ED_gpencil_anim_copybuf_copy(&ac) == false) {
- /* Nothing got copied - An error about this should be been logged already */
+ /* check if anything ended up in the buffer */
+ BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
return OPERATOR_CANCELLED;
}
}
@@ -565,7 +567,11 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
else {
- if (copy_action_keys(&ac)) {
+ /* Both copy function needs to be evaluated to account for mixed selection */
+ const short kf_empty = copy_action_keys(&ac);
+ const bool gpf_ok = ED_gpencil_anim_copybuf_copy(&ac);
+
+ if (kf_empty && !gpf_ok) {
BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
return OPERATOR_CANCELLED;
}
@@ -597,6 +603,8 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge");
const bool flipped = RNA_boolean_get(op->ptr, "flipped");
+ bool gpframes_inbuf = false;
+
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
@@ -608,7 +616,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
/* paste keyframes */
if (ac.datatype == ANIMCONT_GPENCIL) {
if (ED_gpencil_anim_copybuf_paste(&ac, offset_mode) == false) {
- /* An error occurred - Reports should have been fired already */
+ BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste");
return OPERATOR_CANCELLED;
}
}
@@ -620,14 +628,31 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
else {
+ /* Both paste function needs to be evaluated to account for mixed selection */
+ const eKeyPasteError kf_empty = paste_action_keys(&ac, offset_mode, merge_mode, flipped);
/* non-zero return means an error occurred while trying to paste */
- if (paste_action_keys(&ac, offset_mode, merge_mode, flipped)) {
- return OPERATOR_CANCELLED;
+ gpframes_inbuf = ED_gpencil_anim_copybuf_paste(&ac, offset_mode);
+
+ /* Only report an error if nothing was pasted, i.e. when both FCurve and GPencil failed. */
+ if (!gpframes_inbuf) {
+ switch (kf_empty) {
+ case KEYFRAME_PASTE_OK:
+ /* FCurve paste was ok, so it's all good. */
+ break;
+
+ case KEYFRAME_PASTE_NOWHERE_TO_PASTE:
+ BKE_report(op->reports, RPT_ERROR, "No selected F-Curves to paste into");
+ return OPERATOR_CANCELLED;
+
+ case KEYFRAME_PASTE_NOTHING_TO_PASTE:
+ BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste");
+ return OPERATOR_CANCELLED;
+ }
}
}
/* Grease Pencil needs extra update to refresh the added keyframes. */
- if (ac.datatype == ANIMCONT_GPENCIL) {
+ if (ac.datatype == ANIMCONT_GPENCIL || gpframes_inbuf) {
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
}
/* set notifier that keyframes have changed */
@@ -697,94 +722,86 @@ static const EnumPropertyItem prop_actkeys_insertkey_types[] = {
{0, NULL, 0, NULL, NULL},
};
-/* this function is responsible for inserting new keyframes */
-static void insert_action_keys(bAnimContext *ac, short mode)
+static void insert_gpencil_key(bAnimContext *ac,
+ bAnimListElem *ale,
+ const eGP_GetFrame_Mode add_frame_mode,
+ bGPdata **gpd_old)
{
- ListBase anim_data = {NULL, NULL};
- ListBase nla_cache = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
+ Scene *scene = ac->scene;
+ bGPdata *gpd = (bGPdata *)ale->id;
+ bGPDlayer *gpl = (bGPDlayer *)ale->data;
+ BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, add_frame_mode);
+ /* Check if the gpd changes to tag only once. */
+ if (gpd != *gpd_old) {
+ BKE_gpencil_tag(gpd);
+ *gpd_old = gpd;
+ }
+}
+
+static void insert_fcurve_key(bAnimContext *ac,
+ bAnimListElem *ale,
+ const AnimationEvalContext anim_eval_context,
+ eInsertKeyFlags flag,
+ ListBase *nla_cache)
+{
+ FCurve *fcu = (FCurve *)ale->key_data;
ReportList *reports = ac->reports;
Scene *scene = ac->scene;
ToolSettings *ts = scene->toolsettings;
- eInsertKeyFlags flag;
- /* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
- if (mode == 2) {
- filter |= ANIMFILTER_SEL;
- }
- else if (mode == 3) {
- filter |= ANIMFILTER_ACTGROUPED;
+ /* Read value from property the F-Curve represents, or from the curve only?
+ * - ale->id != NULL:
+ * Typically, this means that we have enough info to try resolving the path.
+ *
+ * - ale->owner != NULL:
+ * If this is set, then the path may not be resolvable from the ID alone,
+ * so it's easier for now to just read the F-Curve directly.
+ * (TODO: add the full-blown PointerRNA relative parsing case here...)
+ */
+ if (ale->id && !ale->owner) {
+ insert_keyframe(ac->bmain,
+ reports,
+ ale->id,
+ NULL,
+ ((fcu->grp) ? (fcu->grp->name) : (NULL)),
+ fcu->rna_path,
+ fcu->array_index,
+ &anim_eval_context,
+ ts->keyframe_type,
+ nla_cache,
+ flag);
}
+ else {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
-
- /* Init keyframing flag. */
- flag = ANIM_get_keyframing_flags(scene, true);
-
- /* insert keyframes */
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(ac->depsgraph,
- (float)CFRA);
- for (ale = anim_data.first; ale; ale = ale->next) {
- FCurve *fcu = (FCurve *)ale->key_data;
-
- /* Read value from property the F-Curve represents, or from the curve only?
- * - ale->id != NULL:
- * Typically, this means that we have enough info to try resolving the path.
- *
- * - ale->owner != NULL:
- * If this is set, then the path may not be resolvable from the ID alone,
- * so it's easier for now to just read the F-Curve directly.
- * (TODO: add the full-blown PointerRNA relative parsing case here...)
- */
- if (ale->id && !ale->owner) {
- insert_keyframe(ac->bmain,
- reports,
- ale->id,
- NULL,
- ((fcu->grp) ? (fcu->grp->name) : (NULL)),
- fcu->rna_path,
- fcu->array_index,
- &anim_eval_context,
- ts->keyframe_type,
- &nla_cache,
- flag);
- }
- else {
- AnimData *adt = ANIM_nla_mapping_get(ac, ale);
-
- /* adjust current frame for NLA-scaling */
- float cfra = anim_eval_context.eval_time;
- if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
- }
-
- const float curval = evaluate_fcurve(fcu, cfra);
- insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0);
+ /* adjust current frame for NLA-scaling */
+ float cfra = anim_eval_context.eval_time;
+ if (adt) {
+ cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
- ale->update |= ANIM_UPDATE_DEFAULT;
+ const float curval = evaluate_fcurve(fcu, cfra);
+ insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0);
}
- BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
-
- ANIM_animdata_update(ac, &anim_data);
- ANIM_animdata_freelist(&anim_data);
+ ale->update |= ANIM_UPDATE_DEFAULT;
}
-/* this function is for inserting new grease pencil frames */
-static void insert_gpencil_keys(bAnimContext *ac, short mode)
+/* this function is responsible for inserting new keyframes */
+static void insert_action_keys(bAnimContext *ac, short mode)
{
ListBase anim_data = {NULL, NULL};
+ ListBase nla_cache = {NULL, NULL};
bAnimListElem *ale;
int filter;
Scene *scene = ac->scene;
ToolSettings *ts = scene->toolsettings;
+ eInsertKeyFlags flag;
+
eGP_GetFrame_Mode add_frame_mode;
+ bGPdata *gpd_old = NULL;
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
@@ -792,30 +809,43 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode)
if (mode == 2) {
filter |= ANIMFILTER_SEL;
}
+ else if (mode == 3) {
+ filter |= ANIMFILTER_ACTGROUPED;
+ }
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- /* add a copy or a blank frame? */
+ /* Init keyframing flag. */
+ flag = ANIM_get_keyframing_flags(scene, true);
+
+ /* GPLayers specific flags */
if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
- add_frame_mode = GP_GETFRAME_ADD_COPY; /* XXX: actframe may not be what we want? */
+ add_frame_mode = GP_GETFRAME_ADD_COPY;
}
else {
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- /* Insert gp frames. */
- bGPdata *gpd_old = NULL;
+ /* insert keyframes */
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ ac->depsgraph, (float)scene->r.cfra);
for (ale = anim_data.first; ale; ale = ale->next) {
- bGPdata *gpd = (bGPdata *)ale->id;
- bGPDlayer *gpl = (bGPDlayer *)ale->data;
- BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
- /* Check if the gpd changes to tag only once. */
- if (gpd != gpd_old) {
- BKE_gpencil_tag(gpd);
- gpd_old = gpd;
+ switch (ale->type) {
+ case ANIMTYPE_GPLAYER:
+ insert_gpencil_key(ac, ale, add_frame_mode, &gpd_old);
+ break;
+
+ case ANIMTYPE_FCURVE:
+ insert_fcurve_key(ac, ale, anim_eval_context, flag, &nla_cache);
+ break;
+
+ default:
+ BLI_assert_msg(false, "Keys cannot be inserted into this animation type.");
}
}
+ BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
+
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
@@ -841,12 +871,7 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op)
mode = RNA_enum_get(op->ptr, "type");
/* insert keyframes */
- if (ac.datatype == ANIMCONT_GPENCIL) {
- insert_gpencil_keys(&ac, mode);
- }
- else {
- insert_action_keys(&ac, mode);
- }
+ insert_action_keys(&ac, mode);
/* set notifier that keyframes have changed */
if (ac.datatype == ANIMCONT_GPENCIL) {
@@ -886,14 +911,8 @@ static bool duplicate_action_keys(bAnimContext *ac)
bool changed = false;
/* filter data */
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through filtered data and delete selected keys */
@@ -968,14 +987,8 @@ static bool delete_action_keys(bAnimContext *ac)
bool changed_final = false;
/* filter data */
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through filtered data and delete selected keys */
@@ -993,7 +1006,7 @@ static bool delete_action_keys(bAnimContext *ac)
AnimData *adt = ale->adt;
/* delete selected keyframes only */
- changed = delete_fcurve_keys(fcu);
+ changed = BKE_fcurve_delete_keys_selected(fcu);
/* Only delete curve too if it won't be doing anything anymore */
if (BKE_fcurve_is_empty(fcu)) {
@@ -1063,7 +1076,7 @@ static void clean_action_keys(bAnimContext *ac, float thresh, bool clean_chan)
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ ANIMFILTER_SEL | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through filtered data and clean curves */
@@ -1139,8 +1152,8 @@ static void sample_action_keys(bAnimContext *ac)
int filter;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and add keys between selected keyframes on every frame. */
@@ -1238,7 +1251,7 @@ static void setexpo_action_keys(bAnimContext *ac, short mode)
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ ANIMFILTER_SEL | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through setting mode per F-Curve */
@@ -1352,7 +1365,8 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op)
/* set handle type */
ANIM_animdata_keyframe_callback(&ac,
(ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS),
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY),
ANIM_editkeyframes_ipo(mode));
/* set notifier that keyframe properties have changed */
@@ -1401,7 +1415,8 @@ static int actkeys_easing_exec(bContext *C, wmOperator *op)
/* set handle type */
ANIM_animdata_keyframe_callback(&ac,
(ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS),
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY),
ANIM_editkeyframes_easing(mode));
/* set notifier that keyframe properties have changed */
@@ -1444,8 +1459,8 @@ static void sethandles_action_keys(bAnimContext *ac, short mode)
KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting flags for handles
@@ -1458,7 +1473,7 @@ static void sethandles_action_keys(bAnimContext *ac, short mode)
/* any selected keyframes for editing? */
if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
/* change type of selected handles */
- ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -1527,8 +1542,8 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode)
KeyframeEditFunc set_cb = ANIM_editkeyframes_keytype(mode);
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting BezTriple interpolation
@@ -1536,32 +1551,19 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode)
* Currently that's not necessary here.
*/
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL);
-
- ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
- }
-
- ANIM_animdata_update(ac, &anim_data);
- ANIM_animdata_freelist(&anim_data);
-}
-
-/* this function is responsible for setting the keyframe type for Grease Pencil frames */
-static void setkeytype_gpencil_keys(bAnimContext *ac, short mode)
-{
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
+ switch (ale->type) {
+ case ANIMTYPE_GPLAYER:
+ ED_gpencil_layer_frames_keytype_set(ale->data, mode);
+ ale->update |= ANIM_UPDATE_DEPS;
+ break;
- /* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+ case ANIMTYPE_FCURVE:
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL);
+ ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
+ break;
- /* loop through each layer */
- for (ale = anim_data.first; ale; ale = ale->next) {
- if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_layer_frames_keytype_set(ale->data, mode);
- ale->update |= ANIM_UPDATE_DEPS;
+ default:
+ BLI_assert_msg(false, "Keytype cannot be set into this animation type.");
}
}
@@ -1590,12 +1592,7 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op)
mode = RNA_enum_get(op->ptr, "type");
/* set handle type */
- if (ac.datatype == ANIMCONT_GPENCIL) {
- setkeytype_gpencil_keys(&ac, mode);
- }
- else {
- setkeytype_action_keys(&ac, mode);
- }
+ setkeytype_action_keys(&ac, mode);
/* set notifier that keyframe properties have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
@@ -1653,19 +1650,44 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
/* init edit data */
/* loop over action data, averaging values */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
- }
- else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
+ switch (ale->datatype) {
+ case ALE_GPFRAME: {
+ bGPDlayer *gpl = ale->data;
+ bGPDframe *gpf;
+
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ /* only if selected */
+ if (!(gpf->flag & GP_FRAME_SELECT)) {
+ continue;
+ }
+ /* store average time in float 1 (only do rounding at last step) */
+ ked.f1 += gpf->framenum;
+
+ /* increment number of items */
+ ked.i1++;
+ }
+ break;
+ }
+
+ case ALE_FCURVE: {
+ AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
+ }
+ else {
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
+ }
+ break;
+ }
+
+ default:
+ BLI_assert_msg(false, "Cannot jump to keyframe into this animation type.");
}
}
@@ -1674,8 +1696,8 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
/* set the new current frame value, based on the average time */
if (ked.i1) {
Scene *scene = ac.scene;
- CFRA = round_fl_to_int(ked.f1 / ked.i1);
- SUBFRA = 0.0f;
+ scene->r.cfra = round_fl_to_int(ked.f1 / ked.i1);
+ scene->r.subframe = 0.0f;
}
/* set notifier that things have changed */
@@ -1742,8 +1764,8 @@ static void snap_action_keys(bAnimContext *ac, short mode)
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
}
else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
}
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -1768,11 +1790,11 @@ static void snap_action_keys(bAnimContext *ac, short mode)
}
else if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -1876,14 +1898,8 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
}
/* filter data */
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
- ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* mirror keyframes */
@@ -1898,11 +1914,11 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
}
else if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index aff888818e0..d1a8592ae9d 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -235,13 +235,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel)
KeyframeEditFunc test_cb, sel_cb;
/* determine type-based settings */
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
- ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
/* filter data */
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -416,7 +410,7 @@ static void box_select_elem(
break;
}
- if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+ if (ale->type == ANIMTYPE_SUMMARY) {
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype);
@@ -428,8 +422,10 @@ static void box_select_elem(
ANIM_animdata_freelist(&anim_data);
}
- ANIM_animchannel_keyframes_loop(
- &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
+ if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+ ANIM_animchannel_keyframes_loop(
+ &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
+ }
}
}
}
@@ -658,7 +654,7 @@ static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, b
break;
}
- if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+ if (ale->type == ANIMTYPE_SUMMARY) {
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype);
@@ -670,8 +666,10 @@ static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, b
ANIM_animdata_freelist(&anim_data);
}
- ANIM_animchannel_keyframes_loop(
- &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
+ if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+ ANIM_animchannel_keyframes_loop(
+ &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
+ }
}
}
}
@@ -946,28 +944,36 @@ static void markers_selectkeys_between(bAnimContext *ac)
ked.f2 = max;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* select keys in-between */
for (ale = anim_data.first; ale; ale = ale->next) {
- AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ switch (ale->type) {
+ case ANIMTYPE_GPLAYER:
+ ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD);
+ ale->update |= ANIM_UPDATE_DEPS;
+ break;
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
- }
- else if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD);
- ale->update |= ANIM_UPDATE_DEPS;
- }
- else if (ale->type == ANIMTYPE_MASKLAYER) {
- ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD);
- }
- else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ case ANIMTYPE_MASKLAYER:
+ ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD);
+ break;
+
+ case ANIMTYPE_FCURVE: {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
+ }
+ else {
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ }
+ break;
+ }
+
+ default:
+ BLI_assert_msg(false, "Keys cannot be selected into this animation type.");
}
}
@@ -1000,11 +1006,16 @@ static void columnselect_action_keys(bAnimContext *ac, short mode)
}
}
else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
+ if (ale->datatype == ALE_GPFRAME) {
+ ED_gpencil_layer_make_cfra_list(ale->data, &ked.list, 1);
+ }
+ else {
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
+ }
}
}
ANIM_animdata_freelist(&anim_data);
@@ -1015,7 +1026,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode)
ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
BLI_addtail(&ked.list, ce);
- ce->cfra = (float)CFRA;
+ ce->cfra = (float)scene->r.cfra;
break;
case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
@@ -1033,12 +1044,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode)
/* loop through all of the keys and select additional keyframes
* based on the keys found to be selected above
*/
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1144,7 +1150,7 @@ static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
}
/* loop through all of the keys and select additional keyframes based on these */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1200,7 +1206,7 @@ static void select_moreless_action_keys(bAnimContext *ac, short mode)
build_cb = ANIM_editkeyframes_buildselmap(mode);
/* loop through all of the keys and select additional keyframes based on these */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -1346,41 +1352,44 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se
if (leftright == ACTKEYS_LRSEL_LEFT) {
ked.f1 = MINAFRAMEF;
- ked.f2 = (float)(CFRA + 0.1f);
+ ked.f2 = (float)(scene->r.cfra + 0.1f);
}
else {
- ked.f1 = (float)(CFRA - 0.1f);
+ ked.f1 = (float)(scene->r.cfra - 0.1f);
ked.f2 = MAXFRAMEF;
}
/* filter data */
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
- ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* select keys */
for (ale = anim_data.first; ale; ale = ale->next) {
- AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ switch (ale->type) {
+ case ANIMTYPE_GPLAYER:
+ ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
+ ale->update |= ANIM_UPDATE_DEPS;
+ break;
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
- ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
- }
- else if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
- ale->update |= ANIM_UPDATE_DEPS;
- }
- else if (ale->type == ANIMTYPE_MASKLAYER) {
- ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
- }
- else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ case ANIMTYPE_MASKLAYER:
+ ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
+ break;
+
+ case ANIMTYPE_FCURVE: {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
+ }
+ else {
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
+ }
+ break;
+ }
+
+ default:
+ BLI_assert_msg(false, "Keys cannot be selected into this animation type.");
}
}
@@ -1393,8 +1402,8 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se
TimeMarker *marker;
for (marker = markers->first; marker; marker = marker->next) {
- if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < CFRA)) ||
- ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= CFRA))) {
+ if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < scene->r.cfra)) ||
+ ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= scene->r.cfra))) {
marker->flag |= SELECT;
}
else {
@@ -1464,7 +1473,7 @@ static int actkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wm
/* determine which side of the current frame mouse is on */
x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
- if (x < CFRA) {
+ if (x < scene->r.cfra) {
RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_LEFT);
}
else {
@@ -1539,29 +1548,29 @@ static void actkeys_mselect_single(bAnimContext *ac,
ED_mask_select_frame(ale->data, selx, select_mode);
}
else {
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
- (ale->datatype == ALE_ALL)) {
+ if (ale->type == ANIMTYPE_SUMMARY && ale->datatype == ALE_ALL) {
ListBase anim_data = {NULL, NULL};
int filter;
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- for (ale = anim_data.first; ale; ale = ale->next) {
- if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_select_frame(ale->data, selx, select_mode);
- ale->update |= ANIM_UPDATE_DEPS;
+ /* Loop over all keys that are represented by this summary key. */
+ LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) {
+ if (ale2->type == ANIMTYPE_GPLAYER) {
+ ED_gpencil_select_frame(ale2->data, selx, select_mode);
+ ale2->update |= ANIM_UPDATE_DEPS;
}
- else if (ale->type == ANIMTYPE_MASKLAYER) {
- ED_mask_select_frame(ale->data, selx, select_mode);
+ else if (ale2->type == ANIMTYPE_MASKLAYER) {
+ ED_mask_select_frame(ale2->data, selx, select_mode);
}
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
- else {
+
+ if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
}
}
@@ -1588,35 +1597,29 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se
/* loop through all of the keys and select additional keyframes
* based on the keys found to be selected above
*/
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
- ANIMFILTER_NODUPLIS);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- AnimData *adt = ANIM_nla_mapping_get(ac, ale);
-
- /* set frame for validation callback to refer to */
- if (adt) {
- ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
- }
- else {
- ked.f1 = selx;
- }
-
/* select elements with frame number matching cfra */
if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_select_frame(ale->key_data, selx, select_mode);
+ ED_gpencil_select_frame(ale->data, selx, select_mode);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
- ED_mask_select_frame(ale->key_data, selx, select_mode);
+ ED_mask_select_frame(ale->data, selx, select_mode);
}
else {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+
+ /* set frame for validation callback to refer to */
+ if (adt) {
+ ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
+ }
+ else {
+ ked.f1 = selx;
+ }
+
ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
}
}
@@ -1645,29 +1648,28 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s
ED_mask_select_frames(ale->data, select_mode);
}
else {
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
- (ale->datatype == ALE_ALL)) {
+ if (ale->type == ANIMTYPE_SUMMARY && ale->datatype == ALE_ALL) {
ListBase anim_data = {NULL, NULL};
int filter;
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- for (ale = anim_data.first; ale; ale = ale->next) {
- if (ale->type == ANIMTYPE_GPLAYER) {
- ED_gpencil_select_frames(ale->data, select_mode);
- ale->update |= ANIM_UPDATE_DEPS;
+ LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) {
+ if (ale2->type == ANIMTYPE_GPLAYER) {
+ ED_gpencil_select_frames(ale2->data, select_mode);
+ ale2->update |= ANIM_UPDATE_DEPS;
}
- else if (ale->type == ANIMTYPE_MASKLAYER) {
- ED_mask_select_frames(ale->data, select_mode);
+ else if (ale2->type == ANIMTYPE_MASKLAYER) {
+ ED_mask_select_frames(ale2->data, select_mode);
}
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
- else {
+
+ if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
ANIM_animchannel_keyframes_loop(NULL, ac->ads, ale, NULL, select_cb, NULL);
}
}
@@ -1734,6 +1736,12 @@ static int mouse_action_keys(bAnimContext *ac,
fcu->flag |= FCURVE_SELECTED;
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
}
+ else if (ale->type == ANIMTYPE_GPLAYER) {
+ bGPdata *gpd = (bGPdata *)ale->id;
+ bGPDlayer *gpl = ale->data;
+
+ ED_gpencil_set_active_channel(gpd, gpl);
+ }
}
}
else if (ac->datatype == ANIMCONT_GPENCIL) {
@@ -1745,13 +1753,7 @@ static int mouse_action_keys(bAnimContext *ac,
bGPdata *gpd = (bGPdata *)ale->id;
bGPDlayer *gpl = ale->data;
- gpl->flag |= GP_LAYER_SELECT;
- /* Update other layer status. */
- if (BKE_gpencil_layer_active_get(gpd) != gpl) {
- BKE_gpencil_layer_active_set(gpd, gpl);
- BKE_gpencil_layer_autolock_set(gpd, false);
- WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- }
+ ED_gpencil_set_active_channel(gpd, gpl);
}
}
else if (ac->datatype == ANIMCONT_MASK) {
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 09163842587..3e507f73d1a 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -98,9 +98,9 @@ static SpaceLink *action_create(const ScrArea *area, const Scene *scene)
BLI_addtail(&saction->regionbase, region);
region->regiontype = RGN_TYPE_WINDOW;
- region->v2d.tot.xmin = (float)(SFRA - 10);
+ region->v2d.tot.xmin = (float)(scene->r.sfra - 10);
region->v2d.tot.ymin = (float)(-area->winy) / 3.0f;
- region->v2d.tot.xmax = (float)(EFRA + 10);
+ region->v2d.tot.xmax = (float)(scene->r.efra + 10);
region->v2d.tot.ymax = 0.0f;
region->v2d.cur = region->v2d.tot;
@@ -506,8 +506,8 @@ static void action_listener(const wmSpaceTypeListenerParams *params)
/* context changes */
switch (wmn->category) {
case NC_GPENCIL:
- /* only handle these events in GPencil mode for performance considerations */
- if (saction->mode == SACTCONT_GPENCIL) {
+ /* only handle these events for containers in which GPencil frames are displayed */
+ if (ELEM(saction->mode, SACTCONT_GPENCIL, SACTCONT_DOPESHEET, SACTCONT_TIMELINE)) {
if (wmn->action == NA_EDITED) {
ED_area_tag_redraw(area);
}
@@ -561,8 +561,8 @@ static void action_listener(const wmSpaceTypeListenerParams *params)
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
Scene *scene = wmn->reference;
- region->v2d.tot.xmin = (float)(SFRA - 4);
- region->v2d.tot.xmax = (float)(EFRA + 4);
+ region->v2d.tot.xmin = (float)(scene->r.sfra - 4);
+ region->v2d.tot.xmax = (float)(scene->r.efra + 4);
break;
}
}
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index d53fe2efb03..3d964a95bc0 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -163,6 +163,7 @@ void ED_spacemacros_init(void)
ED_operatormacros_sequencer();
ED_operatormacros_paint();
ED_operatormacros_gpencil();
+ ED_operatormacros_nla();
/* Register dropboxes (can use macros). */
ED_dropboxes_ui();
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index e2f1df74446..7d4f38b1841 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -35,7 +35,6 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 5780b0c9df7..c3479409f0d 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -258,11 +258,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) {
return true;
}
-#ifdef WITH_NEW_CURVES_TYPE
if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) {
return true;
}
-#endif
#ifdef WITH_POINT_CLOUD
if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) {
return true;
@@ -830,9 +828,7 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
-#ifdef WITH_NEW_CURVES_TYPE
"curves",
-#endif
#ifdef WITH_POINT_CLOUD
"pointcloud",
#endif
@@ -926,12 +922,10 @@ int /*eContextResult*/ buttons_context(const bContext *C,
set_pointer_type(path, result, &RNA_LightProbe);
return CTX_RESULT_OK;
}
-#ifdef WITH_NEW_CURVES_TYPE
if (CTX_data_equals(member, "curves")) {
set_pointer_type(path, result, &RNA_Curves);
return CTX_RESULT_OK;
}
-#endif
#ifdef WITH_POINT_CLOUD
if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 052af39319c..e60946b8f66 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -778,6 +778,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
sbuts->preview = 1;
}
break;
+ case NC_WORKSPACE:
+ buttons_area_redraw(area, BCONTEXT_TOOL);
+ break;
case NC_SPACE:
if (wmn->data == ND_SPACE_PROPERTIES) {
ED_area_tag_redraw(area);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 7800ce797aa..78174160eb8 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -131,7 +131,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
{
float x;
int *points, totseg, i, a;
- float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1);
+ float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1);
MovieTracking *tracking = &clip->tracking;
MovieTrackingObject *act_object = BKE_tracking_object_get_active(tracking);
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(&clip->tracking);
@@ -245,14 +245,16 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
/* solver keyframes */
immUniformColor4ub(175, 255, 0, 255);
- draw_keyframe(act_object->keyframe1 + clip->start_frame - 1, CFRA, sfra, framelen, 2, pos);
- draw_keyframe(act_object->keyframe2 + clip->start_frame - 1, CFRA, sfra, framelen, 2, pos);
+ draw_keyframe(
+ act_object->keyframe1 + clip->start_frame - 1, scene->r.cfra, sfra, framelen, 2, pos);
+ draw_keyframe(
+ act_object->keyframe2 + clip->start_frame - 1, scene->r.cfra, sfra, framelen, 2, pos);
immUnbindProgram();
/* movie clip animation */
if ((sc->mode == SC_MODE_MASKEDIT) && sc->mask_info.mask) {
- ED_mask_draw_frames(sc->mask_info.mask, region, CFRA, sfra, efra);
+ ED_mask_draw_frames(sc->mask_info.mask, region, scene->r.cfra, sfra, efra);
}
}
@@ -1175,17 +1177,9 @@ static void draw_plane_marker_image(Scene *scene,
ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
if (ibuf) {
- uchar *display_buffer;
void *cache_handle;
-
- if (image->flag & IMA_VIEW_AS_RENDER) {
- display_buffer = IMB_display_buffer_acquire(
- ibuf, &scene->view_settings, &scene->display_settings, &cache_handle);
- }
- else {
- display_buffer = IMB_display_buffer_acquire(
- ibuf, NULL, &scene->display_settings, &cache_handle);
- }
+ uchar *display_buffer = IMB_display_buffer_acquire(
+ ibuf, &scene->view_settings, &scene->display_settings, &cache_handle);
if (display_buffer) {
float frame_corners[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index cf7c3b51ae3..9a690f36aab 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -1046,7 +1046,7 @@ static int prefetch_get_start_frame(const bContext *C)
{
Scene *scene = CTX_data_scene(C);
- return SFRA;
+ return scene->r.sfra;
}
static int prefetch_get_final_frame(const bContext *C)
@@ -1057,10 +1057,10 @@ static int prefetch_get_final_frame(const bContext *C)
int end_frame;
/* check whether all the frames from prefetch range are cached */
- end_frame = EFRA;
+ end_frame = scene->r.efra;
if (clip->len) {
- end_frame = min_ii(end_frame, SFRA + clip->len - 1);
+ end_frame = min_ii(end_frame, scene->r.sfra + clip->len - 1);
}
return end_frame;
diff --git a/source/blender/editors/space_clip/clip_graph_ops.c b/source/blender/editors/space_clip/clip_graph_ops.c
index d07cf09ffa6..67565a88bab 100644
--- a/source/blender/editors/space_clip/clip_graph_ops.c
+++ b/source/blender/editors/space_clip/clip_graph_ops.c
@@ -629,8 +629,8 @@ static int view_all_exec(bContext *C, wmOperator *UNUSED(op))
NULL);
/* set extents of view to start/end frames */
- v2d->cur.xmin = (float)SFRA;
- v2d->cur.xmax = (float)EFRA;
+ v2d->cur.xmin = (float)scene->r.sfra;
+ v2d->cur.xmax = (float)scene->r.efra;
if (userdata.min < userdata.max) {
v2d->cur.ymin = userdata.min;
@@ -675,8 +675,8 @@ void ED_clip_graph_center_current_frame(Scene *scene, ARegion *region)
float extra = BLI_rctf_size_x(&v2d->cur) / 2.0f;
/* set extents of view to start/end frames */
- v2d->cur.xmin = (float)CFRA - extra;
- v2d->cur.xmax = (float)CFRA + extra;
+ v2d->cur.xmin = (float)scene->r.cfra - extra;
+ v2d->cur.xmax = (float)scene->r.cfra + extra;
}
static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h
index 7f9cf61b748..2efd6b6b473 100644
--- a/source/blender/editors/space_clip/clip_intern.h
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -251,6 +251,9 @@ void CLIP_OT_slide_plane_marker(struct wmOperatorType *ot);
void CLIP_OT_keyframe_insert(struct wmOperatorType *ot);
void CLIP_OT_keyframe_delete(struct wmOperatorType *ot);
+void CLIP_OT_new_image_from_plane_marker(struct wmOperatorType *ot);
+void CLIP_OT_update_image_from_plane_marker(struct wmOperatorType *ot);
+
/* tracking_select.c */
void CLIP_OT_select(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index f5bf850791a..f276c2acd1a 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1061,9 +1061,9 @@ static void change_frame_apply(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
/* set the new frame number */
- CFRA = RNA_int_get(op->ptr, "frame");
- FRAMENUMBER_MIN_CLAMP(CFRA);
- SUBFRA = 0.0f;
+ scene->r.cfra = RNA_int_get(op->ptr, "frame");
+ FRAMENUMBER_MIN_CLAMP(scene->r.cfra);
+ scene->r.subframe = 0.0f;
/* do updates */
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
@@ -1084,7 +1084,7 @@ static int frame_from_event(bContext *C, const wmEvent *event)
int framenr = 0;
if (region->regiontype == RGN_TYPE_WINDOW) {
- float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1);
+ float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1);
framenr = sfra + event->mval[0] / framelen;
}
@@ -1399,7 +1399,7 @@ static void do_sequence_proxy(void *pjv,
ProxyJob *pj = pjv;
MovieClip *clip = pj->clip;
Scene *scene = pj->scene;
- int sfra = SFRA, efra = EFRA;
+ int sfra = scene->r.sfra, efra = scene->r.efra;
ProxyThread *handles;
int tot_thread = BLI_task_scheduler_num_threads();
int width, height;
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 01f2b24c8a4..221b87a8b5a 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -617,8 +617,8 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f);
- immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax);
- immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
+ immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)scene->r.sfra, v2d->cur.ymax);
+ immRectf(pos, (float)scene->r.efra, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
GPU_blend(GPU_BLEND_NONE);
@@ -628,10 +628,10 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
GPU_line_width(1.0f);
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, (float)SFRA, v2d->cur.ymin);
- immVertex2f(pos, (float)SFRA, v2d->cur.ymax);
- immVertex2f(pos, (float)EFRA, v2d->cur.ymin);
- immVertex2f(pos, (float)EFRA, v2d->cur.ymax);
+ immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymin);
+ immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymax);
+ immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymin);
+ immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymax);
immEnd();
immUnbindProgram();
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index a73883e7624..ce6409a7784 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -515,6 +515,9 @@ static void clip_operatortypes(void)
WM_operatortype_append(CLIP_OT_keyframe_insert);
WM_operatortype_append(CLIP_OT_keyframe_delete);
+ WM_operatortype_append(CLIP_OT_new_image_from_plane_marker);
+ WM_operatortype_append(CLIP_OT_update_image_from_plane_marker);
+
/* ** clip_graph_ops.c ** */
/* graph editing */
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index ca224b04da5..cba4157d044 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -16,6 +16,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_movieclip.h"
#include "BKE_report.h"
#include "BKE_tracking.h"
@@ -33,6 +34,9 @@
#include "BLT_translation.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
#include "clip_intern.h"
#include "tracking_ops_intern.h"
@@ -1267,7 +1271,8 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
}
delta = pos == 1 ? 1 : -1;
- while (sc->user.framenr + delta >= SFRA && sc->user.framenr + delta <= EFRA) {
+ while (sc->user.framenr + delta >= scene->r.sfra &&
+ sc->user.framenr + delta <= scene->r.efra) {
int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr + delta);
MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
@@ -1286,7 +1291,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
delta = pos == 3 ? 1 : -1;
framenr += delta;
- while (framenr + delta >= SFRA && framenr + delta <= EFRA) {
+ while (framenr + delta >= scene->r.sfra && framenr + delta <= scene->r.efra) {
MovieReconstructedCamera *cam = BKE_tracking_camera_get_reconstructed(
tracking, object, framenr);
@@ -1300,8 +1305,8 @@ static int frame_jump_exec(bContext *C, wmOperator *op)
}
}
- if (CFRA != sc->user.framenr) {
- CFRA = sc->user.framenr;
+ if (scene->r.cfra != sc->user.framenr) {
+ scene->r.cfra = sc->user.framenr;
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
@@ -2212,3 +2217,144 @@ void CLIP_OT_keyframe_delete(wmOperatorType *ot)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image from plane track marker
+ * \{ */
+
+static ImBuf *sample_plane_marker_image_for_operator(bContext *C)
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ const int clip_frame_number = ED_space_clip_get_clip_frame_number(space_clip);
+
+ MovieClip *clip = ED_space_clip_get_clip(space_clip);
+
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track;
+ const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track,
+ clip_frame_number);
+
+ ImBuf *frame_ibuf = ED_space_clip_get_buffer(space_clip);
+ if (frame_ibuf == NULL) {
+ return NULL;
+ }
+
+ ImBuf *plane_ibuf = BKE_tracking_get_plane_imbuf(frame_ibuf, plane_marker);
+
+ IMB_freeImBuf(frame_ibuf);
+
+ return plane_ibuf;
+}
+
+static bool new_image_from_plane_marker_poll(bContext *C)
+{
+ if (!ED_space_clip_tracking_poll(C)) {
+ return false;
+ }
+
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(space_clip);
+ const MovieTracking *tracking = &clip->tracking;
+
+ if (tracking->act_plane_track == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+static int new_image_from_plane_marker_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(space_clip);
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track;
+
+ ImBuf *plane_ibuf = sample_plane_marker_image_for_operator(C);
+ if (plane_ibuf == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ plane_track->image = BKE_image_add_from_imbuf(CTX_data_main(C), plane_ibuf, plane_track->name);
+
+ IMB_freeImBuf(plane_ibuf);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_new_image_from_plane_marker(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Image from Plane Marker";
+ ot->description = "Create new image from the content of the plane marker";
+ ot->idname = "CLIP_OT_new_image_from_plane_marker";
+
+ /* api callbacks */
+ ot->poll = new_image_from_plane_marker_poll;
+ ot->exec = new_image_from_plane_marker_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static bool update_image_from_plane_marker_poll(bContext *C)
+{
+ if (!ED_space_clip_tracking_poll(C)) {
+ return false;
+ }
+
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(space_clip);
+ const MovieTracking *tracking = &clip->tracking;
+
+ if (tracking->act_plane_track == NULL || tracking->act_plane_track->image == NULL) {
+ return false;
+ }
+
+ const Image *image = tracking->act_plane_track->image;
+ return image->type == IMA_TYPE_IMAGE && ELEM(image->source, IMA_SRC_FILE, IMA_SRC_GENERATED);
+}
+
+static int update_image_from_plane_marker_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(space_clip);
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track;
+
+ ImBuf *plane_ibuf = sample_plane_marker_image_for_operator(C);
+ if (plane_ibuf == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_image_replace_imbuf(plane_track->image, plane_ibuf);
+
+ IMB_freeImBuf(plane_ibuf);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
+ WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, plane_track->image);
+
+ BKE_image_partial_update_mark_full_update(plane_track->image);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_update_image_from_plane_marker(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Update Image from Plane Marker";
+ ot->description =
+ "Update current image used by plane marker from the content of the plane marker";
+ ot->idname = "CLIP_OT_update_image_from_plane_marker";
+
+ /* api callbacks */
+ ot->poll = update_image_from_plane_marker_poll;
+ ot->exec = update_image_from_plane_marker_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index d5223d57490..f6fd2980c19 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -131,10 +131,10 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa
if (sequence) {
if (backwards) {
- tmj->efra = SFRA;
+ tmj->efra = scene->r.sfra;
}
else {
- tmj->efra = EFRA;
+ tmj->efra = scene->r.efra;
}
tmj->efra = BKE_movieclip_remap_scene_to_clip_frame(clip, tmj->efra);
}
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index 17fbef23eac..ef22b1b9f0b 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -413,16 +413,8 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
}
char str[BLI_UTF8_MAX + 1];
- size_t len;
-
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
- }
+ const size_t len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
}
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 87595ecdb88..1829f19bfd6 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -86,12 +86,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
bool rename(StringRefNull new_name) override;
/** Add drag support for catalog items. */
- std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
/** Add dropping support for catalog items. */
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
-class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController {
+class AssetCatalogDragController : public ui::AbstractViewItemDragController {
AssetCatalogTreeItem &catalog_item_;
public:
@@ -103,7 +103,7 @@ class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController
void on_drag_start() override;
};
-class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController {
+class AssetCatalogDropController : public ui::AbstractViewItemDropController {
AssetCatalogTreeItem &catalog_item_;
public:
@@ -142,7 +142,7 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
void build_row(uiLayout &row) override;
- struct DropController : public ui::AbstractTreeViewItemDropController {
+ struct DropController : public ui::AbstractViewItemDropController {
DropController(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
@@ -150,13 +150,13 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
bool on_drop(struct bContext *C, const wmDrag &drag) override;
};
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
- struct DropController : public ui::AbstractTreeViewItemDropController {
+ struct DropController : public ui::AbstractViewItemDropController {
DropController(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
@@ -164,7 +164,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
bool on_drop(struct bContext *C, const wmDrag &drag) override;
};
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
/* ---------------------------------------------------------------------- */
@@ -272,11 +272,11 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row)
return;
}
- uiButTreeRow *tree_row_but = tree_row_button();
+ uiButViewItem *view_item_but = view_item_button();
PointerRNA *props;
props = UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ (uiBut *)view_item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
}
@@ -305,7 +305,7 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column)
0,
&props);
RNA_string_set(&props, "catalog_id", catalog_id_str_buffer);
- uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename");
+ uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename");
/* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done
* in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on
@@ -333,14 +333,14 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
return true;
}
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogDropController>(
static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
}
-std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem::
+std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem::
create_drag_controller() const
{
return std::make_unique<AssetCatalogDragController>(
@@ -351,7 +351,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem
AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
- : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item)
+ : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item)
{
}
@@ -422,10 +422,10 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag)
{
if (drag.type == WM_DRAG_ASSET_CATALOG) {
return drop_asset_catalog_into_catalog(
- drag, tree_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
+ drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
}
return drop_assets_into_catalog(C,
- tree_view<AssetCatalogTreeView>(),
+ get_view<AssetCatalogTreeView>(),
drag,
catalog_item_.get_catalog_id(),
catalog_item_.get_simple_name());
@@ -512,14 +512,14 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag,
::AssetLibrary &AssetCatalogDropController::get_asset_library() const
{
- return *tree_view<AssetCatalogTreeView>().asset_library_;
+ return *get_view<AssetCatalogTreeView>().asset_library_;
}
/* ---------------------------------------------------------------------- */
AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
- : ui::AbstractTreeViewItemDragController(tree_view), catalog_item_(catalog_item)
+ : ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item)
{
}
@@ -538,7 +538,7 @@ void *AssetCatalogDragController::create_drag_data() const
void AssetCatalogDragController::on_drag_start()
{
- AssetCatalogTreeView &tree_view_ = tree_view<AssetCatalogTreeView>();
+ AssetCatalogTreeView &tree_view_ = get_view<AssetCatalogTreeView>();
tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id());
}
@@ -551,15 +551,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
PointerRNA *props;
UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
+ (uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
props = UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ (uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
/* No parent path to use the root level. */
RNA_string_set(props, "parent_path", nullptr);
}
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewAllItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogTreeViewAllItem::DropController>(
@@ -567,7 +567,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllI
}
AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view)
- : ui::AbstractTreeViewItemDropController(tree_view)
+ : ui::AbstractViewItemDropController(tree_view)
{
}
@@ -579,7 +579,7 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag,
}
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(
- drag, *tree_view<AssetCatalogTreeView>().asset_library_);
+ drag, *get_view<AssetCatalogTreeView>().asset_library_);
if (drag_catalog->path.parent() == "") {
*r_disabled_hint = "Catalog is already placed at the highest level";
return false;
@@ -592,7 +592,7 @@ std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDr
{
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(
- drag, *tree_view<AssetCatalogTreeView>().asset_library_);
+ drag, *get_view<AssetCatalogTreeView>().asset_library_);
return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " +
TIP_("to the top level of the tree");
@@ -604,14 +604,14 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext *UNUSE
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
return AssetCatalogDropController::drop_asset_catalog_into_catalog(
drag,
- tree_view<AssetCatalogTreeView>(),
+ get_view<AssetCatalogTreeView>(),
/* No value to drop into the root level. */
std::nullopt);
}
/* ---------------------------------------------------------------------- */
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewUnassignedItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>(
@@ -619,7 +619,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnas
}
AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view)
- : ui::AbstractTreeViewItemDropController(tree_view)
+ : ui::AbstractViewItemDropController(tree_view)
{
}
@@ -647,7 +647,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext
{
/* Assign to nil catalog ID. */
return AssetCatalogDropController::drop_assets_into_catalog(
- C, tree_view<AssetCatalogTreeView>(), drag, CatalogID{});
+ C, get_view<AssetCatalogTreeView>(), drag, CatalogID{});
}
} // namespace blender::ed::asset_browser
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 62bdd583bc1..59d9a15fbab 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1793,7 +1793,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
}
ED_file_change_dir(C);
}
- /* opening file - sends events now, so things get handled on windowqueue level */
+ /* Opening file, sends events now, so things get handled on window-queue level. */
else if (sfile->op) {
wmOperator *op = sfile->op;
char filepath[FILE_MAX];
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index bc2705df314..3b8c6cbd1d0 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -277,7 +277,7 @@ static void graphedit_activekey_update_cb(bContext *UNUSED(C),
/* make sure F-Curve and its handles are still valid after this editing */
sort_time_fcurve(fcu);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* update callback for active keyframe properties - handle-editing wrapper */
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index a946ce22139..608a1f4d73e 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -1348,7 +1348,7 @@ void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, shor
int filter;
/* build list of curves to draw */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY);
filter |= ((sel) ? (ANIMFILTER_SEL) : (ANIMFILTER_UNSEL));
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -1393,7 +1393,8 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
size_t items;
/* build list of channels to draw */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Update max-extent of channels here (taking into account scrollers):
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index cfc4fcf8dad..64a3c603e73 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -109,8 +109,8 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
eInsertKeyFlags flag = 0;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
if (mode & GRAPHKEYS_INSERTKEY_SEL) {
filter |= ANIMFILTER_SEL;
}
@@ -156,10 +156,10 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
x = sipo->cursorTime;
}
else if (adt) {
- x = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ x = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- x = (float)CFRA;
+ x = (float)scene->r.cfra;
}
/* Normalize units of cursor's value. */
@@ -178,7 +178,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
}
else {
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
- ac->depsgraph, (float)CFRA);
+ ac->depsgraph, (float)scene->r.cfra);
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
@@ -211,12 +211,12 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
/* Adjust current frame for NLA-mapping. */
- float cfra = (float)CFRA;
+ float cfra = (float)scene->r.cfra;
if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
cfra = sipo->cursorTime;
}
else if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
const float curval = evaluate_fcurve_only_curve(fcu, cfra);
@@ -457,7 +457,8 @@ static short copy_graph_keys(bAnimContext *ac)
* - First time we try to filter more strictly, allowing only selected channels
* to allow copying animation between channels.
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) {
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -472,13 +473,13 @@ static short copy_graph_keys(bAnimContext *ac)
return ok;
}
-static short paste_graph_keys(bAnimContext *ac,
- const eKeyPasteOffset offset_mode,
- const eKeyMergeMode merge_mode,
- bool flip)
+static eKeyPasteError paste_graph_keys(bAnimContext *ac,
+ const eKeyPasteOffset offset_mode,
+ const eKeyMergeMode merge_mode,
+ bool flip)
{
ListBase anim_data = {NULL, NULL};
- int filter, ok = 0;
+ int filter;
/* Filter data
* - First time we try to filter more strictly, allowing only selected channels
@@ -486,15 +487,15 @@ static short paste_graph_keys(bAnimContext *ac,
* - Second time, we loosen things up if nothing was found the first time, allowing
* users to just paste keyframes back into the original curve again T31670.
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) {
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
}
/* Paste keyframes. */
- ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
+ const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
/* Clean up. */
ANIM_animdata_freelist(&anim_data);
@@ -554,9 +555,18 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op)
/* Ac.reports by default will be the global reports list, which won't show warnings. */
ac.reports = op->reports;
- /* Paste keyframes - non-zero return means an error occurred while trying to paste. */
- if (paste_graph_keys(&ac, offset_mode, merge_mode, flipped)) {
- return OPERATOR_CANCELLED;
+ const eKeyPasteError kf_empty = paste_graph_keys(&ac, offset_mode, merge_mode, flipped);
+ switch (kf_empty) {
+ case KEYFRAME_PASTE_OK:
+ break;
+
+ case KEYFRAME_PASTE_NOWHERE_TO_PASTE:
+ BKE_report(op->reports, RPT_ERROR, "No selected F-Curves to paste into");
+ return OPERATOR_CANCELLED;
+
+ case KEYFRAME_PASTE_NOTHING_TO_PASTE:
+ BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste");
+ return OPERATOR_CANCELLED;
}
/* Set notifier that keyframes have changed. */
@@ -631,8 +641,8 @@ static bool duplicate_graph_keys(bAnimContext *ac)
bool changed = false;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and delete selected keys. */
@@ -702,8 +712,8 @@ static bool delete_graph_keys(bAnimContext *ac)
bool changed_final = false;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and delete selected keys. */
@@ -713,7 +723,7 @@ static bool delete_graph_keys(bAnimContext *ac)
bool changed;
/* Delete selected keyframes only. */
- changed = delete_fcurve_keys(fcu);
+ changed = BKE_fcurve_delete_keys_selected(fcu);
if (changed) {
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -785,8 +795,8 @@ static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan)
int filter;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and clean curves. */
@@ -862,8 +872,8 @@ static void bake_graph_curves(bAnimContext *ac, int start, int end)
int filter;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and add keys between selected keyframes on every frame. */
@@ -949,8 +959,8 @@ static void unbake_graph_curves(bAnimContext *ac, int start, int end)
bAnimListElem *ale;
/* Filter data. */
- const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and add keys between selected keyframes on every frame. */
@@ -1096,12 +1106,12 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op)
}
/* Determine extents of the baking. */
- sbi.cfra = start = CFRA;
- end = CFRA + sbi.length - 1;
+ sbi.cfra = start = scene->r.cfra;
+ end = scene->r.cfra + sbi.length - 1;
/* Filter anim channels. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Loop through all selected F-Curves, replacing its data with the sound samples. */
@@ -1267,8 +1277,8 @@ static void sample_graph_keys(bAnimContext *ac)
int filter;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and add keys between selected keyframes on every frame. */
@@ -1364,8 +1374,8 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode)
int filter;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting mode per F-Curve. */
@@ -1469,8 +1479,8 @@ static void setipo_graph_keys(bAnimContext *ac, short mode)
KeyframeEditFunc set_cb = ANIM_editkeyframes_ipo(mode);
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting BezTriple interpolation
@@ -1478,7 +1488,7 @@ static void setipo_graph_keys(bAnimContext *ac, short mode)
* Currently that's not necessary here.
*/
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
}
@@ -1547,8 +1557,8 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode)
KeyframeEditFunc set_cb = ANIM_editkeyframes_easing(mode);
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting BezTriple easing.
@@ -1556,7 +1566,7 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode)
* Currently that's not necessary here.
*/
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
}
@@ -1625,8 +1635,8 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode)
KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through setting flags for handles.
@@ -1639,7 +1649,7 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode)
/* Any selected keyframes for editing? */
if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
/* Change type of selected handles. */
- ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -1945,7 +1955,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
/* Step 1: extract only the rotation f-curves. */
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ ANIMFILTER_FCURVESONLY | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -2056,7 +2066,8 @@ static KeyframeEditData sum_selected_keyframes(bAnimContext *ac)
memset(&ked, 0, sizeof(KeyframeEditData));
/* Loop over action data, averaging values. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -2119,8 +2130,8 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
}
else {
/* Animation Mode - Affects current frame (int) */
- CFRA = round_fl_to_int(sum_time / num_keyframes);
- SUBFRA = 0.0f;
+ scene->r.cfra = round_fl_to_int(sum_time / num_keyframes);
+ scene->r.subframe = 0.0f;
}
sipo->cursorVal = sum_value / (float)num_keyframes;
@@ -2240,8 +2251,8 @@ static void snap_graph_keys(bAnimContext *ac, short mode)
float cursor_value = 0.0f;
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Init custom data for iterating over keyframes. */
@@ -2284,11 +2295,11 @@ static void snap_graph_keys(bAnimContext *ac, short mode)
/* Perform snapping. */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -2361,8 +2372,8 @@ static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = {
static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
{
/* Filter data. */
- const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -2523,8 +2534,8 @@ static void mirror_graph_keys(bAnimContext *ac, short mode)
edit_cb = ANIM_editkeyframes_mirror(mode);
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Mirror keyframes. */
@@ -2544,11 +2555,11 @@ static void mirror_graph_keys(bAnimContext *ac, short mode)
/* Perform actual mirroring. */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -2620,8 +2631,8 @@ static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op))
}
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Smooth keyframes. */
@@ -2720,7 +2731,8 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
type = RNA_enum_get(op->ptr, "type");
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY);
if (RNA_boolean_get(op->ptr, "only_active")) {
/* FIXME: enforce in this case only a single channel to get handled? */
filter |= ANIMFILTER_ACTIVE;
@@ -2875,14 +2887,14 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
/* Filter data. */
if (RNA_boolean_get(op->ptr, "only_active")) {
/* This should be the default (for buttons) - Just paste to the active FCurve. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_ACTIVE |
+ ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
}
else {
/* This is only if the operator gets called from a hotkey or search -
* Paste to all visible curves. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
}
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -3065,7 +3077,8 @@ static int graph_driver_delete_invalid_exec(bContext *C, wmOperator *op)
/* NOTE: We might need a scene update to evaluate the driver flags. */
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Find invalid drivers. */
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index 128925d4591..8deea21318c 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -72,21 +72,21 @@ static void graphview_cursor_apply(bContext *C, wmOperator *op)
* NOTE: sync this part of the code with ANIM_OT_change_frame
*/
/* 1) frame is rounded to the nearest int, since frames are ints */
- CFRA = round_fl_to_int(frame);
+ scene->r.cfra = round_fl_to_int(frame);
if (scene->r.flag & SCER_LOCK_FRAME_SELECTION) {
/* Clip to preview range
* NOTE: Preview range won't go into negative values,
* so only clamping once should be fine.
*/
- CLAMP(CFRA, PSFRA, PEFRA);
+ CLAMP(scene->r.cfra, PSFRA, PEFRA);
}
else {
/* Prevent negative frames */
- FRAMENUMBER_MIN_CLAMP(CFRA);
+ FRAMENUMBER_MIN_CLAMP(scene->r.cfra);
}
- SUBFRA = 0.0f;
+ scene->r.subframe = 0.0f;
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
}
@@ -234,14 +234,16 @@ static int graphview_curves_hide_exec(bContext *C, wmOperator *op)
/* get list of all channels that selection may need to be flushed to
* - hierarchy must not affect what we have access to here...
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
/* filter data
* - of the remaining visible curves, we want to hide the ones that are
* selected/unselected (depending on "unselected" prop)
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE |
+ ANIMFILTER_NODUPLIS);
if (unselected) {
filter |= ANIMFILTER_UNSEL;
}
@@ -275,7 +277,8 @@ static int graphview_curves_hide_exec(bContext *C, wmOperator *op)
/* unhide selected */
if (unselected) {
/* turn off requirement for visible */
- filter = ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_LIST_CHANNELS;
+ filter = ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY;
/* flushing has been done */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -344,13 +347,15 @@ static int graphview_curves_reveal_exec(bContext *C, wmOperator *op)
/* get list of all channels that selection may need to be flushed to
* - hierarchy must not affect what we have access to here...
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
/* filter data
* - just go through all visible channels, ensuring that everything is set to be curve-visible
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index e71c5114b0a..a36bd5c1461 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -172,7 +172,8 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L
* - if the option to only show keyframes that belong to selected F-Curves is enabled,
* include the 'only selected' flag...
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
if (sipo->flag &
SIPO_SELCUVERTSONLY) { /* FIXME: this should really be check for by the filtering code... */
filter |= ANIMFILTER_SEL;
@@ -342,7 +343,8 @@ void deselect_graph_keys(bAnimContext *ac, bool test, short sel, bool do_channel
KeyframeEditFunc test_cb, sel_cb;
/* determine type-based settings */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
/* filter data */
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -498,7 +500,8 @@ static rctf initialize_box_select_coords(const bAnimContext *ac, const rctf *rec
static int initialize_animdata_selection_filter(const SpaceGraph *sipo)
{
- int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
if (sipo->flag & SIPO_SELCUVERTSONLY) {
filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT;
}
@@ -1150,7 +1153,8 @@ static void markers_selectkeys_between(bAnimContext *ac)
ked.f2 = max;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* select keys in-between */
@@ -1189,7 +1193,8 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode)
/* build list of columns */
switch (mode) {
case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1204,7 +1209,7 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode)
ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
BLI_addtail(&ked.list, ce);
- ce->cfra = (float)CFRA;
+ ce->cfra = (float)scene->r.cfra;
break;
case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
@@ -1222,7 +1227,8 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode)
/* loop through all of the keys and select additional keyframes
* based on the keys found to be selected above
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1314,7 +1320,8 @@ static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
}
/* loop through all of the keys and select additional keyframes based on these */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1372,7 +1379,8 @@ static void select_moreless_graph_keys(bAnimContext *ac, short mode)
memset(&ked, 0, sizeof(KeyframeEditData));
/* loop through all of the keys and select additional keyframes based on these */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1513,15 +1521,15 @@ static void graphkeys_select_leftright(bAnimContext *ac, short leftright, short
if (leftright == GRAPHKEYS_LRSEL_LEFT) {
ked.f1 = MINAFRAMEF;
- ked.f2 = (float)(CFRA + 0.1f);
+ ked.f2 = (float)(scene->r.cfra + 0.1f);
}
else {
- ked.f1 = (float)(CFRA - 0.1f);
+ ked.f1 = (float)(scene->r.cfra - 0.1f);
ked.f2 = MAXFRAMEF;
}
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* select keys */
@@ -1597,7 +1605,7 @@ static int graphkeys_select_leftright_invoke(bContext *C, wmOperator *op, const
/* determine which side of the current frame mouse is on */
x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
- if (x < CFRA) {
+ if (x < scene->r.cfra) {
RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_LEFT);
}
else {
@@ -1797,7 +1805,8 @@ static int mouse_graph_keys(bAnimContext *ac,
* otherwise the active flag won't be set T26452. */
if (!run_modal && (nvi->fcu->flag & FCURVE_SELECTED) && something_was_selected) {
/* NOTE: Sync the filter flags with findnearest_fcurve_vert. */
- int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype);
}
@@ -1873,7 +1882,8 @@ static int graphkeys_mselect_column(bAnimContext *ac,
/* loop through all of the keys and select additional keyframes
* based on the keys found to be selected above
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c
index 313f6ca1561..f3d92911155 100644
--- a/source/blender/editors/space_graph/graph_slider_ops.c
+++ b/source/blender/editors/space_graph/graph_slider_ops.c
@@ -48,8 +48,8 @@
/* Used to obtain a list of animation channels for the operators to work on. */
#define OPERATOR_DATA_FILTER \
- (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \
- ANIMFILTER_NODUPLIS)
+ (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | \
+ ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS)
/* This data type is only used for modal operation. */
typedef struct tGraphSliderOp {
diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c
index d8baa4c643d..82067661d57 100644
--- a/source/blender/editors/space_graph/graph_utils.c
+++ b/source/blender/editors/space_graph/graph_utils.c
@@ -81,7 +81,8 @@ void ED_drivers_editor_init(bContext *C, ScrArea *area)
bAnimListElem *get_active_fcurve_channel(bAnimContext *ac)
{
ListBase anim_data = {NULL, NULL};
- int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE);
+ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE |
+ ANIMFILTER_FCURVESONLY);
size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* We take the first F-Curve only, since some other ones may have had 'active' flag set
@@ -131,7 +132,7 @@ bool graphop_visible_keyframes_poll(bContext *C)
/* loop over the visible (selection doesn't matter) F-Curves, and see if they're suitable
* stopping on the first successful match
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (items == 0) {
return found;
@@ -183,7 +184,8 @@ bool graphop_editable_keyframes_poll(bContext *C)
/* loop over the editable F-Curves, and see if they're suitable
* stopping on the first successful match
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
+ ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (items == 0) {
CTX_wm_operator_poll_msg_set(C, "There is no animation data to operate on");
@@ -286,7 +288,8 @@ bool graphop_selected_fcurve_poll(bContext *C)
/* Get the editable + selected F-Curves, and as long as we got some, we can return.
* NOTE: curve-visible flag isn't included,
* otherwise selecting a curve via list to edit is too cumbersome. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (items == 0) {
return false;
diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c
index 18465018d35..f80c7c17c3a 100644
--- a/source/blender/editors/space_graph/graph_view.c
+++ b/source/blender/editors/space_graph/graph_view.c
@@ -56,7 +56,8 @@ void get_graph_keyframe_extents(bAnimContext *ac,
int filter;
/* Get data to filter, from Dopesheet. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_NODUPLIS);
if (sipo->flag & SIPO_SELCUVERTSONLY) {
filter |= ANIMFILTER_SEL;
}
@@ -398,8 +399,8 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end)
}
/* Filter data. */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
+ ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Loop through filtered data and add keys between selected keyframes on every frame. */
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index 39fb41245bf..c6a1a6a77b4 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -28,7 +28,7 @@ set(SRC
image_edit.c
image_ops.c
image_sequence.c
- image_undo.c
+ image_undo.cc
space_image.c
image_intern.h
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index d0c21f85472..0a774ee679c 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1218,7 +1218,7 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i
if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
/* don't use iuser->framenr directly because it may not be updated if auto-refresh is off */
Scene *scene = CTX_data_scene(C);
- const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL);
+ const int framenr = BKE_image_user_frame_get(iuser, scene->r.cfra, NULL);
char str[MAX_IMAGE_INFO_LEN];
int duration = 0;
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 048c7345b97..f6f9428213f 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -515,7 +515,8 @@ void draw_image_cache(const bContext *C, ARegion *region)
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
Image *image = ED_space_image(sima);
- float x, cfra = CFRA, sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1);
+ float x, cfra = scene->r.cfra, sfra = scene->r.sfra, efra = scene->r.efra,
+ framelen = region->winx / (efra - sfra + 1);
Mask *mask = NULL;
if (!ED_space_image_show_cache(sima)) {
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index 950acd77f6a..0de50474ab8 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -20,6 +20,7 @@
#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_scene.h"
#include "IMB_imbuf_types.h"
@@ -212,13 +213,7 @@ void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
}
else if (sima->image && sima->image->type == IMA_TYPE_R_RESULT && scene) {
/* not very important, just nice */
- *r_width = (scene->r.xsch * scene->r.size) / 100;
- *r_height = (scene->r.ysch * scene->r.size) / 100;
-
- if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) {
- *r_width *= BLI_rctf_size_x(&scene->r.border);
- *r_height *= BLI_rctf_size_y(&scene->r.border);
- }
+ BKE_render_resolution(&scene->r, true, r_width, r_height);
}
/* I know a bit weak... but preview uses not actual image size */
// XXX else if (image_preview_active(sima, r_width, r_height));
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 537132ac428..4036f859231 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -45,6 +45,7 @@
#include "BKE_main.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
+#include "BKE_scene.h"
#include "DEG_depsgraph.h"
@@ -197,10 +198,17 @@ static ImageUser *image_user_from_context(const bContext *C)
return (sima) ? &sima->iuser : NULL;
}
-static ImageUser image_user_from_active_tile(Image *ima)
+static ImageUser image_user_from_context_and_active_tile(const bContext *C, Image *ima)
{
+ /* Try to get image user from context if available, otherwise use default. */
+ ImageUser *iuser_context = image_user_from_context(C);
ImageUser iuser;
- BKE_imageuser_default(&iuser);
+ if (iuser_context) {
+ iuser = *iuser_context;
+ }
+ else {
+ BKE_imageuser_default(&iuser);
+ }
/* Use the file associated with the active tile. Otherwise use the first tile. */
if (ima && ima->source == IMA_SRC_TILED) {
@@ -233,7 +241,7 @@ static bool image_from_context_has_data_poll(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);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, ima);
return BKE_image_has_ibuf(ima, &iuser);
}
@@ -1602,7 +1610,7 @@ static int image_file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *
}
}
else if (ima->source == IMA_SRC_TILED) {
- ImageUser iuser = image_user_from_active_tile(ima);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, ima);
BKE_image_user_file_path(&iuser, ima, filepath);
}
@@ -1823,7 +1831,7 @@ static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOp
}
static bool save_image_op(
- Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts)
+ Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, const ImageSaveOptions *opts)
{
WM_cursor_wait(true);
@@ -2391,7 +2399,7 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports)
if (image_has_valid_path(ima)) {
ImageSaveOptions opts;
Scene *scene = CTX_data_scene(C);
- if (!BKE_image_save_options_init(&opts, bmain, scene, 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;
}
@@ -2698,7 +2706,7 @@ void IMAGE_OT_new(wmOperatorType *ot)
static int image_flip_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
- ImageUser iuser = image_user_from_active_tile(ima);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, 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));
@@ -2716,7 +2724,7 @@ static int image_flip_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser);
+ ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser);
if (is_paint) {
ED_imapaint_clear_partial_redraw();
@@ -2819,7 +2827,7 @@ void IMAGE_OT_flip(wmOperatorType *ot)
static int image_invert_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
- ImageUser iuser = image_user_from_active_tile(ima);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, 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));
@@ -2837,7 +2845,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser);
+ ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser);
if (is_paint) {
ED_imapaint_clear_partial_redraw();
@@ -2943,7 +2951,7 @@ 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);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, 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, &iuser, NULL);
@@ -2957,7 +2965,7 @@ 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);
- ImageUser iuser = image_user_from_active_tile(ima);
+ ImageUser iuser = image_user_from_context_and_active_tile(C, 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));
@@ -2982,7 +2990,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
RNA_property_int_set_array(op->ptr, prop, size);
}
- ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser);
+ ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
IMB_scaleImBuf(ibuf, size[0], size[1]);
@@ -3464,10 +3472,10 @@ void IMAGE_OT_cycle_render_slot(wmOperatorType *ot)
static int image_clear_render_slot_exec(bContext *C, wmOperator *UNUSED(op))
{
- SpaceImage *sima = CTX_wm_space_image(C);
Image *ima = image_from_context(C);
+ ImageUser *iuser = image_user_from_context(C);
- if (!BKE_image_clear_renderslot(ima, &sima->iuser, ima->render_slot)) {
+ if (!BKE_image_clear_renderslot(ima, iuser, ima->render_slot)) {
return OPERATOR_CANCELLED;
}
@@ -3532,10 +3540,10 @@ void IMAGE_OT_add_render_slot(wmOperatorType *ot)
static int image_remove_render_slot_exec(bContext *C, wmOperator *UNUSED(op))
{
- SpaceImage *sima = CTX_wm_space_image(C);
Image *ima = image_from_context(C);
+ ImageUser *iuser = image_user_from_context(C);
- if (!BKE_image_remove_renderslot(ima, &sima->iuser, ima->render_slot)) {
+ if (!BKE_image_remove_renderslot(ima, iuser, ima->render_slot)) {
return OPERATOR_CANCELLED;
}
@@ -3580,9 +3588,9 @@ static void change_frame_apply(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
/* set the new frame number */
- CFRA = RNA_int_get(op->ptr, "frame");
- FRAMENUMBER_MIN_CLAMP(CFRA);
- SUBFRA = 0.0f;
+ scene->r.cfra = RNA_int_get(op->ptr, "frame");
+ FRAMENUMBER_MIN_CLAMP(scene->r.cfra);
+ scene->r.subframe = 0.0f;
/* do updates */
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
@@ -3603,7 +3611,7 @@ static int frame_from_event(bContext *C, const wmEvent *event)
int framenr = 0;
if (region->regiontype == RGN_TYPE_WINDOW) {
- float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1);
+ float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1);
framenr = sfra + event->mval[0] / framelen;
}
@@ -3725,38 +3733,52 @@ static int render_border_exec(bContext *C, wmOperator *op)
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
Render *re = RE_GetSceneRender(scene);
- RenderData *rd;
- rctf border;
+ SpaceImage *sima = CTX_wm_space_image(C);
if (re == NULL) {
/* Shouldn't happen, but better be safe close to the release. */
return OPERATOR_CANCELLED;
}
- rd = RE_engine_get_render_data(re);
- if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) {
- BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render");
- return OPERATOR_CANCELLED;
- }
+ /* Get information about the previous render, or current scene if no render yet. */
+ int width, height;
+ BKE_render_resolution(&scene->r, false, &width, &height);
+ const RenderData *rd = ED_space_image_has_buffer(sima) ? RE_engine_get_render_data(re) :
+ &scene->r;
- /* get rectangle from operator */
+ /* Get rectangle from the operator. */
+ rctf border;
WM_operator_properties_border_to_rctf(op, &border);
UI_view2d_region_to_view_rctf(&region->v2d, &border, &border);
- /* actually set border */
+ /* Adjust for cropping. */
+ if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) {
+ border.xmin = rd->border.xmin + border.xmin * (rd->border.xmax - rd->border.xmin);
+ border.xmax = rd->border.xmin + border.xmax * (rd->border.xmax - rd->border.xmin);
+ border.ymin = rd->border.ymin + border.ymin * (rd->border.ymax - rd->border.ymin);
+ border.ymax = rd->border.ymin + border.ymax * (rd->border.ymax - rd->border.ymin);
+ }
+
CLAMP(border.xmin, 0.0f, 1.0f);
CLAMP(border.ymin, 0.0f, 1.0f);
CLAMP(border.xmax, 0.0f, 1.0f);
CLAMP(border.ymax, 0.0f, 1.0f);
- scene->r.border = border;
- /* drawing a border surrounding the entire camera view switches off border rendering
- * or the border covers no pixels */
+ /* Drawing a border surrounding the entire camera view switches off border rendering
+ * or the border covers no pixels. */
if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) ||
(border.xmin == border.xmax || border.ymin == border.ymax)) {
scene->r.mode &= ~R_BORDER;
}
else {
+ /* Snap border to pixel boundaries, so drawing a border within a pixel selects that pixel. */
+ border.xmin = floorf(border.xmin * width) / width;
+ border.xmax = ceilf(border.xmax * width) / width;
+ border.ymin = floorf(border.ymin * height) / height;
+ border.ymax = ceilf(border.ymax * height) / height;
+
+ /* Set border. */
+ scene->r.border = border;
scene->r.mode |= R_BORDER;
}
diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.cc
index a7a8bde1115..986265afee0 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.cc
@@ -25,6 +25,7 @@
#include "BLI_math.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_map.hh"
#include "DNA_image_types.h"
#include "DNA_object_types.h"
@@ -82,14 +83,31 @@ void ED_image_paint_tile_lock_end(void)
*
* \{ */
-static ImBuf *imbuf_alloc_temp_tile(void)
+static ImBuf *imbuf_alloc_temp_tile()
{
return IMB_allocImBuf(
ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, IB_rectfloat | IB_rect);
}
-typedef struct PaintTile {
- struct PaintTile *next, *prev;
+struct PaintTileKey {
+ int x_tile, y_tile;
+ Image *image;
+ ImBuf *ibuf;
+ /* Copied from iuser.tile in PaintTile. */
+ int iuser_tile;
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash_4(x_tile, y_tile, image, ibuf);
+ }
+ bool operator==(const PaintTileKey &other) const
+ {
+ return x_tile == other.x_tile && y_tile == other.y_tile && image == other.image &&
+ ibuf == other.ibuf && iuser_tile == other.iuser_tile;
+ }
+};
+
+struct PaintTile {
Image *image;
ImBuf *ibuf;
/* For 2D image painting the ImageUser uses most of the values.
@@ -99,14 +117,14 @@ typedef struct PaintTile {
ImageUser iuser;
union {
float *fp;
- uint *uint;
+ uint32_t *uint;
void *pt;
} rect;
- ushort *mask;
+ uint16_t *mask;
bool valid;
bool use_float;
int x_tile, y_tile;
-} PaintTile;
+};
static void ptile_free(PaintTile *ptile)
{
@@ -119,23 +137,25 @@ static void ptile_free(PaintTile *ptile)
MEM_freeN(ptile);
}
-static void ptile_free_list(ListBase *paint_tiles)
-{
- for (PaintTile *ptile = paint_tiles->first, *ptile_next; ptile; ptile = ptile_next) {
- ptile_next = ptile->next;
- ptile_free(ptile);
+struct PaintTileMap {
+ blender::Map<PaintTileKey, PaintTile *> map;
+
+ ~PaintTileMap()
+ {
+ for (PaintTile *ptile : map.values()) {
+ ptile_free(ptile);
+ }
}
- BLI_listbase_clear(paint_tiles);
-}
+};
-static void ptile_invalidate_list(ListBase *paint_tiles)
+static void ptile_invalidate_map(PaintTileMap *paint_tile_map)
{
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
+ for (PaintTile *ptile : paint_tile_map->map.values()) {
ptile->valid = false;
}
}
-void *ED_image_paint_tile_find(ListBase *paint_tiles,
+void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map,
Image *image,
ImBuf *ibuf,
ImageUser *iuser,
@@ -144,28 +164,32 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles,
ushort **r_mask,
bool validate)
{
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
- if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) {
- if (ptile->image == image && ptile->ibuf == ibuf && ptile->iuser.tile == iuser->tile) {
- if (r_mask) {
- /* allocate mask if requested. */
- if (!ptile->mask) {
- ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
- "UndoImageTile.mask");
- }
- *r_mask = ptile->mask;
- }
- if (validate) {
- ptile->valid = true;
- }
- return ptile->rect.pt;
- }
+ PaintTileKey key;
+ key.ibuf = ibuf;
+ key.image = image;
+ key.iuser_tile = iuser->tile;
+ key.x_tile = x_tile;
+ key.y_tile = y_tile;
+ PaintTile **pptile = paint_tile_map->map.lookup_ptr(key);
+ if (pptile == nullptr) {
+ return nullptr;
+ }
+ PaintTile *ptile = *pptile;
+ if (r_mask) {
+ /* allocate mask if requested. */
+ if (!ptile->mask) {
+ ptile->mask = static_cast<uint16_t *>(MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
+ "UndoImageTile.mask"));
}
+ *r_mask = ptile->mask;
+ }
+ if (validate) {
+ ptile->valid = true;
}
- return NULL;
+ return ptile->rect.pt;
}
-void *ED_image_paint_tile_push(ListBase *paint_tiles,
+void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map,
Image *image,
ImBuf *ibuf,
ImBuf **tmpibuf,
@@ -177,37 +201,43 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
bool use_thread_lock,
bool find_prev)
{
- const bool has_float = (ibuf->rect_float != NULL);
+ if (use_thread_lock) {
+ BLI_spin_lock(&paint_tiles_lock);
+ }
+ const bool has_float = (ibuf->rect_float != nullptr);
/* check if tile is already pushed */
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
if (find_prev) {
void *data = ED_image_paint_tile_find(
- paint_tiles, image, ibuf, iuser, x_tile, y_tile, r_mask, true);
+ paint_tile_map, image, ibuf, iuser, x_tile, y_tile, r_mask, true);
if (data) {
+ if (use_thread_lock) {
+ BLI_spin_unlock(&paint_tiles_lock);
+ }
return data;
}
}
- if (*tmpibuf == NULL) {
+ if (*tmpibuf == nullptr) {
*tmpibuf = imbuf_alloc_temp_tile();
}
- PaintTile *ptile = MEM_callocN(sizeof(PaintTile), "PaintTile");
+ PaintTile *ptile = static_cast<PaintTile*>(MEM_callocN(sizeof(PaintTile), "PaintTile"));
ptile->image = image;
ptile->ibuf = ibuf;
ptile->iuser = *iuser;
- ptile->iuser.scene = NULL;
+ ptile->iuser.scene = nullptr;
ptile->x_tile = x_tile;
ptile->y_tile = y_tile;
/* add mask explicitly here */
if (r_mask) {
- *r_mask = ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
- "PaintTile.mask");
+ *r_mask = ptile->mask = static_cast<uint16_t*>(MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
+ "PaintTile.mask"));
}
ptile->rect.pt = MEM_callocN((ibuf->rect_float ? sizeof(float[4]) : sizeof(char[4])) *
@@ -234,13 +264,24 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
SWAP(float *, ptile->rect.fp, (*tmpibuf)->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, (*tmpibuf)->rect);
+ SWAP(uint32_t *, ptile->rect.uint, (*tmpibuf)->rect);
}
- if (use_thread_lock) {
- BLI_spin_lock(&paint_tiles_lock);
+ PaintTileKey key = {};
+ key.ibuf = ibuf;
+ key.image = image;
+ key.iuser_tile = iuser->tile;
+ key.x_tile = x_tile;
+ key.y_tile = y_tile;
+ PaintTile *existing_tile = nullptr;
+ paint_tile_map->map.add_or_modify(
+ key,
+ [&](PaintTile **pptile) { *pptile = ptile; },
+ [&](PaintTile **pptile) { existing_tile = *pptile; });
+ if (existing_tile) {
+ ptile_free(ptile);
+ ptile = existing_tile;
}
- BLI_addtail(paint_tiles, ptile);
if (use_thread_lock) {
BLI_spin_unlock(&paint_tiles_lock);
@@ -248,20 +289,20 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
return ptile->rect.pt;
}
-static void ptile_restore_runtime_list(ListBase *paint_tiles)
+static void ptile_restore_runtime_map(PaintTileMap *paint_tile_map)
{
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
+ for (PaintTile *ptile : paint_tile_map->map.values()) {
Image *image = ptile->image;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, NULL);
- const bool has_float = (ibuf->rect_float != NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, nullptr);
+ const bool has_float = (ibuf->rect_float != nullptr);
if (has_float) {
SWAP(float *, ptile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect);
}
IMB_rectcpy(ibuf,
@@ -277,7 +318,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
SWAP(float *, ptile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect);
}
/* Force OpenGL reload (maybe partial update will operate better?) */
@@ -291,7 +332,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
- BKE_image_release_ibuf(image, ibuf, NULL);
+ BKE_image_release_ibuf(image, ibuf, nullptr);
}
IMB_freeImBuf(tmpibuf);
@@ -303,35 +344,35 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
/** \name Image Undo Tile
* \{ */
-static uint index_from_xy(uint tile_x, uint tile_y, const uint tiles_dims[2])
+static uint32_t index_from_xy(uint32_t tile_x, uint32_t tile_y, const uint32_t tiles_dims[2])
{
BLI_assert(tile_x < tiles_dims[0] && tile_y < tiles_dims[1]);
return (tile_y * tiles_dims[0]) + tile_x;
}
-typedef struct UndoImageTile {
+struct UndoImageTile {
union {
float *fp;
- uint *uint;
+ uint32_t *uint_ptr;
void *pt;
} rect;
int users;
-} UndoImageTile;
+};
static UndoImageTile *utile_alloc(bool has_float)
{
- UndoImageTile *utile = MEM_callocN(sizeof(*utile), "ImageUndoTile");
+ UndoImageTile *utile = static_cast<UndoImageTile *>(MEM_callocN(sizeof(*utile), "ImageUndoTile"));
if (has_float) {
- utile->rect.fp = MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__);
+ utile->rect.fp = static_cast<float*>(MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
}
else {
- utile->rect.uint = MEM_mallocN(sizeof(uint) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__);
+ utile->rect.uint_ptr = static_cast<uint32_t*>(MEM_mallocN(sizeof(uint32_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
}
return utile;
}
static void utile_init_from_imbuf(
- UndoImageTile *utile, const uint x, const uint y, const ImBuf *ibuf, ImBuf *tmpibuf)
+ UndoImageTile *utile, const uint32_t x, const uint32_t y, const ImBuf *ibuf, ImBuf *tmpibuf)
{
const bool has_float = ibuf->rect_float;
@@ -339,7 +380,7 @@ static void utile_init_from_imbuf(
SWAP(float *, utile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, utile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect);
}
IMB_rectcpy(tmpibuf, ibuf, 0, 0, x, y, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
@@ -348,7 +389,7 @@ static void utile_init_from_imbuf(
SWAP(float *, utile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, utile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect);
}
}
@@ -357,13 +398,13 @@ static void utile_restore(
{
const bool has_float = ibuf->rect_float;
float *prev_rect_float = tmpibuf->rect_float;
- uint *prev_rect = tmpibuf->rect;
+ uint32_t *prev_rect = tmpibuf->rect;
if (has_float) {
tmpibuf->rect_float = utile->rect.fp;
}
else {
- tmpibuf->rect = utile->rect.uint;
+ tmpibuf->rect = utile->rect.uint_ptr;
}
IMB_rectcpy(ibuf, tmpibuf, x, y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
@@ -378,7 +419,7 @@ static void utile_decref(UndoImageTile *utile)
BLI_assert(utile->users >= 0);
if (utile->users == 0) {
MEM_freeN(utile->rect.pt);
- MEM_freeN(utile);
+ MEM_delete(utile);
}
}
@@ -401,10 +442,10 @@ typedef struct UndoImageBuf {
UndoImageTile **tiles;
/** Can calculate these from dims, just for convenience. */
- uint tiles_len;
- uint tiles_dims[2];
+ uint32_t tiles_len;
+ uint32_t tiles_dims[2];
- uint image_dims[2];
+ uint32_t image_dims[2];
/** Store variables from the image. */
struct {
@@ -417,7 +458,7 @@ typedef struct UndoImageBuf {
static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
{
- UndoImageBuf *ubuf = MEM_callocN(sizeof(*ubuf), __func__);
+ UndoImageBuf *ubuf = static_cast<UndoImageBuf*>(MEM_callocN(sizeof(*ubuf), __func__));
ubuf->image_dims[0] = ibuf->x;
ubuf->image_dims[1] = ibuf->y;
@@ -426,12 +467,12 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
ubuf->tiles_dims[1] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[1]);
ubuf->tiles_len = ubuf->tiles_dims[0] * ubuf->tiles_dims[1];
- ubuf->tiles = MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__);
+ ubuf->tiles = static_cast<UndoImageTile**>(MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__));
BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name));
ubuf->image_state.gen_type = image->gen_type;
ubuf->image_state.source = image->source;
- ubuf->image_state.use_float = ibuf->rect_float != NULL;
+ ubuf->image_state.use_float = ibuf->rect_float != nullptr;
return ubuf;
}
@@ -447,7 +488,7 @@ static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf)
for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) {
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
- BLI_assert(ubuf->tiles[i] == NULL);
+ BLI_assert(ubuf->tiles[i] == nullptr);
UndoImageTile *utile = utile_alloc(has_float);
utile->users = 1;
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
@@ -467,7 +508,7 @@ static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf)
{
/* We could have both float and rect buffers,
* in this case free the float buffer if it's unused. */
- if ((ibuf->rect_float != NULL) && (ubuf->image_state.use_float == false)) {
+ if ((ibuf->rect_float != nullptr) && (ubuf->image_state.use_float == false)) {
imb_freerectfloatImBuf(ibuf);
}
@@ -533,8 +574,8 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
/* Tiles only added to second set of tiles. */
Image *image = uh->image_ref.ptr;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, NULL);
- if (UNLIKELY(ibuf == NULL)) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, nullptr);
+ if (UNLIKELY(ibuf == nullptr)) {
CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2);
continue;
}
@@ -570,7 +611,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
DEG_id_tag_update(&image->id, 0);
}
- BKE_image_release_ibuf(image, ibuf, NULL);
+ BKE_image_release_ibuf(image, ibuf, nullptr);
}
IMB_freeImBuf(tmpibuf);
@@ -604,16 +645,16 @@ static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh,
return ubuf;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
- BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == NULL);
+ BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == nullptr);
UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf);
BLI_addtail(&uh->buffers, ubuf);
- ubuf->post = NULL;
+ ubuf->post = nullptr;
return ubuf;
}
@@ -621,7 +662,7 @@ static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *
static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->name);
- if (ubuf == NULL) {
+ if (ubuf == nullptr) {
ubuf = uhandle_add_ubuf(uh, image, ibuf);
}
return ubuf;
@@ -636,7 +677,7 @@ static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles,
return uh;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image, int tile_number)
@@ -646,16 +687,16 @@ static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *imag
return uh;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageUser *iuser)
{
- BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == NULL);
- UndoImageHandle *uh = MEM_callocN(sizeof(*uh), __func__);
+ BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == nullptr);
+ UndoImageHandle *uh = static_cast<UndoImageHandle*>(MEM_callocN(sizeof(*uh), __func__));
uh->image_ref.ptr = image;
uh->iuser = *iuser;
- uh->iuser.scene = NULL;
+ uh->iuser.scene = nullptr;
BLI_addtail(undo_handles, uh);
return uh;
}
@@ -663,7 +704,7 @@ static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageU
static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, ImageUser *iuser)
{
UndoImageHandle *uh = uhandle_lookup(undo_handles, image, iuser->tile);
- if (uh == NULL) {
+ if (uh == nullptr) {
uh = uhandle_add(undo_handles, image, iuser);
}
return uh;
@@ -675,7 +716,7 @@ static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, Ima
/** \name Implements ED Undo System
* \{ */
-typedef struct ImageUndoStep {
+struct ImageUndoStep {
UndoStep step;
/** #UndoImageHandle */
@@ -685,12 +726,11 @@ typedef struct ImageUndoStep {
* #PaintTile
* Run-time only data (active during a paint stroke).
*/
- ListBase paint_tiles;
+ PaintTileMap* paint_tile_map;
bool is_encode_init;
ePaintMode paint_mode;
-
-} ImageUndoStep;
+};
/**
* Find the previous undo buffer from this one.
@@ -703,7 +743,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
{
/* Use name lookup because the pointer is cleared for previous steps. */
UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number);
- if (uh_prev != NULL) {
+ if (uh_prev != nullptr) {
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_name);
if (ubuf_reference) {
ubuf_reference = ubuf_reference->post;
@@ -713,7 +753,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
}
}
}
- return NULL;
+ return nullptr;
}
static bool image_undosys_poll(bContext *C)
@@ -737,11 +777,11 @@ static bool image_undosys_poll(bContext *C)
static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
{
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
/* dummy, memory is cleared anyway. */
us->is_encode_init = true;
BLI_listbase_clear(&us->handles);
- BLI_listbase_clear(&us->paint_tiles);
+ us->paint_tile_map = MEM_new<PaintTileMap>(__func__);
}
static bool image_undosys_step_encode(struct bContext *C,
@@ -753,7 +793,7 @@ static bool image_undosys_step_encode(struct bContext *C,
*
* This function ensures there are previous and current states of the image in the undo buffer.
*/
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
BLI_assert(us->step.data_size == 0);
@@ -761,39 +801,38 @@ static bool image_undosys_step_encode(struct bContext *C,
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
- ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active;
+ ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(ED_undo_stack_get()->step_active);
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
- us_reference = (ImageUndoStep *)us_reference->step.prev;
+ us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
}
/* Initialize undo tiles from ptiles (if they exist). */
- for (PaintTile *ptile = us->paint_tiles.first, *ptile_next; ptile; ptile = ptile_next) {
+ for (PaintTile *ptile : us->paint_tile_map->map.values()) {
if (ptile->valid) {
UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image, &ptile->iuser);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf);
- UndoImageTile *utile = MEM_callocN(sizeof(*utile), "UndoImageTile");
+ UndoImageTile *utile = static_cast<UndoImageTile *>(MEM_callocN(sizeof(*utile), "UndoImageTile"));
utile->users = 1;
utile->rect.pt = ptile->rect.pt;
- ptile->rect.pt = NULL;
+ ptile->rect.pt = nullptr;
const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims);
- BLI_assert(ubuf_pre->tiles[tile_index] == NULL);
+ BLI_assert(ubuf_pre->tiles[tile_index] == nullptr);
ubuf_pre->tiles[tile_index] = utile;
}
- ptile_next = ptile->next;
ptile_free(ptile);
}
- BLI_listbase_clear(&us->paint_tiles);
+ us->paint_tile_map->map.clear();
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
LISTBASE_FOREACH (UndoImageBuf *, ubuf_pre, &uh->buffers) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, nullptr);
const bool has_float = ibuf->rect_float;
- BLI_assert(ubuf_pre->post == NULL);
+ BLI_assert(ubuf_pre->post == nullptr);
ubuf_pre->post = ubuf_from_image_no_tiles(uh->image_ref.ptr, ibuf);
UndoImageBuf *ubuf_post = ubuf_pre->post;
@@ -806,7 +845,7 @@ static bool image_undosys_step_encode(struct bContext *C,
UndoImageBuf *ubuf_reference =
(us_reference ? ubuf_lookup_from_reference(
us_reference, uh->image_ref.ptr, uh->iuser.tile, ubuf_post) :
- NULL);
+ nullptr);
int i = 0;
for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) {
@@ -814,34 +853,34 @@ static bool image_undosys_step_encode(struct bContext *C,
for (uint x_tile = 0; x_tile < ubuf_pre->tiles_dims[0]; x_tile += 1) {
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
- if ((ubuf_reference != NULL) && ((ubuf_pre->tiles[i] == NULL) ||
+ if ((ubuf_reference != nullptr) && ((ubuf_pre->tiles[i] == nullptr) ||
/* In this case the paint stroke as has added a tile
* which we have a duplicate reference available. */
(ubuf_pre->tiles[i]->users == 1))) {
- if (ubuf_pre->tiles[i] != NULL) {
+ if (ubuf_pre->tiles[i] != nullptr) {
/* If we have a reference, re-use this single use tile for the post state. */
BLI_assert(ubuf_pre->tiles[i]->users == 1);
ubuf_post->tiles[i] = ubuf_pre->tiles[i];
- ubuf_pre->tiles[i] = NULL;
+ ubuf_pre->tiles[i] = nullptr;
utile_init_from_imbuf(ubuf_post->tiles[i], x, y, ibuf, tmpibuf);
}
else {
- BLI_assert(ubuf_post->tiles[i] == NULL);
+ BLI_assert(ubuf_post->tiles[i] == nullptr);
ubuf_post->tiles[i] = ubuf_reference->tiles[i];
ubuf_post->tiles[i]->users += 1;
}
- BLI_assert(ubuf_pre->tiles[i] == NULL);
+ BLI_assert(ubuf_pre->tiles[i] == nullptr);
ubuf_pre->tiles[i] = ubuf_reference->tiles[i];
ubuf_pre->tiles[i]->users += 1;
- BLI_assert(ubuf_pre->tiles[i] != NULL);
- BLI_assert(ubuf_post->tiles[i] != NULL);
+ BLI_assert(ubuf_pre->tiles[i] != nullptr);
+ BLI_assert(ubuf_post->tiles[i] != nullptr);
}
else {
UndoImageTile *utile = utile_alloc(has_float);
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
- if (ubuf_pre->tiles[i] != NULL) {
+ if (ubuf_pre->tiles[i] != nullptr) {
ubuf_post->tiles[i] = utile;
utile->users = 1;
}
@@ -851,15 +890,15 @@ static bool image_undosys_step_encode(struct bContext *C,
utile->users = 2;
}
}
- BLI_assert(ubuf_pre->tiles[i] != NULL);
- BLI_assert(ubuf_post->tiles[i] != NULL);
+ BLI_assert(ubuf_pre->tiles[i] != nullptr);
+ BLI_assert(ubuf_post->tiles[i] != nullptr);
i += 1;
}
}
BLI_assert(i == ubuf_pre->tiles_len);
BLI_assert(i == ubuf_post->tiles_len);
}
- BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, NULL);
+ BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, nullptr);
}
}
@@ -871,7 +910,7 @@ static bool image_undosys_step_encode(struct bContext *C,
}
}
else {
- BLI_assert(C != NULL);
+ BLI_assert(C != nullptr);
/* Happens when switching modes. */
ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D));
@@ -942,7 +981,7 @@ static void image_undosys_step_decode(
/* NOTE: behavior for undo/redo closely matches sculpt undo. */
BLI_assert(dir != STEP_INVALID);
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
if (dir == STEP_UNDO) {
image_undosys_step_decode_undo(us, is_final);
}
@@ -951,7 +990,7 @@ static void image_undosys_step_decode(
}
if (us->paint_mode == PAINT_MODE_TEXTURE_3D) {
- ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, NULL);
+ ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, nullptr);
}
/* Refresh texture slots. */
@@ -963,15 +1002,16 @@ static void image_undosys_step_free(UndoStep *us_p)
ImageUndoStep *us = (ImageUndoStep *)us_p;
uhandle_free_list(&us->handles);
- /* Typically this list will have been cleared. */
- ptile_free_list(&us->paint_tiles);
+ /* Typically this map will have been cleared. */
+ MEM_delete(us->paint_tile_map);
+ us->paint_tile_map = nullptr;
}
static void image_undosys_foreach_ID_ref(UndoStep *us_p,
UndoTypeForEachIDRefFn foreach_ID_ref_fn,
void *user_data)
{
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
foreach_ID_ref_fn(user_data, ((UndoRefID *)&uh->image_ref));
}
@@ -1011,12 +1051,12 @@ void ED_image_undosys_type(UndoType *ut)
* - So operators can access the pixel-data before the stroke was applied, at run-time.
* \{ */
-ListBase *ED_image_paint_tile_list_get(void)
+PaintTileMap *ED_image_paint_tile_map_get(void)
{
UndoStack *ustack = ED_undo_stack_get();
UndoStep *us_prev = ustack->step_init;
UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE);
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
/* We should always have an undo push started when accessing tiles,
* not doing this means we won't have paint_mode correctly set. */
BLI_assert(us_p == us_prev);
@@ -1024,24 +1064,24 @@ ListBase *ED_image_paint_tile_list_get(void)
/* Fallback value until we can be sure this never happens. */
us->paint_mode = PAINT_MODE_TEXTURE_2D;
}
- return &us->paint_tiles;
+ return us->paint_tile_map;
}
void ED_image_undo_restore(UndoStep *us)
{
- ListBase *paint_tiles = &((ImageUndoStep *)us)->paint_tiles;
- ptile_restore_runtime_list(paint_tiles);
- ptile_invalidate_list(paint_tiles);
+ PaintTileMap *paint_tile_map = reinterpret_cast<ImageUndoStep *>(us)->paint_tile_map;
+ ptile_restore_runtime_map(paint_tile_map);
+ ptile_invalidate_map(paint_tile_map);
}
static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode)
{
UndoStack *ustack = ED_undo_stack_get();
- bContext *C = NULL; /* special case, we never read from this. */
+ bContext *C = nullptr; /* 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;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT));
- us->paint_mode = paint_mode;
+ us->paint_mode = (ePaintMode)paint_mode;
return us;
}
@@ -1060,19 +1100,19 @@ void ED_image_undo_push_begin_with_image(const char *name,
BLI_assert(BKE_image_get_tile(image, iuser->tile));
UndoImageHandle *uh = uhandle_ensure(&us->handles, image, iuser);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf);
- BLI_assert(ubuf_pre->post == NULL);
+ BLI_assert(ubuf_pre->post == nullptr);
- ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active;
+ ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(ED_undo_stack_get()->step_active);
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
- us_reference = (ImageUndoStep *)us_reference->step.prev;
+ us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
}
UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference(
us_reference, image, iuser->tile, ubuf_pre) :
- NULL);
+ nullptr);
if (ubuf_reference) {
memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len);
- for (uint i = 0; i < ubuf_pre->tiles_len; i++) {
+ for (uint32_t i = 0; i < ubuf_pre->tiles_len; i++) {
UndoImageTile *utile = ubuf_pre->tiles[i];
utile->users += 1;
}
@@ -1085,7 +1125,7 @@ void ED_image_undo_push_begin_with_image(const char *name,
void ED_image_undo_push_end(void)
{
UndoStack *ustack = ED_undo_stack_get();
- BKE_undosys_step_push(ustack, NULL, NULL);
+ BKE_undosys_step_push(ustack, nullptr, nullptr);
BKE_undosys_stack_limit_steps_and_memory_defaults(ustack);
WM_file_tag_modified();
}
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index f89bfd2a36a..9652819404e 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -79,7 +79,7 @@ bool nla_panel_context(const bContext *C,
*/
/* XXX: double-check active! */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ACTIVE |
- ANIMFILTER_LIST_CHANNELS);
+ ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -393,8 +393,8 @@ static void nla_panel_properties(const bContext *C, Panel *panel)
/* strip extents */
column = uiLayoutColumn(layout, true);
- uiItemR(column, &strip_ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE);
- uiItemR(column, &strip_ptr, "frame_end", 0, IFACE_("End"), ICON_NONE);
+ uiItemR(column, &strip_ptr, "frame_start_ui", 0, IFACE_("Frame Start"), ICON_NONE);
+ uiItemR(column, &strip_ptr, "frame_end_ui", 0, IFACE_("End"), ICON_NONE);
/* Evaluation-Related Strip Properties ------------------ */
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 40082b08806..a0c6a29c422 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -68,7 +68,8 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index,
/* get the channel that was clicked on */
/* filter channels */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* get channel from index */
@@ -394,7 +395,8 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
int filter;
/* filter channels */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* get channel from index */
@@ -561,7 +563,7 @@ bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel)
/* get a list of the (selected) NLA Tracks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* add tracks... */
@@ -608,7 +610,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac)
/* get a list of the selected AnimData blocks in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
- ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
+ ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* check if selected AnimData blocks are empty, and add tracks if so... */
@@ -710,7 +712,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op))
/* get a list of the AnimData blocks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* delete tracks */
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index bb9e201d94a..3b108a3ba8a 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -617,7 +617,7 @@ static void nla_draw_strip(SpaceNla *snla,
immUnbindProgram();
}
-/* add the relevant text to the cache of text-strings to draw in pixelspace */
+/** Add the relevant text to the cache of text-strings to draw in pixel-space. */
static void nla_draw_strip_text(AnimData *adt,
NlaTrack *nlt,
NlaStrip *strip,
@@ -792,7 +792,8 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
/* build list of channels to draw */
ListBase anim_data = {NULL, NULL};
- int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Update max-extent of channels here (taking into account scrollers):
@@ -902,7 +903,8 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region)
size_t items;
/* build list of channels to draw */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* Update max-extent of channels here (taking into account scrollers):
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 81520445000..801d032a861 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -60,7 +60,8 @@ void ED_nla_postop_refresh(bAnimContext *ac)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FOREDIT);
+ short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
/* get blocks to work on */
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -107,7 +108,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
}
/* get a list of the AnimData blocks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* if no blocks, popup error? */
@@ -211,7 +212,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo)
int filter;
/* get a list of the AnimData blocks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* if no blocks, popup error? */
@@ -318,7 +319,8 @@ static void get_nlastrip_extents(bAnimContext *ac, float *min, float *max, const
bool found_bounds = false;
/* get data to filter */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* set large values to try to override */
@@ -436,7 +438,8 @@ static bool nla_channels_get_selected_extents(bAnimContext *ac, float *r_min, fl
short found = 0;
/* get all items - we need to do it this way */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through all channels, finding the first one that's selected */
@@ -625,7 +628,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
}
scene = ac.scene;
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
/* get action to use */
act = BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"));
@@ -654,7 +657,8 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
/* get a list of the editable tracks being shown in the NLA
* - this is limited to active ones for now, but could be expanded to
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (items == 0) {
@@ -771,7 +775,8 @@ static int nlaedit_add_transition_exec(bContext *C, wmOperator *op)
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each track, find pairs of strips to add transitions to */
@@ -901,11 +906,11 @@ static int nlaedit_add_sound_exec(bContext *C, wmOperator *UNUSED(op))
}
scene = ac.scene;
- cfra = CFRA;
+ cfra = scene->r.cfra;
/* get a list of the editable tracks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_FOREDIT);
+ ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each track, add sound clips if it belongs to a speaker */
@@ -994,7 +999,8 @@ static int nlaedit_add_meta_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each track, find pairs of strips to add transitions to */
@@ -1070,7 +1076,8 @@ static int nlaedit_remove_meta_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each track, find pairs of strips to add transitions to */
@@ -1140,7 +1147,8 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op)
}
/* get a list of editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* duplicate strips in tracks starting from the last one so that we're
@@ -1208,13 +1216,10 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
nlaedit_duplicate_exec(C, op);
- RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION);
- WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event);
-
return OPERATOR_FINISHED;
}
@@ -1240,9 +1245,6 @@ void NLA_OT_duplicate(wmOperatorType *ot)
false,
"Linked",
"When duplicating strips, assign new copies of the actions they use");
-
- /* to give to transform */
- RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
}
/** \} */
@@ -1267,7 +1269,8 @@ static int nlaedit_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, delete all selected strips */
@@ -1430,7 +1433,8 @@ static int nlaedit_split_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, split all selected strips into two strips */
@@ -1518,7 +1522,8 @@ static int nlaedit_toggle_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* go over all selected strips */
@@ -1587,7 +1592,8 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op)
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* consider each track in turn */
@@ -1769,7 +1775,8 @@ static int nlaedit_move_up_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* since we're potentially moving strips from lower tracks to higher tracks, we should
@@ -1860,7 +1867,8 @@ static int nlaedit_move_down_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* loop through the tracks in normal order, since we're pushing strips down,
@@ -1952,7 +1960,8 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op)
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
if (active_only) {
filter |= ANIMFILTER_ACTIVE;
}
@@ -2047,7 +2056,8 @@ static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Ensure that each action used only has a single user
@@ -2155,7 +2165,8 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, apply scale of all selected strips */
@@ -2187,8 +2198,13 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
/* setup iterator, and iterate over all the keyframes in the action,
* applying this scaling */
ked.data = strip;
- ANIM_animchanneldata_keyframes_loop(
- &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve);
+ ANIM_animchanneldata_keyframes_loop(&ked,
+ ac.ads,
+ strip->act,
+ ALE_ACT,
+ NULL,
+ bezt_apply_nlamapping,
+ BKE_fcurve_handles_recalc);
/* clear scale of strip now that it has been applied,
* and recalculate the extents of the action now that it has been scaled
@@ -2265,7 +2281,8 @@ static int nlaedit_clear_scale_exec(bContext *C, wmOperator *UNUSED(op))
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, reset scale of all selected strips */
@@ -2350,7 +2367,8 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op)
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* get some necessary vars */
@@ -2388,7 +2406,7 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op)
/* calculate new start position based on snapping mode */
switch (mode) {
case NLAEDIT_SNAP_CFRA: /* to current frame */
- strip->start = (float)CFRA;
+ strip->start = (float)scene->r.cfra;
break;
case NLAEDIT_SNAP_NEAREST_FRAME: /* to nearest frame */
strip->start = floorf(start + 0.5f);
@@ -2544,7 +2562,8 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op)
}
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, add the specified modifier to all selected strips */
@@ -2655,7 +2674,8 @@ static int nla_fmodifier_copy_exec(bContext *C, wmOperator *op)
ANIM_fmodifiers_copybuf_free();
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, add the specified modifier to all selected strips */
@@ -2734,7 +2754,7 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op)
/* get a list of the editable tracks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, add the specified modifier to all selected strips */
diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c
index 902e7a176a3..3ae73282230 100644
--- a/source/blender/editors/space_nla/nla_ops.c
+++ b/source/blender/editors/space_nla/nla_ops.c
@@ -16,6 +16,8 @@
#include "ED_anim_api.h"
#include "ED_screen.h"
+#include "RNA_access.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -138,6 +140,28 @@ void nla_operatortypes(void)
WM_operatortype_append(NLA_OT_fmodifier_paste);
}
+void ED_operatormacros_nla()
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot = WM_operatortype_append_macro("NLA_OT_duplicate_move",
+ "Duplicate",
+ "Duplicate selected strips and their Actions and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate");
+ RNA_boolean_set(otmacro->ptr, "linked", false);
+ WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+
+ ot = WM_operatortype_append_macro("NLA_OT_duplicate_linked_move",
+ "Duplicate Linked",
+ "Duplicate selected strips and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate");
+ RNA_boolean_set(otmacro->ptr, "linked", true);
+ WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+}
+
/* ************************** registration - keymaps **********************************/
void nla_keymap(wmKeyConfig *keyconf)
diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c
index 1efb91bc99f..a816f8fa4f6 100644
--- a/source/blender/editors/space_nla/nla_select.c
+++ b/source/blender/editors/space_nla/nla_select.c
@@ -85,7 +85,7 @@ static void deselect_nla_strips(bAnimContext *ac, short test, short sel)
/* determine type-based settings */
/* FIXME: double check whether ANIMFILTER_LIST_VISIBLE is needed! */
- filter = (ANIMFILTER_DATA_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY);
/* filter data */
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -223,7 +223,8 @@ static void box_select_nla_strips(bAnimContext *ac, rcti rect, short mode, short
UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* convert selection modes to selection modes */
@@ -278,7 +279,8 @@ static void nlaedit_strip_at_region_position(
0, NLACHANNEL_STEP(snla), 0, NLACHANNEL_FIRST_TOP(ac), view_x, view_y, NULL, &channel_index);
ListBase anim_data = {NULL, NULL};
- int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click
@@ -455,17 +457,17 @@ static void nlaedit_select_leftright(bContext *C,
/* get range, and get the right flag-setting mode */
if (leftright == NLAEDIT_LRSEL_LEFT) {
xmin = MINAFRAMEF;
- xmax = (float)(CFRA + 0.1f);
+ xmax = (float)(scene->r.cfra + 0.1f);
}
else {
- xmin = (float)(CFRA - 0.1f);
+ xmin = (float)(scene->r.cfra - 0.1f);
xmax = MAXFRAMEF;
}
select_mode = selmodes_to_flagmodes(select_mode);
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* select strips on the side where most data occurs */
@@ -540,7 +542,7 @@ static int nlaedit_select_leftright_invoke(bContext *C, wmOperator *op, const wm
/* determine which side of the current frame mouse is on */
x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
- if (x < CFRA) {
+ if (x < scene->r.cfra) {
RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_LEFT);
}
else {
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 42d3d841f4b..13035a9d5fd 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -87,9 +87,9 @@ static SpaceLink *nla_create(const ScrArea *area, const Scene *scene)
BLI_addtail(&snla->regionbase, region);
region->regiontype = RGN_TYPE_WINDOW;
- region->v2d.tot.xmin = (float)(SFRA - 10);
+ region->v2d.tot.xmin = (float)(scene->r.sfra - 10);
region->v2d.tot.ymin = (float)(-area->winy) / 3.0f;
- region->v2d.tot.xmax = (float)(EFRA + 10);
+ region->v2d.tot.xmax = (float)(scene->r.efra + 10);
region->v2d.tot.ymax = 0.0f;
region->v2d.cur = region->v2d.tot;
@@ -235,7 +235,7 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
/* strips and backdrops */
draw_nla_main_data(&ac, snla, region);
- /* text draw cached, in pixelspace now */
+ /* Text draw cached, in pixel-space now. */
UI_view2d_text_cache_draw(region);
}
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index d196f3911a4..6fbea0b0d83 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -329,7 +329,7 @@ static void node_buts_image_user(uiLayout *layout,
Scene *scene = CTX_data_scene(C);
char numstr[32];
- const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr);
+ const int framenr = BKE_image_user_frame_get(iuser, scene->r.cfra, nullptr);
BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr);
uiItemL(layout, numstr, ICON_NONE);
}
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 715b7c80d1d..17906dcae35 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -382,7 +382,7 @@ static bool node_add_group_poll(bContext *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.");
+ "Dropping node groups isn't supported for this");
return false;
}
return true;
@@ -690,8 +690,8 @@ static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *even
snode->runtime->cursor[0] /= UI_DPI_FAC;
snode->runtime->cursor[1] /= UI_DPI_FAC;
- if (RNA_struct_property_is_set(op->ptr, "filepath") ||
- RNA_struct_property_is_set(op->ptr, "name")) {
+ if (WM_operator_properties_id_lookup_is_set(op->ptr) ||
+ RNA_struct_property_is_set(op->ptr, "filepath")) {
return node_add_file_exec(C, op);
}
return WM_operator_filesel(C, op, event);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 7f781f5a1ae..e12b845ef53 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -940,6 +940,19 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
ss << TIP_("\u2022 Volume") << line_end;
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ if (value_log.edit_data_info.has_value()) {
+ const geo_log::GeometryValueLog::EditDataInfo &edit_info = *value_log.edit_data_info;
+ char line[256];
+ BLI_snprintf(line,
+ sizeof(line),
+ TIP_("\u2022 Edit Curves: %s, %s"),
+ edit_info.has_deformed_positions ? TIP_("positions") : TIP_("no positions"),
+ edit_info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices"));
+ ss << line << line_end;
+ }
+ break;
+ }
}
}
@@ -978,6 +991,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
ss << TIP_("Volume");
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ break;
+ }
}
ss << ((type == supported_types.last()) ? "" : ", ");
}
@@ -1700,7 +1716,7 @@ static std::string node_get_execution_time_label(const SpaceNode &snode, const b
{
int node_count = 0;
std::chrono::microseconds exec_time = node_get_execution_time(
- *snode.nodetree, node, snode, node_count);
+ *snode.edittree, node, snode, node_count);
if (node_count == 0) {
return std::string("");
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 f08e23c8371..e328a86b0fd 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -113,14 +113,6 @@ static void attribute_search_update_fn(
Vector<const GeometryAttributeInfo *> infos = get_attribute_info_from_context(*C, *data);
- /* Remove the deprecated normal attribute from the search. */
- for (const int i : infos.index_range()) {
- if (infos[i]->domain == ATTR_DOMAIN_FACE && infos[i]->name == "normal") {
- infos.remove(i);
- break;
- }
- }
-
ui::attribute_search_add_items(str, true, infos, items, is_first);
}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc
index e20958c1b1e..7435fa50a93 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.cc
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc
@@ -1124,8 +1124,7 @@ static Collection *collection_parent_from_ID(ID *id)
return nullptr;
}
-static bool collection_drop_init(
- bContext *C, wmDrag *drag, const int xy[2], const bool is_link, CollectionDrop *data)
+static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], CollectionDrop *data)
{
/* Get collection to drop into. */
TreeElementInsertType insert_type;
@@ -1157,9 +1156,6 @@ static bool collection_drop_init(
/* Get collection to drag out of. */
ID *parent = drag_id->from_parent;
Collection *from_collection = collection_parent_from_ID(parent);
- if (is_link) {
- from_collection = nullptr;
- }
/* Currently this should not be allowed, cannot edit items in an override of a Collection. */
if (from_collection != nullptr && ID_IS_OVERRIDE_LIBRARY(from_collection)) {
@@ -1197,29 +1193,22 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
CollectionDrop data;
- if (((event->modifier & KM_SHIFT) == 0) &&
- collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) {
+ if (((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, event->xy, &data)) {
TreeElement *te = data.te;
TreeStoreElem *tselem = TREESTORE(te);
- if (!data.from || event->modifier & KM_CTRL) {
- tselem->flag |= TSE_DRAG_INTO;
- changed = true;
- }
- else {
- switch (data.insert_type) {
- case TE_INSERT_BEFORE:
- tselem->flag |= TSE_DRAG_BEFORE;
- changed = true;
- break;
- case TE_INSERT_AFTER:
- tselem->flag |= TSE_DRAG_AFTER;
- changed = true;
- break;
- case TE_INSERT_INTO: {
- tselem->flag |= TSE_DRAG_INTO;
- changed = true;
- break;
- }
+ switch (data.insert_type) {
+ case TE_INSERT_BEFORE:
+ tselem->flag |= TSE_DRAG_BEFORE;
+ changed = true;
+ break;
+ case TE_INSERT_AFTER:
+ tselem->flag |= TSE_DRAG_AFTER;
+ changed = true;
+ break;
+ case TE_INSERT_INTO: {
+ tselem->flag |= TSE_DRAG_INTO;
+ changed = true;
+ break;
}
}
if (changed) {
@@ -1242,30 +1231,49 @@ static char *collection_drop_tooltip(bContext *C,
const wmEvent *event = win ? win->eventstate : nullptr;
CollectionDrop data;
- if (event && ((event->modifier & KM_SHIFT) == 0) &&
- collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) {
- TreeElement *te = data.te;
- if (!data.from || event->modifier & KM_CTRL) {
- return BLI_strdup(TIP_("Link inside Collection"));
+ if (event && ((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, xy, &data)) {
+ const bool is_link = !data.from || (event->modifier & KM_CTRL);
+
+ /* Test if we are moving within same parent collection. */
+ bool same_level = false;
+ LISTBASE_FOREACH (CollectionParent *, parent, &data.to->parents) {
+ if (data.from == parent->collection) {
+ same_level = true;
+ }
}
+
+ /* Tooltips when not moving directly into another collection i.e. mouse on border of
+ * collections. Later we will decide which tooltip to return. */
+ const bool tooltip_link = (is_link && !same_level);
+ const char *tooltip_before = tooltip_link ? TIP_("Link before collection") :
+ TIP_("Move before collection");
+ const char *tooltip_between = tooltip_link ? TIP_("Link between collections") :
+ TIP_("Move between collections");
+ const char *tooltip_after = tooltip_link ? TIP_("Link after collection") :
+ TIP_("Move after collection");
+
+ TreeElement *te = data.te;
switch (data.insert_type) {
case TE_INSERT_BEFORE:
if (te->prev && outliner_is_collection_tree_element(te->prev)) {
- return BLI_strdup(TIP_("Move between collections"));
+ return BLI_strdup(tooltip_between);
}
else {
- return BLI_strdup(TIP_("Move before collection"));
+ return BLI_strdup(tooltip_before);
}
break;
case TE_INSERT_AFTER:
if (te->next && outliner_is_collection_tree_element(te->next)) {
- return BLI_strdup(TIP_("Move between collections"));
+ return BLI_strdup(tooltip_between);
}
else {
- return BLI_strdup(TIP_("Move after collection"));
+ return BLI_strdup(tooltip_after);
}
break;
case TE_INSERT_INTO: {
+ if (is_link) {
+ return BLI_strdup(TIP_("Link inside collection"));
+ }
/* Check the type of the drag IDs to avoid the incorrect "Shift to parent"
* for collections. Checking the type of the first ID works fine here since
@@ -1296,7 +1304,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
CollectionDrop data;
- if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) {
+ if (!collection_drop_init(C, drag, event->xy, &data)) {
return OPERATOR_CANCELLED;
}
@@ -1447,7 +1455,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
TSE_GPENCIL_EFFECT_BASE);
const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID;
- wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP);
+ wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP);
if (use_datastack_drag) {
TreeElement *te_bone = nullptr;
@@ -1537,6 +1545,8 @@ static int outliner_item_drag_drop_invoke(bContext *C,
WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent);
}
+ WM_event_start_prepared_drag(C, drag);
+
ED_outliner_select_sync_from_outliner(C, space_outliner);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 753de83a10d..6bab0b938e8 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -37,6 +37,7 @@
#include "BKE_lib_override.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -677,6 +678,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem);
if (tselem->type == TSE_SOME_ID) {
+ BKE_main_namemap_remove_name(bmain, tselem->id, oldname);
BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name);
@@ -742,6 +744,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
}
case TSE_NLA_ACTION: {
bAction *act = (bAction *)tselem->id;
+ BKE_main_namemap_remove_name(bmain, &act->id, oldname);
BLI_libblock_ensure_unique_name(bmain, act->id.name);
WM_msg_publish_rna_prop(mbus, &act->id, &act->id, ID, name);
break;
@@ -852,6 +855,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
case TSE_LAYER_COLLECTION: {
/* The ID is a #Collection, not a #LayerCollection */
Collection *collection = (Collection *)tselem->id;
+ BKE_main_namemap_remove_name(bmain, &collection->id, oldname);
BLI_libblock_ensure_unique_name(bmain, collection->id.name);
WM_msg_publish_rna_prop(mbus, &collection->id, &collection->id, ID, name);
WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr);
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index c4a9398a5f7..32860bc2cff 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -447,7 +447,7 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot)
/** \name ID Delete Operator
* \{ */
-static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem)
+static void id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem)
{
Main *bmain = CTX_data_main(C);
ID *id = tselem->id;
@@ -484,35 +484,39 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
return;
}
if (te->idcode == ID_WS) {
- BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT);
- if (id->tag & LIB_TAG_DOIT) {
+ BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_PRE_EXISTING);
+ if (id->tag & LIB_TAG_PRE_EXISTING) {
BKE_reportf(
reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name);
+ BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false);
return;
}
+ BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false);
}
- BKE_id_delete(bmain, id);
+ id->tag |= LIB_TAG_DOIT;
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
-void id_delete_fn(bContext *C,
- ReportList *reports,
- Scene *UNUSED(scene),
- TreeElement *te,
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
+void id_delete_tag_fn(bContext *C,
+ ReportList *reports,
+ Scene *UNUSED(scene),
+ TreeElement *te,
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *UNUSED(user_data))
{
- id_delete(C, reports, te, tselem);
+ id_delete_tag(C, reports, te, tselem);
}
-static int outliner_id_delete_invoke_do(bContext *C,
- ReportList *reports,
- TreeElement *te,
- const float mval[2])
+static int outliner_id_delete_tag(bContext *C,
+ ReportList *reports,
+ TreeElement *te,
+ const float mval[2])
{
+ int id_tagged_num = 0;
+
if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
TreeStoreElem *tselem = TREESTORE(te);
@@ -522,26 +526,27 @@ static int outliner_id_delete_invoke_do(bContext *C,
RPT_ERROR_INVALID_INPUT,
"Cannot delete indirectly linked library '%s'",
((Library *)tselem->id)->filepath_abs);
- return OPERATOR_CANCELLED;
}
- id_delete(C, reports, te, tselem);
- return OPERATOR_FINISHED;
+ else {
+ id_delete_tag(C, reports, te, tselem);
+ id_tagged_num++;
+ }
}
}
else {
LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
- int ret;
- if ((ret = outliner_id_delete_invoke_do(C, reports, te_sub, mval))) {
- return ret;
+ if ((id_tagged_num += outliner_id_delete_tag(C, reports, te_sub, mval)) != 0) {
+ break;
}
}
}
- return 0;
+ return id_tagged_num;
}
static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ Main *bmain = CTX_data_main(C);
ARegion *region = CTX_wm_region(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
float fmval[2];
@@ -550,15 +555,21 @@ static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
+ int id_tagged_num = 0;
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
- int ret;
-
- if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) {
- return ret;
+ if ((id_tagged_num += outliner_id_delete_tag(C, op->reports, te, fmval)) != 0) {
+ break;
}
}
+ if (id_tagged_num == 0) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ return OPERATOR_CANCELLED;
+ }
- return OPERATOR_CANCELLED;
+ BKE_id_multi_tagged_delete(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ return OPERATOR_FINISHED;
}
void OUTLINER_OT_id_delete(wmOperatorType *ot)
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index a0dcb49aa43..18173b37123 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -437,13 +437,13 @@ void lib_reload_fn(struct bContext *C,
struct TreeStoreElem *tselem,
void *user_data);
-void id_delete_fn(struct bContext *C,
- struct ReportList *reports,
- struct Scene *scene,
- struct TreeElement *te,
- struct TreeStoreElem *tsep,
- struct TreeStoreElem *tselem,
- void *user_data);
+void id_delete_tag_fn(struct bContext *C,
+ struct ReportList *reports,
+ struct Scene *scene,
+ struct TreeElement *te,
+ struct TreeStoreElem *tsep,
+ struct TreeStoreElem *tselem,
+ void *user_data);
void id_remap_fn(struct bContext *C,
struct ReportList *reports,
struct Scene *scene,
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index ec19e8d5e5b..80ee3bbb6a1 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -31,8 +31,11 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -90,7 +93,9 @@ static CLG_LogRef LOG = {"ed.outliner.tools"};
using namespace blender::ed::outliner;
+using blender::Map;
using blender::Set;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
@@ -777,6 +782,25 @@ static void id_local_fn(bContext *C,
}
}
+struct OutlinerLiboverrideDataIDRoot {
+ /** The linked ID that was selected for override. */
+ ID *id_root_reference;
+
+ /** The root of the override hierarchy to which the override of `id_root` belongs, once
+ * known/created. */
+ ID *id_hierarchy_root_override;
+
+ /** The ID that was detected as being a good candidate as instanciation hint for newly overridden
+ * objects, may be null.
+ *
+ * \note Typically currently only used when the root ID to override is a collection instanced by
+ * an emtpy object. */
+ ID *id_instance_hint;
+
+ /** If this override comes from an instancing object (which would be `id_instance_hint` then). */
+ bool is_override_instancing_object;
+};
+
struct OutlinerLibOverrideData {
bool do_hierarchy;
@@ -789,21 +813,44 @@ struct OutlinerLibOverrideData {
* solving broken overrides while not losing *all* of your overrides. */
bool do_resync_hierarchy_enforce;
- /** The override hierarchy root, when known/created. */
- ID *id_hierarchy_root_override;
-
- /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
+ /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
* their newly-created liboverrides in post-process step of override hierarchy creation. */
Set<uint> selected_id_uid;
+
+ /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of
+ * these roots.
+ *
+ * \note the key may be either linked (in which case it will be replaced by the newly created
+ * override), or an actual already existing override. */
+ Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots;
+
+ /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */
+ Set<uint> id_hierarchy_roots_uid;
+
+ void id_root_add(ID *id_hierarchy_root_reference,
+ ID *id_root_reference,
+ ID *id_instance_hint,
+ const bool is_override_instancing_object)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = id_root_reference;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = id_instance_hint;
+ id_root_data.is_override_instancing_object = is_override_instancing_object;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ value.append(id_root_data);
+ }
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
-static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
+static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
+ ReportList *reports,
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
+ TreeElement *te,
+ TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
@@ -829,28 +876,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
-}
-
-static void id_override_library_create_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *tsep,
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
-
- /* We can only safely apply this operation on one item at a time, so only do it on the active
- * one. */
- if ((tselem->flag & TSE_ACTIVE) == 0) {
- return;
- }
-
- ID *id_root_reference = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy = data->do_hierarchy;
- bool success = false;
ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
@@ -866,172 +891,242 @@ static void id_override_library_create_fn(bContext *C,
}
}
- if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
- (ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
+ !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
+ return;
+ }
- id_root_reference->tag |= LIB_TAG_DOIT;
+ Main *bmain = CTX_data_main(C);
- /* For now, remap all local usages of linked ID to local override one here. */
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter)) {
- id_iter->tag &= ~LIB_TAG_DOIT;
- }
- else {
- id_iter->tag |= LIB_TAG_DOIT;
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ ID *id_hierarchy_root_reference = id_root_reference;
+ while ((te = te->parent) != nullptr) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
}
- }
- FOREACH_MAIN_ID_END;
- if (do_hierarchy) {
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- ID *id_hierarchy_root_reference = id_root_reference;
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
+ /* Tentative hierarchy root. */
+ ID *id_current_hierarchy_root = te->store_elem->id;
+
+ /* If the parent ID is from a different library than the reference root one, we are done
+ * with upwards tree processing in any case. */
+ if (id_current_hierarchy_root->lib != id_root_reference->lib) {
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
+ /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
+ * get an actual real override. */
continue;
}
- /* Tentative hierarchy root. */
- ID *id_current_hierarchy_root = te->store_elem->id;
-
- /* If the parent ID is from a different library than the reference root one, we are done
- * with upwards tree processing in any case. */
- if (id_current_hierarchy_root->lib != id_root_reference->lib) {
- if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
- /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
- * get an actual real override. */
- continue;
- }
-
- /* If the parent ID is already an override, and is valid (i.e. local override), we can
- * access its hierarchy root directly. */
- if (!ID_IS_LINKED(id_current_hierarchy_root) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
- id_current_hierarchy_root->override_library->reference->lib ==
- id_root_reference->lib) {
- id_hierarchy_root_reference =
- id_current_hierarchy_root->override_library->hierarchy_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
- break;
- }
-
- if (ID_IS_LINKED(id_current_hierarchy_root)) {
- /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
- * would most likely generate invisible/confusing/hard to use and manage overrides. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- BKE_reportf(reports,
- RPT_WARNING,
- "Invalid anchor ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_current_hierarchy_root->name,
- id_root_reference->name);
- return;
- }
-
- /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
- * current `id_hierarchy_root_reference` is our best candidate. */
-
+ /* If the parent ID is already an override, and is valid (i.e. local override), we can
+ * access its hierarchy root directly. */
+ if (!ID_IS_LINKED(id_current_hierarchy_root) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
+ id_current_hierarchy_root->override_library->reference->lib ==
+ id_root_reference->lib) {
+ id_hierarchy_root_reference =
+ id_current_hierarchy_root->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
- /* If some element in the tree needs to be overridden, but its ID is not overridable,
- * abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
+ if (ID_IS_LINKED(id_current_hierarchy_root)) {
+ /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
+ * would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Could not create library override from data-block '%s', one of its parents "
- "is not overridable ('%s')",
- id_root_reference->name,
- id_current_hierarchy_root->name);
+ "Invalid anchor ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_current_hierarchy_root->name,
+ id_root_reference->name);
return;
}
- id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
- id_hierarchy_root_reference = id_current_hierarchy_root;
+
+ /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
+ * current `id_hierarchy_root_reference` is our best candidate. */
+
+ break;
}
- /* That case can happen when linked data is a complex mix involving several libraries and/or
- * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
- * from another library. Do not try to support such cases for now. */
- if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
- (!ID_IS_LINKED(id_hierarchy_root_reference) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
- id_hierarchy_root_reference->override_library->reference->lib ==
- id_root_reference->lib))) {
+ /* If some element in the tree needs to be overridden, but its ID is not overridable,
+ * abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Invalid hierarchy root ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_hierarchy_root_reference->name,
- id_root_reference->name);
+ "Could not create library override from data-block '%s', one of its parents "
+ "is not overridable ('%s')",
+ id_root_reference->name,
+ id_current_hierarchy_root->name);
return;
}
+ id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
+ id_hierarchy_root_reference = id_current_hierarchy_root;
+ }
+
+ /* That case can happen when linked data is a complex mix involving several libraries and/or
+ * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
+ * from another library. Do not try to support such cases for now. */
+ if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
+ (!ID_IS_LINKED(id_hierarchy_root_reference) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
+ id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib))) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid hierarchy root ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_hierarchy_root_reference->name,
+ id_root_reference->name);
+ return;
+ }
+
+ data->id_root_add(id_hierarchy_root_reference,
+ id_root_reference,
+ id_instance_hint,
+ is_override_instancing_object);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
+ data->id_root_add(
+ id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
+ }
+}
+static void id_override_library_create_hierarchy(
+ Main &bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ OutlinerLibOverrideData &data,
+ ID *id_hierarchy_root_reference,
+ Vector<OutlinerLiboverrideDataIDRoot> &data_idroots,
+ bool &r_aggregated_success)
+{
+ BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+
+ const bool do_hierarchy = data.do_hierarchy;
+
+ /* NOTE: This process is not the most efficient, but allows to re-use existing code.
+ * If this becomes a bottle-neck at some point, we need to implement a new
+ * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
+ * a same hierarchy in a single call. */
+ for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
+ /* For now, remap all local usages of linked ID to local override one here. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ bool success = false;
+ if (do_hierarchy) {
ID *id_root_override = nullptr;
- success = BKE_lib_override_library_create(bmain,
- CTX_data_scene(C),
- CTX_data_view_layer(C),
+ success = BKE_lib_override_library_create(&bmain,
+ scene,
+ view_layer,
nullptr,
- id_root_reference,
+ data_idroot.id_root_reference,
id_hierarchy_root_reference,
- id_instance_hint,
+ data_idroot.id_instance_hint,
&id_root_override,
- data->do_fully_editable);
-
- BLI_assert(id_root_override != nullptr);
- BLI_assert(!ID_IS_LINKED(id_root_override));
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
- if (ID_IS_LINKED(id_hierarchy_root_reference)) {
- BLI_assert(
- id_root_override->override_library->hierarchy_root->override_library->reference ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
- }
- else {
- BLI_assert(id_root_override->override_library->hierarchy_root ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ data.do_fully_editable);
+
+ if (success) {
+ BLI_assert(id_root_override != nullptr);
+ BLI_assert(!ID_IS_LINKED(id_root_override));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+
+ ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
+ if (ID_IS_LINKED(id_hierarchy_root_reference)) {
+ BLI_assert(id_hierarchy_root_override->override_library->reference ==
+ id_hierarchy_root_reference);
+ /* If the hierarchy root reference was a linked data, after the first iteration there is
+ * now a matching override, which shall be used for all further partial overrides with
+ * this same hierarchy. */
+ id_hierarchy_root_reference = id_hierarchy_root_override;
+ }
+ else {
+ BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
+ }
+ data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
+ data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid);
}
}
- else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
- success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
+ else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
+ ID *id_root_override = BKE_lib_override_library_create_from_id(
+ &bmain, data_idroot.id_root_reference, true);
+ success = id_root_override != nullptr;
+ if (success) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+ id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
/* Cleanup. */
- BKE_main_id_newptr_and_tag_clear(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_main_id_newptr_and_tag_clear(&bmain);
+ BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false);
+ }
+ else {
+ BLI_assert_unreachable();
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
+ if (success && data_idroot.is_override_instancing_object) {
+ BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB);
+ ED_object_base_free_and_unlink(
+ &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
}
- }
- if (!success) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Could not create library override from data-block '%s'",
- id_root_reference->name);
+
+ r_aggregated_success = r_aggregated_success && success;
}
}
/* Clear system override flag from newly created overrides which linked reference were previously
* selected in the Outliner tree. */
-static void id_override_library_create_hierarchy_post_process(bContext *C,
- OutlinerLibOverrideData *data)
+static void id_override_library_create_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
{
Main *bmain = CTX_data_main(C);
- ID *id_hierarchy_root_override = data->id_hierarchy_root_override;
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const bool do_hierarchy = data.do_hierarchy;
+
+ bool success = true;
+ for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
+ id_override_library_create_hierarchy(
+ *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
+ }
+
+ if (!success) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not create library override from one or more of the selected data-blocks");
+ }
+
+ if (!do_hierarchy) {
+ return;
+ }
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) {
+ if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
+ continue;
+ }
+ if (!data.id_hierarchy_roots_uid.contains(
+ id_iter->override_library->hierarchy_root->session_uuid)) {
continue;
}
- if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) {
+ if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) ||
+ data.selected_id_uid.contains(id_iter->session_uuid)) {
id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
}
@@ -1995,7 +2090,6 @@ 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,
@@ -2041,12 +2135,6 @@ 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,
@@ -2126,7 +2214,6 @@ 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;
}
@@ -2185,6 +2272,7 @@ static const EnumPropertyItem *outliner_id_operation_itemf(bContext *C,
static int outliner_id_operation_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = CTX_data_scene(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -2261,42 +2349,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
OutlinerLibOverrideData 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;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
+ override_data.do_hierarchy = false;
+ override_data.do_fully_editable = true;
+
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
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");
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Overridden Data");
break;
}
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: {
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
- override_data.do_fully_editable = true;
+ override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
+
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
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 Fully Editable");
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Overridden Data Hierarchy");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: {
@@ -2381,8 +2463,10 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_DELETE: {
if (idlevel > 0) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_delete_fn, nullptr);
+ C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr);
+ BKE_id_multi_tagged_delete(bmain);
ED_undo_push(C, "Delete");
}
break;
@@ -2507,6 +2591,7 @@ static const EnumPropertyItem outliner_lib_op_type_items[] = {
static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
@@ -2522,7 +2607,10 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type");
switch (event) {
case OL_LIB_DELETE: {
- outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ outliner_do_libdata_operation(
+ C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr);
+ BKE_id_multi_tagged_delete(bmain);
ED_undo_push(C, "Delete Library");
break;
}
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index 190e35c81d6..f8e35655c26 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -161,7 +161,6 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay {
ListBase build_hierarchy_for_lib_or_main(Main *bmain,
TreeElement &parent_te,
Library *lib = nullptr);
- void build_hierarchy_for_ID(Main *bmain, ID &override_root_id, TreeElementID &te_id) const;
};
/* -------------------------------------------------------------------- */
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 67798e978ab..f8705c3f0ad 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
@@ -4,25 +4,23 @@
* \ingroup spoutliner
*/
-#include "DNA_ID.h"
-#include "DNA_collection_types.h"
#include "DNA_key_types.h"
#include "DNA_space_types.h"
-#include "BLI_listbase.h"
+#include "BLI_function_ref.hh"
+#include "BLI_ghash.h"
#include "BLI_map.hh"
+
#include "BLI_set.hh"
#include "BLT_translation.h"
-#include "BKE_collection.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "../outliner_intern.hh"
#include "common.hh"
#include "tree_display.hh"
-#include "tree_element_id.hh"
namespace blender::ed::outliner {
@@ -42,8 +40,8 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
TreeElement *current_file_te = outliner_add_element(
&space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1);
current_file_te->name = IFACE_("Current File");
+ AbstractTreeElement::uncollapse_by_default(current_file_te);
{
- AbstractTreeElement::uncollapse_by_default(current_file_te);
build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te);
/* Add dummy child if there's nothing to display. */
@@ -76,11 +74,49 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
return tree;
}
+/* -------------------------------------------------------------------- */
+/** \name Library override hierarchy building
+ * \{ */
+
+class OverrideIDHierarchyBuilder {
+ SpaceOutliner &space_outliner_;
+ MainIDRelations &id_relations_;
+
+ struct HierarchyBuildData {
+ const ID &override_root_id_;
+ /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes
+ * with every level of recursion. */
+ Set<const ID *> parent_ids{};
+ /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with
+ * every level of recursion. */
+ Set<const ID *> sibling_ids{};
+ };
+
+ public:
+ OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations)
+ : space_outliner_(space_outliner), id_relations_(id_relations)
+ {
+ }
+
+ void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand);
+
+ private:
+ void build_hierarchy_for_ID_recursive(const ID &parent_id,
+ HierarchyBuildData &build_data,
+ TreeElement &te_to_expand);
+};
+
ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
Main *bmain, TreeElement &parent_te, Library *lib)
{
ListBase tree = {nullptr};
+ /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before
+ * returning. */
+ BKE_main_relations_create(bmain, 0);
+
+ OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations);
+
/* Keep track over which ID base elements were already added, and expand them once added. */
Map<ID_Type, TreeElement *> id_base_te_map;
/* Index for the ID base elements ("Objects", "Materials", etc). */
@@ -109,108 +145,174 @@ 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, false);
- build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te));
+ builder.build_hierarchy_for_ID(*iter_id, *new_id_te);
}
FOREACH_MAIN_ID_END;
+ BKE_main_relations_free(bmain);
+
return tree;
}
-struct BuildHierarchyForeachIDCbData {
- /* Don't allow copies, the sets below would need deep copying. */
- BuildHierarchyForeachIDCbData(const BuildHierarchyForeachIDCbData &) = delete;
-
- Main &bmain;
- SpaceOutliner &space_outliner;
- ID &override_root_id;
-
- /* The tree element to expand. Changes with every level of recursion. */
- TreeElementID *parent_te;
- /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes
- * with every level of recursion. */
- Set<ID *> parent_ids{};
- /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with
- * every level of recursion. */
- Set<ID *> sibling_ids{};
-};
+void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id,
+ TreeElement &te_to_expand)
+{
+ HierarchyBuildData build_data{override_root_id};
+ build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand);
+}
+
+/* Helpers (defined below). */
+static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
+ const ID &parent_id,
+ FunctionRef<void(ID &)> fn);
+static bool id_is_in_override_hierarchy(const ID &id,
+ const ID &relationship_parent_id,
+ const ID &override_root_id);
+
+void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id,
+ HierarchyBuildData &build_data,
+ TreeElement &te_to_expand)
+{
+ /* In case this isn't added to the parents yet (does nothing if already there). */
+ build_data.parent_ids.add(&parent_id);
+
+ foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) {
+ if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) {
+ return;
+ }
+
+ /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into
+ * itself. */
+ if (build_data.parent_ids.lookup_key_default(&id, nullptr)) {
+ return;
+ }
+
+ /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used
+ * multiple times by the same parent. */
+ if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) {
+ return;
+ }
+
+ TreeElement *new_te = outliner_add_element(
+ &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false);
+
+ build_data.sibling_ids.add(&id);
+
+ /* Recurse into this ID. */
+ HierarchyBuildData child_build_data{build_data.override_root_id_};
+ child_build_data.parent_ids = build_data.parent_ids;
+ child_build_data.parent_ids.add(&id);
+ child_build_data.sibling_ids.reserve(10);
+ build_hierarchy_for_ID_recursive(id, child_build_data, *new_te);
+ });
+}
-static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Helpers for library override hierarchy building
+ * \{ */
+
+/**
+ * Iterate over the IDs \a parent_id uses. E.g. the child collections and contained objects of a
+ * parent collection. Also does special handling for object parenting, so that:
+ * - When iterating over a child object, \a fn is executed for the parent instead.
+ * - When iterating over a parent object, \a fn is _additionally_ executed for all children. Given
+ * that the parent object isn't skipped, the caller has to ensure it's not added in the hierarchy
+ * twice.
+ * This allows us to build the hierarchy in the expected ("natural") order, where parent objects
+ * are actual parent elements in the hierarchy, even though in data, the relation goes the other
+ * way around (children point to or "use" the parent).
+ *
+ * Only handles regular object parenting, not cases like the "Child of" constraint. Other Outliner
+ * display modes don't show this as parent in the hierarchy either.
+ */
+static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
+ const ID &parent_id,
+ FunctionRef<void(ID &)> fn)
{
- if (!*cb_data->id_pointer) {
- return IDWALK_RET_NOP;
+ const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id));
+
+ /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */
+ for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry;
+ to_id_entry = to_id_entry->next) {
+ /* An ID pointed to (used) by the ID to recurse into. */
+ ID &target_id = **to_id_entry->id_pointer.to;
+
+ /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */
+ if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) {
+ continue;
+ }
+
+ /* Special case for objects: Process the parent object instead of the child object. Below the
+ * parent will add the child objects then. */
+ if (GS(target_id.name) == ID_OB) {
+ const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id);
+ if (potential_child_ob.parent) {
+ fn(potential_child_ob.parent->id);
+ continue;
+ }
+ }
+
+ fn(target_id);
+ }
+
+ /* If the ID is an object, find and iterate over any child objects. */
+ if (GS(parent_id.name) == ID_OB) {
+ for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry;
+ from_id_entry = from_id_entry->next) {
+ ID &potential_child_id = *from_id_entry->id_pointer.from;
+
+ if (GS(potential_child_id.name) != ID_OB) {
+ continue;
+ }
+
+ Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id);
+ if (potential_child_ob.parent && &potential_child_ob.parent->id == &parent_id) {
+ fn(potential_child_id);
+ }
+ }
}
+}
- BuildHierarchyForeachIDCbData &build_data = *reinterpret_cast<BuildHierarchyForeachIDCbData *>(
- cb_data->user_data);
- /* Note that this may be an embedded ID (see #real_override_id). */
- ID &id = **cb_data->id_pointer;
+static bool id_is_in_override_hierarchy(const ID &id,
+ const ID &relationship_parent_id,
+ const ID &override_root_id)
+{
/* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the
* override data. So queries of override data should be done via this, but the actual tree
* element we add is the embedded ID. */
const ID *real_override_id = &id;
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) {
+ /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no
+ * other ID directly uses the embedded one. Should be true, but the debug code adds some checks
+ * to validate this assumption. */
+ real_override_id = &relationship_parent_id;
+
+#ifndef NDEBUG
if (GS(id.name) == ID_KE) {
- Key *key = (Key *)&id;
- real_override_id = key->from;
+ const Key *key = (Key *)&id;
+ BLI_assert(real_override_id == key->from);
}
- else if (id.flag & LIB_EMBEDDED_DATA) {
- /* TODO Needs double-checking if this handles all embedded IDs correctly. */
- real_override_id = cb_data->id_owner;
+ else {
+ BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0);
}
+#endif
}
if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) {
- return IDWALK_RET_NOP;
+ return false;
}
/* Is this ID part of the same override hierarchy? */
- if (real_override_id->override_library->hierarchy_root != &build_data.override_root_id) {
- return IDWALK_RET_NOP;
- }
-
- /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into itself.
- */
- if (build_data.parent_ids.lookup_key_default(&id, nullptr)) {
- return IDWALK_RET_NOP;
- }
-
- /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used multiple
- * times by the same parent. */
- if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) {
- return IDWALK_RET_NOP;
+ if (real_override_id->override_library->hierarchy_root != &override_root_id) {
+ return false;
}
- TreeElement *new_te = outliner_add_element(&build_data.space_outliner,
- &build_data.parent_te->getLegacyElement().subtree,
- &id,
- &build_data.parent_te->getLegacyElement(),
- TSE_SOME_ID,
- 0,
- false);
- build_data.sibling_ids.add(&id);
-
- BuildHierarchyForeachIDCbData child_build_data{build_data.bmain,
- build_data.space_outliner,
- build_data.override_root_id,
- tree_element_cast<TreeElementID>(new_te)};
- child_build_data.parent_ids = build_data.parent_ids;
- child_build_data.parent_ids.add(&id);
- child_build_data.sibling_ids.reserve(10);
- BKE_library_foreach_ID_link(
- &build_data.bmain, &id, build_hierarchy_foreach_ID_cb, &child_build_data, IDWALK_READONLY);
-
- return IDWALK_RET_NOP;
+ return true;
}
-void TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_ID(Main *bmain,
- ID &override_root_id,
- TreeElementID &te_id) const
-{
- BuildHierarchyForeachIDCbData build_data{*bmain, space_outliner_, override_root_id, &te_id};
- build_data.parent_ids.add(&override_root_id);
-
- BKE_library_foreach_ID_link(
- bmain, &te_id.get_ID(), build_hierarchy_foreach_ID_cb, &build_data, IDWALK_READONLY);
-}
+/** \} */
} // 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 0f6d6930530..dd6d58ee5a2 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -192,7 +192,7 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type)
Sequence *seq;
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_ensure(scene);
- int timeline_frame = (int)CFRA;
+ int timeline_frame = (int)scene->r.cfra;
int proximity = INT_MAX;
if (!ed || !ed->seqbasep) {
@@ -218,7 +218,7 @@ static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, i
{
Scene *scene = CTX_data_scene(C);
- int timeline_frame = (int)CFRA;
+ int timeline_frame = (int)scene->r.cfra;
/* Effect strips don't need a channel initialized from the mouse. */
if (!(flag & SEQPROP_NOCHAN) && RNA_struct_property_is_set(op->ptr, "channel") == 0) {
diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
index 586cdad7e74..f6561cf07b9 100644
--- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c
+++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
@@ -179,6 +179,7 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
if (max_channel != -1) {
RNA_int_set(drop->ptr, "channel", max_channel);
}
+ SEQ_collection_free(strips);
}
}
}
@@ -233,7 +234,7 @@ static void update_overlay_strip_poistion_data(bContext *C, const int mval[2])
coords->is_intersecting = false;
Sequence dummy_seq = {
.machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len};
- Editing *ed = SEQ_editing_get(scene);
+ Editing *ed = SEQ_editing_ensure(scene);
for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) {
coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq);
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 4b9ff1e170e..eb2e4ef05e5 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -241,329 +241,296 @@ typedef struct WaveVizData {
float pos[2];
float rms_pos;
bool clip;
- bool end;
+ bool draw_line; /* Draw triangle otherwise. */
+ bool final_sample; /* There are no more samples. */
} WaveVizData;
-static int get_section_len(WaveVizData *start, WaveVizData *end)
+static bool seq_draw_waveforms_poll(const bContext *UNUSED(C), SpaceSeq *sseq, Sequence *seq)
{
- int len = 0;
- while (start != end) {
- len++;
- if (start->end) {
- return len;
- }
- start++;
- }
- return len;
-}
+ const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL;
+ const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0;
+ const bool ovelay_option = ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) != 0 ||
+ (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM));
-static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms)
-{
- int strip_len = get_section_len(iter, end);
- if (strip_len > 1) {
- GPU_blend(GPU_BLEND_ALPHA);
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) != 0) {
+ return false;
+ }
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(prim_type, strip_len);
+ if (strip_is_valid && overlays_enabled && ovelay_option) {
+ return true;
+ }
- while (iter != end) {
- if (iter->clip) {
- immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
- }
- else if (use_rms) {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
- }
- else {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
- }
+ return false;
+}
- if (use_rms) {
- immVertex2f(pos, iter->pos[0], iter->rms_pos);
- }
- else {
- immVertex2f(pos, iter->pos[0], iter->pos[1]);
- }
+static void waveform_job_start_if_needed(const bContext *C, Sequence *seq)
+{
+ bSound *sound = seq->sound;
- if (iter->end) {
- /* End of line. */
- iter++;
- strip_len = get_section_len(iter, end);
- if (strip_len != 0) {
- immEnd();
- immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(prim_type, strip_len);
- }
- }
- else {
- iter++;
- }
+ BLI_spin_lock(sound->spinlock);
+ if (!sound->waveform) {
+ /* Load the waveform data if it hasn't been loaded and cached already. */
+ if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
+ /* Prevent sounds from reloading. */
+ sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
+ BLI_spin_unlock(sound->spinlock);
+ sequencer_preview_add_sound(C, seq);
+ }
+ else {
+ BLI_spin_unlock(sound->spinlock);
}
- immEnd();
- immUnbindProgram();
-
- GPU_blend(GPU_BLEND_NONE);
}
+ BLI_spin_unlock(sound->spinlock);
}
-static float clamp_frame_coord_to_pixel(float frame_coord,
- float pixel_frac,
- float frames_per_pixel)
+static size_t get_vertex_count(WaveVizData *waveform_data)
{
- float cur_pixel = (frame_coord / frames_per_pixel);
- float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac;
- if (cur_pixel > new_pixel) {
- new_pixel += 1.0f;
+ bool draw_line = waveform_data->draw_line;
+ size_t length = 0;
+
+ while (waveform_data->draw_line == draw_line && !waveform_data->final_sample) {
+ waveform_data++;
+ length++;
}
- return new_pixel * frames_per_pixel;
+
+ return length;
}
-/**
- * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
- * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
- */
-static void draw_seq_waveform_overlay(View2D *v2d,
- const bContext *C,
- SpaceSeq *sseq,
- Scene *scene,
- Sequence *seq,
- float x1,
- float y1,
- float x2,
- float y2,
- float frames_per_pixel)
+static size_t draw_waveform_segment(WaveVizData *waveform_data, bool use_rms)
{
- if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) ||
- (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
- /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid
- * flickering when moving around the strip.
- * To do this we figure out the fractional offset in pixel space by checking where the
- * window starts.
- * We then append this pixel offset to our strip start coordinate to ensure we are aligned to
- * the screen pixel grid. */
- float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel);
- float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel);
-
- /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
- float x1_offset = max_ff(v2d->cur.xmin, x1_adj);
- float x2_offset = min_ff(v2d->cur.xmax, x2);
-
- /* Calculate how long the strip that is in view is in pixels. */
- int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel);
-
- if (pix_strip_len < 2) {
- return;
- }
+ size_t vertices_done = 0;
+ size_t vertex_count = get_vertex_count(waveform_data);
- bSound *sound = seq->sound;
+ /* Not enough data to draw. */
+ if (vertex_count <= 2) {
+ return vertex_count;
+ }
- BLI_spin_lock(sound->spinlock);
- if (!sound->waveform) {
- /* Load the waveform data if it hasn't been loaded and cached already. */
- if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
- /* Prevent sounds from reloading. */
- sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
- BLI_spin_unlock(sound->spinlock);
- sequencer_preview_add_sound(C, seq);
- }
- else {
- BLI_spin_unlock(sound->spinlock);
- }
- return; /* Nothing to draw. */
- }
- BLI_spin_unlock(sound->spinlock);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPUVertFormat *format = immVertexFormat();
+ GPUPrimType prim_type = waveform_data->draw_line ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(prim_type, vertex_count);
- SoundWaveform *waveform = sound->waveform;
+ while (vertices_done < vertex_count && !waveform_data->final_sample) {
+ /* Color. */
+ if (waveform_data->clip) {
+ immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+ }
+ else if (use_rms) {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
+ }
+ else {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+ }
- /* Waveform could not be built. */
- if (waveform->length == 0) {
- return;
+ /* Vertices. */
+ if (use_rms) {
+ immVertex2f(pos, waveform_data->pos[0], waveform_data->rms_pos);
+ }
+ else {
+ immVertex2f(pos, waveform_data->pos[0], waveform_data->pos[1]);
}
- /* F-Curve lookup is quite expensive, so do this after precondition. */
- FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+ vertices_done++;
+ waveform_data++;
+ }
- WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2,
- "tri_strip");
- WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len,
- "line_strip");
+ immEnd();
+ immUnbindProgram();
- WaveVizData *tri_strip_iter = tri_strip_arr;
- WaveVizData *line_strip_iter = line_strip_arr;
+ GPU_blend(GPU_BLEND_NONE);
- /* The y coordinate for the middle of the strip. */
- float y_mid = (y1 + y2) / 2.0f;
- /* The length from the middle of the strip to the top/bottom. */
- float y_scale = (y2 - y1) / 2.0f;
- float volume = seq->volume;
+ return vertices_done;
+}
- /* Value to keep track if the previous item to be drawn was a line strip. */
- int8_t was_line_strip = -1; /* -1 == no previous value. */
+static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len)
+{
+ size_t items_done = 0;
+ while (items_done < wave_data_len) {
+ if (!waveform_data[items_done].draw_line) { /* Draw RMS. */
+ draw_waveform_segment(&waveform_data[items_done], true);
+ }
+ items_done += draw_waveform_segment(&waveform_data[items_done], false);
+ }
+}
- float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
+static float align_frame_with_pixel(float frame_coord, float frames_per_pixel)
+{
+ return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel;
+}
- /* How many samples do we have for each pixel? */
- float samples_per_pix = samples_per_frame * frames_per_pixel;
+static void write_waveform_data(WaveVizData *waveform_data,
+ const vec2f pos,
+ const float rms,
+ const bool is_clipping,
+ const bool draw_line)
+{
+ waveform_data->pos[0] = pos.x;
+ waveform_data->pos[1] = pos.y;
+ waveform_data->clip = is_clipping;
+ waveform_data->rms_pos = rms;
+ waveform_data->draw_line = draw_line;
+}
- float strip_start_offset = seq->startofs + seq->anim_startofs;
- float start_sample = 0;
+static size_t waveform_append_sample(WaveVizData *waveform_data,
+ vec2f pos,
+ const float value_min,
+ const float value_max,
+ const float y_mid,
+ const float y_scale,
+ const float rms,
+ const bool is_clipping,
+ const bool is_line_strip)
+{
+ size_t data_written = 0;
+ pos.y = y_mid + value_min * y_scale;
+ float rms_value = y_mid + max_ff(-rms, value_min) * y_scale;
+ write_waveform_data(&waveform_data[0], pos, rms_value, is_clipping, is_line_strip);
+ data_written++;
+
+ /* Use `value_max` as second vertex for triangle drawing. */
+ if (!is_line_strip) {
+ pos.y = y_mid + value_max * y_scale;
+ rms_value = y_mid + min_ff(rms, value_max) * y_scale;
+ write_waveform_data(&waveform_data[1], pos, rms_value, is_clipping, is_line_strip);
+ data_written++;
+ }
+ return data_written;
+}
- if (strip_start_offset != 0) {
- /* If start offset is not zero, we need to make sure that we pick the same start sample as if
- * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering
- * when changing start offset as the pixel alignment will not be the same for the drawn
- * samples. */
- strip_start_offset = clamp_frame_coord_to_pixel(
- x1 - strip_start_offset, pixel_frac, frames_per_pixel);
- start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame;
- }
+/**
+ * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
+ * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
+ */
+static void draw_seq_waveform_overlay(
+ const bContext *C, ARegion *region, Sequence *seq, float x1, float y1, float x2, float y2)
+{
+ const View2D *v2d = &region->v2d;
+ Scene *scene = CTX_data_scene(C);
- start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
- /* If we scrolled the start off-screen, then the start sample should be at the first visible
- * sample. */
- start_sample += (x1_offset - x1_adj) * samples_per_frame;
+ const float frames_per_pixel = BLI_rctf_size_x(&region->v2d.cur) / region->winx;
+ const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
+ float samples_per_pixel = samples_per_frame * frames_per_pixel;
- for (int i = 0; i < pix_strip_len; i++) {
- float sample_offset = start_sample + i * samples_per_pix;
- int p = sample_offset;
+ /* Align strip start with nearest pixel to prevent waveform flickering. */
+ const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel);
+ /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
+ const float frame_start = max_ff(v2d->cur.xmin, x1_aligned);
+ const float frame_end = min_ff(v2d->cur.xmax, x2);
+ const int pixels_to_draw = round_fl_to_int((frame_end - frame_start) / frames_per_pixel);
- if (p < 0) {
- continue;
- }
+ if (pixels_to_draw < 2) {
+ return; /* Not much to draw, exit before running job. */
+ }
- if (p >= waveform->length) {
- break;
- }
+ waveform_job_start_if_needed(C, seq);
- float value_min = waveform->data[p * 3];
- float value_max = waveform->data[p * 3 + 1];
- float rms = waveform->data[p * 3 + 2];
-
- if (p + 1 < waveform->length) {
- /* Use simple linear interpolation. */
- float f = sample_offset - p;
- value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3];
- value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4];
- rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5];
- if (samples_per_pix > 1.0f) {
- /* We need to sum up the values we skip over until the next step. */
- float next_pos = sample_offset + samples_per_pix;
- int end_idx = next_pos;
-
- for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) {
- value_min = min_ff(value_min, waveform->data[j * 3]);
- value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
- rms = max_ff(rms, waveform->data[j * 3 + 2]);
- }
- }
- }
+ SoundWaveform *waveform = seq->sound->waveform;
+ if (waveform == NULL || waveform->length == 0) {
+ return; /* Waveform was not built. */
+ }
- if (fcu && !BKE_fcurve_is_empty(fcu)) {
- float evaltime = x1_offset + (i * frames_per_pixel);
- volume = evaluate_fcurve(fcu, evaltime);
- CLAMP_MIN(volume, 0.0f);
- }
+ /* F-Curve lookup is quite expensive, so do this after precondition. */
+ FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+ WaveVizData *waveform_data = MEM_callocN(sizeof(WaveVizData) * pixels_to_draw * 3, __func__);
+ size_t wave_data_len = 0;
- value_min *= volume;
- value_max *= volume;
- rms *= volume;
+ /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */
+ const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs,
+ frames_per_pixel);
+ float start_sample = strip_offset * samples_per_frame;
+ start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
+ /* Add off-screen part of strip to offset. */
+ start_sample += (frame_start - x1_aligned) * samples_per_frame;
- bool clipping = false;
+ for (int i = 0; i < pixels_to_draw; i++) {
+ float sample = start_sample + i * samples_per_pixel;
+ int sample_index = round_fl_to_int(sample);
- if (value_max > 1 || value_min < -1) {
- clipping = true;
+ if (sample_index < 0) {
+ continue;
+ }
- CLAMP_MAX(value_max, 1.0f);
- CLAMP_MIN(value_min, -1.0f);
- }
+ if (sample_index >= waveform->length) {
+ break;
+ }
- bool is_line_strip = (value_max - value_min < 0.05f);
-
- if (!ELEM(was_line_strip, -1, is_line_strip)) {
- /* If the previously added strip type isn't the same as the current one,
- * add transition areas so they transition smoothly between each other. */
- if (is_line_strip) {
- /* This will be a line strip, end the tri strip. */
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = tri_strip_iter->pos[1];
- tri_strip_iter->end = true;
-
- /* End of section. */
- tri_strip_iter++;
-
- /* Check if we are at the end.
- * If so, skip one point line. */
- if (i + 1 == pix_strip_len) {
- continue;
- }
- }
- else {
- /* This will be a tri strip. */
- line_strip_iter--;
- tri_strip_iter->pos[0] = line_strip_iter->pos[0];
- tri_strip_iter->pos[1] = line_strip_iter->pos[1];
- tri_strip_iter->clip = line_strip_iter->clip;
- tri_strip_iter->rms_pos = line_strip_iter->pos[1];
- tri_strip_iter++;
-
- /* Check if line had only one point. */
- line_strip_iter--;
- if (line_strip_iter < line_strip_arr || line_strip_iter->end) {
- /* Only one point, skip it. */
- line_strip_iter++;
- }
- else {
- /* End of section. */
- line_strip_iter++;
- line_strip_iter->end = true;
- line_strip_iter++;
- }
+ float value_min = waveform->data[sample_index * 3];
+ float value_max = waveform->data[sample_index * 3 + 1];
+ float rms = waveform->data[sample_index * 3 + 2];
+
+ if (sample_index + 1 < waveform->length) {
+ /* Use simple linear interpolation. */
+ float f = sample - sample_index;
+ value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3];
+ value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4];
+ rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5];
+ if (samples_per_pixel > 1.0f) {
+ /* We need to sum up the values we skip over until the next step. */
+ float next_pos = sample + samples_per_pixel;
+ int end_idx = next_pos;
+
+ for (int j = sample_index + 1; (j < waveform->length) && (j < end_idx); j++) {
+ value_min = min_ff(value_min, waveform->data[j * 3]);
+ value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
+ rms = max_ff(rms, waveform->data[j * 3 + 2]);
}
}
+ }
- was_line_strip = is_line_strip;
-
- if (is_line_strip) {
- line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- line_strip_iter->pos[1] = y_mid + value_min * y_scale;
- line_strip_iter->clip = clipping;
- line_strip_iter++;
- }
- else {
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale;
- tri_strip_iter++;
-
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_max * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale;
- tri_strip_iter++;
- }
+ float volume = seq->volume;
+ if (fcu && !BKE_fcurve_is_empty(fcu)) {
+ float evaltime = frame_start + (i * frames_per_pixel);
+ volume = evaluate_fcurve(fcu, evaltime);
+ CLAMP_MIN(volume, 0.0f);
}
- WaveVizData *tri_strip_end = tri_strip_iter;
- WaveVizData *line_strip_end = line_strip_iter;
+ value_min *= volume;
+ value_max *= volume;
+ rms *= volume;
+
+ bool is_clipping = false;
+
+ if (value_max > 1 || value_min < -1) {
+ is_clipping = true;
- tri_strip_iter = tri_strip_arr;
- line_strip_iter = line_strip_arr;
+ CLAMP_MAX(value_max, 1.0f);
+ CLAMP_MIN(value_min, -1.0f);
+ }
- draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false);
- draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false);
- draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true);
+ bool is_line_strip = (value_max - value_min < 0.05f);
+ /* The y coordinate for the middle of the strip. */
+ float y_mid = (y1 + y2) / 2.0f;
+ /* The length from the middle of the strip to the top/bottom. */
+ float y_scale = (y2 - y1) / 2.0f;
- MEM_freeN(tri_strip_arr);
- MEM_freeN(line_strip_arr);
+ vec2f pos = {frame_start + i * frames_per_pixel, y_mid + value_min * y_scale};
+ WaveVizData *new_data = &waveform_data[wave_data_len];
+ wave_data_len += waveform_append_sample(
+ new_data, pos, value_min, value_max, y_mid, y_scale, rms, is_clipping, is_line_strip);
}
+
+ /* Terminate array, so `get_segment_length()` can know when to stop. */
+ waveform_data[wave_data_len].final_sample = true;
+ draw_waveform(waveform_data, wave_data_len);
+ MEM_freeN(waveform_data);
}
+/*
+static size_t *waveform_append(WaveVizData *waveform_data,
+ vec2f pos,
+ const float value_min,
+ const float value_max,
+ const float y_mid,
+ const float y_scale,
+ const float rms,
+ const bool is_clipping,
+ const bool is_line_strip)
+*/
+
static void drawmeta_contents(Scene *scene,
Sequence *seqm,
float x1,
@@ -1430,18 +1397,9 @@ static void draw_seq_strip(const bContext *C,
}
/* Draw sound strip waveform. */
- if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) &&
- (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) {
- draw_seq_waveform_overlay(v2d,
- C,
- sseq,
- scene,
- seq,
- x1,
- y_threshold ? y1 + 0.05f : y1,
- x2,
- y_threshold ? text_margin_y : y2,
- BLI_rctf_size_x(&region->v2d.cur) / region->winx);
+ if (seq_draw_waveforms_poll(C, sseq, seq)) {
+ draw_seq_waveform_overlay(
+ C, region, seq, x1, y_threshold ? y1 + 0.05f : y1, x2, y_threshold ? text_margin_y : y2);
}
/* Draw locked state. */
if (SEQ_transform_is_locked(channels, seq)) {
@@ -1762,8 +1720,7 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *region, S
// ED_mask_get_size(C, &width, &height);
//Scene *scene = CTX_data_scene(C);
- width = (scene->r.size * scene->r.xsch) / 100;
- height = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &width, &height);
ED_mask_draw_region(mask,
region,
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index d507c9ae6bc..cb95e9a75de 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -265,7 +265,7 @@ static int sequencer_gap_remove_exec(bContext *C, wmOperator *op)
const bool do_all = RNA_boolean_get(op->ptr, "all");
const Editing *ed = SEQ_editing_get(scene);
- SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all);
+ SEQ_edit_remove_gaps(scene, ed->seqbasep, scene->r.cfra, do_all);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -304,7 +304,7 @@ static int sequencer_gap_insert_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
const int frames = RNA_int_get(op->ptr, "frames");
const Editing *ed = SEQ_editing_get(scene);
- SEQ_transform_offset_after_frame(scene, ed->seqbasep, frames, CFRA);
+ SEQ_transform_offset_after_frame(scene, ed->seqbasep, frames, scene->r.cfra);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -423,7 +423,7 @@ static int sequencer_snap_invoke(bContext *C, wmOperator *op, const wmEvent *UNU
int snap_frame;
- snap_frame = CFRA;
+ snap_frame = scene->r.cfra;
RNA_int_set(op->ptr, "frame", snap_frame);
return sequencer_snap_exec(C, op);
@@ -1465,7 +1465,7 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev
View2D *v2d = UI_view2d_fromcontext(C);
int split_side = RNA_enum_get(op->ptr, "side");
- int split_frame = CFRA;
+ int split_frame = scene->r.cfra;
if (split_side == SEQ_SIDE_MOUSE) {
if (ED_operator_sequencer_active(C) && v2d) {
@@ -1703,8 +1703,10 @@ static int sequencer_delete_exec(bContext *C, wmOperator *op)
static int sequencer_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ ListBase *markers = &scene->markers;
- if (region->regiontype == RGN_TYPE_WINDOW) {
+ if (region->regiontype == RGN_TYPE_WINDOW && !BLI_listbase_is_empty(markers)) {
/* Bounding box of 30 pixels is used for markers shortcuts,
* prevent conflict with markers shortcuts here.
*/
@@ -2091,12 +2093,12 @@ static bool strip_jump_internal(Scene *scene,
const bool do_center)
{
bool changed = false;
- int timeline_frame = CFRA;
+ int timeline_frame = scene->r.cfra;
int next_frame = SEQ_time_find_next_prev_edit(
scene, timeline_frame, side, do_skip_mute, do_center, false);
if (next_frame != timeline_frame) {
- CFRA = next_frame;
+ scene->r.cfra = next_frame;
changed = true;
}
@@ -3408,7 +3410,7 @@ static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op)
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
- const int timeline_frame = CFRA;
+ const int timeline_frame = scene->r.cfra;
StripElem *strip_elem = SEQ_render_give_stripelem(scene, seq, timeline_frame);
if (strip_elem == NULL) {
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index cfdbeaf0658..4aaa3aeb2ff 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -589,8 +589,10 @@ 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_time_right_handle_frame_get(scene, seq_iter) <= CFRA)) ||
- ((x >= CFRA) && (SEQ_time_left_handle_frame_get(scene, seq_iter) >= CFRA))) {
+ if (((x < scene->r.cfra) &&
+ (SEQ_time_right_handle_frame_get(scene, seq_iter) <= scene->r.cfra)) ||
+ ((x >= scene->r.cfra) &&
+ (SEQ_time_left_handle_frame_get(scene, seq_iter) >= scene->r.cfra))) {
/* Select left or right. */
seq_iter->flag |= SELECT;
recurs_sel_seq(seq_iter);
@@ -603,8 +605,8 @@ static void sequencer_select_side_of_frame(const bContext *C,
TimeMarker *tmarker;
for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) {
- if (((x < CFRA) && (tmarker->frame <= CFRA)) ||
- ((x >= CFRA) && (tmarker->frame >= CFRA))) {
+ if (((x < scene->r.cfra) && (tmarker->frame <= scene->r.cfra)) ||
+ ((x >= scene->r.cfra) && (tmarker->frame >= scene->r.cfra))) {
tmarker->flag |= SELECT;
}
else {
@@ -1443,7 +1445,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
if (extend == false) {
ED_sequencer_deselect_all(scene);
}
- const int timeline_frame = CFRA;
+ const int timeline_frame = scene->r.cfra;
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
bool test = false;
switch (side) {
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 4d32c00109a..78fa8c379d9 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -12,6 +12,7 @@
#include "DNA_scene_types.h"
#include "BKE_context.h"
+#include "BKE_scene.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -174,8 +175,7 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
seq_reset_imageofs(sseq);
- imgwidth = (scene->r.size * scene->r.xsch) / 100;
- imgheight = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &imgwidth, &imgheight);
/* Apply aspect, doesn't need to be that accurate. */
imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp));
@@ -227,11 +227,11 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op)
float ratio = RNA_float_get(op->ptr, "ratio");
- float winx = (int)(rd->size * rd->xsch) / 100;
- float winy = (int)(rd->size * rd->ysch) / 100;
+ int winx, winy;
+ BKE_render_resolution(rd, false, &winx, &winy);
- float facx = BLI_rcti_size_x(&v2d->mask) / winx;
- float facy = BLI_rcti_size_y(&v2d->mask) / winy;
+ float facx = BLI_rcti_size_x(&v2d->mask) / (float)winx;
+ float facy = BLI_rcti_size_y(&v2d->mask) / (float)winy;
BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f));
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index 76c7131e268..6a09d3f84c6 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -27,9 +27,8 @@ class DataSource {
* column. (This can be made a bit more generic in the future when necessary.)
*/
virtual void foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> /*fn*/) const
{
- UNUSED_VARS(fn);
}
/**
@@ -37,9 +36,8 @@ class DataSource {
* returned.
*/
virtual std::unique_ptr<ColumnValues> get_column_values(
- const SpreadsheetColumnID &column_id) const
+ const SpreadsheetColumnID & /*column_id*/) const
{
- UNUSED_VARS(column_id);
return {};
}
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 f5315b616d0..fdcf9798b7f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -1,8 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_index_mask_ops.hh"
#include "BLI_virtual_array.hh"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
+#include "BKE_curves.hh"
#include "BKE_editmesh.h"
#include "BKE_geometry_fields.hh"
#include "BKE_global.h"
@@ -20,6 +23,7 @@
#include "DEG_depsgraph_query.h"
+#include "ED_curves_sculpt.h"
#include "ED_spreadsheet.h"
#include "NOD_geometry_nodes_eval_log.hh"
@@ -64,7 +68,12 @@ 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_num(domain_) == 0) {
+ if (!component_->attributes().has_value()) {
+ return;
+ }
+ const bke::AttributeAccessor attributes = *component_->attributes();
+
+ if (attributes.domain_size(domain_) == 0) {
return;
}
@@ -73,8 +82,9 @@ void GeometryDataSource::foreach_default_column_ids(
}
extra_columns_.foreach_default_column_ids(fn);
- component_->attribute_foreach(
- [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+
+ attributes.for_all(
+ [&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
return true;
}
@@ -113,7 +123,11 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
- const int domain_num = component_->attribute_domain_num(domain_);
+ if (!component_->attributes().has_value()) {
+ return {};
+ }
+ const bke::AttributeAccessor attributes = *component_->attributes();
+ const int domain_num = attributes.domain_size(domain_);
if (domain_num == 0) {
return {};
}
@@ -199,7 +213,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
}
}
- bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
+ bke::GAttributeReader attribute = attributes.lookup(column_id.name);
if (!attribute) {
return {};
}
@@ -213,87 +227,112 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
int GeometryDataSource::tot_rows() const
{
- return component_->attribute_domain_num(domain_);
+ if (!component_->attributes().has_value()) {
+ return {};
+ }
+ const bke::AttributeAccessor attributes = *component_->attributes();
+ return attributes.domain_size(domain_);
}
-/**
- * Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
- */
bool GeometryDataSource::has_selection_filter() const
{
Object *object_orig = DEG_get_original_object(object_eval_);
- if (object_orig->type != OB_MESH) {
- return false;
- }
- if (object_orig->mode != OB_MODE_EDIT) {
- return false;
- }
- if (component_->type() != GEO_COMPONENT_TYPE_MESH) {
- return false;
- }
-
- return true;
-}
-
-static IndexMask index_mask_from_bool_array(const VArray<bool> &selection,
- Vector<int64_t> &indices)
-{
- for (const int i : selection.index_range()) {
- if (selection[i]) {
- indices.append(i);
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ if (object_orig->type != OB_MESH) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_EDIT) {
+ return false;
+ }
+ return true;
}
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (object_orig->type != OB_CURVES) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_SCULPT_CURVES) {
+ return false;
+ }
+ return true;
+ }
+ default:
+ return false;
}
- return IndexMask(indices);
}
IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const
{
std::lock_guard lock{mutex_};
+ const IndexMask full_range(this->tot_rows());
+
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ BLI_assert(object_eval_->type == OB_MESH);
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ const Mesh *mesh_eval = geometry_set_.get_mesh_for_read();
+ const bke::AttributeAccessor attributes_eval = bke::mesh_attributes(*mesh_eval);
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ 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 = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm, orig_indices](int vertex_index) -> bool {
+ const int i_orig = orig_indices[vertex_index];
+ if (i_orig < 0) {
+ return false;
+ }
+ if (i_orig >= bm->totvert) {
+ return false;
+ }
+ const BMVert *vert = BM_vert_at_index(bm, i_orig);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 1024, indices);
+ }
- BLI_assert(object_eval_->mode == OB_MODE_EDIT);
- BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
- Object *object_orig = DEG_get_original_object(object_eval_);
- const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
- const Mesh *mesh_eval = mesh_component->get_for_read();
- Mesh *mesh_orig = (Mesh *)object_orig->data;
- BMesh *bm = mesh_orig->edit_mesh->bm;
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-
- 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>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm, orig_indices](int vertex_index) -> bool {
- const int i_orig = orig_indices[vertex_index];
- if (i_orig < 0) {
- return false;
- }
- if (i_orig >= bm->totvert) {
- return false;
- }
- BMVert *vert = bm->vtable[i_orig];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_from_bool_array(selection, indices);
- }
-
- if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_from_bool_array(selection, indices);
- }
-
- return IndexMask(mesh_eval->totvert);
+ if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ VArray<bool> selection = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm](int vertex_index) -> bool {
+ const BMVert *vert = BM_vert_at_index(bm, vertex_index);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 2048, indices);
+ }
+
+ return full_range;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ BLI_assert(object_eval_->type == OB_CURVES);
+ BLI_assert(object_eval_->mode == OB_MODE_SCULPT_CURVES);
+ const CurveComponent &component = static_cast<const CurveComponent &>(*component_);
+ const Curves &curves_id = *component.get_for_read();
+ switch (domain_) {
+ case ATTR_DOMAIN_POINT:
+ return sculpt_paint::retrieve_selected_points(curves_id, indices);
+ case ATTR_DOMAIN_CURVE:
+ return sculpt_paint::retrieve_selected_curves(curves_id, indices);
+ default:
+ BLI_assert_unreachable();
+ }
+ return full_range;
+ }
+ default:
+ return full_range;
+ }
}
void VolumeDataSource::foreach_default_column_ids(
@@ -398,10 +437,15 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
geometry_set.get_component_for_write<PointCloudComponent>();
pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
}
+ else if (object_orig->type == OB_CURVES) {
+ const Curves &curves_id = *(const Curves *)object_orig->data;
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ curve_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly);
+ }
}
else {
if (object_eval->mode == OB_MODE_EDIT && object_eval->type == OB_MESH) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval);
if (mesh == nullptr) {
return geometry_set;
}
@@ -472,18 +516,6 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
}
}
-static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
- return (GeometryComponentType)sspreadsheet->geometry_component_type;
- }
- if (object_eval->type == OB_POINTCLOUD) {
- return GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- return GEO_COMPONENT_TYPE_MESH;
-}
-
class GeometryComponentCacheKey : public SpreadsheetCache::Key {
public:
/* Use the pointer to the geometry component as a key to detect when the geometry changed. */
@@ -527,9 +559,9 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
std::make_unique<GeometryComponentCacheKey>(component));
const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
- const int domain_num = component.attribute_domain_num(domain);
+ const int domain_num = component.attribute_domain_size(domain);
for (const auto item : fields_to_show.items()) {
- StringRef name = item.key;
+ const StringRef name = item.key;
const GField &field = item.value;
/* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
@@ -543,7 +575,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
return evaluated_array;
});
- r_extra_columns.add(std::move(name), evaluated_array.as_span());
+ r_extra_columns.add(name, evaluated_array.as_span());
}
}
@@ -551,9 +583,9 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
- const GeometryComponentType component_type = get_display_component_type(C, object_eval);
+ const GeometryComponentType component_type = GeometryComponentType(
+ sspreadsheet->geometry_component_type);
GeometrySet geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, object_eval);
-
if (!geometry_set.has(component_type)) {
return {};
}
@@ -563,10 +595,10 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
add_fields_as_extra_columns(sspreadsheet, component, extra_columns);
if (component_type == GEO_COMPONENT_TYPE_VOLUME) {
- return std::make_unique<VolumeDataSource>(geometry_set);
+ return std::make_unique<VolumeDataSource>(std::move(geometry_set));
}
return std::make_unique<GeometryDataSource>(
- object_eval, geometry_set, component_type, domain, std::move(extra_columns));
+ object_eval, std::move(geometry_set), component_type, domain, std::move(extra_columns));
}
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 04b4f6d8d06..71bc4768949 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -68,10 +68,6 @@ class GeometryDataSource : public DataSource {
return object_eval_;
}
- /**
- * Only data sets corresponding to mesh objects in edit mode currently support selection
- * filtering.
- */
bool has_selection_filter() const override;
IndexMask apply_selection_filter(Vector<int64_t> &indices) const;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
index ee22f4173ab..146b6091bde 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
@@ -145,7 +145,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row)
* to the right side of the number, which it didn't have with the button. */
char element_count[7];
BLI_str_format_decimal_unit(element_count, *count);
- UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count);
+ UI_but_hint_drawstr_set((uiBut *)this->view_item_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_num(*domain_);
+ return component->attribute_domain_size(*domain_);
}
return 0;
diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c
index f7d7f9c6238..4b3ee1c15c7 100644
--- a/source/blender/editors/space_text/text_format_lua.c
+++ b/source/blender/editors/space_text/text_format_lua.c
@@ -264,7 +264,7 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_
cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
*fmt = FMT_TYPE_STRING;
}
- /* White-space (all ws. has been converted to spaces). */
+ /* White-space (all white-space has been converted to spaces). */
else if (*str == ' ') {
*fmt = FMT_TYPE_WHITESPACE;
}
@@ -287,18 +287,19 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_
else if ((*str != '#') && text_check_delim(*str)) {
*fmt = FMT_TYPE_SYMBOL;
}
- /* Identifiers and other text (no previous ws. or delims. so text continues) */
+ /* Identifiers and other text (no previous white-space/delimiters so text continues). */
else if (prev == FMT_TYPE_DEFAULT) {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = FMT_TYPE_DEFAULT;
}
- /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
+ /* Not white-space, a digit, punctuation, or continuing text.
+ * Must be new, check for special words. */
else {
- /* Keep aligned args for readability. */
+ /* Keep aligned arguments for readability. */
/* clang-format off */
- /* Special vars(v) or built-in keywords(b) */
- /* keep in sync with 'txtfmt_osl_format_identifier()' */
+ /* Special `vars(v)` or built-in `keywords(b)` */
+ /* keep in sync with `txtfmt_osl_format_identifier()`. */
if ((i = txtfmt_lua_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL;
} else if ((i = txtfmt_lua_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD;
}
diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c
index 27dc1174c49..575eadeee66 100644
--- a/source/blender/editors/space_text/text_format_osl.c
+++ b/source/blender/editors/space_text/text_format_osl.c
@@ -285,7 +285,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_
cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
*fmt = FMT_TYPE_STRING;
}
- /* White-space (all ws. has been converted to spaces). */
+ /* White-space (all white-space has been converted to spaces). */
else if (*str == ' ') {
*fmt = FMT_TYPE_WHITESPACE;
}
@@ -298,18 +298,19 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_
else if ((*str != '#') && text_check_delim(*str)) {
*fmt = FMT_TYPE_SYMBOL;
}
- /* Identifiers and other text (no previous ws. or delims. so text continues) */
+ /* Identifiers and other text (no previous white-space or delimiters. so text continues). */
else if (prev == FMT_TYPE_DEFAULT) {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = FMT_TYPE_DEFAULT;
}
- /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
+ /* Not white-space, a digit, punctuation, or continuing text.
+ * Must be new, check for special words. */
else {
- /* Keep aligned args for readability. */
+ /* Keep aligned arguments for readability. */
/* clang-format off */
/* Special vars(v) or built-in keywords(b) */
- /* keep in sync with 'txtfmt_osl_format_identifier()' */
+ /* keep in sync with `txtfmt_osl_format_identifier()`. */
if ((i = txtfmt_osl_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL;
} else if ((i = txtfmt_osl_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD;
} else if ((i = txtfmt_osl_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED;
diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c
index 7c2f098f0fa..7c2c4829ad3 100644
--- a/source/blender/editors/space_text/text_format_pov.c
+++ b/source/blender/editors/space_text/text_format_pov.c
@@ -857,7 +857,7 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_
cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
*fmt = FMT_TYPE_STRING;
}
- /* White-space (all ws. has been converted to spaces). */
+ /* White-space (all white-space has been converted to spaces). */
else if (*str == ' ') {
*fmt = FMT_TYPE_WHITESPACE;
}
@@ -880,18 +880,19 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_
else if (text_check_delim(*str)) {
*fmt = FMT_TYPE_SYMBOL;
}
- /* Identifiers and other text (no previous ws. or delims. so text continues) */
+ /* Identifiers and other text (no previous white-space/delimiters so text continues). */
else if (prev == FMT_TYPE_DEFAULT) {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = FMT_TYPE_DEFAULT;
}
- /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
+ /* Not white-space, a digit, punctuation, or continuing text.
+ * Must be new, check for special words. */
else {
- /* Keep aligned args for readability. */
+ /* Keep aligned arguments for readability. */
/* clang-format off */
/* Special vars(v) or built-in keywords(b) */
- /* keep in sync with 'txtfmt_pov_format_identifier()' */
+ /* keep in sync with `txtfmt_pov_format_identifier()`. */
if ((i = txtfmt_pov_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL;
} else if ((i = txtfmt_pov_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD;
} else if ((i = txtfmt_pov_find_reserved_keywords(str)) != -1) { prev = FMT_TYPE_RESERVED;
diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c
index 6844f08cca6..dda3b7089fe 100644
--- a/source/blender/editors/space_text/text_format_pov_ini.c
+++ b/source/blender/editors/space_text/text_format_pov_ini.c
@@ -435,7 +435,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool
cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
*fmt = FMT_TYPE_STRING;
}
- /* White-space (all ws. has been converted to spaces). */
+ /* White-space (all white-space has been converted to spaces). */
else if (*str == ' ') {
*fmt = FMT_TYPE_WHITESPACE;
}
@@ -458,18 +458,19 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool
else if ((*str != '#') && text_check_delim(*str)) {
*fmt = FMT_TYPE_SYMBOL;
}
- /* Identifiers and other text (no previous ws. or delims. so text continues) */
+ /* Identifiers and other text (no previous white-space/delimiters so text continues). */
else if (prev == FMT_TYPE_DEFAULT) {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = FMT_TYPE_DEFAULT;
}
- /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
+ /* Not white-space, a digit, punctuation, or continuing text.
+ * Must be new, check for special words */
else {
- /* Keep aligned args for readability. */
+ /* Keep aligned arguments for readability. */
/* clang-format off */
/* Special vars(v) or built-in keywords(b) */
- /* keep in sync with 'txtfmt_ini_format_identifier()' */
+ /* keep in sync with `txtfmt_ini_format_identifier()`. */
if ((i = txtfmt_ini_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD;
} else if ((i = txtfmt_ini_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED;
}
diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index 47d0168195b..28f536ffa98 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -59,9 +59,9 @@ static int txtfmt_py_find_builtinfunc(const char *string)
/* clang-format off */
if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len;
- } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len;
+ } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len;
@@ -425,7 +425,7 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n
}
*fmt = FMT_TYPE_STRING;
}
- /* White-space (all ws. has been converted to spaces). */
+ /* White-space (all white-space has been converted to spaces). */
else if (*str == ' ') {
*fmt = FMT_TYPE_WHITESPACE;
}
@@ -447,18 +447,19 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n
else if ((*str != '@') && text_check_delim(*str)) {
*fmt = FMT_TYPE_SYMBOL;
}
- /* Identifiers and other text (no previous ws. or delims. so text continues) */
+ /* Identifiers and other text (no previous white-space/delimiters so text continues). */
else if (prev == FMT_TYPE_DEFAULT) {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = FMT_TYPE_DEFAULT;
}
- /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
+ /* Not white-space, a digit, punctuation, or continuing text.
+ * Must be new, check for special words. */
else {
- /* Keep aligned args for readability. */
+ /* Keep aligned arguments for readability. */
/* clang-format off */
/* Special vars(v) or built-in keywords(b) */
- /* keep in sync with 'txtfmt_py_format_identifier()' */
+ /* keep in sync with `txtfmt_py_format_identifier()`. */
if ((i = txtfmt_py_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL;
} else if ((i = txtfmt_py_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD;
} else if ((i = txtfmt_py_find_decorator(str)) != -1) { prev = FMT_TYPE_DIRECTIVE;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 05d51cf6362..33219092d20 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -3396,7 +3396,8 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm
return OPERATOR_PASS_THROUGH;
}
- if (!(event->ascii >= '0' && event->ascii <= '9')) {
+ const char event_ascii = WM_event_utf8_to_ascii(event);
+ if (!(event_ascii >= '0' && event_ascii <= '9')) {
return OPERATOR_PASS_THROUGH;
}
@@ -3406,7 +3407,7 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm
}
jump_to *= 10;
- jump_to += (int)(event->ascii - '0');
+ jump_to += (int)(event_ascii - '0');
txt_move_toline(text, jump_to - 1, 0);
last_jump = time;
@@ -3495,16 +3496,8 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
char str[BLI_UTF8_MAX + 1];
- size_t len;
-
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
- }
+ const size_t len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c
index e9a9e690e60..bc68de1dfce 100644
--- a/source/blender/editors/space_topbar/space_topbar.c
+++ b/source/blender/editors/space_topbar/space_topbar.c
@@ -155,6 +155,9 @@ static void topbar_header_listener(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
+ case NC_WORKSPACE:
+ ED_region_tag_redraw(region);
+ break;
case NC_SPACE:
if (wmn->data == ND_SPACE_INFO) {
ED_region_tag_redraw(region);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 9ed2fec96db..a423a842019 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1300,6 +1300,10 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
+ case NC_WORKSPACE:
+ /* In case the region displays workspace settings. */
+ ED_region_tag_redraw(region);
+ break;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index a879a05d41a..4a1bd6ba945 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -597,9 +597,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
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;
- snap_elements |= SCE_SNAP_MODE_FACE;
+ if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
+ snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
}
snap_data->is_enabled = true;
@@ -614,7 +614,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
snap_data->snap_elem = SCE_SNAP_MODE_NONE;
return;
}
- snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
+ snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
}
#endif
@@ -649,6 +649,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
.edit_mode_type = edit_mode_type,
.use_occlusion_test = use_occlusion_test,
},
+ NULL,
mval_fl,
prev_co,
&dist_px,
@@ -744,7 +745,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
(SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
snap_elem_index[1] = index;
}
- else if (snap_elem == SCE_SNAP_MODE_FACE) {
+ else if (snap_elem == SCE_SNAP_MODE_FACE_RAYCAST) {
snap_elem_index[2] = index;
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index b9e4c19295d..df5ff163cf2 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1294,7 +1294,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
static void draw_selected_name(
Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset)
{
- const int cfra = CFRA;
+ const int cfra = scene->r.cfra;
const char *msg_pin = " (Pinned)";
const char *msg_sep = " : ";
@@ -2247,16 +2247,16 @@ void view3d_depths_rect_create(ARegion *region, rcti *rect, ViewDepths *r_d)
static ViewDepths *view3d_depths_create(ARegion *region)
{
ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths");
- d->w = region->winx;
- d->h = region->winy;
{
GPUViewport *viewport = WM_draw_region_get_viewport(region);
GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport);
uint32_t *int_depths = GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0);
+ d->w = GPU_texture_width(depth_tx);
+ d->h = GPU_texture_height(depth_tx);
d->depths = (float *)int_depths;
/* Convert in-place. */
- int pixel_count = GPU_texture_width(depth_tx) * GPU_texture_height(depth_tx);
+ int pixel_count = d->w * d->h;
for (int i = 0; i < pixel_count; i++) {
d->depths[i] = (int_depths[i] >> 8u) / (float)0xFFFFFF;
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 5fbdbef676c..d6ddd6d044e 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -413,7 +413,9 @@ static void view3d_set_1_to_1_viewborder(Scene *scene,
{
RegionView3D *rv3d = region->regiondata;
float size[2];
- int im_width = (scene->r.size * scene->r.xsch) / 100;
+
+ int im_width, im_height;
+ BKE_render_resolution(&scene->r, false, &im_width, &im_height);
ED_view3d_calc_camera_border_size(scene, depsgraph, region, v3d, rv3d, size);
@@ -908,12 +910,13 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
CTX_data_ensure_evaluated_depsgraph(C),
region,
v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 9aae30c4a7e..62dc461e05c 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -357,11 +357,12 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
depsgraph,
ruler_info->region,
v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_CAGE,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
@@ -522,7 +523,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
}
- gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
BKE_gpencil_free_strokes(gpf);
for (ruler_item = gzgroup_ruler_item_first_get(gzgroup); ruler_item;
@@ -576,7 +577,7 @@ static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup)
gpl = view3d_ruler_layer_get(scene->gpd);
if (gpl) {
bGPDframe *gpf;
- gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
if (gpf) {
bGPDstroke *gps;
for (gps = gpf->strokes.first; gps; gps = gps->next) {
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index 471231b5f27..f1e9ac22882 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -659,7 +659,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event)
if (event->type == TIMER && event->customdata == walk->timer) {
walk->redraw = true;
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
#ifdef USE_TABLET_SUPPORT
if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) {
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 306394ce53d..85b1af8e55d 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -633,7 +633,7 @@ bool ED_view3d_camera_autokey(const Scene *scene,
const bool do_translate)
{
if (autokeyframe_cfra_can_key(scene, id_key)) {
- const float cfra = (float)CFRA;
+ const float cfra = (float)scene->r.cfra;
ListBase dsources = {NULL, NULL};
/* add data-source override for the camera object */
@@ -1492,10 +1492,12 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain,
depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) {
ObjectTfmProtectedChannels obtfm;
float obmat_new[4][4];
+ bool is_ortho_camera = false;
if ((camera_ob_eval->type == OB_CAMERA) &&
(((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
((Camera *)camera_ob->data)->ortho_scale = scale;
+ is_ortho_camera = true;
}
copy_m4_m4(obmat_new, camera_ob_eval->obmat);
@@ -1508,6 +1510,9 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain,
/* notifiers */
DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM);
+ if (is_ortho_camera) {
+ DEG_id_tag_update_ex(bmain, camera_ob->data, ID_RECALC_PARAMETERS);
+ }
return true;
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 68c4f4e76ca..6984dcb18d4 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC
transform_convert_mesh_edge.c
transform_convert_mesh_skin.c
transform_convert_mesh_uv.c
+ transform_convert_mesh_vert_cdata.c
transform_convert_nla.c
transform_convert_node.c
transform_convert_object.c
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 960d9a25ca7..8dcbf07b776 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -525,7 +525,8 @@ static void viewRedrawPost(bContext *C, TransInfo *t)
UVCALC_TRANSFORM_CORRECT_SLIDE :
UVCALC_TRANSFORM_CORRECT;
- if ((t->data_type == TC_MESH_VERTS) && (t->settings->uvcalc_flag & uvcalc_correct_flag)) {
+ if ((t->data_type == &TransConvertType_Mesh) &&
+ (t->settings->uvcalc_flag & uvcalc_correct_flag)) {
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
}
@@ -847,7 +848,7 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
return false;
}
- if (t->data_type == TC_SEQ_IMAGE_DATA) {
+ if (t->data_type == &TransConvertType_SequencerImage) {
/* Setup the 2d msg string so it writes out the transform space. */
msg_2d = msg_3d;
@@ -1327,7 +1328,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
- if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (t->redraw && !ISMOUSE_MOTION(event->type)) {
WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
}
@@ -1577,7 +1578,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
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;
+ short *snap_flag_ptr;
wmMsgParams_RNA msg_key_params = {{0}};
RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index a35942aedec..fc59787e1ec 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -38,6 +38,7 @@ struct ReportList;
struct Scene;
struct ScrArea;
struct SnapObjectContext;
+struct TransConvertTypeInfo;
struct TransDataContainer;
struct TransInfo;
struct TransSnap;
@@ -204,36 +205,6 @@ typedef enum {
HLP_TRACKBALL = 6,
} eTHelpline;
-typedef enum {
- TC_NONE = 0,
- TC_ACTION_DATA,
- TC_POSE,
- TC_ARMATURE_VERTS,
- TC_CURSOR_IMAGE,
- TC_CURSOR_SEQUENCER,
- TC_CURSOR_VIEW3D,
- TC_CURVE_VERTS,
- TC_GRAPH_EDIT_DATA,
- TC_GPENCIL,
- TC_LATTICE_VERTS,
- TC_MASKING_DATA,
- TC_MBALL_VERTS,
- TC_MESH_VERTS,
- TC_MESH_EDGES,
- TC_MESH_SKIN,
- TC_MESH_UV,
- TC_NLA_DATA,
- TC_NODE_DATA,
- TC_OBJECT,
- TC_OBJECT_TEXSPACE,
- TC_PAINT_CURVE_VERTS,
- TC_PARTICLE_VERTS,
- TC_SCULPT,
- TC_SEQ_DATA,
- TC_SEQ_IMAGE_DATA,
- TC_TRACKING_DATA,
-} eTConvertType;
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -306,9 +277,9 @@ typedef struct TransSnap {
eSnapTargetSelect target_select;
bool align;
bool project;
- bool snap_self;
bool peel;
bool use_backface_culling;
+ short face_nearest_steps;
eTSnap status;
/* Snapped Element Type (currently for objects only). */
eSnapMode snapElem;
@@ -463,6 +434,8 @@ typedef struct TransDataContainer {
int data_len;
/** Total number of transformed data_mirror. */
int data_mirror_len;
+ /** Total number of transformed gp-frames. */
+ int data_gpf_len;
struct Object *obedit;
@@ -516,7 +489,7 @@ typedef struct TransInfo {
int data_len_all;
/** TODO: It should be a member of #TransDataContainer. */
- eTConvertType data_type;
+ struct TransConvertTypeInfo *data_type;
/** Current context/options for transform. */
eTContext options;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 0bb00032561..658901a6991 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -402,7 +402,7 @@ static void applyAxisConstraintVec(const TransInfo *t,
if (activeSnap(t)) {
if (validSnap(t)) {
is_snap_to_edge = (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) != 0;
- is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE) != 0;
+ is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE_RAYCAST) != 0;
is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
}
else if (t->tsnap.snapElem & SCE_SNAP_MODE_GRID) {
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index d9b971c5478..d1c2af75274 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -479,132 +479,6 @@ 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;
- float min[2], max[2];
-
- /* Check if the current image in UV editor is a tiled image or not. */
- const SpaceImage *sima = t->area->spacedata.first;
- const Image *image = sima->image;
- const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
- /* Stores the coordinates of the closest UDIM tile.
- * Also acts as an offset to the tile from the origin of UV space. */
- float base_offset[2] = {0.0f, 0.0f};
-
- /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
- if (is_tiled_image) {
- int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global);
- if (nearest_tile_index != -1) {
- nearest_tile_index -= 1001;
- /* Getting coordinates of nearest tile from the tile index. */
- base_offset[0] = nearest_tile_index % 10;
- base_offset[1] = nearest_tile_index / 10;
- }
- }
-
- min[0] = min[1] = FLT_MAX;
- max[0] = max[1] = FLT_MIN;
-
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
-
- TransData *td;
- int a;
-
- for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
- minmax_v2v2_v2(min, max, td->loc);
- }
- }
-
- if (resize) {
- /* 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]) {
- vec[0] += base_offset[0] - min[0];
- }
- else if (max[0] > base_offset[0] + t->aspect[0]) {
- vec[0] -= max[0] - base_offset[0] - t->aspect[0];
- }
- else {
- clipx = 0;
- }
-
- if (min[1] < base_offset[1]) {
- vec[1] += base_offset[1] - min[1];
- }
- else if (max[1] > base_offset[1] + t->aspect[1]) {
- vec[1] -= max[1] - base_offset[1] - t->aspect[1];
- }
- else {
- clipy = 0;
- }
- }
-
- return (clipx || clipy);
-}
-
void clipUVData(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -769,7 +643,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
}
else {
/* Delete Keyframe */
- delete_fcurve_key(fcu, i, 0);
+ BKE_fcurve_delete_key(fcu, i);
}
/* Update count of how many we've deleted
@@ -779,7 +653,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
}
else {
/* Always delete - Unselected keys don't matter */
- delete_fcurve_key(fcu, i, 0);
+ BKE_fcurve_delete_key(fcu, i);
}
/* Stop the RK search... we've found our match now */
@@ -902,62 +776,12 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
return;
}
- BLI_assert(CTX_data_main(t->context) == CTX_data_main(C));
- switch (t->data_type) {
- case TC_ACTION_DATA:
- special_aftertrans_update__actedit(C, t);
- break;
- case TC_POSE:
- special_aftertrans_update__pose(C, t);
- break;
- case TC_GRAPH_EDIT_DATA:
- special_aftertrans_update__graph(C, t);
- break;
- case TC_MASKING_DATA:
- special_aftertrans_update__mask(C, t);
- break;
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- special_aftertrans_update__mesh(C, t);
- break;
- case TC_NLA_DATA:
- special_aftertrans_update__nla(C, t);
- break;
- case TC_NODE_DATA:
- special_aftertrans_update__node(C, t);
- break;
- case TC_OBJECT:
- special_aftertrans_update__object(C, t);
- break;
- case TC_SCULPT:
- special_aftertrans_update__sculpt(C, t);
- break;
- case TC_SEQ_DATA:
- special_aftertrans_update__sequencer(C, t);
- break;
- case TC_SEQ_IMAGE_DATA:
- special_aftertrans_update__sequencer_image(C, t);
- break;
- case TC_TRACKING_DATA:
- special_aftertrans_update__movieclip(C, t);
- break;
- case TC_ARMATURE_VERTS:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_UV:
- case TC_MESH_SKIN:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_PARTICLE_VERTS:
- case TC_NONE:
- default:
- break;
+ if (!t->data_type || !t->data_type->special_aftertrans_update) {
+ return;
}
+
+ BLI_assert(CTX_data_main(t->context) == CTX_data_main(C));
+ t->data_type->special_aftertrans_update(C, t);
}
int special_transform_moving(TransInfo *t)
@@ -1018,54 +842,44 @@ static int countAndCleanTransDataContainer(TransInfo *t)
static void init_proportional_edit(TransInfo *t)
{
- eTConvertType convert_type = t->data_type;
- switch (convert_type) {
- case TC_ACTION_DATA:
- case TC_CURVE_VERTS:
- case TC_GRAPH_EDIT_DATA:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MASKING_DATA:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- case TC_MESH_SKIN:
- case TC_MESH_UV:
- case TC_NODE_DATA:
- case TC_OBJECT:
- case TC_PARTICLE_VERTS:
- break;
- case TC_POSE: /* Disable PET, its not usable in pose mode yet T32444. */
- case TC_ARMATURE_VERTS:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_NLA_DATA:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_SCULPT:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- case TC_NONE:
- default:
- t->options |= CTX_NO_PET;
- t->flag &= ~T_PROP_EDIT_ALL;
- return;
+ /* NOTE: PET is not usable in pose mode yet T32444. */
+ if (!ELEM(t->data_type,
+ &TransConvertType_Action,
+ &TransConvertType_Curve,
+ &TransConvertType_Graph,
+ &TransConvertType_GPencil,
+ &TransConvertType_Lattice,
+ &TransConvertType_Mask,
+ &TransConvertType_MBall,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshEdge,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshUV,
+ &TransConvertType_MeshVertCData,
+ &TransConvertType_Node,
+ &TransConvertType_Object,
+ &TransConvertType_Particle)) {
+ /* Disable PET */
+ t->options |= CTX_NO_PET;
+ t->flag &= ~T_PROP_EDIT_ALL;
+ return;
}
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
- if (convert_type == TC_OBJECT) {
+ if (t->data_type == &TransConvertType_Object) {
/* Selected objects are already first, no need to presort. */
}
else {
sort_trans_data_selected_first(t);
}
- if (ELEM(convert_type, TC_ACTION_DATA, TC_GRAPH_EDIT_DATA)) {
+ if (ELEM(t->data_type, &TransConvertType_Action, &TransConvertType_Graph)) {
/* Distance has already been set. */
}
- else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN)) {
+ else if (ELEM(t->data_type,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshVertCData)) {
if (t->flag & T_PROP_CONNECTED) {
/* Already calculated by transform_convert_mesh_connectivity_distance. */
}
@@ -1073,10 +887,10 @@ static void init_proportional_edit(TransInfo *t)
set_prop_dist(t, false);
}
}
- else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) {
+ else if (t->data_type == &TransConvertType_MeshUV && t->flag & T_PROP_CONNECTED) {
/* Already calculated by uv_set_connectivity_distance. */
}
- else if (convert_type == TC_CURVE_VERTS) {
+ else if (t->data_type == &TransConvertType_Curve) {
BLI_assert(t->obedit_type == OB_CURVES_LEGACY);
set_prop_dist(t, false);
}
@@ -1099,44 +913,26 @@ static void init_TransDataContainers(TransInfo *t,
Object **objects,
uint objects_len)
{
- switch (t->data_type) {
- case TC_POSE:
- case TC_ARMATURE_VERTS:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- case TC_MESH_SKIN:
- case TC_MESH_UV:
- break;
- case TC_ACTION_DATA:
- case TC_GRAPH_EDIT_DATA:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_MASKING_DATA:
- case TC_NLA_DATA:
- case TC_NODE_DATA:
- case TC_OBJECT:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_PARTICLE_VERTS:
- case TC_SCULPT:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- case TC_NONE:
- default:
- /* Does not support Multi object editing. */
- return;
+ if (!ELEM(t->data_type,
+ &TransConvertType_Pose,
+ &TransConvertType_EditArmature,
+ &TransConvertType_Curve,
+ &TransConvertType_GPencil,
+ &TransConvertType_Lattice,
+ &TransConvertType_MBall,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshEdge,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshUV,
+ &TransConvertType_MeshVertCData)) {
+ /* Does not support Multi object editing. */
+ return;
}
const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
const short object_type = obact ? obact->type : -1;
- if ((object_mode & OB_MODE_EDIT) || (t->data_type == TC_GPENCIL) ||
+ if ((object_mode & OB_MODE_EDIT) || (t->data_type == &TransConvertType_GPencil) ||
((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE))) {
if (t->data_container) {
MEM_freeN(t->data_container);
@@ -1178,7 +974,7 @@ static void init_TransDataContainers(TransInfo *t,
tc->poseobj = objects[i];
tc->use_local_mat = true;
}
- else if (t->data_type == TC_GPENCIL) {
+ else if (t->data_type == &TransConvertType_GPencil) {
tc->use_local_mat = true;
}
@@ -1201,173 +997,131 @@ static void init_TransDataContainers(TransInfo *t,
}
}
-static eTFlag flags_from_data_type(eTConvertType data_type)
-{
- switch (data_type) {
- case TC_ACTION_DATA:
- case TC_GRAPH_EDIT_DATA:
- case TC_MASKING_DATA:
- case TC_NLA_DATA:
- case TC_NODE_DATA:
- case TC_PAINT_CURVE_VERTS:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- return T_POINTS | T_2D_EDIT;
- case TC_ARMATURE_VERTS:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_SKIN:
- return T_EDIT | T_POINTS;
- case TC_MESH_EDGES:
- return T_EDIT;
- case TC_MESH_UV:
- return T_EDIT | T_POINTS | T_2D_EDIT;
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- return T_2D_EDIT;
- case TC_PARTICLE_VERTS:
- return T_POINTS;
- case TC_POSE:
- case TC_CURSOR_VIEW3D:
- case TC_OBJECT:
- case TC_OBJECT_TEXSPACE:
- case TC_SCULPT:
- case TC_NONE:
- default:
- break;
- }
- return 0;
-}
-
-static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armature)
+static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj_armature)
{
ViewLayer *view_layer = t->view_layer;
Object *ob = OBACT(view_layer);
- eTConvertType convert_type = TC_NONE;
/* if tests must match recalcData for correct updates */
if (t->options & CTX_CURSOR) {
if (t->spacetype == SPACE_IMAGE) {
- convert_type = TC_CURSOR_IMAGE;
- }
- else if (t->spacetype == SPACE_SEQ) {
- convert_type = TC_CURSOR_SEQUENCER;
+ return &TransConvertType_CursorImage;
}
- else {
- convert_type = TC_CURSOR_VIEW3D;
+
+ if (t->spacetype == SPACE_SEQ) {
+ return &TransConvertType_CursorSequencer;
}
+
+ return &TransConvertType_Cursor3D;
}
- else if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
- (ob->mode == OB_MODE_SCULPT) && ob->sculpt) {
- convert_type = TC_SCULPT;
+ if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
+ (ob->mode == OB_MODE_SCULPT) && ob->sculpt) {
+ return &TransConvertType_Sculpt;
}
- else if (t->options & CTX_TEXTURE_SPACE) {
- convert_type = TC_OBJECT_TEXSPACE;
+ if (t->options & CTX_TEXTURE_SPACE) {
+ return &TransConvertType_ObjectTexSpace;
}
- else if (t->options & CTX_EDGE_DATA) {
- convert_type = TC_MESH_EDGES;
+ if (t->options & CTX_EDGE_DATA) {
+ return &TransConvertType_MeshEdge;
}
- else if (t->options & CTX_GPENCIL_STROKES) {
- convert_type = TC_GPENCIL;
+ if (t->options & CTX_GPENCIL_STROKES) {
+ return &TransConvertType_GPencil;
}
- else if (t->spacetype == SPACE_IMAGE) {
+ if (t->spacetype == SPACE_IMAGE) {
if (t->options & CTX_MASK) {
- convert_type = TC_MASKING_DATA;
+ return &TransConvertType_Mask;
}
- else if (t->options & CTX_PAINT_CURVE) {
+ if (t->options & CTX_PAINT_CURVE) {
if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
- convert_type = TC_PAINT_CURVE_VERTS;
+ return &TransConvertType_PaintCurve;
}
}
else if (t->obedit_type == OB_MESH) {
- convert_type = TC_MESH_UV;
+ return &TransConvertType_MeshUV;
}
+ return NULL;
}
- else if (t->spacetype == SPACE_ACTION) {
- convert_type = TC_ACTION_DATA;
+ if (t->spacetype == SPACE_ACTION) {
+ return &TransConvertType_Action;
}
- else if (t->spacetype == SPACE_NLA) {
- convert_type = TC_NLA_DATA;
+ if (t->spacetype == SPACE_NLA) {
+ return &TransConvertType_NLA;
}
- else if (t->spacetype == SPACE_SEQ) {
+ if (t->spacetype == SPACE_SEQ) {
if (t->options & CTX_SEQUENCER_IMAGE) {
- convert_type = TC_SEQ_IMAGE_DATA;
- }
- else {
- convert_type = TC_SEQ_DATA;
+ return &TransConvertType_SequencerImage;
}
+ return &TransConvertType_Sequencer;
}
- else if (t->spacetype == SPACE_GRAPH) {
- convert_type = TC_GRAPH_EDIT_DATA;
+ if (t->spacetype == SPACE_GRAPH) {
+ return &TransConvertType_Graph;
}
- else if (t->spacetype == SPACE_NODE) {
- convert_type = TC_NODE_DATA;
+ if (t->spacetype == SPACE_NODE) {
+ return &TransConvertType_Node;
}
- else if (t->spacetype == SPACE_CLIP) {
+ if (t->spacetype == SPACE_CLIP) {
if (t->options & CTX_MOVIECLIP) {
- convert_type = TC_TRACKING_DATA;
+ return &TransConvertType_Tracking;
}
- else if (t->options & CTX_MASK) {
- convert_type = TC_MASKING_DATA;
+ if (t->options & CTX_MASK) {
+ return &TransConvertType_Mask;
}
+ return NULL;
}
- else if (t->obedit_type != -1) {
+ if (t->obedit_type != -1) {
if (t->obedit_type == OB_MESH) {
if (t->mode == TFM_SKIN_RESIZE) {
- convert_type = TC_MESH_SKIN;
+ return &TransConvertType_MeshSkin;
}
- else {
- convert_type = TC_MESH_VERTS;
+ if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) {
+ return &TransConvertType_MeshVertCData;
}
+ return &TransConvertType_Mesh;
}
- else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) {
- convert_type = TC_CURVE_VERTS;
+ if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) {
+ return &TransConvertType_Curve;
}
- else if (t->obedit_type == OB_LATTICE) {
- convert_type = TC_LATTICE_VERTS;
+ if (t->obedit_type == OB_LATTICE) {
+ return &TransConvertType_Lattice;
}
- else if (t->obedit_type == OB_MBALL) {
- convert_type = TC_MBALL_VERTS;
+ if (t->obedit_type == OB_MBALL) {
+ return &TransConvertType_MBall;
}
- else if (t->obedit_type == OB_ARMATURE) {
- convert_type = TC_ARMATURE_VERTS;
+ if (t->obedit_type == OB_ARMATURE) {
+ return &TransConvertType_EditArmature;
}
+ return NULL;
}
- else if (ob && (ob->mode & OB_MODE_POSE)) {
- convert_type = TC_POSE;
+ if (ob && (ob->mode & OB_MODE_POSE)) {
+ return &TransConvertType_Pose;
}
- else if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
+ if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
Object *ob_armature = transform_object_deform_pose_armature_get(t, ob);
if (ob_armature) {
*r_obj_armature = ob_armature;
- convert_type = TC_POSE;
+ return &TransConvertType_Pose;
}
+ return NULL;
}
- else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) &&
- PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) {
- convert_type = TC_PARTICLE_VERTS;
+ if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) &&
+ PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) {
+ return &TransConvertType_Particle;
}
- else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
+ if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
- convert_type = TC_PAINT_CURVE_VERTS;
+ return &TransConvertType_PaintCurve;
}
+ return NULL;
}
- else if ((ob) && (ELEM(ob->mode,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL))) {
+ if ((ob) && (ELEM(ob->mode,
+ OB_MODE_PAINT_GPENCIL,
+ OB_MODE_SCULPT_GPENCIL,
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL))) {
/* In grease pencil all transformations must be canceled if not Object or Edit. */
+ return NULL;
}
- else {
- convert_type = TC_OBJECT;
- }
-
- return convert_type;
+ return &TransConvertType_Object;
}
void createTransData(bContext *C, TransInfo *t)
@@ -1376,7 +1130,14 @@ void createTransData(bContext *C, TransInfo *t)
Object *ob_armature = NULL;
t->data_type = convert_type_get(t, &ob_armature);
- t->flag |= flags_from_data_type(t->data_type);
+ if (t->data_type == NULL) {
+ printf("edit type not implemented!\n");
+ BLI_assert(t->data_len_all == -1);
+ t->data_len_all = 0;
+ return;
+ }
+
+ t->flag |= t->data_type->flags;
if (ob_armature) {
init_TransDataContainers(t, ob_armature, &ob_armature, 1);
@@ -1387,122 +1148,46 @@ void createTransData(bContext *C, TransInfo *t)
init_TransDataContainers(t, ob, NULL, 0);
}
- switch (t->data_type) {
- case TC_ACTION_DATA:
- createTransActionData(C, t);
- break;
- case TC_POSE:
- t->options |= CTX_POSE_BONE;
+ if (t->data_type == &TransConvertType_Object) {
+ t->options |= CTX_OBJECT;
- /* XXX active-layer checking isn't done
- * as that should probably be checked through context instead. */
- createTransPose(t);
- break;
- case TC_ARMATURE_VERTS:
- createTransArmatureVerts(t);
- break;
- case TC_CURSOR_IMAGE:
- createTransCursor_image(t);
- break;
- case TC_CURSOR_SEQUENCER:
- createTransCursor_sequencer(t);
- break;
- case TC_CURSOR_VIEW3D:
- createTransCursor_view3d(t);
- break;
- case TC_CURVE_VERTS:
- createTransCurveVerts(t);
- break;
- case TC_GRAPH_EDIT_DATA:
- createTransGraphEditData(C, t);
- break;
- case TC_GPENCIL:
- createTransGPencil(C, t);
- break;
- case TC_LATTICE_VERTS:
- createTransLatticeVerts(t);
- break;
- case TC_MASKING_DATA:
- createTransMaskingData(C, t);
- break;
- case TC_MBALL_VERTS:
- createTransMBallVerts(t);
- break;
- case TC_MESH_VERTS:
- createTransEditVerts(t);
- break;
- case TC_MESH_EDGES:
- createTransEdge(t);
- break;
- case TC_MESH_SKIN:
- createTransMeshSkin(t);
- break;
- case TC_MESH_UV:
- createTransUVs(C, t);
- break;
- case TC_NLA_DATA:
- createTransNlaData(C, t);
- break;
- case TC_NODE_DATA:
- createTransNodeData(t);
- break;
- case TC_OBJECT:
- t->options |= CTX_OBJECT;
-
- /* Needed for correct Object.obmat after duplication, see: T62135. */
- BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
-
- if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
- t->options |= CTX_OBMODE_XFORM_OBDATA;
- }
- if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
- t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
- }
- createTransObject(C, t);
- /* Check if we're transforming the camera from the camera */
- if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
- View3D *v3d = t->view;
- RegionView3D *rv3d = t->region->regiondata;
- if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
- /* we could have a flag to easily check an object is being transformed */
- if (v3d->camera->id.tag & LIB_TAG_DOIT) {
- t->options |= CTX_CAMERA;
- }
- }
- else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
+ /* Needed for correct Object.obmat after duplication, see: T62135. */
+ BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
+
+ if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_OBDATA;
+ }
+ if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
+ }
+ TransConvertType_Object.createTransData(C, t);
+ /* Check if we're transforming the camera from the camera */
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ View3D *v3d = t->view;
+ RegionView3D *rv3d = t->region->regiondata;
+ if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
+ /* we could have a flag to easily check an object is being transformed */
+ if (v3d->camera->id.tag & LIB_TAG_DOIT) {
t->options |= CTX_CAMERA;
}
}
- break;
- case TC_OBJECT_TEXSPACE:
- createTransTexspace(t);
- break;
- case TC_PAINT_CURVE_VERTS:
- createTransPaintCurveVerts(C, t);
- break;
- case TC_PARTICLE_VERTS:
- createTransParticleVerts(t);
- break;
- case TC_SCULPT:
- createTransSculpt(C, t);
- break;
- case TC_SEQ_DATA:
- t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */
- createTransSeqData(t);
- break;
- case TC_SEQ_IMAGE_DATA:
+ else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
+ t->options |= CTX_CAMERA;
+ }
+ }
+ }
+ else {
+ if (t->data_type == &TransConvertType_Pose) {
+ t->options |= CTX_POSE_BONE;
+ }
+ else if (t->data_type == &TransConvertType_Sequencer) {
+ /* Sequencer has no use for floating point transform. */
+ t->num.flag |= NUM_NO_FRACTION;
+ }
+ else if (t->data_type == &TransConvertType_SequencerImage) {
t->obedit_type = -1;
- createTransSeqImageData(t);
- break;
- case TC_TRACKING_DATA:
- createTransTrackingData(C, t);
- break;
- case TC_NONE:
- default:
- printf("edit type not implemented!\n");
- BLI_assert(t->data_len_all == -1);
- t->data_len_all = 0;
- return;
+ }
+ t->data_type->createTransData(C, t);
}
countAndCleanTransDataContainer(t);
@@ -1706,89 +1391,10 @@ void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const fl
void recalcData(TransInfo *t)
{
- switch (t->data_type) {
- case TC_ACTION_DATA:
- recalcData_actedit(t);
- break;
- case TC_POSE:
- recalcData_pose(t);
- break;
- case TC_ARMATURE_VERTS:
- recalcData_edit_armature(t);
- break;
- case TC_CURVE_VERTS:
- recalcData_curve(t);
- break;
- case TC_CURSOR_IMAGE:
- recalcData_cursor_image(t);
- break;
- case TC_CURSOR_SEQUENCER:
- recalcData_cursor_sequencer(t);
- break;
- case TC_CURSOR_VIEW3D:
- recalcData_cursor_view3d(t);
- break;
- case TC_GRAPH_EDIT_DATA:
- recalcData_graphedit(t);
- break;
- case TC_GPENCIL:
- recalcData_gpencil_strokes(t);
- break;
- case TC_MASKING_DATA:
- recalcData_mask_common(t);
- break;
- case TC_MESH_VERTS:
- recalcData_mesh(t);
- break;
- case TC_MESH_EDGES:
- recalcData_mesh_edge(t);
- break;
- case TC_MESH_SKIN:
- recalcData_mesh_skin(t);
- break;
- case TC_MESH_UV:
- recalcData_uv(t);
- break;
- case TC_NLA_DATA:
- recalcData_nla(t);
- break;
- case TC_NODE_DATA:
- flushTransNodes(t);
- break;
- case TC_OBJECT:
- recalcData_objects(t);
- break;
- case TC_OBJECT_TEXSPACE:
- recalcData_texspace(t);
- break;
- case TC_PAINT_CURVE_VERTS:
- flushTransPaintCurve(t);
- break;
- case TC_SCULPT:
- recalcData_sculpt(t);
- break;
- case TC_SEQ_DATA:
- recalcData_sequencer(t);
- break;
- case TC_SEQ_IMAGE_DATA:
- recalcData_sequencer_image(t);
- break;
- case TC_TRACKING_DATA:
- recalcData_tracking(t);
- break;
- case TC_MBALL_VERTS:
- recalcData_mball(t);
- break;
- case TC_LATTICE_VERTS:
- recalcData_lattice(t);
- break;
- case TC_PARTICLE_VERTS:
- recalcData_particles(t);
- break;
- case TC_NONE:
- default:
- break;
+ if (!t->data_type || !t->data_type->recalcData) {
+ return;
}
+ t->data_type->recalcData(t);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 037fbe26c77..f32bff6dcff 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -21,6 +21,25 @@ struct TransDataCurveHandleFlags;
struct TransInfo;
struct bContext;
+typedef struct TransConvertTypeInfo {
+ int flags; /* eTFlag */
+
+ /**
+ * Allocate and initialize `t->data`.
+ */
+ void (*createTransData)(bContext *C, TransInfo *t);
+
+ /**
+ * Force recalculation of data during transformation.
+ */
+ void (*recalcData)(TransInfo *t);
+
+ /**
+ * Called when the operation is finished.
+ */
+ void (*special_aftertrans_update)(bContext *C, TransInfo *t);
+} TransConvertTypeInfo;
+
/* transform_convert.c */
/**
@@ -34,7 +53,6 @@ int special_transform_moving(TransInfo *t);
void special_aftertrans_update(struct bContext *C, TransInfo *t);
void sort_trans_data_dist(TransInfo *t);
void createTransData(struct bContext *C, TransInfo *t);
-bool clipUVTransform(TransInfo *t, float vec[2], bool resize);
void clipUVData(TransInfo *t);
void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, float y_fac);
/**
@@ -99,82 +117,53 @@ void animrecord_check_state(TransInfo *t, struct ID *id);
/* transform_convert_action.c */
-void createTransActionData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Action Editor transforms */
-void recalcData_actedit(TransInfo *t);
-void special_aftertrans_update__actedit(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Action;
/* transform_convert_armature.c */
+extern TransConvertTypeInfo TransConvertType_EditArmature;
+extern TransConvertTypeInfo TransConvertType_Pose;
+
/**
* Sets transform flags in the bones.
* Returns total number of bones with #BONE_TRANSFORM.
*/
void transform_convert_pose_transflags_update(Object *ob, int mode, short around);
-/**
- * When objects array is NULL, use 't->data_container' as is.
- */
-void createTransPose(TransInfo *t);
-void createTransArmatureVerts(TransInfo *t);
-void recalcData_edit_armature(TransInfo *t);
-void recalcData_pose(TransInfo *t);
-void special_aftertrans_update__pose(bContext *C, TransInfo *t);
-
/* transform_convert_cursor.c */
-void createTransCursor_image(TransInfo *t);
-void createTransCursor_sequencer(TransInfo *t);
-void createTransCursor_view3d(TransInfo *t);
-void recalcData_cursor_image(TransInfo *t);
-void recalcData_cursor_sequencer(TransInfo *t);
-void recalcData_cursor_view3d(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_CursorImage;
+extern TransConvertTypeInfo TransConvertType_CursorSequencer;
+extern TransConvertTypeInfo TransConvertType_Cursor3D;
/* transform_convert_curve.c */
-void createTransCurveVerts(TransInfo *t);
-void recalcData_curve(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Curve;
/* transform_convert_graph.c */
-/**
- * It is important to note that this doesn't always act on the selection (like it's usually done),
- * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
- * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
- * selected left or right handles accordingly.
- * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
- * functions may need to be made aware of this. It's ugly that these act based on selection state
- * anyway.
- */
-void createTransGraphEditData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Graph Editor transforms */
-void recalcData_graphedit(TransInfo *t);
-void special_aftertrans_update__graph(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Graph;
/* transform_convert_gpencil.c */
-void createTransGPencil(bContext *C, TransInfo *t);
-/* force recalculation of triangles during transformation */
-void recalcData_gpencil_strokes(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_GPencil;
/* transform_convert_lattice.c */
-void createTransLatticeVerts(TransInfo *t);
-void recalcData_lattice(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Lattice;
/* transform_convert_mask.c */
-void createTransMaskingData(bContext *C, TransInfo *t);
-void recalcData_mask_common(TransInfo *t);
-void special_aftertrans_update__mask(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Mask;
/* transform_convert_mball.c */
-void createTransMBallVerts(TransInfo *t);
-void recalcData_mball(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MBall;
/* transform_convert_mesh.c */
+extern TransConvertTypeInfo TransConvertType_Mesh;
+
struct TransIslandData {
float (*center)[3];
float (*axismtx)[3][3];
@@ -233,84 +222,60 @@ void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3],
struct TransData *r_td);
void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_crazyspace_data);
-void createTransEditVerts(TransInfo *t);
-void recalcData_mesh(TransInfo *t);
void special_aftertrans_update__mesh(bContext *C, TransInfo *t);
/* transform_convert_mesh_edge.c */
-void createTransEdge(TransInfo *t);
-void recalcData_mesh_edge(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshEdge;
/* transform_convert_mesh_skin.c */
-void createTransMeshSkin(TransInfo *t);
-void recalcData_mesh_skin(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshSkin;
/* transform_convert_mesh_uv.c */
-void createTransUVs(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Image Editor transforms */
-void recalcData_uv(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshUV;
+
+/* transform_convert_mesh_vert_cdata.c */
+
+extern TransConvertTypeInfo TransConvertType_MeshVertCData;
/* transform_convert_nla.c */
-void createTransNlaData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for NLA Editor transforms */
-void recalcData_nla(TransInfo *t);
-void special_aftertrans_update__nla(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_NLA;
/* transform_convert_node.c */
-void createTransNodeData(TransInfo *t);
-void flushTransNodes(TransInfo *t);
-void special_aftertrans_update__node(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Node;
/* transform_convert_object.c */
-void createTransObject(bContext *C, TransInfo *t);
-/* helper for recalcData() - for object transforms, typically in the 3D view */
-void recalcData_objects(TransInfo *t);
-void special_aftertrans_update__object(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Object;
/* transform_convert_object_texspace.c */
-void createTransTexspace(TransInfo *t);
-/* helper for recalcData() - for object transforms, typically in the 3D view */
-void recalcData_texspace(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_ObjectTexSpace;
/* transform_convert_paintcurve.c */
-void createTransPaintCurveVerts(bContext *C, TransInfo *t);
-void flushTransPaintCurve(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_PaintCurve;
/* transform_convert_particle.c */
-void createTransParticleVerts(TransInfo *t);
-void recalcData_particles(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Particle;
/* transform_convert_sculpt.c */
-void createTransSculpt(bContext *C, TransInfo *t);
-void recalcData_sculpt(TransInfo *t);
-void special_aftertrans_update__sculpt(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Sculpt;
/* transform_convert_sequencer.c */
-void createTransSeqData(TransInfo *t);
-/* helper for recalcData() - for sequencer transforms */
-void recalcData_sequencer(TransInfo *t);
-void special_aftertrans_update__sequencer(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Sequencer;
/* transform_convert_sequencer_image.c */
-void createTransSeqImageData(TransInfo *t);
-void recalcData_sequencer_image(TransInfo *t);
-void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_SequencerImage;
/* transform_convert_tracking.c */
-void createTransTrackingData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Movie Clip transforms */
-void recalcData_tracking(TransInfo *t);
-void special_aftertrans_update__movieclip(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Tracking;
diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c
index 71c245cd512..41635522d26 100644
--- a/source/blender/editors/transform/transform_convert_action.c
+++ b/source/blender/editors/transform/transform_convert_action.c
@@ -289,7 +289,7 @@ static int MaskLayerToTransData(TransData *td,
return count;
}
-void createTransActionData(bContext *C, TransInfo *t)
+static void createTransActionData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
TransData *td = NULL;
@@ -311,6 +311,7 @@ void createTransActionData(bContext *C, TransInfo *t)
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
int count = 0;
+ int gpf_count = 0;
float cfra;
float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->region->v2d.cur);
@@ -320,17 +321,12 @@ void createTransActionData(bContext *C, TransInfo *t)
}
/* filter data */
- if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
- }
- else {
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
- }
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* which side of the current frame should be allowed */
if (t->mode == TFM_TIME_EXTEND) {
- t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA);
+ t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
}
else {
/* normal transform - both sides of current frame are considered */
@@ -345,10 +341,10 @@ void createTransActionData(bContext *C, TransInfo *t)
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
}
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
@@ -365,13 +361,16 @@ void createTransActionData(bContext *C, TransInfo *t)
}
if (adt_count > 0) {
+ if (ELEM(ale->type, ANIMTYPE_GPLAYER, ANIMTYPE_MASKLAYER)) {
+ gpf_count += adt_count;
+ }
count += adt_count;
ale->tag = true;
}
}
/* stop if trying to build list if nothing selected */
- if (count == 0) {
+ if (count == 0 && gpf_count == 0) {
/* cleanup temp list */
ANIM_animdata_freelist(&anim_data);
return;
@@ -387,8 +386,9 @@ void createTransActionData(bContext *C, TransInfo *t)
td = tc->data;
td2d = tc->data_2d;
- if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
- tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata");
+ if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
+ tc->data_gpf_len = gpf_count;
+ tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * gpf_count, "tGPFtransdata");
tc->custom.type.use_free = true;
}
@@ -399,7 +399,7 @@ void createTransActionData(bContext *C, TransInfo *t)
continue;
}
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
{
AnimData *adt;
@@ -447,10 +447,10 @@ void createTransActionData(bContext *C, TransInfo *t)
adt = ANIM_nla_mapping_get(&ac, ale);
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
}
if (ale->type == ANIMTYPE_GPLAYER) {
@@ -558,13 +558,14 @@ static void flushTransIntFrameActionData(TransInfo *t)
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
tGPFtransdata *tfd = tc->custom.type.data;
- /* flush data! */
- for (int i = 0; i < tc->data_len; i++, tfd++) {
+ /* flush data!
+ * Expects data_gpf_len to be set in the data container. */
+ for (int i = 0; i < tc->data_gpf_len; i++, tfd++) {
*(tfd->sdata) = round_fl_to_int(tfd->val);
}
}
-void recalcData_actedit(TransInfo *t)
+static void recalcData_actedit(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
@@ -589,7 +590,7 @@ void recalcData_actedit(TransInfo *t)
ANIM_animdata_context_getdata(&ac);
/* perform flush */
- if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+ if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
/* flush transform values back to actual coordinates */
flushTransIntFrameActionData(t);
}
@@ -735,7 +736,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act)
int filter;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, act, ANIMCONT_ACTION);
/* loop through relevant data, removing keyframes as appropriate
@@ -758,7 +759,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act)
ANIM_animdata_freelist(&anim_data);
}
-void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
+static void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
{
SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
bAnimContext ac;
@@ -776,32 +777,44 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY, ANIMCONT_TIMELINE)) {
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
+ short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
/* get channels to work on */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
- /* these should all be F-Curves */
for (ale = anim_data.first; ale; ale = ale->next) {
- AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
- FCurve *fcu = (FCurve *)ale->key_data;
-
- /* 3 cases here for curve cleanups:
- * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
- * 2) canceled == 0 -> user confirmed the transform,
- * so duplicates should be removed
- * 3) canceled + duplicate -> user canceled the transform,
- * but we made duplicates, so get rid of these
- */
- if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
- if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
- posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
- }
- else {
- posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */
+ switch (ale->datatype) {
+ case ALE_GPFRAME:
+ ale->id->tag &= ~LIB_TAG_DOIT;
+ posttrans_gpd_clean((bGPdata *)ale->id);
+ break;
+
+ case ALE_FCURVE: {
+ AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
+ FCurve *fcu = (FCurve *)ale->key_data;
+
+ /* 3 cases here for curve cleanups:
+ * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
+ * 2) canceled == 0 -> user confirmed the transform,
+ * so duplicates should be removed
+ * 3) canceled + duplicate -> user canceled the transform,
+ * but we made duplicates, so get rid of these
+ */
+ if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
+ if (adt) {
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
+ posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
+ }
+ else {
+ posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */
+ }
+ }
+ break;
}
+
+ default:
+ BLI_assert_msg(false, "Keys cannot be transformed into this animation type.");
}
}
@@ -847,15 +860,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
if (ale->datatype == ALE_GPFRAME) {
- ale->id->tag |= LIB_TAG_DOIT;
- }
- }
- LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
- if (ale->datatype == ALE_GPFRAME) {
- if (ale->id->tag & LIB_TAG_DOIT) {
- ale->id->tag &= ~LIB_TAG_DOIT;
- posttrans_gpd_clean((bGPdata *)ale->id);
- }
+ ale->id->tag &= ~LIB_TAG_DOIT;
+ posttrans_gpd_clean((bGPdata *)ale->id);
}
}
ANIM_animdata_freelist(&anim_data);
@@ -878,15 +884,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
if (ale->datatype == ALE_MASKLAY) {
- ale->id->tag |= LIB_TAG_DOIT;
- }
- }
- LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
- if (ale->datatype == ALE_MASKLAY) {
- if (ale->id->tag & LIB_TAG_DOIT) {
- ale->id->tag &= ~LIB_TAG_DOIT;
- posttrans_mask_clean((Mask *)ale->id);
- }
+ ale->id->tag &= ~LIB_TAG_DOIT;
+ posttrans_mask_clean((Mask *)ale->id);
}
}
ANIM_animdata_freelist(&anim_data);
@@ -927,3 +926,10 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Action = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransActionData,
+ /* recalcData */ recalcData_actedit,
+ /* special_aftertrans_update */ special_aftertrans_update__actedit,
+};
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index e1b25acb21e..97d9ab2964a 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -93,8 +93,8 @@ static void autokeyframe_pose(
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase nla_cache = {NULL, NULL};
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
- (float)CFRA);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (float)scene->r.cfra);
eInsertKeyFlags flag = 0;
/* flag is initialized from UserPref keyframing settings
@@ -701,7 +701,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
td->con = pchan->constraints.first;
}
-void createTransPose(TransInfo *t)
+static void createTransPose(bContext *UNUSED(C), TransInfo *t)
{
Main *bmain = CTX_data_main(t->context);
@@ -879,7 +879,7 @@ void createTransPose(TransInfo *t)
}
}
-void createTransArmatureVerts(TransInfo *t)
+static void createTransArmatureVerts(bContext *UNUSED(C), TransInfo *t)
{
t->data_len_all = 0;
@@ -1189,10 +1189,10 @@ static void restoreBones(TransDataContainer *tc)
}
}
-void recalcData_edit_armature(TransInfo *t)
+static void recalcData_edit_armature(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -1412,7 +1412,7 @@ static void restoreMirrorPoseBones(TransDataContainer *tc)
}
}
-void recalcData_pose(TransInfo *t)
+static void recalcData_pose(TransInfo *t)
{
if (t->mode == TFM_BONESIZE) {
/* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be
@@ -1684,7 +1684,7 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob)
}
}
-void special_aftertrans_update__pose(bContext *C, TransInfo *t)
+static void special_aftertrans_update__pose(bContext *C, TransInfo *t)
{
Object *ob;
@@ -1768,3 +1768,17 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_EditArmature = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransArmatureVerts,
+ /* recalcData */ recalcData_edit_armature,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_Pose = {
+ /* flags */ 0,
+ /* createTransData */ createTransPose,
+ /* recalcData */ recalcData_pose,
+ /* special_aftertrans_update */ special_aftertrans_update__pose,
+};
diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c
index 5e6eee192f2..0bf6f06a9f2 100644
--- a/source/blender/editors/transform/transform_convert_cursor.c
+++ b/source/blender/editors/transform/transform_convert_cursor.c
@@ -82,13 +82,13 @@ static void recalcData_cursor_2D_impl(TransInfo *t)
/** \name Image Cursor
* \{ */
-void createTransCursor_image(TransInfo *t)
+static void createTransCursor_image(bContext *UNUSED(C), TransInfo *t)
{
SpaceImage *sima = t->area->spacedata.first;
createTransCursor_2D_impl(t, sima->cursor);
}
-void recalcData_cursor_image(TransInfo *t)
+static void recalcData_cursor_image(TransInfo *t)
{
recalcData_cursor_2D_impl(t);
}
@@ -99,7 +99,7 @@ void recalcData_cursor_image(TransInfo *t)
/** \name Sequencer Cursor
* \{ */
-void createTransCursor_sequencer(TransInfo *t)
+static void createTransCursor_sequencer(bContext *UNUSED(C), TransInfo *t)
{
SpaceSeq *sseq = t->area->spacedata.first;
if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
@@ -108,7 +108,7 @@ void createTransCursor_sequencer(TransInfo *t)
createTransCursor_2D_impl(t, sseq->cursor);
}
-void recalcData_cursor_sequencer(TransInfo *t)
+static void recalcData_cursor_sequencer(TransInfo *t)
{
recalcData_cursor_2D_impl(t);
}
@@ -119,7 +119,7 @@ void recalcData_cursor_sequencer(TransInfo *t)
/** \name View 3D Cursor
* \{ */
-void createTransCursor_view3d(TransInfo *t)
+static void createTransCursor_view3d(bContext *UNUSED(C), TransInfo *t)
{
TransData *td;
@@ -178,9 +178,30 @@ void createTransCursor_view3d(TransInfo *t)
td->ext->rotOrder = cursor->rotation_mode;
}
-void recalcData_cursor_view3d(TransInfo *t)
+static void recalcData_cursor_view3d(TransInfo *t)
{
DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_CursorImage = {
+ /* flags */ T_2D_EDIT,
+ /* createTransData */ createTransCursor_image,
+ /* recalcData */ recalcData_cursor_image,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_CursorSequencer = {
+ /* flags */ T_2D_EDIT,
+ /* createTransData */ createTransCursor_sequencer,
+ /* recalcData */ recalcData_cursor_sequencer,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_Cursor3D = {
+ /* flags */ 0,
+ /* createTransData */ createTransCursor_view3d,
+ /* recalcData */ recalcData_cursor_view3d,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index 51acfb2a788..404b1293208 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -62,7 +62,7 @@ static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const boo
return flag;
}
-void createTransCurveVerts(TransInfo *t)
+static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t)
{
#define SEL_F1 (1 << 0)
@@ -415,10 +415,10 @@ void createTransCurveVerts(TransInfo *t)
#undef SEL_F3
}
-void recalcData_curve(TransInfo *t)
+static void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -446,3 +446,10 @@ void recalcData_curve(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Curve = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransCurveVerts,
+ /* recalcData */ recalcData_curve,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index a88d42b7f30..0029eaefaba 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -672,7 +672,7 @@ static void createTransGPencil_strokes(bContext *C,
}
}
-void createTransGPencil(bContext *C, TransInfo *t)
+static void createTransGPencil(bContext *C, TransInfo *t)
{
if (t->data_container_len == 0) {
return;
@@ -685,7 +685,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
bGPdata *gpd = obact->data;
BLI_assert(gpd != NULL);
- const int cfra_scene = CFRA;
+ const int cfra_scene = scene->r.cfra;
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) !=
@@ -737,7 +737,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
-void recalcData_gpencil_strokes(TransInfo *t)
+static void recalcData_gpencil_strokes(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
GHash *strokes = BLI_ghash_ptr_new(__func__);
@@ -762,3 +762,10 @@ void recalcData_gpencil_strokes(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_GPencil = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransGPencil,
+ /* recalcData */ recalcData_gpencil_strokes,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c
index 2039daee386..aca2439d5fb 100644
--- a/source/blender/editors/transform/transform_convert_graph.c
+++ b/source/blender/editors/transform/transform_convert_graph.c
@@ -199,7 +199,16 @@ static void graph_key_shortest_dist(
}
}
-void createTransGraphEditData(bContext *C, TransInfo *t)
+/**
+ * It is important to note that this doesn't always act on the selection (like it's usually done),
+ * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
+ * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
+ * selected left or right handles accordingly.
+ * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
+ * functions may need to be made aware of this. It's ugly that these act based on selection state
+ * anyway.
+ */
+static void createTransGraphEditData(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
Scene *scene = t->scene;
@@ -232,13 +241,14 @@ void createTransGraphEditData(bContext *C, TransInfo *t)
anim_map_flag |= ANIM_get_normalization_flags(&ac);
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* which side of the current frame should be allowed */
/* XXX we still want this mode, but how to get this using standard transform too? */
if (t->mode == TFM_TIME_EXTEND) {
- t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA);
+ t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
}
else {
/* normal transform - both sides of current frame are considered */
@@ -263,10 +273,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t)
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
}
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
@@ -369,10 +379,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t)
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
}
unit_scale = ANIM_unit_mapping_get_factor(
@@ -560,10 +570,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t)
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
- cfra = (float)CFRA;
+ cfra = (float)scene->r.cfra;
}
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
@@ -885,7 +895,7 @@ static void remake_graph_transdata(TransInfo *t, ListBase *anim_data)
}
}
-void recalcData_graphedit(TransInfo *t)
+static void recalcData_graphedit(TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
ViewLayer *view_layer = t->view_layer;
@@ -915,7 +925,8 @@ void recalcData_graphedit(TransInfo *t)
flushTransGraphData(t);
/* get curves to check if a re-sort is needed */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* now test if there is a need to re-sort */
@@ -932,7 +943,7 @@ void recalcData_graphedit(TransInfo *t)
dosort++;
}
else {
- calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG);
+ BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG);
}
/* set refresh tags for objects using this animation,
@@ -958,7 +969,7 @@ void recalcData_graphedit(TransInfo *t)
/** \name Special After Transform Graph
* \{ */
-void special_aftertrans_update__graph(bContext *C, TransInfo *t)
+static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
bAnimContext ac;
@@ -975,7 +986,8 @@ void special_aftertrans_update__graph(bContext *C, TransInfo *t)
if (ac.datatype) {
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
+ short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
+ ANIMFILTER_FCURVESONLY);
/* get channels to work on */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1018,3 +1030,10 @@ void special_aftertrans_update__graph(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Graph = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransGraphEditData,
+ /* recalcData */ recalcData_graphedit,
+ /* special_aftertrans_update */ special_aftertrans_update__graph,
+};
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index f02d4e94448..b77538dc249 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -25,7 +25,7 @@
/** \name Curve/Surfaces Transform Creation
* \{ */
-void createTransLatticeVerts(TransInfo *t)
+static void createTransLatticeVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -98,10 +98,10 @@ void createTransLatticeVerts(TransInfo *t)
}
}
-void recalcData_lattice(TransInfo *t)
+static void recalcData_lattice(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -114,3 +114,10 @@ void recalcData_lattice(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Lattice = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransLatticeVerts,
+ /* recalcData */ recalcData_lattice,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index b07d2bda0c5..2dca59a5da1 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -117,7 +117,7 @@ static void MaskPointToTransData(Scene *scene,
const bool is_sel_any = MASKPOINT_ISSEL_ANY(point);
float parent_matrix[3][3], parent_inverse_matrix[3][3];
- BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix);
+ BKE_mask_point_parent_matrix_get(point, scene->r.cfra, parent_matrix);
invert_m3_m3(parent_inverse_matrix, parent_matrix);
if (is_prop_edit || is_sel_point) {
@@ -245,7 +245,7 @@ static void MaskPointToTransData(Scene *scene,
}
}
-void createTransMaskingData(bContext *C, TransInfo *t)
+static void createTransMaskingData(bContext *C, TransInfo *t)
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
@@ -416,7 +416,7 @@ static void flushTransMasking(TransInfo *t)
}
}
-void recalcData_mask_common(TransInfo *t)
+static void recalcData_mask_common(TransInfo *t)
{
Mask *mask = CTX_data_edit_mask(t->context);
@@ -431,7 +431,7 @@ void recalcData_mask_common(TransInfo *t)
/** \name Special After Transform Mask
* \{ */
-void special_aftertrans_update__mask(bContext *C, TransInfo *t)
+static void special_aftertrans_update__mask(bContext *C, TransInfo *t)
{
Mask *mask = NULL;
@@ -455,7 +455,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t)
if (IS_AUTOKEY_ON(t->scene)) {
Scene *scene = t->scene;
- if (ED_mask_layer_shape_auto_key_select(mask, CFRA)) {
+ if (ED_mask_layer_shape_auto_key_select(mask, scene->r.cfra)) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id);
DEG_id_tag_update(&mask->id, 0);
}
@@ -463,3 +463,10 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Mask = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransMaskingData,
+ /* recalcData */ recalcData_mask_common,
+ /* special_aftertrans_update */ special_aftertrans_update__mask,
+};
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 7cba4f97886..7ae93524d0b 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -22,7 +22,7 @@
/** \name Meta Elements Transform Creation
* \{ */
-void createTransMBallVerts(TransInfo *t)
+static void createTransMBallVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
MetaBall *mb = (MetaBall *)tc->obedit->data;
@@ -119,10 +119,10 @@ void createTransMBallVerts(TransInfo *t)
/** \name Recalc Meta Ball
* \{ */
-void recalcData_mball(TransInfo *t)
+static void recalcData_mball(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->data_len) {
@@ -132,3 +132,10 @@ void recalcData_mball(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MBall = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMBallVerts,
+ /* recalcData */ recalcData_mball,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index d4b12142162..b5a85f57b84 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1408,7 +1408,6 @@ static void VertsToTransData(TransInfo *t,
TransDataExtension *tx,
BMEditMesh *em,
BMVert *eve,
- float *bweight,
const struct TransIslandData *island_data,
const int island_index)
{
@@ -1449,17 +1448,13 @@ static void VertsToTransData(TransInfo *t,
td->ext = NULL;
td->val = NULL;
td->extra = eve;
- if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) {
- td->val = bweight;
- td->ival = *bweight;
- }
- else if (t->mode == TFM_SHRINKFATTEN) {
+ if (t->mode == TFM_SHRINKFATTEN) {
td->ext = tx;
tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT);
}
}
-void createTransEditVerts(TransInfo *t)
+static void createTransEditVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransDataExtension *tx = NULL;
@@ -1589,17 +1584,6 @@ void createTransEditVerts(TransInfo *t)
"TransObData ext");
}
- int cd_vert_bweight_offset = -1;
- int cd_vert_crease_offset = -1;
- if (t->mode == TFM_BWEIGHT) {
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT);
- cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
- }
- else if (t->mode == TFM_VERT_CREASE) {
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_CREASE);
- cd_vert_crease_offset = CustomData_get_offset(&bm->vdata, CD_CREASE);
- }
-
TransData *tob = tc->data;
TransDataMirror *td_mirror = tc->data_mirror;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
@@ -1632,15 +1616,9 @@ void createTransEditVerts(TransInfo *t)
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- float *bweight = (cd_vert_bweight_offset != -1) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) :
- (cd_vert_crease_offset != -1) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) :
- NULL;
-
/* Do not use the island center in case we are using islands
* only to get axis for snap/rotate to normal... */
- VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index);
+ VertsToTransData(t, tob, tx, em, eve, &island_data, island_index);
if (tx) {
tx++;
}
@@ -1940,7 +1918,7 @@ static void tc_mesh_partial_types_calc(TransInfo *t, struct PartialTypeState *r_
}
/* With projection, transform isn't affine. */
- if (activeSnap_with_project(t)) {
+ if (activeSnap_SnappingIndividual(t)) {
if (partial_for_looptri == PARTIAL_TYPE_GROUP) {
partial_for_looptri = PARTIAL_TYPE_ALL;
}
@@ -2051,12 +2029,12 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
}
}
-void recalcData_mesh(TransInfo *t)
+static void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
/* Apply corrections. */
if (!is_canceling) {
- applyProject(t);
+ applySnappingIndividual(t);
bool do_mirror = !(t->flag & T_NO_MIRROR);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -2151,3 +2129,10 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Mesh = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransEditVerts,
+ /* recalcData */ recalcData_mesh,
+ /* special_aftertrans_update */ special_aftertrans_update__mesh,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c
index 2d6c6a933d6..becf3c7ce5a 100644
--- a/source/blender/editors/transform/transform_convert_mesh_edge.c
+++ b/source/blender/editors/transform/transform_convert_mesh_edge.c
@@ -23,7 +23,7 @@
/** \name Edge (for crease) Transform Creation
* \{ */
-void createTransEdge(TransInfo *t)
+static void createTransEdge(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -99,8 +99,8 @@ void createTransEdge(TransInfo *t)
td->ext = NULL;
fl_ptr = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_float_offset);
- td->val = fl_ptr;
- td->ival = *fl_ptr;
+ td->loc = fl_ptr;
+ td->iloc[0] = *fl_ptr;
td++;
}
@@ -108,7 +108,7 @@ void createTransEdge(TransInfo *t)
}
}
-void recalcData_mesh_edge(TransInfo *t)
+static void recalcData_mesh_edge(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
@@ -116,3 +116,10 @@ void recalcData_mesh_edge(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshEdge = {
+ /* flags */ T_EDIT,
+ /* createTransData */ createTransEdge,
+ /* recalcData */ recalcData_mesh_edge,
+ /* special_aftertrans_update */ special_aftertrans_update__mesh,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c
index fdc2ba59035..376e559181e 100644
--- a/source/blender/editors/transform/transform_convert_mesh_skin.c
+++ b/source/blender/editors/transform/transform_convert_mesh_skin.c
@@ -66,7 +66,7 @@ static void tc_mesh_skin_transdata_create(TransDataBasic *td,
td->extra = eve;
}
-void createTransMeshSkin(TransInfo *t)
+static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t)
{
BLI_assert(t->mode == TFM_SKIN_RESIZE);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -271,7 +271,7 @@ static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
}
}
-void recalcData_mesh_skin(TransInfo *t)
+static void recalcData_mesh_skin(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
@@ -289,3 +289,10 @@ void recalcData_mesh_skin(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshSkin = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMeshSkin,
+ /* recalcData */ recalcData_mesh_skin,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index 18868643b6d..d95bc7b976f 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -234,11 +234,10 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as
MEM_freeN(dists_prev);
}
-void createTransUVs(bContext *C, TransInfo *t)
+static void createTransUVs(bContext *C, TransInfo *t)
{
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = t->scene;
- ToolSettings *ts = CTX_data_tool_settings(C);
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
@@ -266,8 +265,7 @@ void createTransUVs(bContext *C, TransInfo *t)
/* count */
if (is_island_center) {
/* create element map with island information */
- const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
- elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
+ elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true);
if (elementmap == NULL) {
continue;
}
@@ -448,7 +446,7 @@ static void flushTransUVs(TransInfo *t)
}
}
-void recalcData_uv(TransInfo *t)
+static void recalcData_uv(TransInfo *t)
{
SpaceImage *sima = t->area->spacedata.first;
@@ -465,3 +463,10 @@ void recalcData_uv(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshUV = {
+ /* flags */ (T_EDIT | T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransUVs,
+ /* recalcData */ recalcData_uv,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c
new file mode 100644
index 00000000000..f05688f3325
--- /dev/null
+++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
+
+/** \file
+ * \ingroup edtransform
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_crazyspace.h"
+#include "BKE_editmesh.h"
+#include "BKE_modifier.h"
+#include "BKE_scene.h"
+
+#include "ED_mesh.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "transform.h"
+#include "transform_orientations.h"
+
+#include "transform_convert.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Edit Mesh #CD_BWEIGHT and #CD_CREASE Transform Creation
+ * \{ */
+
+static float *tc_mesh_cdata_transdata_center(const struct TransIslandData *island_data,
+ const int island_index,
+ BMVert *eve)
+{
+ if (island_data->center && island_index != -1) {
+ return island_data->center[island_index];
+ }
+ return eve->co;
+}
+
+static void tc_mesh_cdata_transdata_create(TransDataBasic *td,
+ BMVert *eve,
+ float *weight,
+ const struct TransIslandData *island_data,
+ const int island_index)
+{
+ BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
+
+ td->loc = weight;
+ td->iloc[0] = *weight;
+
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ td->flag |= TD_SELECTED;
+ }
+
+ copy_v3_v3(td->center, tc_mesh_cdata_transdata_center(island_data, island_index, eve));
+ td->extra = eve;
+}
+
+static void createTransMeshVertCData(bContext *UNUSED(C), TransInfo *t)
+{
+ BLI_assert(ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE));
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ Mesh *me = tc->obedit->data;
+ BMesh *bm = em->bm;
+ BMVert *eve;
+ BMIter iter;
+ float mtx[3][3], smtx[3][3];
+ int a;
+ const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
+
+ struct TransIslandData island_data = {NULL};
+ struct TransMirrorData mirror_data = {NULL};
+ struct TransMeshDataCrazySpace crazyspace_data = {NULL};
+
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
+ continue;
+ }
+
+ int cd_offset = -1;
+ if (t->mode == TFM_BWEIGHT) {
+ BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT);
+ cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
+ }
+ else {
+ BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_CREASE);
+ cd_offset = CustomData_get_offset(&bm->vdata, CD_CREASE);
+ }
+
+ if (cd_offset == -1) {
+ continue;
+ }
+
+ int data_len = 0;
+ if (prop_mode) {
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ data_len++;
+ }
+ }
+ }
+ else {
+ data_len = bm->totvertsel;
+ }
+
+ if (data_len == 0) {
+ continue;
+ }
+
+ const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
+ if (is_island_center) {
+ /* In this specific case, near-by vertices will need to know
+ * the island of the nearest connected vertex. */
+ const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
+ (t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+ (em->selectmode & SCE_SELECT_VERTEX));
+
+ const bool calc_island_center = false;
+ const bool calc_island_axismtx = false;
+
+ transform_convert_mesh_islands_calc(
+ em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
+ }
+
+ copy_m3_m4(mtx, tc->obedit->obmat);
+ /* we use a pseudo-inverse so that when one of the axes is scaled to 0,
+ * matrix inversion still works and we can still moving along the other */
+ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+
+ /* Original index of our connected vertex when connected distances are calculated.
+ * Optional, allocate if needed. */
+ int *dists_index = NULL;
+ float *dists = NULL;
+ if (prop_mode & T_PROP_CONNECTED) {
+ dists = MEM_mallocN(bm->totvert * sizeof(float), __func__);
+ if (is_island_center) {
+ dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__);
+ }
+ transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index);
+ }
+
+ /* Create TransDataMirror. */
+ if (tc->use_mirror_axis_any) {
+ bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
+ bool use_select = (t->flag & T_PROP_EDIT) == 0;
+ const bool mirror_axis[3] = {
+ tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z};
+ transform_convert_mesh_mirrordata_calc(
+ em, use_select, use_topology, mirror_axis, &mirror_data);
+
+ if (mirror_data.vert_map) {
+ tc->data_mirror_len = mirror_data.mirror_elem_len;
+ tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
+ __func__);
+
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ if (mirror_data.vert_map[a].index != -1) {
+ data_len--;
+ }
+ }
+ }
+ }
+ }
+
+ /* Detect CrazySpace [tm]. */
+ transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data);
+
+ /* Create TransData. */
+ BLI_assert(data_len >= 1);
+ tc->data_len = data_len;
+ tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)");
+
+ TransData *td = tc->data;
+ TransDataMirror *td_mirror = tc->data_mirror;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
+ int island_index = -1;
+ if (island_data.island_vert_map) {
+ const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
+ island_index = island_data.island_vert_map[connected_index];
+ }
+
+ float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset);
+ if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
+ tc_mesh_cdata_transdata_create(
+ (TransDataBasic *)td_mirror, eve, weight, &island_data, island_index);
+
+ int elem_index = mirror_data.vert_map[a].index;
+ BMVert *v_src = BM_vert_at_index(bm, elem_index);
+
+ td_mirror->flag |= mirror_data.vert_map[a].flag;
+ td_mirror->loc_src = BM_ELEM_CD_GET_VOID_P(v_src, cd_offset);
+ td_mirror++;
+ }
+ else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ tc_mesh_cdata_transdata_create(
+ (TransDataBasic *)td, eve, weight, &island_data, island_index);
+
+ if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
+ createSpaceNormal(td->axismtx, eve->no);
+ }
+ else {
+ /* Setting normals */
+ copy_v3_v3(td->axismtx[2], eve->no);
+ td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] =
+ td->axismtx[1][1] = td->axismtx[1][2] = 0.0f;
+ }
+
+ if (prop_mode) {
+ if (prop_mode & T_PROP_CONNECTED) {
+ td->dist = dists[a];
+ }
+ else {
+ td->flag |= TD_NOTCONNECTED;
+ td->dist = FLT_MAX;
+ }
+ }
+
+ /* CrazySpace */
+ transform_convert_mesh_crazyspace_transdata_set(
+ mtx,
+ smtx,
+ crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL,
+ crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ?
+ crazyspace_data.quats[a] :
+ NULL,
+ td);
+
+ td++;
+ }
+ }
+
+ transform_convert_mesh_islanddata_free(&island_data);
+ transform_convert_mesh_mirrordata_free(&mirror_data);
+ transform_convert_mesh_crazyspace_free(&crazyspace_data);
+ if (dists) {
+ MEM_freeN(dists);
+ }
+ if (dists_index) {
+ MEM_freeN(dists_index);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Mesh Data
+ * \{ */
+
+static void tc_mesh_cdata_apply_to_mirror(TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->use_mirror_axis_any) {
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ td_mirror->loc[0] = td_mirror->loc_src[0];
+ }
+ }
+ }
+}
+
+static void recalcData_mesh_cdata(TransInfo *t)
+{
+ bool is_canceling = t->state == TRANS_CANCEL;
+ /* mirror modifier clipping? */
+ if (!is_canceling) {
+ if (!(t->flag & T_NO_MIRROR)) {
+ tc_mesh_cdata_apply_to_mirror(t);
+ }
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BKE_editmesh_looptri_and_normals_calc(em);
+ }
+}
+
+/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshVertCData = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMeshVertCData,
+ /* recalcData */ recalcData_mesh_cdata,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index 2fa8fcf65ba..cfa933d1600 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -59,7 +59,7 @@ typedef struct TransDataNla {
/** \name NLA Transform Creation
* \{ */
-void createTransNlaData(bContext *C, TransInfo *t)
+static void createTransNlaData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
SpaceNla *snla = NULL;
@@ -82,12 +82,13 @@ void createTransNlaData(bContext *C, TransInfo *t)
snla = (SpaceNla *)ac.sl;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* which side of the current frame should be allowed */
if (t->mode == TFM_TIME_EXTEND) {
- t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA);
+ t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
}
else {
/* normal transform - both sides of current frame are considered */
@@ -108,10 +109,10 @@ void createTransNlaData(bContext *C, TransInfo *t)
/* transition strips can't get directly transformed */
if (strip->type != NLASTRIP_TYPE_TRANSITION) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
- if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) {
+ if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) {
count++;
}
- if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) {
+ if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) {
count++;
}
}
@@ -122,7 +123,7 @@ void createTransNlaData(bContext *C, TransInfo *t)
/* stop if trying to build list if nothing selected */
if (count == 0) {
/* clear temp metas that may have been created but aren't needed now
- * because they fell on the wrong side of CFRA
+ * because they fell on the wrong side of scene->r.cfra
*/
for (ale = anim_data.first; ale; ale = ale->next) {
NlaTrack *nlt = (NlaTrack *)ale->data;
@@ -181,12 +182,12 @@ void createTransNlaData(bContext *C, TransInfo *t)
tdn->h2[0] = strip->end;
tdn->h2[1] = yval;
- center[0] = (float)CFRA;
+ center[0] = (float)scene->r.cfra;
center[1] = yval;
center[2] = 0.0f;
/* set td's based on which handles are applicable */
- if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) {
+ if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) {
/* just set tdn to assume that it only has one handle for now */
tdn->handle = -1;
@@ -206,7 +207,7 @@ void createTransNlaData(bContext *C, TransInfo *t)
td->extra = tdn;
td++;
}
- if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) {
+ if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) {
/* if tdn is already holding the start handle,
* then we're doing both, otherwise, only end */
tdn->handle = (tdn->handle) ? 2 : 1;
@@ -248,7 +249,7 @@ void createTransNlaData(bContext *C, TransInfo *t)
ANIM_animdata_freelist(&anim_data);
}
-void recalcData_nla(TransInfo *t)
+static void recalcData_nla(TransInfo *t)
{
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
@@ -463,7 +464,7 @@ void recalcData_nla(TransInfo *t)
/** \name Special After Transform NLA
* \{ */
-void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
+static void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
{
bAnimContext ac;
@@ -475,7 +476,7 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
if (ac.datatype) {
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
+ short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY);
/* get channels to work on */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -505,3 +506,10 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_NLA = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransNlaData,
+ /* recalcData */ recalcData_nla,
+ /* special_aftertrans_update */ special_aftertrans_update__nla,
+};
diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c
index 8281052c314..e18f75b71ae 100644
--- a/source/blender/editors/transform/transform_convert_node.c
+++ b/source/blender/editors/transform/transform_convert_node.c
@@ -89,7 +89,7 @@ static bool is_node_parent_select(bNode *node)
return false;
}
-void createTransNodeData(TransInfo *t)
+static void createTransNodeData(bContext *UNUSED(C), TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
SpaceNode *snode = t->area->spacedata.first;
@@ -150,7 +150,7 @@ void createTransNodeData(TransInfo *t)
/** \name Node Transform Creation
* \{ */
-void flushTransNodes(TransInfo *t)
+static void flushTransNodes(TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
@@ -220,7 +220,7 @@ void flushTransNodes(TransInfo *t)
/** \name Special After Transform Node
* \{ */
-void special_aftertrans_update__node(bContext *C, TransInfo *t)
+static void special_aftertrans_update__node(bContext *C, TransInfo *t)
{
struct Main *bmain = CTX_data_main(C);
const bool canceled = (t->state == TRANS_CANCEL);
@@ -249,3 +249,10 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Node = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransNodeData,
+ /* recalcData */ flushTransNodes,
+ /* special_aftertrans_update */ special_aftertrans_update__node,
+};
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 5879a65eb4b..be5f59c61c0 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -480,7 +480,7 @@ static void clear_trans_object_base_flags(TransInfo *t)
}
}
-void createTransObject(bContext *C, TransInfo *t)
+static void createTransObject(bContext *C, TransInfo *t)
{
Main *bmain = CTX_data_main(C);
TransData *td = NULL;
@@ -732,8 +732,8 @@ static void autokeyframe_object(
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase dsources = {NULL, NULL};
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
- (float)CFRA);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (float)scene->r.cfra);
eInsertKeyFlags flag = 0;
/* Get flags used for inserting keyframes. */
@@ -860,12 +860,12 @@ static bool motionpath_need_update_object(Scene *scene, Object *ob)
/** \name Recalc Data object
* \{ */
-void recalcData_objects(TransInfo *t)
+static void recalcData_objects(TransInfo *t)
{
bool motionpath_update = false;
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -918,7 +918,7 @@ void recalcData_objects(TransInfo *t)
/** \name Special After Transform Object
* \{ */
-void special_aftertrans_update__object(bContext *C, TransInfo *t)
+static void special_aftertrans_update__object(bContext *C, TransInfo *t)
{
BLI_assert(t->options & CTX_OBJECT);
@@ -991,3 +991,10 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Object = {
+ /* flags */ 0,
+ /* createTransData */ createTransObject,
+ /* recalcData */ recalcData_objects,
+ /* special_aftertrans_update */ special_aftertrans_update__object,
+};
diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c
index 1f58ec80f02..39bf22a9af9 100644
--- a/source/blender/editors/transform/transform_convert_object_texspace.c
+++ b/source/blender/editors/transform/transform_convert_object_texspace.c
@@ -29,7 +29,7 @@
*
* \{ */
-void createTransTexspace(TransInfo *t)
+static void createTransTexspace(bContext *UNUSED(C), TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
TransData *td;
@@ -86,11 +86,11 @@ void createTransTexspace(TransInfo *t)
/** \name Recalc Data object
* \{ */
-void recalcData_texspace(TransInfo *t)
+static void recalcData_texspace(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -106,3 +106,10 @@ void recalcData_texspace(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_ObjectTexSpace = {
+ /* flags */ 0,
+ /* createTransData */ createTransTexspace,
+ /* recalcData */ recalcData_texspace,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_paintcurve.c b/source/blender/editors/transform/transform_convert_paintcurve.c
index b2566016496..61b1cb9493b 100644
--- a/source/blender/editors/transform/transform_convert_paintcurve.c
+++ b/source/blender/editors/transform/transform_convert_paintcurve.c
@@ -108,7 +108,7 @@ static void PaintCurvePointToTransData(PaintCurvePoint *pcp,
}
}
-void createTransPaintCurveVerts(bContext *C, TransInfo *t)
+static void createTransPaintCurveVerts(bContext *C, TransInfo *t)
{
Paint *paint = BKE_paint_get_active_from_context(C);
PaintCurve *pc;
@@ -188,7 +188,7 @@ void createTransPaintCurveVerts(bContext *C, TransInfo *t)
/** \name Paint Curve Transform Flush
* \{ */
-void flushTransPaintCurve(TransInfo *t)
+static void flushTransPaintCurve(TransInfo *t)
{
int i;
@@ -204,3 +204,10 @@ void flushTransPaintCurve(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_PaintCurve = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransPaintCurveVerts,
+ /* recalcData */ flushTransPaintCurve,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c
index d7b0f378c8a..41f37b34af0 100644
--- a/source/blender/editors/transform/transform_convert_particle.c
+++ b/source/blender/editors/transform/transform_convert_particle.c
@@ -28,7 +28,7 @@
/** \name Particle Edit Transform Creation
* \{ */
-void createTransParticleVerts(TransInfo *t)
+static void createTransParticleVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -235,12 +235,19 @@ static void flushTransParticles(TransInfo *t)
/** \name Recalc Transform Particles Data
* \{ */
-void recalcData_particles(TransInfo *t)
+static void recalcData_particles(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
flushTransParticles(t);
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Particle = {
+ /* flags */ T_POINTS,
+ /* createTransData */ createTransParticleVerts,
+ /* recalcData */ recalcData_particles,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_sculpt.c b/source/blender/editors/transform/transform_convert_sculpt.c
index 5bf6bfa8644..95958b816ab 100644
--- a/source/blender/editors/transform/transform_convert_sculpt.c
+++ b/source/blender/editors/transform/transform_convert_sculpt.c
@@ -23,7 +23,7 @@
/** \name Sculpt Transform Creation
* \{ */
-void createTransSculpt(bContext *C, TransInfo *t)
+static void createTransSculpt(bContext *C, TransInfo *t)
{
TransData *td;
@@ -94,13 +94,13 @@ void createTransSculpt(bContext *C, TransInfo *t)
/** \name Recalc Data object
* \{ */
-void recalcData_sculpt(TransInfo *t)
+static void recalcData_sculpt(TransInfo *t)
{
Object *ob = OBACT(t->view_layer);
ED_sculpt_update_modal_transform(t->context, ob);
}
-void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
+static void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
if (!BKE_id_is_editable(CTX_data_main(C), &scene->id)) {
@@ -114,3 +114,10 @@ void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Sculpt = {
+ /* flags */ 0,
+ /* createTransData */ createTransSculpt,
+ /* recalcData */ recalcData_sculpt,
+ /* special_aftertrans_update */ special_aftertrans_update__sculpt,
+};
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 849641fd320..eefc9d0cc2a 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -90,7 +90,7 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag)
if (t->mode == TFM_TIME_EXTEND) {
/* *** Extend Transform *** */
- int cfra = CFRA;
+ int cfra = scene->r.cfra;
int left = SEQ_time_left_handle_frame_get(scene, seq);
int right = SEQ_time_right_handle_frame_get(scene, seq);
@@ -465,7 +465,7 @@ static SeqCollection *query_time_dependent_strips_strips(TransInfo *t)
return dependent;
}
-void createTransSeqData(TransInfo *t)
+static void createTransSeqData(bContext *UNUSED(C), TransInfo *t)
{
Scene *scene = t->scene;
Editing *ed = SEQ_editing_get(t->scene);
@@ -489,7 +489,7 @@ void createTransSeqData(TransInfo *t)
}
tc->custom.type.free_cb = freeSeqData;
- t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA);
+ t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
count = SeqTransCount(t, ed->seqbasep);
@@ -659,7 +659,7 @@ static void flushTransSeq(TransInfo *t)
SEQ_collection_free(transformed_strips);
}
-void recalcData_sequencer(TransInfo *t)
+static void recalcData_sequencer(TransInfo *t)
{
TransData *td;
int a;
@@ -689,7 +689,7 @@ void recalcData_sequencer(TransInfo *t)
/** \name Special After Transform Sequencer
* \{ */
-void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
+static void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
{
if (t->state == TRANS_CANCEL) {
return;
@@ -734,3 +734,10 @@ void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2])
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Sequencer = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransSeqData,
+ /* recalcData */ recalcData_sequencer,
+ /* special_aftertrans_update */ special_aftertrans_update__sequencer,
+};
diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c
index 46606d5814f..3d0c3ddaa4c 100644
--- a/source/blender/editors/transform/transform_convert_sequencer_image.c
+++ b/source/blender/editors/transform/transform_convert_sequencer_image.c
@@ -105,7 +105,7 @@ static void freeSeqData(TransInfo *UNUSED(t),
MEM_freeN(td->extra);
}
-void createTransSeqImageData(TransInfo *t)
+static void createTransSeqImageData(bContext *UNUSED(C), TransInfo *t)
{
Editing *ed = SEQ_editing_get(t->scene);
const SpaceSeq *sseq = t->area->spacedata.first;
@@ -173,25 +173,25 @@ static bool autokeyframe_sequencer_image(bContext *C,
bool changed = false;
if (do_rot) {
prop = RNA_struct_find_property(&ptr, "rotation");
- changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false);
}
if (do_loc) {
prop = RNA_struct_find_property(&ptr, "offset_x");
- changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false);
prop = RNA_struct_find_property(&ptr, "offset_y");
- changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false);
}
if (do_scale) {
prop = RNA_struct_find_property(&ptr, "scale_x");
- changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false);
prop = RNA_struct_find_property(&ptr, "scale_y");
- changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false);
+ changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false);
}
return changed;
}
-void recalcData_sequencer_image(TransInfo *t)
+static void recalcData_sequencer_image(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
TransData *td = NULL;
@@ -247,7 +247,7 @@ void recalcData_sequencer_image(TransInfo *t)
}
}
-void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
+static void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
@@ -271,3 +271,10 @@ void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *
}
}
}
+
+TransConvertTypeInfo TransConvertType_SequencerImage = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransSeqImageData,
+ /* recalcData */ recalcData_sequencer_image,
+ /* special_aftertrans_update */ special_aftertrans_update__sequencer_image,
+};
diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c
index d447cd71a40..c0c660289a5 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -518,7 +518,7 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
}
}
-void createTransTrackingData(bContext *C, TransInfo *t)
+static void createTransTrackingData(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
@@ -694,7 +694,7 @@ static void flushTransTracking(TransInfo *t)
}
}
-void recalcData_tracking(TransInfo *t)
+static void recalcData_tracking(TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
@@ -747,7 +747,7 @@ void recalcData_tracking(TransInfo *t)
/** \name Special After Transform Tracking
* \{ */
-void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
+static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
MovieClip *clip = ED_space_clip_get_clip(sc);
@@ -790,3 +790,10 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Tracking = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransTrackingData,
+ /* recalcData */ recalcData_tracking,
+ /* special_aftertrans_update */ special_aftertrans_update__movieclip,
+};
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e45cac36736..56a7d045dfd 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -555,7 +555,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Release confirms preference should not affect node editor (T69288, T70504). */
- if (ISMOUSE(t->launch_event) &&
+ if (ISMOUSE_BUTTON(t->launch_event) &&
((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) {
/* Global "release confirm" on mouse bindings */
t->flag |= T_RELEASE_CONFIRM;
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index 83f1bd35f81..d8da7a11d28 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -292,6 +292,9 @@ void constraintTransLim(const TransInfo *t, TransData *td)
continue;
}
+ /* Initialize the custom space for use in calculating the matrices. */
+ BKE_constraint_custom_object_space_init(&cob, con);
+
/* get constraint targets if needed */
BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime);
@@ -549,18 +552,14 @@ void ElementRotation_ex(const TransInfo *t,
mul_m3_m3m3(totmat, mat, td->mtx);
mul_m3_m3m3(smat, td->smtx, totmat);
- /* apply gpencil falloff */
+ /* Apply gpencil falloff. */
if (t->options & CTX_GPENCIL_STROKES) {
bGPDstroke *gps = (bGPDstroke *)td->extra;
- float sx = smat[0][0];
- float sy = smat[1][1];
- float sz = smat[2][2];
-
- mul_m3_fl(smat, gps->runtime.multi_frame_falloff);
- /* fix scale */
- smat[0][0] = sx;
- smat[1][1] = sy;
- smat[2][2] = sz;
+ if (gps->runtime.multi_frame_falloff != 1.0f) {
+ float ident_mat[3][3];
+ unit_m3(ident_mat);
+ interp_m3_m3m3(smat, ident_mat, smat, gps->runtime.multi_frame_falloff);
+ }
}
sub_v3_v3v3(vec, td->iloc, center);
@@ -945,7 +944,11 @@ void ElementResize(const TransInfo *t,
if (td->ext && td->ext->size) {
float fsize[3];
- if (ELEM(t->data_type, TC_SCULPT, TC_OBJECT, TC_OBJECT_TEXSPACE, TC_POSE)) {
+ if (ELEM(t->data_type,
+ &TransConvertType_Sculpt,
+ &TransConvertType_Object,
+ &TransConvertType_ObjectTexSpace,
+ &TransConvertType_Pose)) {
float obsizemat[3][3];
/* Reorient the size mat to fit the oriented object. */
mul_m3_m3m3(obsizemat, tmat, td->axismtx);
@@ -1205,7 +1208,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
break;
}
- if (t->data_type == TC_MESH_VERTS) {
+ if (t->data_type == &TransConvertType_Mesh) {
/* Init Custom Data correction.
* Ideally this should be called when creating the TransData. */
transform_convert_mesh_customdatacorrect_init(t);
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index aa4d608de04..f7f9e14b8ac 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -64,10 +64,8 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
- if (*td->val <= 0.0f) {
- *td->val = 0.001f;
- }
+ *td->val = interpf(*td->val, td->ival, td->factor);
+ CLAMP_MIN(*td->val, 0.0f);
}
}
}
@@ -93,10 +91,6 @@ void initCurveShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
-#ifdef USE_NUM_NO_ZERO
- t->num.val_flag[0] |= NUM_NO_ZERO;
-#endif
-
t->flag |= T_NO_CONSTRAINT;
}
diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
index 987d8396907..e96e74b596c 100644
--- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c
+++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
@@ -44,16 +44,11 @@ static void transdata_elem_bevel_weight(const TransInfo *UNUSED(t),
TransData *td,
const float weight)
{
- if (td->val == NULL) {
+ if (td->loc == NULL) {
return;
}
- *td->val = td->ival + weight * td->factor;
- if (*td->val < 0.0f) {
- *td->val = 0.0f;
- }
- if (*td->val > 1.0f) {
- *td->val = 1.0f;
- }
+ *td->loc = td->iloc[0] + weight * td->factor;
+ CLAMP(*td->loc, 0.0f, 1.0f);
}
static void transdata_elem_bevel_weight_fn(void *__restrict iter_data_v,
diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c
index f1acc2a4c9a..1a3ccf30387 100644
--- a/source/blender/editors/transform/transform_mode_edge_crease.c
+++ b/source/blender/editors/transform/transform_mode_edge_crease.c
@@ -44,17 +44,12 @@ static void transdata_elem_crease(const TransInfo *UNUSED(t),
TransData *td,
const float crease)
{
- if (td->val == NULL) {
+ if (td->loc == NULL) {
return;
}
- *td->val = td->ival + crease * td->factor;
- if (*td->val < 0.0f) {
- *td->val = 0.0f;
- }
- if (*td->val > 1.0f) {
- *td->val = 1.0f;
- }
+ *td->loc = td->iloc[0] + crease * td->factor;
+ CLAMP(*td->loc, 0.0f, 1.0f);
}
static void transdata_elem_crease_fn(void *__restrict iter_data_v,
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index 2327aa4e9c4..8d790b4699b 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -84,7 +84,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
transform_snap_increment(t, &angle);
- applySnapping(t, &angle);
+ applySnappingAsGroup(t, &angle);
applyNumInput(&t->num, &angle);
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index 9a732562709..5ca1fdf75c6 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -87,7 +87,7 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
}
else {
copy_v2_v2(values_final, t->values);
- applySnapping(t, values_final);
+ applySnappingAsGroup(t, values_final);
transform_convert_sequencer_channel_clamp(t, values_final);
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index b8e9a0d1a4d..a3c49d2362f 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1292,7 +1292,7 @@ static void edge_slide_snap_apply(TransInfo *t, float *value)
side_index = t_snap >= t_mid;
}
- if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) {
float co_dir[3];
sub_v3_v3v3(co_dir, co_dest[side_index], co_orig);
normalize_v3(co_dir);
@@ -1444,7 +1444,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0] + t->values_modal_offset[0];
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!validSnap(t)) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 83dce17d104..8b9431b65ea 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -74,7 +74,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
CLAMP(*td->val, 0.0f, 1.0f);
}
}
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 796d5c7ae9c..d8ec7d4ff50 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -74,7 +74,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;
}
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 19a3deade63..e2ccf61796b 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -90,7 +90,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
}
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;
}
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index ffae651e4aa..1ccda96fecb 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -11,6 +11,7 @@
#include "BLI_task.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_unit.h"
#include "ED_screen.h"
@@ -84,6 +85,103 @@ static void ApplySnapResize(TransInfo *t, float vec[3])
}
}
+/**
+ * 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;
+ }
+}
+
+static bool clip_uv_transform_resize(TransInfo *t, float vec[2])
+{
+ /* Check if the current image in UV editor is a tiled image or not. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ const Image *image = sima->image;
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+
+ /* Stores the coordinates of the closest UDIM tile.
+ * Also acts as an offset to the tile from the origin of UV space. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
+ if (is_tiled_image) {
+ BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset);
+ }
+
+ /* Assume no change is required. */
+ float scale = 1.0f;
+
+ /* Are we scaling U and V together, or just one axis? */
+ const bool adjust_u = !(t->con.mode & CON_AXIS1);
+ const bool adjust_v = !(t->con.mode & CON_AXIS0);
+ const bool use_local_center = transdata_check_local_center(t, t->around);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
+
+ /* Get scale origin. */
+ const float *scale_origin = use_local_center ? td->center : t->center_global;
+
+ /* Alias td->loc as min and max just in case we need to optimize later. */
+ const float *min = td->loc;
+ const float *max = td->loc;
+
+ if (adjust_u) {
+ /* Update U against the left border. */
+ constrain_scale_to_boundary(
+ scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale);
+
+ /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */
+ constrain_scale_to_boundary(
+ base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale);
+ }
+
+ /* Do the same for the V co-ordinate. */
+ if (adjust_v) {
+ constrain_scale_to_boundary(
+ scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale);
+
+ constrain_scale_to_boundary(
+ base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale);
+ }
+ }
+ }
+ vec[0] *= scale;
+ vec[1] *= scale;
+ return scale != 1.0f;
+}
+
static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
{
float mat[3][3];
@@ -105,7 +203,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
constraintNumInput(t, t->values_final);
}
- applySnapping(t, t->values_final);
+ applySnappingAsGroup(t, t->values_final);
}
size_to_mat3(mat, t->values_final);
@@ -157,7 +255,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
}
/* Evil hack - redo resize if clipping needed. */
- if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values_final, 1)) {
+ if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) {
size_to_mat3(mat, t->values_final);
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index f75c65448df..a7207b36578 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -305,7 +305,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
final = large_rotation_limit(final);
}
else {
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!(activeSnap(t) && validSnap(t))) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 8099449ec23..bdbb66b72f4 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -99,7 +99,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
constraintNumInput(t, t->values_final);
}
- applySnapping(t, t->values_final);
+ applySnappingAsGroup(t, t->values_final);
}
size_to_mat3(mat_final, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c
index 4130f6dc034..1474bc4591a 100644
--- a/source/blender/editors/transform/transform_mode_timescale.c
+++ b/source/blender/editors/transform/transform_mode_timescale.c
@@ -59,7 +59,7 @@ static void applyTimeScaleValue(TransInfo *t, float value)
* (this is only valid when not in NLA)
*/
AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
- float startx = CFRA;
+ float startx = scene->r.cfra;
float fac = value;
/* take proportional editing into account */
@@ -107,7 +107,7 @@ void initTimeScale(TransInfo *t)
t->mode = TFM_TIME_SCALE;
t->transform = applyTimeScale;
- /* recalculate center2d to use CFRA and mouse Y, since that's
+ /* recalculate center2d to use scene->r.cfra and mouse Y, since that's
* what is used in time scale */
if ((t->flag & T_OVERRIDE_CENTER) == 0) {
t->center_global[0] = t->scene->r.cfra;
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 3c6b6ea4117..04a41814b53 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -16,6 +16,7 @@
#include "BLI_task.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_report.h"
#include "BKE_unit.h"
@@ -434,6 +435,54 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
custom_data->prev.rotate_mode = rotate_mode;
}
+static bool clip_uv_transform_translation(TransInfo *t, float vec[2])
+{
+ /* Check if the current image in UV editor is a tiled image or not. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ const Image *image = sima->image;
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+
+ /* Stores the coordinates of the closest UDIM tile.
+ * Also acts as an offset to the tile from the origin of UV space. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
+ if (is_tiled_image) {
+ BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset);
+ }
+
+ float min[2], max[2];
+ min[0] = min[1] = FLT_MAX;
+ max[0] = max[1] = -FLT_MAX;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
+ minmax_v2v2_v2(min, max, td->loc);
+ }
+ }
+
+ bool result = false;
+ if (min[0] < base_offset[0]) {
+ vec[0] += base_offset[0] - min[0];
+ result = true;
+ }
+ else if (max[0] > base_offset[0] + t->aspect[0]) {
+ vec[0] -= max[0] - base_offset[0] - t->aspect[0];
+ result = true;
+ }
+
+ if (min[1] < base_offset[1]) {
+ vec[1] += base_offset[1] - min[1];
+ result = true;
+ }
+ else if (max[1] > base_offset[1] + t->aspect[1]) {
+ vec[1] -= max[1] - base_offset[1] - t->aspect[1];
+ result = true;
+ }
+
+ return result;
+}
+
static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
@@ -470,7 +519,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
}
t->tsnap.snapElem = SCE_SNAP_MODE_NONE;
- applySnapping(t, global_dir);
+ applySnappingAsGroup(t, global_dir);
transform_snap_grid(t, global_dir);
if (t->con.mode & CON_APPLY) {
@@ -498,7 +547,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
applyTranslationValue(t, global_dir);
/* evil hack - redo translation if clipping needed */
- if (t->flag & T_CLIP_UV && clipUVTransform(t, global_dir, 0)) {
+ if (t->flag & T_CLIP_UV && clip_uv_transform_translation(t, global_dir)) {
applyTranslationValue(t, global_dir);
/* In proportional edit it can happen that */
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 77c5707d814..674ffcf17a8 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -539,7 +539,7 @@ static void vert_slide_snap_apply(TransInfo *t, float *value)
getSnapPoint(t, dvec);
sub_v3_v3(dvec, t->tsnap.snapTarget);
- if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) {
float co_dir[3];
sub_v3_v3v3(co_dir, co_curr_3d, co_orig_3d);
normalize_v3(co_dir);
@@ -568,7 +568,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0] + t->values_modal_offset[0];
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!validSnap(t)) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index a64eff8f981..7c94241f3e3 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -566,6 +566,17 @@ static bool transform_poll_property(const bContext *UNUSED(C),
}
}
+ /* Snapping. */
+ {
+ PropertyRNA *prop_snap = RNA_struct_find_property(op->ptr, "snap");
+ if (prop_snap && (prop_snap != prop) &&
+ (RNA_property_boolean_get(op->ptr, prop_snap) == false)) {
+ if (STRPREFIX(prop_id, "snap") || STRPREFIX(prop_id, "use_snap")) {
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -644,28 +655,63 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
}
if (flags & P_SNAP) {
- prop = RNA_def_boolean(ot->srna, "snap", 0, "Use Snapping Options", "");
+ prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_enum(ot->srna,
+ "snap_elements",
+ rna_enum_snap_element_items,
+ SCE_SNAP_MODE_INCREMENT,
+ "Snap to Elements",
+ "");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+
+ RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
+
if (flags & P_GEO_SNAP) {
- /* 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", "");
+ /* 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, "Snap With", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ /* Target selection. */
+ prop = RNA_def_boolean(ot->srna, "use_snap_self", true, "Target: Include Active", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "use_snap_edit", true, "Target: Include Edit", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ /* Face Nearest options */
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_to_same_target", false, "Snap to Same Target", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_int(
+ ot->srna, "snap_face_nearest_steps", 1, 1, 32767, "Face Nearest Steps", "", 1, 32767);
+ 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);
RNA_def_property_flag(prop, PROP_HIDDEN);
if (flags & P_ALIGN_SNAP) {
- prop = RNA_def_boolean(ot->srna, "snap_align", 0, "Align with Point Normal", "");
+ prop = RNA_def_boolean(ot->srna, "snap_align", false, "Align with Point Normal", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_float_vector(
ot->srna, "snap_normal", 3, NULL, -FLT_MAX, FLT_MAX, "Normal", "", -FLT_MAX, FLT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
}
+ else {
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ }
}
if (flags & P_GPENCIL_EDIT) {
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 649217092aa..9a563aaf473 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -126,8 +126,12 @@ bool activeSnap(const TransInfo *t)
((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT);
}
-bool activeSnap_with_project(const TransInfo *t)
+bool activeSnap_SnappingIndividual(const TransInfo *t)
{
+ if (activeSnap(t) && t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) {
+ return true;
+ }
+
if (!t->tsnap.project) {
return false;
}
@@ -143,6 +147,27 @@ bool activeSnap_with_project(const TransInfo *t)
return true;
}
+bool activeSnap_SnappingAsGroup(const TransInfo *t)
+{
+ if (!activeSnap(t)) {
+ return false;
+ }
+
+ if (t->tsnap.mode == SCE_SNAP_MODE_FACE_RAYCAST && t->tsnap.project) {
+ return false;
+ }
+
+ if (t->tsnap.mode == SCE_SNAP_MODE_FACE_NEAREST) {
+ return false;
+ }
+
+ if (doForceIncrementSnap(t)) {
+ return false;
+ }
+
+ return true;
+}
+
bool transformModeUseSnap(const TransInfo *t)
{
ToolSettings *ts = t->settings;
@@ -343,93 +368,157 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
return status;
}
-void applyProject(TransInfo *t)
+static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td)
{
- if (!activeSnap_with_project(t)) {
- return;
+ if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ return false;
+ }
+
+ float iloc[3], loc[3], no[3];
+ float mval_fl[2];
+
+ copy_v3_v3(iloc, td->loc);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, iloc);
+ }
+ else if (t->options & CTX_OBJECT) {
+ BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
+ copy_v3_v3(iloc, td->ob->obmat[3]);
+ }
+
+ if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) !=
+ V3D_PROJ_RET_OK) {
+ return false;
+ }
+
+ eSnapMode hit = ED_transform_snap_object_project_view3d(
+ t->tsnap.object_context,
+ t->depsgraph,
+ t->region,
+ t->view,
+ SCE_SNAP_MODE_FACE_RAYCAST,
+ &(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,
+ },
+ NULL,
+ mval_fl,
+ NULL,
+ 0,
+ loc,
+ no);
+ if (hit != SCE_SNAP_MODE_FACE_RAYCAST) {
+ return false;
}
float tvec[3];
- int i;
+ sub_v3_v3v3(tvec, loc, iloc);
- /* XXX FLICKER IN OBJECT MODE */
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- float iloc[3], loc[3], no[3];
- float mval_fl[2];
- if (td->flag & TD_SKIP) {
- continue;
- }
+ mul_m3_v3(td->smtx, tvec);
- if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
- continue;
- }
+ add_v3_v3(td->loc, tvec);
- copy_v3_v3(iloc, td->loc);
- if (tc->use_local_mat) {
- mul_m4_v3(tc->mat, iloc);
- }
- else if (t->options & CTX_OBJECT) {
- BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
- copy_v3_v3(iloc, td->ob->obmat[3]);
- }
+ if (t->tsnap.align && (t->options & CTX_OBJECT)) {
+ /* handle alignment as well */
+ const float *original_normal;
+ float mat[3][3];
- if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) ==
- V3D_PROJ_RET_OK) {
- 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;
- }
+ /* In pose mode, we want to align normals with Y axis of bones. */
+ original_normal = td->axismtx[2];
-#if 0
- if (tc->use_local_mat) {
- mul_m4_v3(tc->imat, loc);
- }
-#endif
+ rotation_between_vecs_to_mat3(mat, original_normal, no);
+
+ transform_data_ext_rotate(td, mat, true);
+
+ /* TODO: support constraints for rotation too? see #ElementRotation. */
+ }
+ return true;
+}
+
+static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td)
+{
+ if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST)) {
+ return;
+ }
- sub_v3_v3v3(tvec, loc, iloc);
+ float init_loc[3];
+ float prev_loc[3];
+ float snap_loc[3], snap_no[3];
- mul_m3_v3(td->smtx, tvec);
+ copy_v3_v3(init_loc, td->iloc);
+ copy_v3_v3(prev_loc, td->loc);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, init_loc);
+ mul_m4_v3(tc->mat, prev_loc);
+ }
+ else if (t->options & CTX_OBJECT) {
+ BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
+ copy_v3_v3(init_loc, td->ob->obmat[3]);
+ }
- add_v3_v3(td->loc, tvec);
+ eSnapMode hit = ED_transform_snap_object_project_view3d(
+ t->tsnap.object_context,
+ t->depsgraph,
+ t->region,
+ t->view,
+ SCE_SNAP_MODE_FACE_NEAREST,
+ &(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 = false,
+ .face_nearest_steps = t->tsnap.face_nearest_steps,
+ .keep_on_same_target = t->tsnap.flag & SCE_SNAP_KEEP_ON_SAME_OBJECT,
+ },
+ init_loc,
+ NULL,
+ prev_loc,
+ 0,
+ snap_loc,
+ snap_no);
+
+ if (hit != SCE_SNAP_MODE_FACE_NEAREST) {
+ return;
+ }
- if (t->tsnap.align && (t->options & CTX_OBJECT)) {
- /* handle alignment as well */
- const float *original_normal;
- float mat[3][3];
+ float tvec[3];
+ sub_v3_v3v3(tvec, snap_loc, prev_loc);
+ mul_m3_v3(td->smtx, tvec);
+ add_v3_v3(td->loc, tvec);
- /* In pose mode, we want to align normals with Y axis of bones... */
- original_normal = td->axismtx[2];
+ /* TODO: support snap alignment similar to #SCE_SNAP_MODE_FACE_RAYCAST? */
+}
- rotation_between_vecs_to_mat3(mat, original_normal, no);
+void applySnappingIndividual(TransInfo *t)
+{
+ if (!activeSnap_SnappingIndividual(t)) {
+ return;
+ }
- transform_data_ext_rotate(td, mat, true);
+ /* XXX FLICKER IN OBJECT MODE */
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ TransData *td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
- /* TODO: support constraints for rotation too? see #ElementRotation. */
- }
+ if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
+ continue;
}
- }
+ /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
+ * fallback to face nearest ray-cast does not hit. */
+ bool hit = applyFaceProject(t, tc, td);
+ if (!hit) {
+ applyFaceNearest(t, tc, td);
+ }
#if 0 /* TODO: support this? */
- constraintTransLim(t, td);
+ constraintTransLim(t, td);
#endif
+ }
}
}
@@ -483,15 +572,9 @@ void applyGridAbsolute(TransInfo *t)
}
}
-void applySnapping(TransInfo *t, float *vec)
+void applySnappingAsGroup(TransInfo *t, float *vec)
{
- /* Each Trans Data already makes the snap to face */
- if (doForceIncrementSnap(t)) {
- return;
- }
-
- if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) {
- /* A similar snap will be applied to each transdata in `applyProject`. */
+ if (!activeSnap_SnappingAsGroup(t)) {
return;
}
@@ -644,70 +727,76 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t)
return SCE_SNAP_MODE_INCREMENT;
}
-static eSnapTargetSelect snap_select_type_get(TransInfo *t)
+static eSnapTargetSelect snap_target_select_from_spacetype(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
Base *base_act = view_layer->basact;
+
+ eSnapTargetSelect ret = SCE_SNAP_TARGET_ALL;
+
+ bool use_snap_active = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_ACTIVE) == 0;
+ bool use_snap_edit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_EDITED) == 0;
+ bool use_snap_nonedit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_NONEDITED) == 0;
+ bool use_snap_selectable_only = (t->tsnap.target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0;
+
if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) {
+ if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
+ /* Particles edit mode. */
+ return ret;
+ }
+
+ if (use_snap_selectable_only) {
+ ret |= SCE_SNAP_TARGET_ONLY_SELECTABLE;
+ }
+
if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
/* In "Edit Strokes" mode,
* snap tool can perform snap to selected or active objects (see T49632)
* TODO: perform self snap in gpencil_strokes.
*
* When we're moving the origins, allow snapping onto our own geometry (see T69132). */
- return SCE_SNAP_TARGET_ALL;
+ return ret;
}
const int obedit_type = t->obedit_type;
if (obedit_type != -1) {
/* Edit mode */
- if (ELEM(obedit_type,
- OB_MESH,
- OB_ARMATURE,
- OB_CURVES_LEGACY,
- OB_SURF,
- OB_LATTICE,
- OB_MBALL)) {
- /* Temporary limited to edit mode meshes, armature, curves, lattice and metaballs. */
-
- if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
- /* Exclude editmesh if using proportional edit */
- return SCE_SNAP_TARGET_NOT_EDITED;
+ if (obedit_type == OB_MESH) {
+ /* Editing a mesh */
+ if ((t->flag & T_PROP_EDIT) != 0) {
+ /* Exclude editmesh when using proportional edit */
+ ret |= SCE_SNAP_TARGET_NOT_EDITED;
}
-
- if (!t->tsnap.snap_self) {
- return SCE_SNAP_TARGET_NOT_ACTIVE;
+ if (!use_snap_active) {
+ ret |= SCE_SNAP_TARGET_NOT_ACTIVE;
+ }
+ if (!use_snap_edit) {
+ ret |= SCE_SNAP_TARGET_NOT_EDITED;
+ }
+ if (!use_snap_nonedit) {
+ ret |= SCE_SNAP_TARGET_NOT_NONEDITED;
}
-
- return SCE_SNAP_TARGET_NOT_SELECTED;
}
-
- return SCE_SNAP_TARGET_ALL;
+ else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) {
+ /* Temporary limited to edit mode armature, curves, surfaces, lattices, and metaballs. */
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED;
+ }
}
-
- if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
- /* Particles edit mode. */
- return SCE_SNAP_TARGET_ALL;
+ else {
+ /* Object or pose mode. */
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE;
}
-
- /* Object or pose mode. */
- return SCE_SNAP_TARGET_NOT_SELECTED;
}
-
- if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
- return SCE_SNAP_TARGET_NOT_SELECTED;
+ else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED;
}
- return SCE_SNAP_TARGET_ALL;
+ return ret;
}
static void initSnappingMode(TransInfo *t)
{
- ToolSettings *ts = t->settings;
- t->tsnap.mode = snap_mode_from_spacetype(t);
- t->tsnap.target_select = snap_select_type_get(t);
-
- if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) {
+ if ((t->spacetype != SPACE_VIEW3D) || !(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) {
/* Force project off when not supported. */
t->tsnap.project = false;
}
@@ -724,7 +813,7 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.use_backface_culling = snap_use_backface_culling(t);
t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0);
- if (t->data_type == TC_MESH_VERTS) {
+ if (t->data_type == &TransConvertType_Mesh) {
/* Ignore elements being transformed. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
@@ -753,9 +842,14 @@ static void initSnappingMode(TransInfo *t)
void initSnapping(TransInfo *t, wmOperator *op)
{
+ ToolSettings *ts = t->settings;
+ eSnapSourceSelect snap_source = ts->snap_target;
+
resetSnapping(t);
+ t->tsnap.mode = snap_mode_from_spacetype(t);
t->tsnap.flag = snap_flag_from_spacetype(t);
- eSnapSourceSelect snap_source = t->settings->snap_target;
+ t->tsnap.target_select = snap_target_select_from_spacetype(t);
+ t->tsnap.face_nearest_steps = max_ii(ts->snap_face_nearest_steps, 1);
/* if snap property exists */
PropertyRNA *prop;
@@ -764,11 +858,16 @@ void initSnapping(TransInfo *t, wmOperator *op)
if (RNA_property_boolean_get(op->ptr, prop)) {
t->modifiers |= MOD_SNAP;
+ if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ t->tsnap.mode = 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). */
if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
RNA_property_is_set(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);
}
@@ -791,9 +890,33 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.project = RNA_property_boolean_get(op->ptr, prop);
}
+ /* use_snap_self is misnamed and should be use_snap_active */
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) &&
RNA_property_is_set(op->ptr, prop)) {
- t->tsnap.snap_self = RNA_property_boolean_get(op->ptr, prop);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_ACTIVE);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_EDITED);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_NONEDITED);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_ONLY_SELECTABLE);
}
}
}
@@ -805,8 +928,19 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.align = ((t->tsnap.flag & SCE_SNAP_ROTATE) != 0);
t->tsnap.project = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
- t->tsnap.snap_self = !((t->tsnap.flag & SCE_SNAP_NO_SELF) != 0);
t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ (ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE),
+ SCE_SNAP_TARGET_NOT_ACTIVE);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED),
+ SCE_SNAP_TARGET_NOT_EDITED);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED),
+ SCE_SNAP_TARGET_NOT_NONEDITED);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ (ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE),
+ SCE_SNAP_TARGET_ONLY_SELECTABLE);
}
t->tsnap.source_select = snap_source;
@@ -991,8 +1125,8 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec))
found = (snap_elem != SCE_SNAP_MODE_NONE);
}
if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) {
- found = peelObjectsTransform(
- t, mval, (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, no, NULL);
+ bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0;
+ found = peelObjectsTransform(t, mval, use_peel, loc, no, NULL);
if (found) {
snap_elem = SCE_SNAP_MODE_VOLUME;
@@ -1026,7 +1160,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec))
objects,
objects_len,
t->mval,
- t->tsnap.target_select == SCE_SNAP_TARGET_NOT_SELECTED,
+ t->tsnap.target_select & SCE_SNAP_TARGET_NOT_SELECTED,
&dist_sq,
t->tsnap.snapPoint)) {
t->tsnap.snapPoint[0] *= t->aspect[0];
@@ -1118,7 +1252,7 @@ static void snap_target_grid_ensure(TransInfo *t)
{
/* Only need to calculate once. */
if ((t->tsnap.status & TARGET_GRID_INIT) == 0) {
- if (t->data_type == TC_CURSOR_VIEW3D) {
+ if (t->data_type == &TransConvertType_Cursor3D) {
/* Use a fallback when transforming the cursor.
* In this case the center is _not_ derived from the cursor which is being transformed. */
copy_v3_v3(t->tsnap.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc);
@@ -1321,9 +1455,10 @@ eSnapMode snapObjectsTransform(
&(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 = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
+ .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE_RAYCAST,
.use_backface_culling = t->tsnap.use_backface_culling,
},
+ NULL,
mval,
target,
dist_px,
@@ -1423,7 +1558,7 @@ bool peelObjectsTransform(TransInfo *t,
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_target_select == SCE_SNAP_TARGET_NOT_SELECTED && !(node->flag & NODE_SELECT)) ||
+ 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);
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index 6db027df067..3672e76c778 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -40,15 +40,16 @@ float transform_snap_increment_get(const TransInfo *t);
bool transform_snap_grid(TransInfo *t, float *val);
bool activeSnap(const TransInfo *t);
-bool activeSnap_with_project(const TransInfo *t);
+bool activeSnap_SnappingIndividual(const TransInfo *t);
+bool activeSnap_SnappingAsGroup(const TransInfo *t);
bool validSnap(const TransInfo *t);
void initSnapping(struct TransInfo *t, struct wmOperator *op);
void freeSnapping(struct TransInfo *t);
-void applyProject(TransInfo *t);
+void applySnappingIndividual(TransInfo *t);
void applyGridAbsolute(TransInfo *t);
-void applySnapping(TransInfo *t, float *vec);
+void applySnappingAsGroup(TransInfo *t, float *vec);
void resetSnapping(TransInfo *t);
eRedrawFlag handleSnapping(TransInfo *t, const struct wmEvent *event);
void drawSnapping(const struct bContext *C, TransInfo *t);
diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc
index bd3d29fac8a..479214ee2d3 100644
--- a/source/blender/editors/transform/transform_snap_object.cc
+++ b/source/blender/editors/transform/transform_snap_object.cc
@@ -405,6 +405,62 @@ static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
return sod;
}
+static BVHTreeFromMesh *snap_object_data_mesh_treedata_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ const Mesh *me_eval,
+ bool use_hide)
+{
+ SnapData_Mesh *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
+ return &sod->treedata_mesh;
+}
+
+static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ BMEditMesh *em)
+{
+ SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
+
+ BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
+
+ if (treedata->tree == nullptr) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BLI_assert(sod->treedata_editmesh.em ==
+ BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
+ em = sod->treedata_editmesh.em;
+
+ if (sctx->callbacks.edit_mesh.test_face_fn) {
+ BMesh *bm = em->bm;
+ BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri);
+
+ BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
+ int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
+ bm,
+ elem_mask,
+ sctx->callbacks.edit_mesh.test_face_fn,
+ sctx->callbacks.edit_mesh.user_data);
+
+ bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6);
+
+ MEM_freeN(elem_mask);
+ }
+ else {
+ /* Only cache if BVH-tree is created without a mask.
+ * This helps keep a standardized BVH-tree in cache. */
+ BKE_bvhtree_from_editmesh_get(treedata,
+ em,
+ 4,
+ BVHTREE_FROM_EM_LOOPTRI,
+ &sod->mesh_runtime->bvh_cache,
+ static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex));
+ }
+ }
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return nullptr;
+ }
+
+ return treedata;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -419,16 +475,16 @@ using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx,
void *data);
static bool snap_object_is_snappable(const SnapObjectContext *sctx,
- const eSnapTargetSelect snap_select,
+ const eSnapTargetSelect snap_target_select,
const Base *base_act,
- const Base *base,
- const bool is_in_object_mode)
+ const Base *base)
{
if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
return false;
}
- if ((snap_select == SCE_SNAP_TARGET_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
+ if ((snap_target_select == SCE_SNAP_TARGET_ALL) ||
+ (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
return true;
}
@@ -436,25 +492,37 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return false;
}
- if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) {
- return base_act != base;
- }
+ /* Get attributes of potential target. */
+ const bool is_active = (base_act == base);
+ const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL);
+ const bool is_edited = (base->object->mode == OB_MODE_EDIT);
+ const bool is_selectable = (base->flag & BASE_SELECTABLE);
+ /* Get attributes of state. */
+ const bool is_in_object_mode = (base_act == NULL) || (base_act->object->mode == OB_MODE_OBJECT);
- if (snap_select == SCE_SNAP_TARGET_NOT_EDITED) {
- return base->object->mode != OB_MODE_EDIT;
+ if (is_in_object_mode) {
+ /* Handle target selection options that make sense for object mode. */
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_selected) {
+ /* What is selectable or not is part of the object and depends on the mode. */
+ return false;
+ }
}
-
- if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) {
- if (is_in_object_mode) {
- return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL));
+ else {
+ /* Handle target selection options that make sense for edit/pose mode. */
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) && is_active) {
+ return false;
+ }
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) && is_edited && !is_active) {
+ /* Base is edited, but not active. */
+ return false;
+ }
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) {
+ return false;
}
-
- /* What is selectable or not is part of the object and depends on the mode. */
- return true;
}
- if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) {
- return (base->flag & BASE_SELECTABLE) != 0;
+ if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) {
+ return false;
}
return true;
@@ -470,11 +538,10 @@ static void iter_snap_objects(SnapObjectContext *sctx,
{
ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph);
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_target_select, base_act, base, is_in_object_mode)) {
+ if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
continue;
}
@@ -850,40 +917,9 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
len_diff = 0.0f;
}
- BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
-
- if (treedata->tree == nullptr) {
- em = sod->treedata_editmesh.em;
-
- if (sctx->callbacks.edit_mesh.test_face_fn) {
- BMesh *bm = em->bm;
- BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri);
-
- BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
- int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
- bm,
- elem_mask,
- sctx->callbacks.edit_mesh.test_face_fn,
- sctx->callbacks.edit_mesh.user_data);
-
- bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6);
-
- MEM_freeN(elem_mask);
- }
- else {
- /* Only cache if bvhtree is created without a mask.
- * This helps keep a standardized bvhtree in cache. */
- BKE_bvhtree_from_editmesh_get(treedata,
- em,
- 4,
- BVHTREE_FROM_EM_LOOPTRI,
- &sod->mesh_runtime->bvh_cache,
- static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex));
- }
-
- if (treedata->tree == nullptr) {
- return retval;
- }
+ BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
+ if (treedata == nullptr) {
+ return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
@@ -1098,7 +1134,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
* \param r_hit_list: List of #SnapObjectHitDepth (caller must free).
@@ -1150,6 +1186,324 @@ static bool raycastObjects(SnapObjectContext *sctx,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Surface Snap Funcs
+ * \{ */
+
+struct NearestWorldObjUserData {
+ const float *init_co;
+ const float *curr_co;
+ /* return args */
+ float *r_loc;
+ float *r_no;
+ int *r_index;
+ float r_dist_sq;
+ Object **r_ob;
+ float (*r_obmat)[4];
+ ListBase *r_hit_list;
+ bool ret;
+};
+
+static void nearest_world_tree_co(BVHTree *tree,
+ BVHTree_NearestPointCallback nearest_cb,
+ void *treedata,
+ float co[3],
+ float r_co[3],
+ float r_no[3],
+ int *r_index,
+ float *r_dist_sq)
+{
+ BVHTreeNearest nearest = {};
+ nearest.index = -1;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.dist_sq = FLT_MAX;
+
+ BLI_bvhtree_find_nearest(tree, co, &nearest, nearest_cb, treedata);
+
+ if (r_co) {
+ copy_v3_v3(r_co, nearest.co);
+ }
+ if (r_no) {
+ copy_v3_v3(r_no, nearest.no);
+ }
+ if (r_index) {
+ *r_index = nearest.index;
+ }
+ if (r_dist_sq) {
+ float diff[3];
+ sub_v3_v3v3(diff, co, nearest.co);
+ *r_dist_sq = len_squared_v3(diff);
+ }
+}
+
+static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
+ const struct SnapObjectParams *params,
+ BVHTree *tree,
+ BVHTree_NearestPointCallback nearest_cb,
+ void *treedata,
+ const float (*obmat)[4],
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ if (curr_co == nullptr || init_co == nullptr) {
+ /* No location to work with, so just return. */
+ return false;
+ }
+
+ float imat[4][4];
+ invert_m4_m4(imat, obmat);
+
+ float timat[3][3]; /* transpose inverse matrix for normals */
+ transpose_m3_m4(timat, imat);
+
+ /* compute offset between init co and prev co in local space */
+ float init_co_local[3], curr_co_local[3];
+ float delta_local[3];
+ mul_v3_m4v3(init_co_local, imat, init_co);
+ mul_v3_m4v3(curr_co_local, imat, curr_co);
+ sub_v3_v3v3(delta_local, curr_co_local, init_co_local);
+
+ float dist_sq;
+ if (params->keep_on_same_target) {
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, init_co_local, nullptr, nullptr, nullptr, &dist_sq);
+ }
+ else {
+ /* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
+ * the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
+ * also complicate the code. Foregoing slight optimization for code clarity. */
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, curr_co_local, nullptr, nullptr, nullptr, &dist_sq);
+ }
+ if (*r_dist_sq <= dist_sq) {
+ return false;
+ }
+ *r_dist_sq = dist_sq;
+
+ /* scale to make `snap_face_nearest_steps` steps */
+ float step_scale_factor = 1.0f / max_ff(1.0f, (float)params->face_nearest_steps);
+ mul_v3_fl(delta_local, step_scale_factor);
+
+ float co_local[3];
+ float no_local[3];
+ int index;
+
+ copy_v3_v3(co_local, init_co_local);
+
+ for (int i = 0; i < params->face_nearest_steps; i++) {
+ add_v3_v3(co_local, delta_local);
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, co_local, co_local, no_local, &index, nullptr);
+ }
+
+ mul_v3_m4v3(r_loc, obmat, co_local);
+
+ if (r_no) {
+ mul_v3_m3v3(r_no, timat, no_local);
+ normalize_v3(r_no);
+ }
+
+ if (r_index) {
+ *r_index = index;
+ }
+
+ return true;
+}
+
+static bool nearest_world_mesh(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ const Mesh *me_eval,
+ const float (*obmat)[4],
+ bool use_hide,
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ BVHTreeFromMesh *treedata = snap_object_data_mesh_treedata_get(sctx, ob_eval, me_eval, use_hide);
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return false;
+ }
+
+ return nearest_world_tree(sctx,
+ params,
+ treedata->tree,
+ treedata->nearest_callback,
+ treedata,
+ obmat,
+ init_co,
+ curr_co,
+ r_dist_sq,
+ r_loc,
+ r_no,
+ r_index);
+}
+
+static bool nearest_world_editmesh(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ BMEditMesh *em,
+ const float (*obmat)[4],
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return false;
+ }
+
+ return nearest_world_tree(sctx,
+ params,
+ treedata->tree,
+ treedata->nearest_callback,
+ treedata,
+ obmat,
+ init_co,
+ curr_co,
+ r_dist_sq,
+ r_loc,
+ r_no,
+ r_index);
+}
+static void nearest_world_object_fn(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ bool is_object_active,
+ void *data)
+{
+ struct NearestWorldObjUserData *dt = static_cast<NearestWorldObjUserData *>(data);
+
+ bool retval = false;
+ switch (ob_eval->type) {
+ case OB_MESH: {
+ const eSnapEditType edit_mode_type = params->edit_mode_type;
+ bool use_hide = false;
+ const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval) {
+ retval = nearest_world_mesh(sctx,
+ params,
+ ob_eval,
+ me_eval,
+ obmat,
+ use_hide,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ else {
+ BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
+ BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)),
+ "Make sure there is only one pointer for looptris");
+ retval = nearest_world_editmesh(sctx,
+ params,
+ ob_eval,
+ em,
+ obmat,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ break;
+ }
+ case OB_CURVES_LEGACY:
+ case OB_SURF:
+ case OB_FONT:
+ if (!is_object_active) {
+ const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ if (me_eval) {
+ retval = nearest_world_mesh(sctx,
+ params,
+ ob_eval,
+ me_eval,
+ obmat,
+ false,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ }
+ break;
+ }
+
+ if (retval) {
+ if (dt->r_ob) {
+ *dt->r_ob = ob_eval;
+ }
+ if (dt->r_obmat) {
+ copy_m4_m4(dt->r_obmat, obmat);
+ }
+ dt->ret = true;
+ }
+}
+
+/**
+ * Main Nearest World Surface Function
+ * ===================================
+ *
+ * Walks through all objects in the scene to find the nearest location on target surface.
+ *
+ * \param sctx: Snap context to store data.
+ * \param params: Settings for snapping.
+ * \param init_co: Initial location of source point.
+ * \param prev_co: Current location of source point after transformation but before snapping.
+ *
+ * Output Args
+ * -----------
+ *
+ * \param r_loc: Location of nearest point on target surface.
+ * \param r_no: Normal of nearest point on target surface.
+ * \param r_index: Index of nearest polygon on target surface.
+ * \param r_ob: Nearest target object.
+ * \param r_obmat: Nearest target matrix (may not be #Object.obmat with dupli-instances).
+ */
+static bool nearestWorldObjects(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_loc /* NOLINT */,
+ float *r_no /* NOLINT */,
+ int *r_index /* NOLINT */,
+ Object **r_ob,
+ float r_obmat[4][4])
+{
+ NearestWorldObjUserData data = {};
+ data.init_co = init_co;
+ data.curr_co = curr_co;
+ data.r_loc = r_loc;
+ data.r_no = r_no;
+ data.r_index = r_index;
+ data.r_dist_sq = FLT_MAX;
+ data.r_ob = r_ob;
+ data.r_obmat = r_obmat;
+ data.ret = false;
+
+ iter_snap_objects(sctx, params, nearest_world_object_fn, &data);
+ return data.ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Snap Nearest utilities
* \{ */
@@ -1842,7 +2196,8 @@ static eSnapMode snapArmature(SnapObjectContext *sctx,
{
eSnapMode retval = SCE_SNAP_MODE_NONE;
- if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */
+ if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE_RAYCAST) {
+ /* Currently only edge and vert */
return retval;
}
@@ -2268,7 +2623,7 @@ static eSnapMode snapCamera(const SnapObjectContext *sctx,
if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) {
BKE_tracking_camera_get_reconstructed_interpolate(
- tracking, tracking_object, CFRA, reconstructed_camera_mat);
+ tracking, tracking_object, scene->r.cfra, reconstructed_camera_mat);
invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat);
}
@@ -2328,7 +2683,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
float r_no[3],
int *r_index)
{
- BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
+ BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST);
if (me_eval->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
@@ -2506,9 +2861,9 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx,
float r_no[3],
int *r_index)
{
- BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
+ BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST);
- if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
+ if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE_RAYCAST) == SCE_SNAP_MODE_VERTEX) {
if (em->bm->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
@@ -2811,7 +3166,7 @@ static void snap_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
*/
@@ -3014,6 +3369,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
const View3D *v3d,
const eSnapMode snap_to_flag,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3045,11 +3401,36 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
- if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) {
+ /* Note: if both face raycast and face nearest are enabled, first find result of nearest, then
+ * override with raycast. */
+ if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
+ has_hit = nearestWorldObjects(
+ sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat);
+
+ if (has_hit) {
+ retval = SCE_SNAP_MODE_FACE_NEAREST;
+
+ copy_v3_v3(r_loc, loc);
+ if (r_no) {
+ copy_v3_v3(r_no, no);
+ }
+ if (r_ob) {
+ *r_ob = ob_eval;
+ }
+ if (r_obmat) {
+ copy_m4_m4(r_obmat, obmat);
+ }
+ if (r_index) {
+ *r_index = index;
+ }
+ }
+ }
+
+ if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST || use_occlusion_test) {
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 SCE_SNAP_MODE_NONE;
+ return retval;
}
float dummy_ray_depth = BVH_RAYCAST_DIST_MAX;
@@ -3071,8 +3452,8 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
copy_v3_v3(r_face_nor, no);
}
- if ((snap_to_flag & SCE_SNAP_MODE_FACE)) {
- retval = SCE_SNAP_MODE_FACE;
+ if ((snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ retval = SCE_SNAP_MODE_FACE_RAYCAST;
copy_v3_v3(r_loc, loc);
if (r_no) {
@@ -3193,6 +3574,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
const View3D *v3d,
const eSnapMode snap_to,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3209,6 +3591,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
v3d,
snap_to,
params,
+ init_co,
mval,
prev_co,
dist_px,
@@ -3226,6 +3609,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
const View3D *v3d,
const eSnapMode snap_to,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3238,6 +3622,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
v3d,
snap_to,
params,
+ init_co,
mval,
prev_co,
dist_px,
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index 7972410ad67..06d9bb05206 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -28,6 +28,7 @@
#include "SEQ_time.h"
#include "transform.h"
+#include "transform_convert.h"
#include "transform_snap.h"
typedef struct TransSeqSnapData {
@@ -191,7 +192,7 @@ static void seq_snap_target_points_build(Scene *scene,
int i = 0;
if (snap_mode & SEQ_SNAP_TO_CURRENT_FRAME) {
- snap_data->target_snap_points[i] = CFRA;
+ snap_data->target_snap_points[i] = scene->r.cfra;
i++;
}
@@ -244,7 +245,7 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t)
TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
{
- if (t->data_type == TC_SEQ_IMAGE_DATA) {
+ if (t->data_type == &TransConvertType_SequencerImage) {
return NULL;
}
@@ -375,7 +376,7 @@ bool ED_transform_snap_sequencer_to_closest_strip_calc(Scene *scene,
t.scene = scene;
t.region = region;
t.values[0] = 0;
- t.data_type = TC_SEQ_DATA;
+ t.data_type = &TransConvertType_Sequencer;
t.tsnap.mode = SEQ_tool_settings_snap_mode_get(scene);
*r_snap_distance = transform_snap_sequencer_to_closest_strip_ex(&t, frame_1, frame_2);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 5c2a3374aa1..cdfe40c7d35 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_abstract_view.hh
../include/UI_grid_view.hh
../include/UI_icons.h
../include/UI_interface.h
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
index cf6f26652a7..fc5fb9f9c28 100644
--- a/source/blender/editors/util/ed_util_imbuf.c
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -291,7 +291,7 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e
Scene *scene = CTX_data_scene(C);
SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
ARegion *region = CTX_wm_region(C);
- ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, CFRA, 0, NULL);
+ ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, scene->r.cfra, 0, NULL);
ImageSampleInfo *info = op->customdata;
float fx, fy;
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index be6ac6e13e6..60cbc2a2df6 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -311,6 +311,7 @@ static bool editstr_is_simple_numinput(const char ascii)
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
const char *utf8_buf = NULL;
+ const char event_ascii = WM_event_utf8_to_ascii(event);
char ascii[2] = {'\0', '\0'};
bool updated = false;
short idx = n->idx, idx_max = n->idx_max;
@@ -321,8 +322,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
if (U.flag & USER_FLAG_NUMINPUT_ADVANCED)
#endif
{
- if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') &&
- strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) {
+ if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event_ascii != '\0') &&
+ strchr("01234567890@%^&*-+/{}()[]<>.|", event_ascii)) {
if (!(n->flag & NUM_EDIT_FULL)) {
n->flag |= NUM_EDITED;
n->flag |= NUM_EDIT_FULL;
@@ -333,7 +334,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
#ifdef USE_FAKE_EDIT
/* XXX Hack around keyboards without direct access to '=' nor '*'... */
- if (ELEM(event->ascii, '=', '*')) {
+ if (ELEM(event_ascii, '=', '*')) {
if (!(n->flag & NUM_EDIT_FULL)) {
n->flag |= NUM_EDIT_FULL;
n->val_flag[idx] |= NUM_EDITED;
@@ -357,7 +358,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
else {
/* might be a char too... */
utf8_buf = event->utf8_buf;
- ascii[0] = event->ascii;
+ ascii[0] = event_ascii;
}
break;
case EVT_BACKSPACEKEY:
@@ -523,9 +524,9 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
break;
}
- if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
+ if (!updated && !utf8_buf && event->utf8_buf[0]) {
utf8_buf = event->utf8_buf;
- ascii[0] = event->ascii;
+ ascii[0] = event_ascii;
}
/* Up to this point, if we have a ctrl modifier, skip.
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c
index e1752ae5a29..9a31fd6469d 100644
--- a/source/blender/editors/uvedit/uvedit_islands.c
+++ b/source/blender/editors/uvedit/uvedit_islands.c
@@ -256,16 +256,12 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const floa
* Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
*/
static float uv_nearest_image_tile_distance(const Image *image,
- float coords[2],
+ const float coords[2],
float nearest_tile_co[2])
{
- int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords);
- if (nearest_image_tile_index == -1) {
- nearest_image_tile_index = 1001;
+ if (BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co) == -1) {
+ zero_v2(nearest_tile_co);
}
-
- nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10;
- nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10;
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 0b5d6592426..74a9989f550 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -372,6 +372,198 @@ typedef enum eUVWeldAlign {
UV_WELD,
} eUVWeldAlign;
+static bool uvedit_uv_align_weld(Scene *scene,
+ BMesh *bm,
+ const eUVWeldAlign tool,
+ const float cent[2])
+{
+ bool changed = false;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, efa, 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);
+ if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
+ if (luv->uv[0] != cent[0]) {
+ luv->uv[0] = cent[0];
+ changed = true;
+ }
+ }
+ if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
+ if (luv->uv[1] != cent[1]) {
+ luv->uv[1] = cent[1];
+ changed = true;
+ }
+ }
+ }
+ }
+ return changed;
+}
+
+/** Bitwise-or together, then choose #MLoopUV with highest value. */
+typedef enum eUVEndPointPrecedence {
+ UVEP_INVALID = 0,
+ UVEP_SELECTED = (1 << 0),
+ UVEP_PINNED = (1 << 1), /* i.e. Pinned verts are preferred to selected. */
+} eUVEndPointPrecedence;
+
+static eUVEndPointPrecedence uvedit_line_update_get_precedence(const MLoopUV *luv)
+{
+ eUVEndPointPrecedence precedence = UVEP_SELECTED;
+ if (luv->flag & MLOOPUV_PINNED) {
+ precedence |= UVEP_PINNED;
+ }
+ return precedence;
+}
+
+/**
+ * Helper to find two endpoints (`a` and `b`) which have higher precedence, and are far apart.
+ * Note that is only a heuristic and won't always find the best two endpoints.
+ */
+static bool uvedit_line_update_endpoint(const MLoopUV *luv,
+ float uv_a[2],
+ eUVEndPointPrecedence *prec_a,
+ float uv_b[2],
+ eUVEndPointPrecedence *prec_b)
+{
+ eUVEndPointPrecedence flags = uvedit_line_update_get_precedence(luv);
+
+ float len_sq_a = len_squared_v2v2(uv_a, luv->uv);
+ float len_sq_b = len_squared_v2v2(uv_b, luv->uv);
+
+ /* Caching the value of `len_sq_ab` is unlikely to be faster than recalculating.
+ * Profile before optimizing. */
+ float len_sq_ab = len_squared_v2v2(uv_a, uv_b);
+
+ if ((*prec_a < flags && 0.0f < len_sq_b) || (*prec_a == flags && len_sq_ab < len_sq_b)) {
+ *prec_a = flags;
+ copy_v2_v2(uv_a, luv->uv);
+ return true;
+ }
+
+ if ((*prec_b < flags && 0.0f < len_sq_a) || (*prec_b == flags && len_sq_ab < len_sq_a)) {
+ *prec_b = flags;
+ copy_v2_v2(uv_b, luv->uv);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Find two end extreme points to specify a line, then straighten `len` elements
+ * by moving UVs on the X-axis, Y-axis, or the closest point on the line segment.
+ */
+static bool uvedit_uv_straighten_elements(const UvElement *element,
+ const int len,
+ const int cd_loop_uv_offset,
+ const eUVWeldAlign tool)
+{
+ float uv_start[2];
+ float uv_end[2];
+ eUVEndPointPrecedence prec_start = UVEP_INVALID;
+ eUVEndPointPrecedence prec_end = UVEP_INVALID;
+
+ /* Find start and end of line. */
+ for (int i = 0; i < 10; i++) { /* Heuristic to prevent infinite loop. */
+ bool update = false;
+ for (int j = 0; j < len; j++) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(element[j].l, cd_loop_uv_offset);
+ update |= uvedit_line_update_endpoint(luv, uv_start, &prec_start, uv_end, &prec_end);
+ }
+ if (!update) {
+ break;
+ }
+ }
+
+ if (prec_start == UVEP_INVALID || prec_end == UVEP_INVALID) {
+ return false; /* Unable to find two endpoints. */
+ }
+
+ float a = 0.0f; /* Similar to "slope". */
+ eUVWeldAlign tool_local = tool;
+
+ if (tool_local == UV_STRAIGHTEN_X) {
+ if (uv_start[1] == uv_end[1]) {
+ /* Caution, different behavior outside line segment. */
+ tool_local = UV_STRAIGHTEN;
+ }
+ else {
+ a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
+ }
+ }
+ else if (tool_local == UV_STRAIGHTEN_Y) {
+ if (uv_start[0] == uv_end[0]) {
+ /* Caution, different behavior outside line segment. */
+ tool_local = UV_STRAIGHTEN;
+ }
+ else {
+ a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
+ }
+ }
+
+ bool changed = false;
+ for (int j = 0; j < len; j++) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(element[j].l, cd_loop_uv_offset);
+ /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
+ * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
+ * Maybe this should be a BLI func? Or is it already existing?
+ * Could use interp_v2_v2v2, but not sure it's worth it here. */
+ if (tool_local == UV_STRAIGHTEN_X) {
+ luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
+ }
+ else if (tool_local == UV_STRAIGHTEN_Y) {
+ luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
+ }
+ else {
+ closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
+ }
+ changed = true; /* TODO: Did the UV actually move? */
+ }
+ return changed;
+}
+
+/**
+ * Group selected UVs into islands, then apply uvedit_uv_straighten_elements to each island.
+ */
+static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ if (cd_loop_uv_offset == -1) {
+ return false;
+ }
+
+ UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true);
+ if (element_map == NULL) {
+ return false;
+ }
+
+ bool changed = false;
+
+ /* Loop backwards to simplify logic. */
+ int j1 = element_map->totalUVs;
+ for (int i = element_map->totalIslands - 1; i >= 0; --i) {
+ int j0 = element_map->islandIndices[i];
+ changed |= uvedit_uv_straighten_elements(
+ element_map->buf + j0, j1 - j0, cd_loop_uv_offset, tool);
+ j1 = j0;
+ }
+
+ BM_uv_element_map_free(element_map);
+ return changed;
+}
+
static void uv_weld_align(bContext *C, eUVWeldAlign tool)
{
Scene *scene = CTX_data_scene(C);
@@ -429,194 +621,12 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool)
continue;
}
- const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
-
- if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
- BMIter iter, liter;
- BMFace *efa;
- BMLoop *l;
-
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[0] = cent[0];
- changed = true;
- }
- }
- }
- }
-
- if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
- BMIter iter, liter;
- BMFace *efa;
- BMLoop *l;
-
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[1] = cent[1];
- changed = true;
- }
- }
- }
+ if (ELEM(tool, UV_ALIGN_AUTO, UV_ALIGN_X, UV_ALIGN_Y, UV_WELD)) {
+ changed |= uvedit_uv_align_weld(scene, em->bm, tool, cent);
}
if (ELEM(tool, UV_STRAIGHTEN, UV_STRAIGHTEN_X, UV_STRAIGHTEN_Y)) {
- BMEdge *eed;
- BMLoop *l;
- BMVert *eve;
- BMVert *eve_start;
- BMIter iter, liter, eiter;
-
- /* clear tag */
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
-
- /* tag verts with a selected UV */
- BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
- if (!uvedit_face_visible_test(scene, l->f)) {
- continue;
- }
-
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- BM_elem_flag_enable(eve, BM_ELEM_TAG);
- break;
- }
- }
- }
-
- /* flush vertex tags to edges */
- BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
- BM_elem_flag_set(
- eed,
- BM_ELEM_TAG,
- (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) && BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
- }
-
- /* find a vertex with only one tagged edge */
- eve_start = NULL;
- BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- int tot_eed_tag = 0;
- BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
- if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
- tot_eed_tag++;
- }
- }
-
- if (tot_eed_tag == 1) {
- eve_start = eve;
- break;
- }
- }
-
- if (eve_start) {
- BMVert **eve_line = NULL;
- BMVert *eve_next = NULL;
- BLI_array_declare(eve_line);
- int i;
-
- eve = eve_start;
-
- /* walk over edges, building an array of verts in a line */
- while (eve) {
- BLI_array_append(eve_line, eve);
- /* don't touch again */
- BM_elem_flag_disable(eve, BM_ELEM_TAG);
-
- eve_next = NULL;
-
- /* find next eve */
- BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
- if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
- BMVert *eve_other = BM_edge_other_vert(eed, eve);
- if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
- /* this is a tagged vert we didn't walk over yet, step onto it */
- eve_next = eve_other;
- break;
- }
- }
- }
-
- eve = eve_next;
- }
-
- /* now we have all verts, make into a line */
- if (BLI_array_len(eve_line) > 2) {
-
- /* we know the returns from these must be valid */
- const float *uv_start = uvedit_first_selected_uv_from_vertex(
- scene, eve_line[0], cd_loop_uv_offset);
- const float *uv_end = uvedit_first_selected_uv_from_vertex(
- scene, eve_line[BLI_array_len(eve_line) - 1], cd_loop_uv_offset);
- /* For UV_STRAIGHTEN_X & UV_STRAIGHTEN_Y modes */
- float a = 0.0f;
- eUVWeldAlign tool_local = tool;
-
- if (tool_local == UV_STRAIGHTEN_X) {
- if (uv_start[1] == uv_end[1]) {
- tool_local = UV_STRAIGHTEN;
- }
- else {
- a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
- }
- }
- else if (tool_local == UV_STRAIGHTEN_Y) {
- if (uv_start[0] == uv_end[0]) {
- tool_local = UV_STRAIGHTEN;
- }
- else {
- a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
- }
- }
-
- /* go over all verts except for endpoints */
- for (i = 0; i < BLI_array_len(eve_line); i++) {
- BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
- if (!uvedit_face_visible_test(scene, l->f)) {
- continue;
- }
-
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
- * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
- * Maybe this should be a BLI func? Or is it already existing?
- * Could use interp_v2_v2v2, but not sure it's worth it here. */
- if (tool_local == UV_STRAIGHTEN_X) {
- luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
- }
- else if (tool_local == UV_STRAIGHTEN_Y) {
- luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
- }
- else {
- closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
- }
- changed = true;
- }
- }
- }
- }
- else {
- /* error - not a line, needs 3+ points. */
- }
-
- if (eve_line) {
- MEM_freeN(eve_line);
- }
- }
- else {
- /* error - can't find an endpoint. */
- }
+ changed |= uvedit_uv_straighten(scene, em->bm, tool);
}
if (changed) {
@@ -1026,6 +1036,12 @@ static bool uv_snap_cursor_to_selection(Scene *scene,
return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around);
}
+static void uv_snap_cursor_to_origin(float uvco[2])
+{
+ uvco[0] = 0;
+ uvco[1] = 0;
+}
+
static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -1048,6 +1064,10 @@ static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
MEM_freeN(objects);
break;
}
+ case 2:
+ uv_snap_cursor_to_origin(sima->cursor);
+ changed = true;
+ break;
}
if (!changed) {
@@ -1064,6 +1084,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot)
static const EnumPropertyItem target_items[] = {
{0, "PIXELS", 0, "Pixels", ""},
{1, "SELECTED", 0, "Selected", ""},
+ {2, "ORIGIN", 0, "Origin", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
index 7c6960a634a..31a1b60167e 100644
--- a/source/blender/editors/uvedit/uvedit_path.c
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -56,75 +56,6 @@
#include "bmesh_tools.h"
/* -------------------------------------------------------------------- */
-/** \name Local Utilities
- * \{ */
-
-/**
- * Support edge-path using vert-path calculation code.
- *
- * Cheat! Pick 2 closest loops and do vertex path,
- * in practices only obscure/contrived cases will make give noticeably worse behavior.
- *
- * While the code below is a bit awkward, it's significantly less overhead than
- * adding full edge selection which is nearly the same as vertex path in the case of UV's.
- *
- * \param use_nearest: When false use the post distant pair of loops,
- * use when filling a region as we want both verts from each edge to be included in the region.
- */
-static void bm_loop_calc_vert_pair_from_edge_pair(const bool use_nearest,
- const int cd_loop_uv_offset,
- const float aspect_y,
- BMElem **ele_src_p,
- BMElem **ele_dst_p,
- BMElem **r_ele_dst_final)
-{
- BMLoop *l_src = (BMLoop *)*ele_src_p;
- BMLoop *l_dst = (BMLoop *)*ele_dst_p;
-
- const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
- const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset);
- const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset);
- const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset);
-
- const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y};
- const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y};
- const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y};
- const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y};
-
- struct {
- int src_index;
- int dst_index;
- float len_sq;
- } tests[4] = {
- {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)},
- {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)},
- {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)},
- {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)},
- };
- int i_best = 0;
- for (int i = 1; i < ARRAY_SIZE(tests); i++) {
- if (use_nearest) {
- if (tests[i].len_sq < tests[i_best].len_sq) {
- i_best = i;
- }
- }
- else {
- if (tests[i].len_sq > tests[i_best].len_sq) {
- i_best = i;
- }
- }
- }
-
- *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src);
- *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst);
-
- /* Ensure the edge is selected, not just the vertices up until we hit it. */
- *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Path Select Struct & Properties
* \{ */
@@ -180,12 +111,12 @@ static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *
* \{ */
/* callbacks */
-static bool looptag_filter_cb(BMLoop *l, void *user_data_v)
+static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
{
struct UserData_UV *user_data = user_data_v;
return uvedit_face_visible_test(user_data->scene, l->f);
}
-static bool looptag_test_cb(BMLoop *l, void *user_data_v)
+static bool verttag_test_cb(BMLoop *l, void *user_data_v)
{
/* All connected loops are selected or we return false. */
struct UserData_UV *user_data = user_data_v;
@@ -195,7 +126,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v)
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
- if (looptag_filter_cb(l_iter, user_data)) {
+ if (verttag_filter_cb(l_iter, user_data)) {
const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
if (equals_v2v2(luv->uv, luv_iter->uv)) {
if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
@@ -206,7 +137,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v)
}
return true;
}
-static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v)
+static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
{
struct UserData_UV *user_data = user_data_v;
const Scene *scene = user_data->scene;
@@ -216,7 +147,7 @@ static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v)
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
- if (looptag_filter_cb(l_iter, user_data)) {
+ if (verttag_filter_cb(l_iter, user_data)) {
MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
if (equals_v2v2(luv->uv, luv_iter->uv)) {
uvedit_uv_select_set(scene, em, l_iter, val, false, cd_loop_uv_offset);
@@ -233,42 +164,10 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
const float aspect_y,
const int cd_loop_uv_offset)
{
- const char uv_selectmode = ED_uvedit_select_mode_get(scene);
- /* TODO(@sidd017): Implement logic to calculate shortest path for UV edges, since we now support
- * proper edge selection for UVs (D12028).
- * Till then continue using vertex path to fake shortest path calculation for edges. */
- const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
int flush = 0;
- /* Variables to use when `use_fake_edge_select` is set. */
- struct {
- BMLoop *l_dst_activate;
- BMLoop *l_dst_add_to_path;
- } fake_edge_select = {NULL};
-
- if (use_fake_edge_select) {
- fake_edge_select.l_dst_activate = l_dst;
-
- /* Use most distant when doing region selection.
- * without this we get dangling edges outside the region. */
- bool use_neaerst = (op_params->use_fill == false);
- BMElem *ele_src = (BMElem *)l_src;
- BMElem *ele_dst = (BMElem *)l_dst;
- BMElem *ele_dst_final = NULL;
- bm_loop_calc_vert_pair_from_edge_pair(
- use_neaerst, cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final);
-
- if (op_params->use_fill == false) {
- /* Always activate the item under the cursor. */
- fake_edge_select.l_dst_add_to_path = (BMLoop *)ele_dst_final;
- }
-
- l_src = (BMLoop *)ele_src;
- l_dst = (BMLoop *)ele_dst;
- }
-
struct UserData_UV user_data = {
.scene = scene,
.em = em,
@@ -291,33 +190,23 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
(BMElem *)l_src,
(BMElem *)l_dst,
params.cd_loop_uv_offset,
- looptag_filter_cb,
+ verttag_filter_cb,
&user_data);
}
else {
is_path_ordered = true;
- path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, looptag_filter_cb, &user_data);
+ path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, verttag_filter_cb, &user_data);
}
}
BMLoop *l_dst_last = l_dst;
if (path) {
- if (use_fake_edge_select) {
- if ((fake_edge_select.l_dst_add_to_path != NULL) &&
- (BLI_linklist_index(path, fake_edge_select.l_dst_add_to_path) == -1)) {
- /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */
- LinkNode *path_last = BLI_linklist_find_last(path);
- BLI_linklist_insert_after(&path_last, fake_edge_select.l_dst_add_to_path);
- BLI_assert(BLI_linklist_find_last(path)->link == fake_edge_select.l_dst_add_to_path);
- }
- }
-
/* toggle the flag */
bool all_set = true;
LinkNode *node = path;
do {
- if (!looptag_test_cb((BMLoop *)node->link, &user_data)) {
+ if (!verttag_test_cb((BMLoop *)node->link, &user_data)) {
all_set = false;
break;
}
@@ -328,7 +217,7 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
do {
if ((is_path_ordered == false) ||
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
- looptag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ verttag_set_cb((BMLoop *)node->link, !all_set, &user_data);
if (is_path_ordered) {
l_dst_last = node->link;
}
@@ -339,23 +228,133 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
flush = all_set ? -1 : 1;
}
else {
- const bool is_act = !looptag_test_cb(l_dst, &user_data);
- looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ const bool is_act = !verttag_test_cb(l_dst, &user_data);
+ verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
}
if (op_params->track_active) {
- /* Fake edge selection. */
- if (use_fake_edge_select) {
- BMLoop *l_dst_activate = fake_edge_select.l_dst_activate;
- /* TODO(campbell): Search for an active loop attached to 'l_dst'.
- * when `BLI_linklist_index(path, l_dst_activate) == -1`
- * In practice this rarely happens though. */
- ED_uvedit_active_edge_loop_set(bm, l_dst_activate);
+ ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ }
+ return flush;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name UV Edge Path
+ * \{ */
+
+/* callbacks */
+static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, l->f);
+}
+static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
+{
+ /* All connected loops (UV) are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) {
+ if (edgetag_filter_cb(l_iter, user_data)) {
+ if (BM_loop_uv_share_edge_check(l, l_iter, cd_loop_uv_offset)) {
+ if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ BMEditMesh *em = user_data->em;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ uvedit_edge_select_set_with_sticky(scene, em, l, val, false, cd_loop_uv_offset);
+}
+
+static int mouse_mesh_uv_shortest_path_edge(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ int flush = 0;
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .em = em,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ LinkNode *path = NULL;
+ bool is_path_ordered = false;
+
+ if (l_src != l_dst) {
+ if (op_params->use_fill) {
+ path = BM_mesh_calc_path_uv_region_edge(bm,
+ (BMElem *)l_src,
+ (BMElem *)l_dst,
+ params.cd_loop_uv_offset,
+ edgetag_filter_cb,
+ &user_data);
}
else {
- ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ is_path_ordered = true;
+ path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, &params, edgetag_filter_cb, &user_data);
}
}
+
+ BMLoop *l_dst_last = l_dst;
+
+ if (path) {
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ l_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ flush = all_set ? -1 : 1;
+ }
+ else {
+ const bool is_act = !edgetag_test_cb(l_dst, &user_data);
+ edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ ED_uvedit_active_edge_loop_set(bm, l_dst_last);
+ }
return flush;
}
@@ -514,13 +513,24 @@ static bool uv_shortest_path_pick_ex(Scene *scene,
ok = true;
}
else if (ele_src->head.htype == BM_LOOP) {
- flush = mouse_mesh_uv_shortest_path_vert(scene,
- obedit,
- op_params,
- (BMLoop *)ele_src,
- (BMLoop *)ele_dst,
- aspect_y,
- cd_loop_uv_offset);
+ if (uv_selectmode & UV_SELECT_EDGE) {
+ flush = mouse_mesh_uv_shortest_path_edge(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ }
+ else {
+ flush = mouse_mesh_uv_shortest_path_vert(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ }
ok = true;
}
@@ -529,24 +539,9 @@ static bool uv_shortest_path_pick_ex(Scene *scene,
const bool select = (flush == 1);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (ts->uv_flag & UV_SYNC_SELECTION) {
- if (uv_selectmode & UV_SELECT_EDGE) {
- /* Special case as we don't use true edge selection,
- * flush the selection from the vertices. */
- BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL);
- }
ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
}
else {
- if (uv_selectmode & UV_SELECT_EDGE) {
- /* TODO(@sidd017): Remove this case when adding proper uv edge support for this operator.
- * In the meantime, this case helps ensures proper UV selection states for edge mode. */
- if (select) {
- uvedit_select_flush(scene, em);
- }
- else {
- uvedit_deselect_flush(scene, em);
- }
- }
ED_uvedit_selectmode_flush(scene, em);
}
}
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index db834f6a0fd..d59dcb4f4ed 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -3582,6 +3582,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
}
}
else if (use_edge && !pinned) {
+ bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -3596,11 +3597,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
+ do_second_pass = false;
}
l_prev = l;
luv_prev = luv;
}
}
+ /* Do a second pass if no complete edges could be selected.
+ * This matches wire-frame edit-mesh selection in the 3D view. */
+ if (do_second_pass) {
+ /* Second pass to check if edges partially overlap with the selection area (box). */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
+ MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ l_prev = l;
+ luv_prev = luv;
+ }
+ }
+ }
}
else {
/* other selection modes */
@@ -3920,6 +3945,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region,
return false;
}
+static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region,
+ const rcti *clip_rect,
+ const int mcoords[][2],
+ const int mcoords_len,
+ const float co_test_a[2],
+ const float co_test_b[2])
+{
+ int co_screen_a[2], co_screen_b[2];
+ if (UI_view2d_view_to_region_segment_clip(
+ &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
+ BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
+ BLI_lasso_is_edge_inside(
+ mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) {
+ return true;
+ }
+ return false;
+}
+
static bool do_lasso_select_mesh_uv(bContext *C,
const int mcoords[][2],
const int mcoords_len,
@@ -3988,6 +4031,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
}
}
else if (use_edge) {
+ bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -4004,12 +4048,37 @@ static bool do_lasso_select_mesh_uv(bContext *C,
region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
+ do_second_pass = false;
changed = true;
}
l_prev = l;
luv_prev = luv;
}
}
+ /* Do a second pass if no complete edges could be selected.
+ * This matches wire-frame edit-mesh selection in the 3D view. */
+ if (do_second_pass) {
+ /* Second pass to check if edges partially overlap with the selection area (lasso). */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
+ MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (do_lasso_select_mesh_uv_is_edge_inside(
+ region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ l_prev = l;
+ luv_prev = luv;
+ }
+ }
+ }
}
else { /* Vert Selection. */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
@@ -5154,7 +5223,7 @@ static void uv_isolate_selected_islands(const Scene *scene,
BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0);
BMFace *efa;
BMIter iter, liter;
- UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true);
+ UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true);
if (elementmap == NULL) {
return;
}
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 55e44607f6f..579674930a6 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -1902,15 +1902,7 @@ static StitchState *stitch_init(bContext *C,
* for stitch this isn't useful behavior, see T86924. */
const int selectmode_orig = scene->toolsettings->selectmode;
scene->toolsettings->selectmode = SCE_SELECT_VERTEX;
-
- /* in uv synch selection, all uv's are visible */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true);
- }
- else {
- state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true);
- }
-
+ state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true);
scene->toolsettings->selectmode = selectmode_orig;
if (!state->element_map) {
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index ae81aaffeb2..2c7ad012dd2 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
/** \name Average UV Islands Scale Operator
* \{ */
-static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
+static int average_islands_scale_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ /* RNA props */
+ const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv");
+ const bool shear = RNA_boolean_get(op->ptr, "shear");
+
ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
- GEO_uv_parametrizer_average(handle, false);
+ GEO_uv_parametrizer_average(handle, false, scale_uv, shear);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot)
/* api callbacks */
ot->exec = average_islands_scale_exec;
ot->poll = ED_operator_uvedit;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently");
+ RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands");
}
/** \} */
@@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene,
result_info ? &result_info->count_failed : NULL);
GEO_uv_parametrizer_lscm_end(handle);
- GEO_uv_parametrizer_average(handle, true);
+ GEO_uv_parametrizer_average(handle, true, false, false);
GEO_uv_parametrizer_flush(handle);
@@ -1907,7 +1915,7 @@ static int unwrap_exec(bContext *C, wmOperator *op)
UnwrapOptions options = {
.topology_from_uvs = false,
- .only_selected_faces = false,
+ .only_selected_faces = true,
.only_selected_uvs = false,
.fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
.correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"),
@@ -1918,7 +1926,6 @@ static int unwrap_exec(bContext *C, wmOperator *op)
if (CTX_wm_space_image(C)) {
/* Inside the UV Editor, only unwrap selected UVs. */
options.only_selected_uvs = true;
- options.only_selected_faces = true;
options.pin_unselected = true;
}